Merge from master.

This commit is contained in:
dg
2021-12-05 16:37:04 +00:00
71 changed files with 1933 additions and 548 deletions

View File

@@ -52,7 +52,7 @@ CFLAGS += -Ilib -Idep/fmt -Iarch
export OBJDIR = .obj
all: .obj/build.ninja
@ninja -f .obj/build.ninja
@ninja -f .obj/build.ninja -k 0
@if command -v cscope > /dev/null; then cscope -bRq; fi
clean:

View File

@@ -8,7 +8,6 @@
#include "bytes.h"
#include "fmt/format.h"
#include "lib/decoders/decoders.pb.h"
#include "lib/data.pb.h"
#include <string.h>
#include <algorithm>
@@ -30,7 +29,7 @@ public:
_config(config.amiga())
{}
RecordType advanceToNextRecord()
RecordType advanceToNextRecord() override
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
@@ -38,7 +37,7 @@ public:
return SECTOR_RECORD;
}
void decodeSectorRecord()
void decodeSectorRecord() override
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))

View File

@@ -144,7 +144,7 @@ public:
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
int logicalTrack;
if (physicalSide != 0)

View File

@@ -231,7 +231,7 @@ public:
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image)
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
/* The format ID Character # 1 and # 2 are in the .d64 image only present
* in track 18 sector zero which contains the BAM info in byte 162 and 163.

View File

@@ -98,7 +98,7 @@ public:
_config(config.ibm())
{}
RecordType advanceToNextRecord()
RecordType advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -108,6 +108,7 @@ public:
_currentHeaderLength = (matcher == &MFM_PATTERN) ? 3 : 0;
Fluxmap::Position here = tell();
resetFluxDecoder();
if (_currentHeaderLength > 0)
readRawBits(_currentHeaderLength*16);
auto idbits = readRawBits(16);
@@ -129,7 +130,7 @@ public:
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
void decodeSectorRecord() override
{
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16);
@@ -155,7 +156,7 @@ public:
_sector->logicalTrack = _sector->physicalCylinder;
}
void decodeDataRecord()
void decodeDataRecord() override
{
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16);

View File

@@ -74,7 +74,7 @@ public:
{}
/* Search for FM or MFM sector record */
RecordType advanceToNextRecord()
RecordType advanceToNextRecord() override
{
nanoseconds_t now = _fmr->tell().ns();
@@ -140,7 +140,7 @@ public:
return UNKNOWN_RECORD;
}
void decodeSectorRecord()
void decodeSectorRecord() override
{
unsigned recordSize, payloadSize, headerSize;

View File

@@ -73,7 +73,7 @@ public:
void decodeSectorRecord()
{
/* Skip the sync marker bit. */
readRawBits(23);
readRawBits(22);
/* Read header. */
@@ -96,7 +96,7 @@ public:
void decodeDataRecord()
{
/* Skip the sync marker bit. */
readRawBits(23);
readRawBits(22);
/* Read data. */

200
arch/victor9k/encoder.cc Normal file
View File

@@ -0,0 +1,200 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "victor9k.h"
#include "crc.h"
#include "sector.h"
#include "writer.h"
#include "image.h"
#include "fmt/format.h"
#include "arch/victor9k/victor9k.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <ctype.h>
#include "bytes.h"
static bool lastBit;
static void write_zero_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 0;
}
}
static void write_one_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 1;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
ByteReader br(bytes);
BitReader bitr(br);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static int encode_data_gcr(uint8_t data)
{
switch (data & 0x0f)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
for (uint8_t b : bytes)
{
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
write_bits(bits, cursor, encode_data_gcr(b), 5);
}
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor,
const Victor9kEncoderProto::TrackdataProto& trackdata,
const Sector& sector)
{
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide<<7);
uint8_t encodedSector = sector.logicalSector;
write_bytes(bits, cursor, Bytes {
encodedTrack,
encodedSector,
(uint8_t)(encodedTrack + encodedSector),
});
write_zero_bits(bits, cursor, trackdata.post_header_gap_bits());
write_one_bits(bits, cursor, trackdata.pre_data_sync_bits());
write_bits(bits, cursor, VICTOR9K_DATA_RECORD, 10);
write_bytes(bits, cursor, sector.data);
Bytes checksum(2);
checksum.writer().write_le16(sumBytes(sector.data));
write_bytes(bits, cursor, checksum);
write_zero_bits(bits, cursor, trackdata.post_data_gap_bits());
}
class Victor9kEncoder : public AbstractEncoder
{
public:
Victor9kEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.victor9k())
{}
private:
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_min_cylinder() && (cylinder < f.min_cylinder()))
continue;
if (f.has_max_cylinder() && (cylinder > f.max_cylinder()))
continue;
if (f.has_head() && (head != f.head()))
continue;
trackdata.MergeFrom(f);
}
}
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
for (int i = 0; i < trackdata.sector_range().sector_count(); i++)
{
int sectorId = trackdata.sector_range().start_sector() + i;
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
unsigned bitsPerRevolution = trackdata.original_data_rate_khz() * trackdata.original_period_ms();
std::vector<bool> bits(bitsPerRevolution);
double clockRateUs = 166666.0 / bitsPerRevolution;
unsigned cursor = 0;
fillBitmapTo(bits, cursor, trackdata.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, trackdata, *sector);
if (cursor >= bits.size())
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
private:
const Victor9kEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
}
// vim: sw=4 ts=4 et

View File

@@ -1,11 +1,22 @@
#ifndef VICTOR9K_H
#define VICTOR9K_H
#define VICTOR9K_SECTOR_RECORD 0xfffffeab
#define VICTOR9K_DATA_RECORD 0xfffffea4
class AbstractEncoder;
class AbstractDecoder;
class EncoderProto;
class DecoderProto;
/* ... 1101 0101 0111
* ^^ ^^^^ ^^^^ ten bit IO byte */
#define VICTOR9K_SECTOR_RECORD 0xfffffd57
/* ... 1101 0100 1001
* ^^ ^^^^ ^^^^ ten bit IO byte */
#define VICTOR9K_DATA_RECORD 0xfffffd49
#define VICTOR9K_SECTOR_LENGTH 512
extern std::unique_ptr<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config);
#endif

View File

@@ -1,4 +1,32 @@
syntax = "proto2";
import "lib/common.proto";
message Victor9kDecoderProto {}
// NEXT: 12
message Victor9kEncoderProto {
message TrackdataProto {
message SectorRangeProto {
optional int32 start_sector = 1 [(help) = "first sector ID on track"];
optional int32 sector_count = 2 [(help) = "number of sectors on track"];
}
optional int32 min_cylinder = 1 [(help) = "minimum cylinder this format applies to"];
optional int32 max_cylinder = 2 [(help) = "maximum cylinder this format applies to"];
optional int32 head = 3 [(help) = "which head this format applies to"];
optional double original_period_ms = 4 [(help) = "original rotational period of this cylinder"];
optional double original_data_rate_khz = 5 [(help) = "original data rate of this cylinder"];
optional double post_index_gap_us = 6 [(help) = "size of post-index gap"];
optional int32 pre_header_sync_bits = 10 [(help) = "number of sync bits before the sector header"];
optional int32 pre_data_sync_bits = 8 [(help) = "number of sync bits before the sector data"];
optional int32 post_data_gap_bits = 9 [(help) = "size of gap between data and the next header"];
optional int32 post_header_gap_bits = 11 [(help) = "size of gap between header and the data"];
optional SectorRangeProto sector_range = 7 [(help) = "write these sectors on each track"];
}
repeated TrackdataProto trackdata = 1;
}

View File

@@ -86,6 +86,10 @@ and there's too many configuration options to usefully list. Use `fluxengine
write` to list all formats, and try `fluxengine write ibm1440 --config` to see
a sample configuration.
Some image formats, such as DIM, specify the image format, For these you can
specify the `ibm` format and FluxEngine will automatically determine the
correct format to use.
Mixed-format disks
------------------

View File

@@ -6,9 +6,28 @@ which used a disk format very reminiscent of the Commodore format; not a
coincidence, as Chuck Peddle designed them both. They're 80-track, 512-byte
sector GCR disks, with a variable-speed drive and a varying number of sectors
per track --- from 19 to 12. Disks can be double-sided, meaning that they can
store 1224kB per disk, which was almost unheard of back then.
store 1224kB per disk, which was almost unheard of back then. Because the way
that the tracks on head 1 are offset from head 0 (this happens with all disks),
the speed zone allocation on head 1 differ from head 0...
FluxEngine reads these.
| Zone | Head 0 tracks | Head 1 tracks | Sectors | Original period (ms) |
|:----:|:-------------:|:-------------:|:-------:|:--------------------:|
| 0 | 0-3 | | 19 | 237.9 |
| 1 | 4-15 | 0-7 | 18 | 224.5 |
| 2 | 16-26 | 8-18 | 17 | 212.2 |
| 3 | 27-37 | 19-29 | 16 | 199.9 |
| 4 | 38-48 | 30-40 | 15 | 187.6 |
| 5 | 49-59 | 41-51 | 14 | 175.3 |
| 6 | 60-70 | 52-62 | 13 | 163.0 |
| 7 | 71-79 | 63-74 | 12 | 149.6 |
| 8 | | 75-79 | 11 | 144.0 |
(The Original Period column is the original rotation rate. When used in
FluxEngine, the disk always spins at 360 rpm, which corresponds to a rotational
period of 166 ms.)
FluxEngine can read and write the single-sided variant of these. (Double-sided
will be trivial to do, it's just not done yet.)
Reading discs
-------------
@@ -16,18 +35,25 @@ Reading discs
Just do:
```
fluxengine read victor9k
fluxengine read victor9k-ss
```
You should end up with an `victor9k.img` which is 774656 bytes long.
if you want the double-sided variety, use `--heads 0-1`.
You should end up with an `victor9k.img` which is 627200 bytes long.
**Big warning!** The image may not work in an emulator. Victor disk images are
complicated due to the way the tracks are different sizes and the odd sector
size. FluxEngine chooses to store them in a simple 512 x 19 x 1 x 80 layout,
with holes where missing sectors should be. This was easiest. If anyone can
suggest a better way, please [get in
touch](https://github.com/davidgiven/fluxengine/issues/new).
**Big warning!** The image is triangular, where each track occupies a different
amount of space. Victor disk images are complicated due to the way the tracks
are different sizes and the odd sector size.
Writing discs
-------------
Just do:
```
fluxengine read victor9k-ss -i victor9k.img
```
**Big warning!** This uses the same triangular disk image that reading uses.
Useful references

View File

@@ -233,7 +233,7 @@ FluxEngine supports a number of ways to get or put flux. When using the `-s` or
FluxEngine also supports a number of file system image formats. When using the
`-i` or `-o` options (for input and output), you can use any of these strings:
- `<filename.adf>`, `<filename.d81>`, `<filename.img>`, `<filename.st>`
- `<filename.adf>`, `<filename.d81>`, `<filename.img>`, `<filename.st>`, `<filename.xdf>`
Read from or write to a simple headerless image file (all these formats are
the same). This will probably want configuration via the
@@ -256,6 +256,28 @@ FluxEngine also supports a number of file system image formats. When using the
Read from a JV3 image file, commonly used by TRS-80 emulators. **Read
only.**
- `<filename.dim>`
Read from a [DIM image file](https://www.pc98.org/project/doc/dim.html),
commonly used by X68000 emulators. Supports automatically configuring
the encoder. **Read Only.**
- `<filename.fdi>`
Read from a [FDI image file](https://www.pc98.org/project/doc/hdi.html),
commonly used by PC-98 emulators. **Read Only.**
- `<filename.d88>`
Read from a [D88 image file](https://www.pc98.org/project/doc/d88.html),
commonly used by various Japanese PC emulators, including the NEC PC-88. **Read Only.**
FluxEngine is currently limited to reading only the first floppy image in a
D88 file.
The D88 reader should be used with the `ibm` profile and will override
most encoding parameters on a track-by-track basis.
- `<filename.ldbs>`

View File

@@ -1,40 +0,0 @@
syntax = "proto2";
// Images
enum SectorStatus {
UNKNOWN = 0;
OK = 1;
BAD_CHECKSUM = 2;
MISSING = 3;
DATA_MISSING = 4;
CONFLICT = 5;
INTERNAL_ERROR = 6;
}
message SectorProto {
optional int32 logical_sector = 1;
optional bytes data = 2;
optional SectorStatus status = 3;
optional uint64 clock = 4;
optional uint64 header_starttime_ns = 5;
optional uint64 header_endtime_ns = 6;
optional uint64 data_starttime_ns = 7;
optional uint64 data_endtime_ns = 8;
optional int32 physical_cylinder = 9;
optional int32 physical_head = 10;
optional int32 logical_track = 11;
optional int32 logical_side = 12;
}
message TrackProto {
map<int32, SectorProto> sectors = 1;
optional int32 logical_track = 2;
optional int32 logical_side = 3;
}
message ImageProto {
map<int32, TrackProto> tracks = 1;
}

View File

@@ -25,7 +25,6 @@
#include "sector.h"
#include "image.h"
#include "lib/decoders/decoders.pb.h"
#include "lib/data.pb.h"
#include "fmt/format.h"
#include <numeric>
@@ -78,18 +77,20 @@ std::unique_ptr<TrackDataFlux> AbstractDecoder::decodeToSectors(
_sector->physicalHead = physicalHead;
Fluxmap::Position recordStart = fmr.tell();
_decoder.reset(new FluxDecoder(&fmr, _sector->clock, _config));
RecordType r = advanceToNextRecord();
if (fmr.eof() || !_sector->clock)
return std::move(_trackdata);
if ((r == UNKNOWN_RECORD) || (r == DATA_RECORD))
{
fmr.findEvent(F_BIT_PULSE);
fmr.skipToEvent(F_BIT_PULSE);
continue;
}
/* Read the sector record. */
recordStart = fmr.tell();
resetFluxDecoder();
decodeSectorRecord();
Fluxmap::Position recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
@@ -104,12 +105,15 @@ std::unique_ptr<TrackDataFlux> AbstractDecoder::decodeToSectors(
r = advanceToNextRecord();
if (r != UNKNOWN_RECORD)
break;
if (fmr.findEvent(F_BIT_PULSE) == 0)
if (fmr.eof())
break;
}
recordStart = fmr.tell();
if (r == DATA_RECORD)
{
resetFluxDecoder();
decodeDataRecord();
}
recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
}
@@ -133,13 +137,19 @@ void AbstractDecoder::pushRecord(const Fluxmap::Position& start, const Fluxmap::
record->clock = _sector->clock;
_fmr->seek(start);
record->rawData = toBytes(_fmr->readRawBits(end, _sector->clock));
FluxDecoder decoder(_fmr, _sector->clock, _config);
record->rawData = toBytes(decoder.readBits(end));
_fmr->seek(here);
}
void AbstractDecoder::resetFluxDecoder()
{
_decoder.reset(new FluxDecoder(_fmr, _sector->clock, _config));
}
std::vector<bool> AbstractDecoder::readRawBits(unsigned count)
{
return _fmr->readRawBits(count, _sector->clock);
return _decoder->readBits(count);
}
std::set<unsigned> AbstractDecoder::requiredSectors(unsigned cylinder, unsigned head) const

View File

@@ -4,6 +4,7 @@
#include "bytes.h"
#include "sector.h"
#include "decoders/fluxmapreader.h"
#include "decoders/fluxdecoder.h"
class Sector;
class Fluxmap;
@@ -47,8 +48,8 @@ public:
std::unique_ptr<TrackDataFlux> decodeToSectors(std::shared_ptr<const Fluxmap> fluxmap, unsigned cylinder, unsigned head);
void pushRecord(const Fluxmap::Position& start, const Fluxmap::Position& end);
void resetFluxDecoder();
std::vector<bool> readRawBits(unsigned count);
//{ return _fmr->readRawBits(count, _sector->clock); }
Fluxmap::Position tell()
{ return _fmr->tell(); }
@@ -68,6 +69,7 @@ protected:
FluxmapReader* _fmr = nullptr;
std::unique_ptr<TrackDataFlux> _trackdata;
std::shared_ptr<Sector> _sector;
std::unique_ptr<FluxDecoder> _decoder;
};
#endif

View File

@@ -18,16 +18,19 @@ import "arch/zilogmcz/zilogmcz.proto";
import "lib/fluxsink/fluxsink.proto";
import "lib/common.proto";
//NEXT: 27
message DecoderProto {
optional double pulse_debounce_threshold = 1 [default = 0.30,
(help) = "ignore pulses with intervals shorter than this, in fractions of a clock"];
optional double bit_error_threshold = 2 [default = 0.40,
(help) = "amount of error to tolerate in pulse timing, in fractions of a clock"];
optional double clock_interval_bias = 3 [default = -0.02,
(help) = "adjust intervals between pulses by this many clocks before decoding"];
optional double minimum_clock_us = 4 [default = 0.75,
(help) = "refuse to detect clocks shorter than this, to avoid false positives"];
optional double pll_adjust = 25 [default = 0.04];
optional double pll_phase = 26 [default = 0.60];
optional double flux_scale = 27 [default = 1.0];
oneof format {
IbmDecoderProto ibm = 5;
BrotherDecoderProto brother = 6;

117
lib/decoders/fluxdecoder.cc Normal file
View File

@@ -0,0 +1,117 @@
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/fluxdecoder.h"
#include "lib/decoders/decoders.pb.h"
/* This is a port of the samdisk code:
*
* https://github.com/simonowen/samdisk/blob/master/src/FluxDecoder.cpp
*
* I'm not actually terribly sure how it works, but it does, and much better
* than my code.
*/
FluxDecoder::FluxDecoder(FluxmapReader* fmr, nanoseconds_t bitcell,
const DecoderProto& config):
_fmr(fmr),
_pll_phase(config.pll_phase()),
_pll_adjust(config.pll_adjust()),
_flux_scale(config.flux_scale()),
_clock(bitcell),
_clock_centre(bitcell),
_clock_min(bitcell * (1.0 - _pll_adjust)),
_clock_max(bitcell * (1.0 + _pll_adjust)),
_flux(0),
_leading_zeroes(fmr->tell().zeroes)
{}
bool FluxDecoder::readBit()
{
if (_leading_zeroes > 0)
{
_leading_zeroes--;
return false;
}
else if (_leading_zeroes == 0)
{
_leading_zeroes--;
return true;
}
while (!_fmr->eof() && (_flux < (_clock/2)))
{
_flux += nextFlux() * _flux_scale;;
_clocked_zeroes = 0;
}
_flux -= _clock;
if (_flux >= (_clock/2))
{
_clocked_zeroes++;
_goodbits++;
return false;
}
/* PLL adjustment: change the clock frequency according to the phase
* mismatch */
if (_clocked_zeroes <= 3)
{
/* In sync: adjust base clock */
_clock += _flux * _pll_adjust;
}
else
{
/* Out of sync: adjust the base clock back towards the centre */
_clock += (_clock_centre - _clock) * _pll_adjust;
/* We require 256 good bits before reporting another sync loss event. */
if (_goodbits >= 256)
_sync_lost = true;
_goodbits = 0;
}
/* Clamp the clock's adjustment range. */
_clock = std::min(std::max(_clock_min, _clock), _clock_max);
/* I'm not sure what this does, but the original comment is:
* Authentic PLL: Do not snap the timing window to each flux transition
*/
_flux = _flux * (1.0 - _pll_phase);
_goodbits++;
return true;
}
std::vector<bool> FluxDecoder::readBits(unsigned count)
{
std::vector<bool> result;
while (!_fmr->eof() && count--)
{
bool b = readBit();
result.push_back(b);
}
return result;
}
std::vector<bool> FluxDecoder::readBits(const Fluxmap::Position& until)
{
std::vector<bool> result;
while (!_fmr->eof() && (_fmr->tell().bytes < until.bytes))
{
bool b = readBit();
result.push_back(b);
}
return result;
}
nanoseconds_t FluxDecoder::nextFlux()
{
return _fmr->readInterval(_clock_centre) * NS_PER_TICK;
}

View File

@@ -0,0 +1,37 @@
#ifndef FLUXDECODER_H
#define FLUXDECODER_H
class FluxmapReader;
class FluxDecoder
{
public:
FluxDecoder(FluxmapReader* fmr, nanoseconds_t bitcell,
const DecoderProto& config);
bool readBit();
std::vector<bool> readBits(unsigned count);
std::vector<bool> readBits(const Fluxmap::Position& until);
private:
nanoseconds_t nextFlux();
private:
FluxmapReader* _fmr;
double _pll_phase;
double _pll_adjust;
double _flux_scale;
nanoseconds_t _clock = 0;
nanoseconds_t _clock_centre;
nanoseconds_t _clock_min;
nanoseconds_t _clock_max;
nanoseconds_t _flux = 0;
unsigned _clocked_zeroes = 0;
unsigned _goodbits = 0;
bool _index = false;
bool _sync_lost = false;
int _leading_zeroes;
};
#endif

View File

@@ -18,7 +18,7 @@ FluxmapReader::FluxmapReader(const Fluxmap& fluxmap):
rewind();
}
uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
void FluxmapReader::getNextEvent(int& event, unsigned& ticks)
{
ticks = 0;
@@ -26,30 +26,42 @@ uint8_t FluxmapReader::getNextEvent(unsigned& ticks)
{
uint8_t b = _bytes[_pos.bytes++];
ticks += b & 0x3f;
if (b & (F_BIT_PULSE|F_BIT_INDEX))
if (!b || (b & (F_BIT_PULSE|F_BIT_INDEX)))
{
_pos.ticks += ticks;
return b;
event = b & 0xc0;
return;
}
}
_pos.ticks += ticks;
return 0;
event = F_EOF;
}
unsigned FluxmapReader::findEvent(uint8_t target)
void FluxmapReader::skipToEvent(int event)
{
unsigned ticks = 0;
unsigned ticks;
findEvent(event, ticks);
}
bool FluxmapReader::findEvent(int event, unsigned& ticks)
{
ticks = 0;
for (;;)
{
unsigned thisTicks;
uint8_t bits = getNextEvent(thisTicks);
ticks += thisTicks;
int thisEvent;
getNextEvent(thisEvent, thisTicks);
ticks += thisTicks;
if (thisEvent == F_EOF)
return false;
if (eof())
return 0;
if (bits & target)
return ticks;
return false;
if ((event == thisEvent) || (event & thisEvent))
return true;
}
}
@@ -58,10 +70,10 @@ unsigned FluxmapReader::readInterval(nanoseconds_t clock)
unsigned thresholdTicks = (clock * _config.pulse_debounce_threshold()) / NS_PER_TICK;
unsigned ticks = 0;
while (ticks < thresholdTicks)
while (ticks <= thresholdTicks)
{
unsigned thisTicks = findEvent(F_BIT_PULSE);
if (!thisTicks)
unsigned thisTicks;
if (!findEvent(F_BIT_PULSE, thisTicks))
break;
ticks += thisTicks;
}
@@ -183,8 +195,9 @@ void FluxmapReader::seek(nanoseconds_t ns)
while (!eof() && (_pos.ticks < ticks))
{
int e;
unsigned t;
getNextEvent(t);
getNextEvent(e, t);
}
_pos.zeroes = 0;
}
@@ -225,7 +238,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
positions[i] = positions[i+1];
candidates[i] = candidates[i+1];
}
candidates[intervalCount] = findEvent(F_BIT_PULSE);
findEvent(F_BIT_PULSE, candidates[intervalCount]);
positions[intervalCount] = tell();
}
@@ -236,44 +249,7 @@ nanoseconds_t FluxmapReader::seekToPattern(const FluxMatcher& pattern, const Flu
void FluxmapReader::seekToIndexMark()
{
findEvent(F_BIT_INDEX);
skipToEvent(F_BIT_INDEX);
_pos.zeroes = 0;
}
bool FluxmapReader::readRawBit(nanoseconds_t clockPeriod)
{
assert(clockPeriod != 0);
if (_pos.zeroes)
{
_pos.zeroes--;
return false;
}
nanoseconds_t interval = readInterval(clockPeriod)*NS_PER_TICK;
double clocks = (double)interval / clockPeriod + _config.clock_interval_bias();
if (clocks < 1.0)
clocks = 1.0;
_pos.zeroes = (int)round(clocks) - 1;
return true;
}
std::vector<bool> FluxmapReader::readRawBits(unsigned count, nanoseconds_t clockPeriod)
{
std::vector<bool> result;
while (!eof() && count--)
{
bool b = readRawBit(clockPeriod);
result.push_back(b);
}
return result;
}
std::vector<bool> FluxmapReader::readRawBits(const Fluxmap::Position& until, nanoseconds_t clockPeriod)
{
std::vector<bool> result;
while (!eof() && (_pos.bytes < until.bytes))
result.push_back(readRawBit(clockPeriod));
return result;
}

View File

@@ -93,8 +93,9 @@ public:
return (_fluxmap.duration());
}
uint8_t getNextEvent(unsigned& ticks);
unsigned findEvent(uint8_t bits);
void getNextEvent(int& event, unsigned& ticks);
void skipToEvent(int event);
bool findEvent(int event, unsigned& ticks);
unsigned readInterval(nanoseconds_t clock); /* with debounce support */
/* Important! You can only reliably seek to 1 bits. */
@@ -104,10 +105,6 @@ public:
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
nanoseconds_t seekToPattern(const FluxMatcher& pattern, const FluxMatcher*& matching);
bool readRawBit(nanoseconds_t clockPeriod);
std::vector<bool> readRawBits(unsigned count, nanoseconds_t clockPeriod);
std::vector<bool> readRawBits(const Fluxmap::Position& until, nanoseconds_t clockPeriod);
private:
const Fluxmap& _fluxmap;
const uint8_t* _bytes;

View File

@@ -10,6 +10,7 @@
#include "arch/micropolis/micropolis.h"
#include "arch/northstar/northstar.h"
#include "arch/tids990/tids990.h"
#include "arch/victor9k/victor9k.h"
#include "lib/encoders/encoders.pb.h"
#include "protocol.h"
@@ -26,6 +27,7 @@ std::unique_ptr<AbstractEncoder> AbstractEncoder::create(const EncoderProto& con
{ EncoderProto::kMicropolis,createMicropolisEncoder },
{ EncoderProto::kNorthstar, createNorthstarEncoder },
{ EncoderProto::kTids990, createTids990Encoder },
{ EncoderProto::kVictor9K, createVictor9kEncoder },
};
auto encoder = encoders.find(config.format_case());

View File

@@ -8,6 +8,7 @@ import "arch/macintosh/macintosh.proto";
import "arch/micropolis/micropolis.proto";
import "arch/northstar/northstar.proto";
import "arch/tids990/tids990.proto";
import "arch/victor9k/victor9k.proto";
//import "lib/common.proto";
message EncoderProto {
@@ -20,5 +21,6 @@ message EncoderProto {
Commodore64EncoderProto c64 = 8;
NorthstarEncoderProto northstar = 9;
MicropolisEncoderProto micropolis = 10;
Victor9kEncoderProto victor9k = 11;
}
}

17
lib/fl2.proto Normal file
View File

@@ -0,0 +1,17 @@
syntax = "proto2";
enum FluxFileVersion {
VERSION_1 = 1;
}
message TrackFluxProto {
optional int32 cylinder = 1;
optional int32 head = 2;
optional bytes flux = 3;
}
message FluxFileProto {
optional FluxFileVersion version = 1;
repeated TrackFluxProto track = 2;
}

View File

@@ -56,6 +56,12 @@ Fluxmap& Fluxmap::appendIndex()
return *this;
}
Fluxmap& Fluxmap::appendDesync()
{
appendByte(F_DESYNC);
return *this;
}
void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
{
uint8_t junk = 0xff;

View File

@@ -46,6 +46,7 @@ public:
Fluxmap& appendInterval(uint32_t ticks);
Fluxmap& appendPulse();
Fluxmap& appendIndex();
Fluxmap& appendDesync();
Fluxmap& appendBytes(const Bytes& bytes);
Fluxmap& appendBytes(const uint8_t* ptr, size_t len);

View File

@@ -68,14 +68,15 @@ public:
while (!fmr.eof())
{
unsigned ticks;
uint8_t bits = fmr.getNextEvent(ticks);
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
timestamp += ticks;
if (bits & F_BIT_PULSE)
if (event & F_BIT_PULSE)
data[timestamp*channels + 0] = 0x7f;
if (_config.index_markers() && (bits & F_BIT_INDEX))
if (_config.index_markers() && (event & F_BIT_INDEX))
data[timestamp*channels + 1] = 0x7f;
}

View File

@@ -0,0 +1,69 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "fluxsink/fluxsink.h"
#include "decoders/fluxmapreader.h"
#include "lib/fluxsink/fluxsink.pb.h"
#include "proto.h"
#include "fmt/format.h"
#include "lib/fl2.pb.h"
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
class Fl2FluxSink : public FluxSink
{
public:
Fl2FluxSink(const Fl2FluxSinkProto& lconfig):
_config(lconfig),
_of(lconfig.filename(), std::ios::out | std::ios::binary)
{
if (!_of.is_open())
Error() << "cannot open output file";
}
~Fl2FluxSink()
{
FluxFileProto proto;
proto.set_version(FluxFileVersion::VERSION_1);
for (const auto& e : _data)
{
auto track = proto.add_track();
track->set_cylinder(e.first.first);
track->set_head(e.first.second);
track->set_flux(e.second);
}
if (!proto.SerializeToOstream(&_of))
Error() << "unable to write output file";
_of.close();
if (_of.fail())
Error() << "FL2 write I/O error: " << strerror(errno);
}
public:
void writeFlux(int cylinder, int head, Fluxmap& fluxmap)
{
_data[std::make_pair(cylinder, head)] = fluxmap.rawBytes();
}
operator std::string () const
{
return fmt::format("fl2({})", _config.filename());
}
private:
const Fl2FluxSinkProto& _config;
std::ofstream _of;
std::map<std::pair<unsigned, unsigned>, Bytes> _data;
};
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(const Fl2FluxSinkProto& config)
{
return std::unique_ptr<FluxSink>(new Fl2FluxSink(config));
}

View File

@@ -26,6 +26,9 @@ std::unique_ptr<FluxSink> FluxSink::create(const FluxSinkProto& config)
case FluxSinkProto::kScp:
return createScpFluxSink(config.scp());
case FluxSinkProto::kFl2:
return createFl2FluxSink(config.fl2());
default:
Error() << "bad output disk config";
return std::unique_ptr<FluxSink>();
@@ -38,6 +41,7 @@ void FluxSink::updateConfigForFilename(FluxSinkProto* proto, const std::string&
{
{ std::regex("^(.*\\.flux)$"), [&](const auto& s) { proto->set_fluxfile(s); }},
{ std::regex("^(.*\\.scp)$"), [&](const auto& s) { proto->mutable_scp()->set_filename(s); }},
{ std::regex("^(.*\\.fl2)$"), [&](const auto& s) { proto->mutable_fl2()->set_filename(s); }},
{ std::regex("^vcd:(.*)$"), [&](const auto& s) { proto->mutable_vcd()->set_directory(s); }},
{ std::regex("^au:(.*)$"), [&](const auto& s) { proto->mutable_au()->set_directory(s); }},
{ std::regex("^drive:(.*)"), [&](const auto& s) { proto->mutable_drive()->set_drive(std::stoi(s)); }},

View File

@@ -10,6 +10,7 @@ class HardwareFluxSinkProto;
class AuFluxSinkProto;
class VcdFluxSinkProto;
class ScpFluxSinkProto;
class Fl2FluxSinkProto;
class FluxSink
{
@@ -21,6 +22,7 @@ public:
static std::unique_ptr<FluxSink> createAuFluxSink(const AuFluxSinkProto& config);
static std::unique_ptr<FluxSink> createVcdFluxSink(const VcdFluxSinkProto& config);
static std::unique_ptr<FluxSink> createScpFluxSink(const ScpFluxSinkProto& config);
static std::unique_ptr<FluxSink> createFl2FluxSink(const Fl2FluxSinkProto& config);
static std::unique_ptr<FluxSink> create(const FluxSinkProto& config);
static void updateConfigForFilename(FluxSinkProto* proto, const std::string& filename);

View File

@@ -24,6 +24,10 @@ message ScpFluxSinkProto {
optional int32 type_byte = 4 [default = 0xff, (help) = "set the SCP disk type byte"];
}
message Fl2FluxSinkProto {
optional string filename = 1 [default = "flux.fl2", (help) = ".fl2 file to write to"];
}
message FluxSinkProto {
oneof dest {
string fluxfile = 1 [(help) = "name of destination flux file"];
@@ -31,6 +35,7 @@ message FluxSinkProto {
AuFluxSinkProto au = 3;
VcdFluxSinkProto vcd = 4;
ScpFluxSinkProto scp = 5;
Fl2FluxSinkProto fl2 = 6;
}
}

View File

@@ -47,7 +47,6 @@ public:
_fileheader.file_id[2] = 'P';
_fileheader.version = 0x18; /* Version 1.8 of the spec */
_fileheader.type = _config.type_byte();
_fileheader.revolutions = 5;
_fileheader.start_track = strackno(config.cylinders().start(), config.heads().start());
_fileheader.end_track = strackno(config.cylinders().end(), config.heads().end());
_fileheader.flags = (_config.align_with_index() ? SCP_FLAG_INDEXED : 0)
@@ -55,7 +54,7 @@ public:
_fileheader.cell_width = 0;
_fileheader.heads = singlesided;
std::cout << fmt::format("Writing 96 tpi {} SCP file containing {} SCP tracks\n",
std::cout << fmt::format("SCP: writing 96 tpi {} file containing {} tracks\n",
singlesided ? "single sided" : "double sided",
_fileheader.end_track - _fileheader.start_track + 1
);
@@ -71,7 +70,7 @@ public:
appendChecksum(checksum, _trackdata);
write_le32(_fileheader.checksum, checksum);
std::cout << "Writing output file...\n";
std::cout << "SCP: writing output file...\n";
std::ofstream of(_config.filename(), std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
@@ -98,7 +97,7 @@ public:
ByteWriter fluxdataWriter(fluxdata);
if (_config.align_with_index())
fmr.findEvent(F_BIT_INDEX);
fmr.skipToEvent(F_BIT_INDEX);
int revolution = 0;
unsigned revTicks = 0;
@@ -108,12 +107,14 @@ public:
while (revolution < 5)
{
unsigned ticks;
uint8_t bits = fmr.getNextEvent(ticks);
int event;
fmr.getNextEvent(event, ticks);
ticksSinceLastPulse += ticks;
totalTicks += ticks;
revTicks += ticks;
if (fmr.eof() || (bits & F_BIT_INDEX))
if (fmr.eof() || (event & F_BIT_INDEX))
{
auto* revheader = &trackheader.revolution[revolution];
write_le32(revheader->offset, startOffset + sizeof(ScpTrack));
@@ -125,7 +126,7 @@ public:
startOffset = fluxdataWriter.pos;
}
if (bits & F_BIT_PULSE)
if (event & F_BIT_PULSE)
{
unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25;
while (t >= 0x10000)
@@ -138,6 +139,7 @@ public:
}
}
_fileheader.revolutions = revolution - 1;
write_le32(_fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader));
trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader));
trackdataWriter += fluxdata;

View File

@@ -43,7 +43,8 @@ public:
while (!fmr.eof())
{
unsigned ticks;
uint8_t bits = fmr.getNextEvent(ticks);
int event;
fmr.getNextEvent(event, ticks);
if (fmr.eof())
break;
@@ -55,9 +56,9 @@ public:
of << fmt::format("#{} ", (uint64_t)(timestamp * NS_PER_TICK));
}
if (bits & F_BIT_PULSE)
if (event & F_BIT_PULSE)
of << "1p ";
if (bits & F_BIT_INDEX)
if (event & F_BIT_INDEX)
of << "1i ";
lasttimestamp = timestamp;

View File

@@ -139,77 +139,3 @@ std::unique_ptr<FluxSource> FluxSource::createCwfFluxSource(const CwfFluxSourceP
{
return std::unique_ptr<FluxSource>(new CwfFluxSource(config));
}
#if 0
#include "globals.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
static std::ifstream inputFile;
static sqlite3* outputDb;
static CwfHeader header;
static double clockRate;
static void syntax()
{
std::cout << "Syntax: fluxengine convert cwftoflux <cwffile> <fluxfile>\n";
exit(0);
}
static void check_for_error()
{
if (inputFile.fail())
Error() << fmt::format("I/O error: {}", strerror(errno));
}
static void read_header()
{
}
static void read_track()
{
CwfTrack trackheader;
inputFile.read((char*) &trackheader, sizeof(trackheader));
check_for_error();
uint32_t length = Bytes(trackheader.length, 4).reader().read_le32() - sizeof(CwfTrack);
unsigned track_number = trackheader.track * header.step;
std::cout << fmt::format("{}.{}: {} input bytes; ", track_number, trackheader.side, length)
<< std::flush;
std::vector<uint8_t> inputdata(length);
inputFile.read((char*) &inputdata[0], length);
check_for_error();
std::cout << fmt::format(" {} ms in {} output bytes\n",
fluxmap.duration() / 1e6, fluxmap.bytes());
sqlWriteFlux(outputDb, track_number, trackheader.side, fluxmap);
}
int mainConvertCwfToFlux(int argc, const char* argv[])
{
if (argc != 3)
syntax();
inputFile.open(argv[1], std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << fmt::format("cannot open input file '{}'", argv[1]);
outputDb = sqlOpen(argv[2], SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
sqlPrepareFlux(outputDb);
sqlWriteIntProperty(outputDb, "version", FLUX_VERSION_CURRENT);
sqlStmt(outputDb, "BEGIN;");
read_header();
inputFile.seekg(sizeof(header), std::ios::beg);
for (unsigned i=0; i<(header.cylinders*header.sides); i++)
read_track();
sqlStmt(outputDb, "COMMIT;");
sqlClose(outputDb);
return 0;
}
#endif

View File

@@ -0,0 +1,55 @@
#include "globals.h"
#include "fluxmap.h"
#include "lib/fluxsource/fluxsource.pb.h"
#include "lib/fl2.pb.h"
#include "fluxsource/fluxsource.h"
#include "proto.h"
#include "fmt/format.h"
#include <fstream>
class Fl2FluxSource : public FluxSource
{
public:
Fl2FluxSource(const Fl2FluxSourceProto& config):
_config(config)
{
std::ifstream ifs(_config.filename(), std::ios::in | std::ios::binary);
if (!ifs.is_open())
Error() << fmt::format("cannot open input file '{}': {}",
_config.filename(), strerror(errno));
if (!_proto.ParseFromIstream(&ifs))
Error() << "unable to read input file";
}
public:
std::unique_ptr<Fluxmap> readFlux(int cylinder, int head)
{
for (const auto& track : _proto.track())
{
if ((track.cylinder() == cylinder) && (track.head() == head))
return std::make_unique<Fluxmap>(track.flux());
}
return std::unique_ptr<Fluxmap>();
}
void recalibrate() {}
private:
void check_for_error(std::ifstream& ifs)
{
if (ifs.fail())
Error() << fmt::format("FL2 read I/O error: {}", strerror(errno));
}
private:
const Fl2FluxSourceProto& _config;
FluxFileProto _proto;
};
std::unique_ptr<FluxSource> FluxSource::createFl2FluxSource(const Fl2FluxSourceProto& config)
{
return std::unique_ptr<FluxSource>(new Fl2FluxSource(config));
}

View File

@@ -39,6 +39,9 @@ std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)
case FluxSourceProto::kCwf:
return createCwfFluxSource(config.cwf());
case FluxSourceProto::kFl2:
return createFl2FluxSource(config.fl2());
default:
Error() << "bad input disk configuration";
return std::unique_ptr<FluxSource>();
@@ -52,6 +55,7 @@ void FluxSource::updateConfigForFilename(FluxSourceProto* proto, const std::stri
{ std::regex("^(.*\\.flux)$"), [&](const auto& s) { proto->set_fluxfile(s); }},
{ std::regex("^(.*\\.scp)$"), [&](const auto& s) { proto->mutable_scp()->set_filename(s); }},
{ std::regex("^(.*\\.cwf)$"), [&](const auto& s) { proto->mutable_cwf()->set_filename(s); }},
{ std::regex("^(.*\\.fl2)$"), [&](const auto& s) { proto->mutable_fl2()->set_filename(s); }},
{ std::regex("^erase:$"), [&](const auto& s) { proto->mutable_erase(); }},
{ std::regex("^kryoflux:(.*)$"), [&](const auto& s) { proto->mutable_kryoflux()->set_directory(s); }},
{ std::regex("^testpattern:(.*)"), [&](const auto& s) { proto->mutable_test_pattern(); }},

View File

@@ -5,6 +5,7 @@
class CwfFluxSourceProto;
class EraseFluxSourceProto;
class Fl2FluxSourceProto;
class FluxSourceProto;
class FluxSpec;
class Fluxmap;
@@ -21,6 +22,7 @@ public:
private:
static std::unique_ptr<FluxSource> createCwfFluxSource(const CwfFluxSourceProto& config);
static std::unique_ptr<FluxSource> createEraseFluxSource(const EraseFluxSourceProto& config);
static std::unique_ptr<FluxSource> createFl2FluxSource(const Fl2FluxSourceProto& config);
static std::unique_ptr<FluxSource> createHardwareFluxSource(const HardwareFluxSourceProto& config);
static std::unique_ptr<FluxSource> createKryofluxFluxSource(const KryofluxFluxSourceProto& config);
static std::unique_ptr<FluxSource> createScpFluxSource(const ScpFluxSourceProto& config);

View File

@@ -29,10 +29,15 @@ message ScpFluxSourceProto {
}
message CwfFluxSourceProto {
optional string filename = 1 [default = "flux.xwf",
optional string filename = 1 [default = "flux.cwf",
(help) = ".cwf file to read flux from"];
}
message Fl2FluxSourceProto {
optional string filename = 1 [default = "flux.fl2",
(help) = ".fl2 file to read flux from"];
}
message FluxSourceProto {
oneof source {
string fluxfile = 1 [default = "name of source flux file"];
@@ -42,6 +47,7 @@ message FluxSourceProto {
KryofluxFluxSourceProto kryoflux = 5;
ScpFluxSourceProto scp = 6;
CwfFluxSourceProto cwf = 7;
Fl2FluxSourceProto fl2 = 8;
}
}

View File

@@ -18,6 +18,10 @@
#define mkdir(A, B) _mkdir(A)
#endif
template <class T>
static inline std::vector<T> vector_of(T item)
{ return std::vector<T> { item }; }
typedef double nanoseconds_t;
class Bytes;
@@ -45,5 +49,4 @@ private:
std::stringstream _stream;
};
#endif

View File

@@ -16,7 +16,7 @@ public:
ImageReader(config)
{}
Image readImage()
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
@@ -53,7 +53,7 @@ public:
return 17;
};
Image image;
std::unique_ptr<Image> image(new Image);
for (int track = 0; track < 40; track++)
{
int numSectors = sectorsPerTrack(track);
@@ -62,7 +62,7 @@ public:
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = image.put(track, head, sectorId);
const auto& sector = image->put(track, head, sectorId);
if ((offset < inputFileSize))
{ //still data available sector OK
br.seek(offset);
@@ -88,7 +88,7 @@ public:
}
}
image.calculateSize();
image->calculateSize();
return image;
}
};

View File

@@ -0,0 +1,176 @@
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "imagereader/imagereader.h"
#include "image.h"
#include "proto.h"
#include "lib/config.pb.h"
#include "imagereader/imagereaderimpl.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
// reader based on this partial documentation of the D88 format:
// https://www.pc98.org/project/doc/d88.html
class D88ImageReader : public ImageReader
{
public:
D88ImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
Bytes header(0x24); // read first entry of track table as well
inputFile.read((char*) header.begin(), header.size());
// the DIM header technically has a bit field for sectors present,
// however it is currently ignored by this reader
std::string diskName = header.slice(0, 0x16);
if (diskName[0])
std::cout << "D88: disk name: " << diskName << "\n";
ByteReader headerReader(header);
// media flag indicates media density, currently unused
char mediaFlag = headerReader.seek(0x1b).read_8();
inputFile.seekg( 0, std::ios::end );
int fileSize = inputFile.tellg();
int diskSize = headerReader.seek(0x1c).read_le32();
if (diskSize > fileSize)
std::cout << "D88: found multiple disk images. Only using first\n";
int trackTableEnd = headerReader.seek(0x20).read_le32();
int trackTableSize = trackTableEnd - 0x20;
Bytes trackTable(trackTableSize);
inputFile.seekg(0x20);
inputFile.read((char*) trackTable.begin(), trackTable.size());
ByteReader trackTableReader(trackTable);
int diskSectorsPerTrack = -1;
if (config.encoder().format_case() != EncoderProto::FormatCase::FORMAT_NOT_SET)
std::cout << "D88: overriding configured format";
auto ibm = config.mutable_encoder()->mutable_ibm();
config.mutable_cylinders()->set_end(0);
std::unique_ptr<Image> image(new Image);
for (int track = 0; track < trackTableSize / 4; track++)
{
int trackOffset = trackTableReader.seek(track * 4).read_le32();
if (trackOffset == 0) continue;
int currentTrackOffset = trackOffset;
int currentTrackCylinder = -1;
int currentSectorsInTrack = 0xffff; // don't know # of sectors until we read the first one
int trackSectorSize = -1;
int trackMfm = -1;
auto trackdata = ibm->add_trackdata();
trackdata->set_clock_rate_khz(500);
trackdata->set_track_length_ms(167);
auto sectors = trackdata->mutable_sectors();
for (int sectorInTrack = 0; sectorInTrack < currentSectorsInTrack; sectorInTrack++){
Bytes sectorHeader(0x10);
inputFile.read((char*) sectorHeader.begin(), sectorHeader.size());
ByteReader sectorHeaderReader(sectorHeader);
int cylinder = sectorHeaderReader.seek(0).read_8();
int head = sectorHeaderReader.seek(1).read_8();
int sectorId = sectorHeaderReader.seek(2).read_8();
int sectorSize = 128 << sectorHeaderReader.seek(3).read_8();
int sectorsInTrack = sectorHeaderReader.seek(4).read_le16();
int fm = sectorHeaderReader.seek(6).read_8();
int ddam = sectorHeaderReader.seek(7).read_8();
int fddStatusCode = sectorHeaderReader.seek(8).read_8();
int rpm = sectorHeaderReader.seek(13).read_8();
// D88 provides much more sector information that is currently ignored
if (ddam != 0)
Error() << "D88: nonzero ddam currently unsupported";
if (rpm != 0)
Error() << "D88: 1.44MB 300rpm formats currently unsupported";
if (fddStatusCode != 0)
Error() << "D88: nonzero fdd status codes are currently unsupported";
if (currentSectorsInTrack == 0xffff) {
currentSectorsInTrack = sectorsInTrack;
} else if (currentSectorsInTrack != sectorsInTrack) {
Error() << "D88: mismatched number of sectors in track";
}
if (diskSectorsPerTrack < 0) {
diskSectorsPerTrack = sectorsInTrack;
} else if (diskSectorsPerTrack != sectorsInTrack) {
Error() << "D88: varying numbers of sectors per track is currently unsupported";
}
if (currentTrackCylinder < 0) {
currentTrackCylinder = cylinder;
} else if (currentTrackCylinder != cylinder) {
Error() << "D88: all sectors in a track must belong to the same cylinder";
}
if (trackSectorSize < 0) {
trackSectorSize = sectorSize;
// this is the first sector we've read, use it settings for per-track data
trackdata->set_cylinder(cylinder);
trackdata->set_head(head);
trackdata->set_sector_size(sectorSize);
trackdata->set_use_fm(fm);
if (fm) {
//trackdata->set_clock_rate_khz(250*300/360);
trackdata->set_idam_byte(0xf57e);
trackdata->set_dam_byte(0xf56f);
}
// create timings to approximately match N88-BASIC
if (sectorSize <= 128) {
trackdata->set_gap0(0x1b);
trackdata->set_gap2(0x09);
trackdata->set_gap3(0x1b);
} else if (sectorSize <= 256) {
trackdata->set_gap0(0x36);
trackdata->set_gap3(0x36);
}
} else if (trackSectorSize != sectorSize) {
Error() << "D88: multiple sector sizes per track are currently unsupported";
}
Bytes data(sectorSize);
inputFile.read((char*) data.begin(), data.size());
const auto& sector = image->put(cylinder, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = cylinder;
sector->physicalCylinder = cylinder;
sector->logicalSide = sector->physicalHead = head;
sector->logicalSector = sectorId;
sector->data = data;
sectors->add_sector(sectorId);
if (config.cylinders().end() < cylinder)
config.mutable_cylinders()->set_end(cylinder);
}
}
image->calculateSize();
const Geometry& geometry = image->getGeometry();
std::cout << fmt::format("D88: read {} tracks, {} sides\n",
geometry.numTracks, geometry.numSides);
return image;
}
};
std::unique_ptr<ImageReader> ImageReader::createD88ImageReader(
const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new D88ImageReader(config));
}

View File

@@ -0,0 +1,150 @@
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "imagereader/imagereader.h"
#include "image.h"
#include "proto.h"
#include "lib/config.pb.h"
#include "imagereader/imagereaderimpl.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
// reader based on this partial documentation of the DIM format:
// https://www.pc98.org/project/doc/dim.html
class DimImageReader : public ImageReader
{
public:
DimImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
Bytes header(256);
inputFile.read((char*) header.begin(), header.size());
if (header.slice(0xAB, 13) != Bytes("DIFC HEADER "))
Error() << "DIM: could not find DIM header, is this a DIM file?";
// the DIM header technically has a bit field for sectors present,
// however it is currently ignored by this reader
char mediaByte = header[0];
int tracks;
int sectorsPerTrack;
int sectorSize;
switch (mediaByte) {
case 0:
tracks = 77;
sectorsPerTrack = 8;
sectorSize = 1024;
break;
case 1:
tracks = 80;
sectorsPerTrack = 9;
sectorSize = 1024;
break;
case 2:
tracks = 80;
sectorsPerTrack = 15;
sectorSize = 512;
break;
case 3:
tracks = 80;
sectorsPerTrack = 18;
sectorSize = 512;
break;
default:
Error() << "DIM: unsupported media byte";
break;
}
std::unique_ptr<Image> image(new Image);
int trackCount = 0;
for (int track = 0; track < tracks; track++)
{
if (inputFile.eof())
break;
int physicalCylinder = track;
for (int side = 0; side < 2; side++)
{
std::vector<unsigned> sectors;
for (int i = 0; i < sectorsPerTrack; i++)
sectors.push_back(i + 1);
for (int sectorId : sectors)
{
Bytes data(sectorSize);
inputFile.read((char*) data.begin(), data.size());
const auto& sector = image->put(physicalCylinder, side, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = track;
sector->physicalCylinder = physicalCylinder;
sector->logicalSide = sector->physicalHead = side;
sector->logicalSector = sectorId;
sector->data = data;
}
}
trackCount++;
}
if (config.encoder().format_case() == EncoderProto::FormatCase::FORMAT_NOT_SET)
{
auto ibm = config.mutable_encoder()->mutable_ibm();
auto trackdata = ibm->add_trackdata();
trackdata->set_clock_rate_khz(500);
auto sectors = trackdata->mutable_sectors();
switch (mediaByte) {
case 0x00:
std::cout << "DIM: automatically setting format to 1.2MB (1024 byte sectors)\n";
config.mutable_cylinders()->set_end(76);
trackdata->set_track_length_ms(167);
trackdata->set_sector_size(1024);
for (int i = 0; i < 9; i++)
sectors->add_sector(i);
break;
case 0x02:
std::cout << "DIM: automatically setting format to 1.2MB (512 byte sectors)\n";
trackdata->set_track_length_ms(167);
trackdata->set_sector_size(512);
for (int i = 0; i < 15; i++)
sectors->add_sector(i);
break;
case 0x03:
std::cout << "DIM: automatically setting format to 1.44MB\n";
trackdata->set_track_length_ms(200);
trackdata->set_sector_size(512);
for (int i = 0; i < 18; i++)
sectors->add_sector(i);
break;
default:
Error() << fmt::format("DIM: unknown media byte 0x%02x, could not determine write profile automatically", mediaByte);
break;
}
}
image->calculateSize();
const Geometry& geometry = image->getGeometry();
std::cout << fmt::format("DIM: read {} tracks, {} sides, {} kB total\n",
geometry.numTracks, geometry.numSides,
((int)inputFile.tellg() - 256) / 1024);
return image;
}
};
std::unique_ptr<ImageReader> ImageReader::createDimImageReader(
const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new DimImageReader(config));
}

View File

@@ -16,7 +16,7 @@ public:
ImageReader(config)
{}
Image readImage()
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
@@ -89,7 +89,7 @@ public:
uint32_t dataPtr = 0x54;
uint32_t tagPtr = dataPtr + dataSize;
Image image;
std::unique_ptr<Image> image(new Image);
for (int track = 0; track < numCylinders; track++)
{
int numSectors = sectorsPerTrack(track);
@@ -105,7 +105,7 @@ public:
Bytes tag = br.read(12);
tagPtr += 12;
const auto& sector = image.put(track, head, sectorId);
const auto& sector = image->put(track, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = track;
sector->logicalSide = sector->physicalHead = head;
@@ -115,7 +115,7 @@ public:
}
}
image.setGeometry({
image->setGeometry({
.numTracks = numCylinders,
.numSides = numHeads,
.numSectors = 12,

View File

@@ -0,0 +1,94 @@
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "imagereader/imagereader.h"
#include "image.h"
#include "lib/config.pb.h"
#include "imagereader/imagereaderimpl.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
// reader based on this partial documentation of the FDI format:
// https://www.pc98.org/project/doc/hdi.html
class FdiImageReader : public ImageReader
{
public:
FdiImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
Bytes header(32);
inputFile.read((char*) header.begin(), header.size());
ByteReader headerReader(header);
if (headerReader.seek(0).read_le32() != 0)
Error() << "FDI: could not find FDI header, is this a FDI file?";
// we currently don't use fddType but it could be used to automatically select
// profile parameters in the future
//
int fddType = headerReader.seek(4).read_le32();
int headerSize = headerReader.seek(0x08).read_le32();
int sectorSize = headerReader.seek(0x10).read_le32();
int sectorsPerTrack = headerReader.seek(0x14).read_le32();
int sides = headerReader.seek(0x18).read_le32();
int tracks = headerReader.seek(0x1c).read_le32();
inputFile.seekg(headerSize);
std::unique_ptr<Image> image(new Image);
int trackCount = 0;
for (int track = 0; track < tracks; track++)
{
if (inputFile.eof())
break;
int physicalCylinder = track;
for (int side = 0; side < sides; side++)
{
std::vector<unsigned> sectors;
for (int i = 0; i < sectorsPerTrack; i++)
sectors.push_back(i + 1);
for (int sectorId : sectors)
{
Bytes data(sectorSize);
inputFile.read((char*) data.begin(), data.size());
const auto& sector = image->put(physicalCylinder, side, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = track;
sector->physicalCylinder = physicalCylinder;
sector->logicalSide = sector->physicalHead = side;
sector->logicalSector = sectorId;
sector->data = data;
}
}
trackCount++;
}
image->calculateSize();
const Geometry& geometry = image->getGeometry();
std::cout << fmt::format("FDI: read {} tracks, {} sides, {} kB total\n",
geometry.numTracks, geometry.numSides,
((int)inputFile.tellg() - headerSize) / 1024);
return image;
}
};
std::unique_ptr<ImageReader> ImageReader::createFdiImageReader(
const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new FdiImageReader(config));
}

View File

@@ -14,6 +14,15 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& config)
{
switch (config.format_case())
{
case ImageReaderProto::kDim:
return ImageReader::createDimImageReader(config);
case ImageReaderProto::kD88:
return ImageReader::createD88ImageReader(config);
case ImageReaderProto::kFdi:
return ImageReader::createFdiImageReader(config);
case ImageReaderProto::kImd:
return ImageReader::createIMDImageReader(config);
@@ -49,12 +58,16 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st
{".jv3", [&]() { proto->mutable_jv3(); }},
{".d64", [&]() { proto->mutable_d64(); }},
{".d81", [&]() { proto->mutable_img(); }},
{".d88", [&]() { proto->mutable_d88(); }},
{".dim", [&]() { proto->mutable_dim(); }},
{".diskcopy", [&]() { proto->mutable_diskcopy(); }},
{".fdi", [&]() { proto->mutable_fdi(); }},
{".imd", [&]() { proto->mutable_imd(); }},
{".img", [&]() { proto->mutable_img(); }},
{".st", [&]() { proto->mutable_img(); }},
{".nsi", [&]() { proto->mutable_nsi(); }},
{".td0", [&]() { proto->mutable_td0(); }},
{".TD0", [&]() { proto->mutable_td0(); }},
{".xdf", [&]() { proto->mutable_img(); }},
};
for (const auto& it : formats)
@@ -90,4 +103,3 @@ void getTrackFormat(const ImgInputOutputProto& config,
trackdata.MergeFrom(f);
}
}

View File

@@ -1,9 +1,10 @@
#ifndef IMAGEREADER_H
#define IMAGEREADER_H
#include "image.h"
class ImageSpec;
class ImageReaderProto;
class Image;
class ImageReader
{
@@ -23,9 +24,12 @@ public:
static std::unique_ptr<ImageReader> createIMDImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createNsiImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createTd0ImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createDimImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createFdiImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createD88ImageReader(const ImageReaderProto& config);
public:
virtual Image readImage() = 0;
virtual std::unique_ptr<Image> readImage() = 0;
protected:
const ImageReaderProto& _config;

View File

@@ -38,6 +38,9 @@ message Jv3InputProto {}
message D64InputProto {}
message NsiInputProto {}
message Td0InputProto {}
message DimInputProto {}
message FdiInputProto {}
message D88InputProto {}
message ImageReaderProto {
optional string filename = 1 [(help) = "filename of input sector image"];
@@ -49,6 +52,9 @@ message ImageReaderProto {
D64InputProto d64 = 6;
NsiInputProto nsi = 7;
Td0InputProto td0 = 8;
DimInputProto dim = 9;
FdiInputProto fdi = 10;
D88InputProto d88 = 11;
}
}

View File

@@ -3,6 +3,7 @@
#include "sector.h"
#include "imagereader/imagereader.h"
#include "image.h"
#include "proto.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
@@ -11,68 +12,68 @@
static unsigned getModulationandSpeed(uint8_t flags, bool *mfm)
{
switch (flags)
{
case 0: /* 500 kbps FM */
//clockRateKhz.setDefaultValue(250);
*mfm = false;
return 500;
break;
switch (flags)
{
case 0: /* 500 kbps FM */
//clockRateKhz.setDefaultValue(250);
*mfm = false;
return 500;
break;
case 1: /* 300 kbps FM */
*mfm = false;
return 300;
break;
case 1: /* 300 kbps FM */
*mfm = false;
return 300;
break;
case 2: /* 250 kbps FM */
*mfm = false;
return 250;
break;
case 2: /* 250 kbps FM */
*mfm = false;
return 250;
break;
case 3: /* 500 kbps MFM */
*mfm = true;
return 500;
break;
case 3: /* 500 kbps MFM */
*mfm = true;
return 500;
break;
case 4: /* 300 kbps MFM */
*mfm = true;
return 300;
break;
case 4: /* 300 kbps MFM */
*mfm = true;
return 300;
break;
case 5: /* 250 kbps MFM */
*mfm = true;
return 250;
break;
case 5: /* 250 kbps MFM */
*mfm = true;
return 250;
break;
default:
Error() << fmt::format("don't understand IMD disks with this modulation and speed {}", flags);
throw 0;
}
default:
Error() << fmt::format("don't understand IMD disks with this modulation and speed {}", flags);
throw 0;
}
}
struct TrackHeader
{
uint8_t ModeValue;
uint8_t track;
uint8_t Head;
uint8_t numSectors;
uint8_t SectorSize;
uint8_t ModeValue;
uint8_t track;
uint8_t Head;
uint8_t numSectors;
uint8_t SectorSize;
};
static unsigned getSectorSize(uint8_t flags)
{
switch (flags)
{
case 0: return 128;
case 1: return 256;
case 2: return 512;
case 3: return 1024;
case 4: return 2048;
case 5: return 4096;
case 6: return 8192;
}
Error() << "not reachable";
switch (flags)
{
case 0: return 128;
case 1: return 256;
case 2: return 512;
case 3: return 1024;
case 4: return 2048;
case 5: return 4096;
case 6: return 8192;
}
Error() << "not reachable";
}
@@ -85,204 +86,171 @@ static unsigned getSectorSize(uint8_t flags)
class IMDImageReader : public ImageReader
{
public:
IMDImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
IMDImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
Image readImage()
/*
IMAGE FILE FORMAT
The overall layout of an ImageDisk .IMD image file is:
IMD v.vv: dd/mm/yyyy hh:mm:ss
Comment (ASCII only - unlimited size)
1A byte - ASCII EOF character
- For each track on the disk:
1 byte Mode value see getModulationspeed for definition
1 byte Cylinder
1 byte Head
1 byte number of sectors in track
1 byte sector size see getsectorsize for definition
sector numbering map
sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
sector head map (optional) definied in high byte of head (since head is 0 or 1)
sector data records
<End of file>
*/
{
//Read File
std::unique_ptr<Image> readImage()
/*
IMAGE FILE FORMAT
The overall layout of an ImageDisk .IMD image file is:
IMD v.vv: dd/mm/yyyy hh:mm:ss
Comment (ASCII only - unlimited size)
1A byte - ASCII EOF character
- For each track on the disk:
1 byte Mode value see getModulationspeed for definition
1 byte Cylinder
1 byte Head
1 byte number of sectors in track
1 byte sector size see getsectorsize for definition
sector numbering map
sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
sector head map (optional) definied in high byte of head (since head is 0 or 1)
sector data records
<End of file>
*/
{
//Read File
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
//define some variables
bool mfm = false; //define coding just to show in comment for setting the right write parameters
inputFile.seekg(0, inputFile.end);
int inputFileSize = inputFile.tellg(); // determine filesize
inputFile.seekg(0, inputFile.beg);
Bytes data;
data.writer() += inputFile;
ByteReader br(data);
Image image;
TrackHeader header = {0, 0, 0, 0, 0};
//define some variables
bool mfm = false; //define coding just to show in comment for setting the right write parameters
inputFile.seekg(0, inputFile.end);
int inputFileSize = inputFile.tellg(); // determine filesize
inputFile.seekg(0, inputFile.beg);
Bytes data;
data.writer() += inputFile;
ByteReader br(data);
std::unique_ptr<Image> image(new Image);
TrackHeader header = {0, 0, 0, 0, 0};
unsigned n = 0;
unsigned headerPtr = 0;
unsigned Modulation_Speed = 0;
unsigned sectorSize = 0;
std::string sector_skew;
int b;
unsigned char comment[8192]; //i choose a fixed value. dont know how to make dynamic arrays in C++. This should be enough
// Read comment
while ((b = br.read_8()) != EOF && b != 0x1A)
{
comment[n++] = (unsigned char)b;
}
headerPtr = n; //set pointer to after comment
comment[n] = '\0'; // null-terminate the string
//write comment to screen
std::cout << "Comment in IMD image:\n"
<< fmt::format("{}\n",
comment);
unsigned n = 0;
unsigned headerPtr = 0;
unsigned Modulation_Speed = 0;
unsigned sectorSize = 0;
std::string sector_skew;
int b;
unsigned char comment[8192]; //i choose a fixed value. dont know how to make dynamic arrays in C++. This should be enough
// Read comment
while ((b = br.read_8()) != EOF && b != 0x1A)
{
comment[n++] = (unsigned char)b;
}
headerPtr = n; //set pointer to after comment
comment[n] = '\0'; // null-terminate the string
//write comment to screen
std::cout << "Comment in IMD image:\n"
<< fmt::format("{}\n",
comment);
//first read header
for (;;)
{
if (headerPtr >= inputFileSize-1)
{
break;
}
header.ModeValue = br.read_8();
headerPtr++;
Modulation_Speed = getModulationandSpeed(header.ModeValue, &mfm);
header.track = br.read_8();
headerPtr++;
header.Head = br.read_8();
headerPtr++;
header.numSectors = br.read_8();
headerPtr++;
header.SectorSize = br.read_8();
headerPtr++;
sectorSize = getSectorSize(header.SectorSize);
//first read header
for (;;)
{
if (headerPtr >= inputFileSize-1)
{
break;
}
header.ModeValue = br.read_8();
headerPtr++;
Modulation_Speed = getModulationandSpeed(header.ModeValue, &mfm);
header.track = br.read_8();
headerPtr++;
header.Head = br.read_8();
headerPtr++;
header.numSectors = br.read_8();
headerPtr++;
header.SectorSize = br.read_8();
headerPtr++;
sectorSize = getSectorSize(header.SectorSize);
//Read optional cylinder map To Do
//Read optional cylinder map To Do
//Read optional sector head map To Do
//Read optional sector head map To Do
//read sector numbering map
std::vector<unsigned> sector_map(header.numSectors);
bool blnBaseOne = false;
sector_skew.clear();
for (b = 0; b < header.numSectors; b++)
{
sector_map[b] = br.read_8();
sector_skew.push_back(sector_map[b] + '0');
if (b == 0) //first sector see if base is 0 or 1 Fluxengine wants 0
{
if (sector_map[b]==1)
{
blnBaseOne = true;
}
}
if (blnBaseOne==true)
{
sector_map[b] = (sector_map[b]-1);
}
headerPtr++;
}
//read the sectors
for (int s = 0; s < header.numSectors; s++)
{
Bytes sectordata;
const auto& sector = image.put(header.track, header.Head, sector_map[s]);
//read the status of the sector
unsigned int Status_Sector = br.read_8();
headerPtr++;
//read sector numbering map
std::vector<unsigned> sector_map(header.numSectors);
sector_skew.clear();
for (int b = 0; b < header.numSectors; b++)
{
sector_map[b] = br.read_8();
headerPtr++;
}
switch (Status_Sector)
{
case 0: /* Sector data unavailable - could not be read */
//read the sectors
for (int s = 0; s < header.numSectors; s++)
{
Bytes sectordata;
const auto& sector = image->put(header.track, header.Head, sector_map[s]);
//read the status of the sector
unsigned int Status_Sector = br.read_8();
headerPtr++;
break;
switch (Status_Sector)
{
case 0: /* Sector data unavailable - could not be read */
break;
case 1: /* Normal data: (Sector Size) bytes follow */
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->data.writer().append(sectordata);
case 1: /* Normal data: (Sector Size) bytes follow */
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->data.writer().append(sectordata);
break;
break;
case 2: /* Compressed: All bytes in sector have same value (xx) */
sectordata = br.read(1);
headerPtr++;
sector->data.writer().append(sectordata);
case 2: /* Compressed: All bytes in sector have same value (xx) */
sectordata = br.read(1);
headerPtr++;
sector->data.writer().append(sectordata);
for (int k = 1; k < sectorSize; k++)
{
//fill data till sector is full
sector->data.writer().append(sectordata);
}
break;
for (int k = 1; k < sectorSize; k++)
{
//fill data till sector is full
sector->data.writer().append(sectordata);
}
case 3: /* Normal data with "Deleted-Data address mark" */
case 4: /* Compressed with "Deleted-Data address mark"*/
case 5: /* Normal data read with data error */
case 6: /* Compressed read with data error" */
case 7: /* Deleted data read with data error" */
case 8: /* Compressed, Deleted read with data error" */
default:
Error() << fmt::format("don't understand IMD disks with sector status {}", Status_Sector);
}
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = header.track;
sector->logicalSide = sector->physicalHead = header.Head;
sector->logicalSector = (sector_map[s]);
}
break;
}
//Write format detected in IMD image to screen to help user set the right write parameters
case 3: /* Normal data with "Deleted-Data address mark" */
image->setGeometry({
.numTracks = header.track,
.numSides = header.Head + 1U,
.numSectors = header.numSectors,
.sectorSize = sectorSize
});
break;
case 4: /* Compressed with "Deleted-Data address mark"*/
break;
case 5: /* Normal data read with data error */
break;
case 6: /* Compressed read with data error" */
break;
case 7: /* Deleted data read with data error" */
break;
case 8: /* Compressed, Deleted read with data error" */
break;
default:
Error() << fmt::format("don't understand IMD disks with sector status {}", Status_Sector);
}
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = header.track;
sector->logicalSide = sector->physicalHead = header.Head;
sector->logicalSector = (sector_map[s]);
}
}
//Write format detected in IMD image to screen to help user set the right write parameters
image.setGeometry({
.numTracks = header.track,
.numSides = header.Head + 1U,
.numSectors = header.numSectors,
.sectorSize = sectorSize
});
size_t headSize = header.numSectors * sectorSize;
size_t headSize = header.numSectors * sectorSize;
size_t trackSize = headSize * (header.Head + 1);
std::cout << "reading IMD image\n"
<< fmt::format("{} tracks, {} heads; {}; {} kbps; {} sectoren; sectorsize {}; sectormap {}; {} kB total \n",
header.track, header.Head + 1,
mfm ? "MFM" : "FM",
Modulation_Speed, header.numSectors, sectorSize, sector_skew, (header.track+1) * trackSize / 1024);
std::cout << fmt::format("IMD: {} tracks, {} heads; {}; {} kbps; {} sectors; sectorsize {};\n"
" sectormap {}; {} kB total\n",
header.track, header.Head + 1,
mfm ? "MFM" : "FM",
Modulation_Speed, header.numSectors, sectorSize, sector_skew, (header.track+1) * trackSize / 1024);
return image;
}
}
};
std::unique_ptr<ImageReader> ImageReader::createIMDImageReader(
const ImageReaderProto& config)
const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new IMDImageReader(config));
}
// vim: ts=4 sw=4 et

View File

@@ -17,7 +17,7 @@ public:
ImageReader(config)
{}
Image readImage()
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
@@ -26,7 +26,7 @@ public:
if (!_config.img().tracks() || !_config.img().sides())
Error() << "IMG: bad configuration; did you remember to set the tracks, sides and trackdata fields?";
Image image;
std::unique_ptr<Image> image(new Image);
int trackCount = 0;
for (int track = 0; track < _config.img().tracks(); track++)
{
@@ -44,7 +44,7 @@ public:
Bytes data(trackdata.sector_size());
inputFile.read((char*) data.begin(), data.size());
const auto& sector = image.put(physicalCylinder, side, sectorId);
const auto& sector = image->put(physicalCylinder, side, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = track;
sector->physicalCylinder = physicalCylinder;
@@ -57,8 +57,8 @@ public:
trackCount++;
}
image.calculateSize();
const Geometry& geometry = image.getGeometry();
image->calculateSize();
const Geometry& geometry = image->getGeometry();
std::cout << fmt::format("IMG: read {} tracks, {} sides, {} kB total\n",
geometry.numTracks, geometry.numSides,
inputFile.tellg() / 1024);

View File

@@ -81,7 +81,7 @@ public:
ImageReader(config)
{}
Image readImage()
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
@@ -90,7 +90,7 @@ public:
inputFile.seekg( 0, std::ios::end);
unsigned inputFileSize = inputFile.tellg();
unsigned headerPtr = 0;
Image image;
std::unique_ptr<Image> image(new Image);
for (;;)
{
unsigned dataPtr = headerPtr + 2901*3 + 1;
@@ -110,7 +110,7 @@ public:
inputFile.read((char*) data.begin(), sectorSize);
unsigned head = !!(header.flags & JV3_SIDE);
const auto& sector = image.put(header.track, head, header.sector);
const auto& sector = image->put(header.track, head, header.sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = header.track;
sector->logicalSide = sector->physicalHead = head;
@@ -127,7 +127,7 @@ public:
headerPtr = dataPtr;
}
image.calculateSize();
image->calculateSize();
return image;
}
};

View File

@@ -18,7 +18,7 @@ public:
ImageReader(config)
{}
Image readImage()
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
@@ -64,7 +64,7 @@ public:
numCylinders * numHeads * trackSize / 1024)
<< std::endl;
Image image;
std::unique_ptr<Image> image(new Image);
unsigned sectorFileOffset;
for (unsigned head = 0; head < numHeads; head++)
@@ -87,7 +87,7 @@ public:
Bytes data(sectorSize);
inputFile.read((char*) data.begin(), sectorSize);
const auto& sector = image.put(track, head, sectorId);
const auto& sector = image->put(track, head, sectorId);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalCylinder = track;
sector->logicalSide = sector->physicalHead = head;
@@ -97,7 +97,7 @@ public:
}
}
image.setGeometry({
image->setGeometry({
.numTracks = numCylinders,
.numSides = numHeads,
.numSectors = numSectors,

View File

@@ -50,7 +50,7 @@ public:
ImageReader(config)
{}
Image readImage()
std::unique_ptr<Image> readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
@@ -97,7 +97,7 @@ public:
version / 10, version % 10, comment);
unsigned totalSize = 0;
Image image;
std::unique_ptr<Image> image(new Image);
for (;;)
{
/* Read track header */
@@ -177,7 +177,7 @@ public:
}
}
const auto& sector = image.put(logicalTrack, logicalSide, sectorId);
const auto& sector = image->put(logicalTrack, logicalSide, sectorId);
sector->status = Sector::OK;
sector->physicalCylinder = physicalCylinder;
sector->physicalHead = physicalHead;
@@ -186,8 +186,8 @@ public:
}
}
image.calculateSize();
const Geometry& geometry = image.getGeometry();
image->calculateSize();
const Geometry& geometry = image->getGeometry();
std::cout << fmt::format("TD0: found {} tracks, {} sides, {} sectors, {} bytes per sector, {} kB total\n",
geometry.numTracks, geometry.numSides, geometry.numSectors,
geometry.sectorSize,

View File

@@ -47,6 +47,7 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st
{".ldbs", [&]() { proto->mutable_ldbs(); }},
{".st", [&]() { proto->mutable_img(); }},
{".nsi", [&]() { proto->mutable_nsi(); }},
{".xdf", [&]() { proto->mutable_img(); }},
};
for (const auto& it : formats)

View File

@@ -16,7 +16,6 @@
#include "fmt/format.h"
#include "proto.h"
#include "lib/decoders/decoders.pb.h"
#include "lib/data.pb.h"
#include <iostream>
#include <fstream>
@@ -27,11 +26,9 @@ static std::shared_ptr<Fluxmap> readFluxmap(FluxSource& fluxsource, unsigned cyl
std::cout << fmt::format("{0:>3}.{1}: ", cylinder, head) << std::flush;
std::shared_ptr<Fluxmap> fluxmap = fluxsource.readFlux(cylinder, head);
std::cout << fmt::format(
"{0} ms in {1} bytes\n",
"{0:.0} ms in {1} bytes\n",
fluxmap->duration()/1e6,
fluxmap->bytes());
if (outputFluxSink)
outputFluxSink->writeFlux(cylinder, head, *fluxmap);
return fluxmap;
}
@@ -83,10 +80,13 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
auto track = std::make_unique<TrackFlux>();
std::set<std::shared_ptr<Sector>> track_sectors;
std::set<std::shared_ptr<Record>> track_records;
Fluxmap totalFlux;
for (int retry = config.decoder().retries(); retry >= 0; retry--)
{
auto fluxmap = readFluxmap(fluxsource, cylinder, head);
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
{
auto trackdata = decoder.decodeToSectors(fluxmap, cylinder, head);
@@ -105,6 +105,7 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
track_records.insert(trackdata->records.begin(), trackdata->records.end());
track->trackDatas.push_back(std::move(trackdata));
}
auto collected_sectors = collect_sectors(track_sectors);
std::cout << fmt::format("{} distinct sectors; ", collected_sectors.size());
@@ -147,6 +148,9 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
std::cout << retry << " retries remaining" << std::endl;
}
if (outputFluxSink)
outputFluxSink->writeFlux(cylinder, head, totalFlux);
if (config.decoder().dump_records())
{
std::cout << "\nRaw (undecoded) records follow:\n\n";

View File

@@ -108,6 +108,11 @@
typedef int FileHandle;
static FileHandle open_serial_port(const std::string& name)
{
#ifdef __APPLE__
if (name.find("/dev/tty.") != std::string::npos)
std::cerr << "Warning: you probably want to be using a /dev/cu.* device\n";
#endif
int fd = open(name.c_str(), O_RDWR);
if (fd == -1)
Error() << fmt::format("cannot open GreaseWeazle serial port '{}': {}",
@@ -515,7 +520,7 @@ public:
{
do_command({ CMD_SELECT, 3, (uint8_t)drive });
do_command({ CMD_MOTOR, 4, (uint8_t)drive, 1 });
do_command({ CMD_SET_PIN, 4, 2, (uint8_t)(high_density ? 0 : 1) });
do_command({ CMD_SET_PIN, 4, 2, (uint8_t)(high_density ? 1 : 0) });
}
void measureVoltages(struct voltages_frame* voltages)

View File

@@ -7,11 +7,15 @@ bool beginsWith(const std::string& value, const std::string& ending)
return std::equal(ending.begin(), ending.end(), value.begin());
}
// Case-insensitive for endings within ASCII.
bool endsWith(const std::string& value, const std::string& ending)
{
if (ending.size() > value.size())
return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
std::string lowercase(ending.size(), 0);
std::transform(value.rbegin(), value.rbegin() + ending.size(), lowercase.begin(), [](unsigned char c){ return std::tolower(c); });
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()) ||
std::equal(ending.rbegin(), ending.rend(), lowercase.begin());
}

View File

@@ -56,7 +56,7 @@ void writeTracksAndVerify(
AbstractEncoder& encoder,
FluxSource& fluxSource,
AbstractDecoder& decoder,
Image& image)
const Image& image)
{
std::cout << "Writing to: " << fluxSink << std::endl;
@@ -129,10 +129,9 @@ void fillBitmapTo(std::vector<bool>& bitmap,
}
}
void writeDiskCommand(ImageReader& imageReader, AbstractEncoder& encoder, FluxSink& fluxSink,
void writeDiskCommand(const Image& image, AbstractEncoder& encoder, FluxSink& fluxSink,
AbstractDecoder* decoder, FluxSource* fluxSource)
{
Image image = imageReader.readImage();
if (fluxSource && decoder)
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, image);
else

View File

@@ -1,21 +1,22 @@
#ifndef WRITER_H
#define WRITER_H
class Fluxmap;
class AbstractDecoder;
class AbstractEncoder;
class ImageReader;
class FluxSource;
class FluxSink;
extern void writeTracks(FluxSink& fluxSink, const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);
extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor, unsigned terminateAt,
const std::vector<bool>& pattern);
extern void writeDiskCommand(ImageReader& imageReader, AbstractEncoder& encoder, FluxSink& fluxSink,
AbstractDecoder* decoder = nullptr, FluxSource* fluxSource = nullptr);
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
#endif
#ifndef WRITER_H
#define WRITER_H
class Fluxmap;
class AbstractDecoder;
class AbstractEncoder;
class ImageReader;
class FluxSource;
class FluxSink;
class Image;
extern void writeTracks(FluxSink& fluxSink, const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);
extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor, unsigned terminateAt,
const std::vector<bool>& pattern);
extern void writeDiskCommand(const Image& image, AbstractEncoder& encoder, FluxSink& fluxSink,
AbstractDecoder* decoder = nullptr, FluxSource* fluxSource = nullptr);
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
#endif

View File

@@ -4,7 +4,7 @@ set -e
cat <<EOF
rule cxx
command = $CXX $CFLAGS \$flags -I. -c -o \$out \$in -MMD -MF \$out.d
description = CXX \$in
description = CXX \$out
depfile = \$out.d
deps = gcc
@@ -36,8 +36,8 @@ rule test
description = TEST \$in
rule encodedecode
command = sh scripts/encodedecodetest.sh \$format \$configs > \$out
description = ENCODEDECODE \$format
command = sh scripts/encodedecodetest.sh \$format \$fluxx \$configs > \$out
description = ENCODEDECODE \$fluxx \$format
rule strip
command = cp -f \$in \$out && $STRIP \$out
@@ -244,14 +244,12 @@ runtest() {
buildlibrary lib$prog.a \
-Idep/snowhouse/include \
-d $OBJDIR/proto/libconfig.def \
-d $OBJDIR/proto/libdata.def \
"$@"
buildprogram $OBJDIR/$prog \
lib$prog.a \
libbackend.a \
libconfig.a \
libdata.a \
libtestproto.a \
libagg.a \
libfmt.a
@@ -264,9 +262,18 @@ encodedecodetest() {
format=$1
shift
echo "build $OBJDIR/$format.encodedecode.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*"
echo "build $OBJDIR/$format.encodedecode.flux.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*"
echo " format=$format"
echo " configs=$*"
echo " fluxx=flux"
echo "build $OBJDIR/$format.encodedecode.scp.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*"
echo " format=$format"
echo " configs=$*"
echo " fluxx=scp"
echo "build $OBJDIR/$format.encodedecode.fl2.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*"
echo " format=$format"
echo " configs=$*"
echo " fluxx=fl2"
}
buildlibrary libagg.a \
@@ -304,13 +311,13 @@ buildproto libconfig.a \
lib/imagewriter/imagewriter.proto \
lib/usb/usb.proto \
buildproto libdata.a \
lib/data.proto
buildproto libfl2.a \
lib/fl2.proto
buildlibrary libbackend.a \
-I$OBJDIR/proto \
-d $OBJDIR/proto/libconfig.def \
-d $OBJDIR/proto/libdata.def \
-d $OBJDIR/proto/libfl2.def \
arch/aeslanier/decoder.cc \
arch/amiga/amiga.cc \
arch/amiga/decoder.cc \
@@ -334,18 +341,21 @@ buildlibrary libbackend.a \
arch/tids990/decoder.cc \
arch/tids990/encoder.cc \
arch/victor9k/decoder.cc \
arch/victor9k/encoder.cc \
arch/zilogmcz/decoder.cc \
lib/bitmap.cc \
lib/bytes.cc \
lib/crc.cc \
lib/csvreader.cc \
lib/decoders/decoders.cc \
lib/decoders/fluxdecoder.cc \
lib/decoders/fluxmapreader.cc \
lib/decoders/fmmfm.cc \
lib/encoders/encoders.cc \
lib/flags.cc \
lib/fluxmap.cc \
lib/fluxsink/aufluxsink.cc \
lib/fluxsink/fl2fluxsink.cc \
lib/fluxsink/fluxsink.cc \
lib/fluxsink/hardwarefluxsink.cc \
lib/fluxsink/scpfluxsink.cc \
@@ -353,6 +363,7 @@ buildlibrary libbackend.a \
lib/fluxsink/vcdfluxsink.cc \
lib/fluxsource/cwffluxsource.cc \
lib/fluxsource/erasefluxsource.cc \
lib/fluxsource/fl2fluxsource.cc \
lib/fluxsource/fluxsource.cc \
lib/fluxsource/hardwarefluxsource.cc \
lib/fluxsource/kryoflux.cc \
@@ -371,6 +382,9 @@ buildlibrary libbackend.a \
lib/imagereader/jv3imagereader.cc \
lib/imagereader/nsiimagereader.cc \
lib/imagereader/td0imagereader.cc \
lib/imagereader/dimimagereader.cc \
lib/imagereader/fdiimagereader.cc \
lib/imagereader/d88imagereader.cc \
lib/imagewriter/d64imagewriter.cc \
lib/imagewriter/diskcopyimagewriter.cc \
lib/imagewriter/imagewriter.cc \
@@ -416,6 +430,7 @@ FORMATS="\
hplif770 \
ibm \
ibm1200_525 \
ibm1232 \
ibm1440 \
ibm180_525 \
ibm360_525 \
@@ -429,7 +444,7 @@ FORMATS="\
northstar350 \
northstar87 \
tids990 \
victor9k \
victor9k_ss \
zilogmcz \
"
@@ -443,7 +458,6 @@ buildmktable formats $OBJDIR/formats.cc $FORMATS
buildlibrary libfrontend.a \
-I$OBJDIR/proto \
-d $OBJDIR/proto/libconfig.def \
-d $OBJDIR/proto/libdata.def \
$(for a in $FORMATS; do echo $OBJDIR/proto/src/formats/$a.cc; done) \
$OBJDIR/formats.cc \
src/fe-analysedriveresponse.cc \
@@ -464,7 +478,7 @@ buildprogram fluxengine \
libfrontend.a \
libbackend.a \
libconfig.a \
libdata.a \
libfl2.a \
libfmt.a \
libagg.a \
@@ -497,6 +511,7 @@ runtest bytes-test tests/bytes.cc
runtest compression-test tests/compression.cc
runtest csvreader-test tests/csvreader.cc
runtest flags-test tests/flags.cc
runtest fluxmapreader-test tests/fluxmapreader.cc
runtest fluxpattern-test tests/fluxpattern.cc
runtest fmmfm-test tests/fmmfm.cc
runtest greaseweazle-test tests/greaseweazle.cc
@@ -504,7 +519,6 @@ runtest kryoflux-test tests/kryoflux.cc
runtest ldbs-test tests/ldbs.cc
runtest proto-test -I$OBJDIR/proto \
-d $OBJDIR/proto/libconfig.def \
-d $OBJDIR/proto/libdata.def \
-d $OBJDIR/proto/libtestproto.def \
tests/proto.cc \
$OBJDIR/proto/tests/testproto.cc
@@ -521,6 +535,7 @@ encodedecodetest atarist820
encodedecodetest brother120
encodedecodetest brother240
encodedecodetest ibm1200_525
encodedecodetest ibm1232
encodedecodetest ibm1440
encodedecodetest ibm180_525
encodedecodetest ibm360_525
@@ -531,6 +546,7 @@ encodedecodetest commodore1581
encodedecodetest commodore1541 scripts/commodore1541_test.textpb
encodedecodetest mac400 scripts/mac400_test.textpb
encodedecodetest mac800 scripts/mac800_test.textpb
encodedecodetest victor9k_ss
# vim: sw=4 ts=4 et

View File

@@ -88,7 +88,9 @@ enum
enum
{
F_BIT_PULSE = 0x80,
F_BIT_INDEX = 0x40
F_BIT_INDEX = 0x40,
F_DESYNC = 0x00,
F_EOF = 0x100 /* synthetic, only produced by library */
};
struct frame_header

View File

@@ -8,10 +8,11 @@ fi
tmp=/tmp/$$
srcfile=$tmp.src.img
fluxfile=$tmp.flux
fluxfile=$tmp.$2
destfile=$tmp.dest.img
format=$1
shift
shift
trap "rm -f $srcfile $fluxfile $destfile" EXIT

View File

@@ -257,10 +257,11 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
FluxmapReader fmr(inFluxmap);
fmr.seek((double)period*0.1); /* skip first 10% and last 10% as contains junk */
fmr.findEvent(F_BIT_PULSE);
fmr.skipToEvent(F_BIT_PULSE);
while (fmr.tell().ns() < ((double)period*0.9))
{
unsigned ticks = fmr.findEvent(F_BIT_PULSE);
unsigned ticks;
fmr.findEvent(F_BIT_PULSE, ticks);
if (ticks < numColumns)
frequencies[row][ticks]++;
}

View File

@@ -3,6 +3,7 @@
#include "reader.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/fluxdecoder.h"
#include "decoders/decoders.h"
#include "fluxsource/fluxsource.h"
#include "protocol.h"
@@ -91,7 +92,8 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap)
while (!fr.eof())
{
unsigned interval = fr.findEvent(F_BIT_PULSE);
unsigned interval;
fr.findEvent(F_BIT_PULSE, interval);
if (interval > 0xff)
continue;
buckets[interval]++;
@@ -233,7 +235,9 @@ int mainInspect(int argc, const char* argv[])
nanoseconds_t lasttransition = 0;
while (!fmr.eof())
{
ticks += fmr.findEvent(F_BIT_PULSE);
unsigned thisTicks;
fmr.findEvent(F_BIT_PULSE, thisTicks);
ticks += thisTicks;
nanoseconds_t transition = ticks*NS_PER_TICK;
nanoseconds_t next;
@@ -277,6 +281,7 @@ int mainInspect(int argc, const char* argv[])
std::cout << fmt::format("\n\nAligned bitstream from {:.3f}ms follows:\n",
fmr.tell().ns() / 1000000.0);
FluxDecoder decoder(&fmr, clockPeriod, config.decoder());
while (!fmr.eof())
{
std::cout << fmt::format("{:06x} {: 10.3f} : ",
@@ -285,7 +290,7 @@ int mainInspect(int argc, const char* argv[])
{
if (fmr.eof())
break;
bool b = fmr.readRawBit(clockPeriod);
bool b = decoder.readBit();
std::cout << (b ? 'X' : '-');
}

View File

@@ -1,7 +1,7 @@
#include "globals.h"
#include "flags.h"
#include "usb/usb.h"
#include "fluxsource/fluxsource.cc"
#include "fluxsource/fluxsource.h"
#include "proto.h"
#include "protocol.h"

View File

@@ -62,6 +62,8 @@ int mainWrite(int argc, const char* argv[])
flags.parseFlagsWithConfigFiles(argc, argv, formats);
std::unique_ptr<ImageReader> reader(ImageReader::create(config.image_reader()));
std::unique_ptr<Image> image = reader->readImage();
std::unique_ptr<AbstractEncoder> encoder(AbstractEncoder::create(config.encoder()));
std::unique_ptr<FluxSink> fluxSink(FluxSink::create(config.flux_sink()));
@@ -73,7 +75,7 @@ int mainWrite(int argc, const char* argv[])
if (config.has_flux_source() && config.flux_source().has_drive())
fluxSource = FluxSource::create(config.flux_source());
writeDiskCommand(*reader, *encoder, *fluxSink, decoder.get(), fluxSource.get());
writeDiskCommand(*image, *encoder, *fluxSink, decoder.get(), fluxSource.get());
return 0;
}

View File

@@ -1,4 +1,4 @@
comment: 'PC 3.5"/5.25" autodetect double sided format (ro)'
comment: 'PC 3.5"/5.25" autodetect double sided format'
image_writer {
filename: "ibm.img"

View File

@@ -0,0 +1,68 @@
comment: 'Japanese PC 1232kB 5.25"/3.5" 77-track 8-sector DSHD'
flux_sink {
drive {
high_density: true
}
}
flux_source {
drive {
high_density: true
}
}
image_reader {
filename: "ibm1232.img"
img {
tracks: 77
sides: 2
trackdata {
sector_size: 1024
sector_range {
start_sector: 1
sector_count: 8
}
}
}
}
image_writer {
filename: "ibm1232.img"
img {}
}
encoder {
ibm {
trackdata {
sector_size: 1024
track_length_ms: 167
clock_rate_khz: 500
sectors {
sector: 1
sector: 2
sector: 3
sector: 4
sector: 5
sector: 6
sector: 7
sector: 8
}
}
}
}
decoder {
ibm {}
}
cylinders {
start: 0
end: 76
}
heads {
start: 0
end: 1
}

View File

@@ -1,21 +0,0 @@
comment: 'Victor 9000 / Sirius One 1224kB SSHD GCR variable sector (ro)'
image_writer {
filename: "victor9k.img"
img {}
}
decoder {
victor9k {}
}
cylinders {
start: 0
end: 79
}
heads {
start: 0
end: 0
}

View File

@@ -0,0 +1,259 @@
comment: 'Victor 9000 / Sirius One 612kB SSHD GCR variable sector)'
image_reader {
filename: "victor9k.img"
img {
tracks: 80
sides: 1
trackdata {
sector_size: 512
}
trackdata {
track: 0
up_to_track: 3
sector_range {
start_sector: 1
sector_count: 19
}
}
trackdata {
track: 4
up_to_track: 15
sector_range {
start_sector: 1
sector_count: 18
}
}
trackdata {
track: 16
up_to_track: 26
sector_range {
start_sector: 1
sector_count: 17
}
}
trackdata {
track: 27
up_to_track: 37
sector_range {
start_sector: 1
sector_count: 16
}
}
trackdata {
track: 38
up_to_track: 48
sector_range {
start_sector: 1
sector_count: 15
}
}
trackdata {
track: 49
up_to_track: 59
sector_range {
start_sector: 1
sector_count: 14
}
}
trackdata {
track: 60
up_to_track: 70
sector_range {
start_sector: 1
sector_count: 13
}
}
trackdata {
track: 71
up_to_track: 79
sector_range {
start_sector: 1
sector_count: 12
}
}
}
}
image_writer {
filename: "victor9k.img"
img {
tracks: 80
sides: 1
trackdata {
sector_size: 512
}
trackdata {
track: 0
up_to_track: 3
sector_range {
start_sector: 1
sector_count: 19
}
}
trackdata {
track: 4
up_to_track: 15
sector_range {
start_sector: 1
sector_count: 18
}
}
trackdata {
track: 16
up_to_track: 26
sector_range {
start_sector: 1
sector_count: 17
}
}
trackdata {
track: 27
up_to_track: 37
sector_range {
start_sector: 1
sector_count: 16
}
}
trackdata {
track: 38
up_to_track: 48
sector_range {
start_sector: 1
sector_count: 15
}
}
trackdata {
track: 49
up_to_track: 59
sector_range {
start_sector: 1
sector_count: 14
}
}
trackdata {
track: 60
up_to_track: 70
sector_range {
start_sector: 1
sector_count: 13
}
}
trackdata {
track: 71
up_to_track: 79
sector_range {
start_sector: 1
sector_count: 12
}
}
}
}
encoder {
victor9k {
trackdata {
original_data_rate_khz: 500
post_index_gap_us: 1000.0
pre_header_sync_bits: 60
post_header_gap_bits: 90
pre_data_sync_bits: 50
post_data_gap_bits: 300
}
trackdata {
head: 0
min_cylinder: 0
max_cylinder: 3
original_period_ms: 237.9
sector_range {
start_sector: 1
sector_count: 19
}
}
trackdata {
head: 0
min_cylinder: 4
max_cylinder: 15
original_period_ms: 224.5
sector_range {
start_sector: 1
sector_count: 18
}
}
trackdata {
head: 0
min_cylinder: 16
max_cylinder: 26
original_period_ms: 212.2
sector_range {
start_sector: 1
sector_count: 17
}
}
trackdata {
head: 0
min_cylinder: 27
max_cylinder: 37
original_period_ms: 199.9
sector_range {
start_sector: 1
sector_count: 16
}
}
trackdata {
head: 0
min_cylinder: 38
max_cylinder: 48
original_period_ms: 187.6
sector_range {
start_sector: 1
sector_count: 15
}
}
trackdata {
head: 0
min_cylinder: 49
max_cylinder: 59
original_period_ms: 175.3
sector_range {
start_sector: 1
sector_count: 14
}
}
trackdata {
head: 0
min_cylinder: 60
max_cylinder: 70
original_period_ms: 163.0
sector_range {
start_sector: 1
sector_count: 13
}
}
trackdata {
head: 0
min_cylinder: 71
max_cylinder: 79
original_period_ms: 149.6
sector_range {
start_sector: 1
sector_count: 12
}
}
}
}
decoder {
victor9k {}
}
cylinders {
start: 0
end: 79
}
heads {
start: 0
end: 0
}

89
tests/fluxmapreader.cc Normal file
View File

@@ -0,0 +1,89 @@
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "fmt/format.h"
#include "tests.h"
#include <sstream>
static Fluxmap fluxmap(Bytes {
F_DESYNC,
F_BIT_PULSE | 0x30,
F_BIT_INDEX | 0x30,
F_BIT_PULSE | F_BIT_INDEX | 0x30,
0x30, F_BIT_PULSE | 0x30,
F_DESYNC,
F_BIT_PULSE | 0x30,
0x30,
F_DESYNC,
0x30,
F_BIT_PULSE | 0x30
});
#define ASSERT_NEXT_EVENT(wantedEvent, wantedTicks) \
do { \
unsigned gotTicks; \
int gotEvent; \
fmr.getNextEvent(gotEvent, gotTicks); \
assert((gotEvent == (wantedEvent)) && (gotTicks == (wantedTicks))); \
} while(0)
#define ASSERT_READ_SPECIFIC_EVENT(wantedEvent, wantedTicks) \
do { \
unsigned gotTicks; \
fmr.findEvent(wantedEvent, gotTicks); \
assertThat(gotTicks).isEqualTo(wantedTicks); \
} while(0)
void test_read_all_events()
{
FluxmapReader fmr(fluxmap);
ASSERT_NEXT_EVENT(F_DESYNC, 0);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x30);
ASSERT_NEXT_EVENT(F_BIT_INDEX, 0x30);
ASSERT_NEXT_EVENT(F_BIT_PULSE | F_BIT_INDEX, 0x30);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x60);
ASSERT_NEXT_EVENT(F_DESYNC, 0);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x30);
ASSERT_NEXT_EVENT(F_DESYNC, 0x30);
ASSERT_NEXT_EVENT(F_BIT_PULSE, 0x60);
ASSERT_NEXT_EVENT(F_EOF, 0);
ASSERT_NEXT_EVENT(F_EOF, 0);
}
void test_read_pulses()
{
FluxmapReader fmr(fluxmap);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x30);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x30);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_PULSE, 0x90);
}
void test_read_indices()
{
FluxmapReader fmr(fluxmap);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 0x30);
ASSERT_READ_SPECIFIC_EVENT(F_BIT_INDEX, 6*0x30);
}
void test_read_desyncs()
{
FluxmapReader fmr(fluxmap);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0xf0);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0x60);
ASSERT_READ_SPECIFIC_EVENT(F_DESYNC, 0x60);
}
int main(int argc, const char* argv[])
{
test_read_all_events();
test_read_pulses();
test_read_indices();
test_read_desyncs();
return 0;
}

38
tests/tests.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef TESTS_H
#define TESTS_H
class AssertionError
{};
template <class T>
class Subject
{
public:
Subject(const std::string& filename, int lineno, T value):
_filename(filename),
_lineno(lineno),
_value(value) {}
public:
void isEqualTo(T wanted)
{
if (_value != wanted)
fail(fmt::format("wanted {}, got {}", wanted, _value));
}
private:
void fail(const std::string& message)
{
Error() << "assertion failed: " << _filename << ":" << _lineno << ": " << message;
}
private:
const std::string _filename;
int _lineno;
T _value;
};
#define assertThat(value) Subject(__FILE__, __LINE__, value)
#endif