Merge from master.

This commit is contained in:
David Given
2021-07-11 17:38:09 +02:00
43 changed files with 1730 additions and 1653 deletions

View File

@@ -5,18 +5,6 @@
#define AESLANIER_SECTOR_LENGTH 256 #define AESLANIER_SECTOR_LENGTH 256
#define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5) #define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5)
class Sector; extern std::unique_ptr<AbstractDecoder> createAesLanierDecoder(const DecoderProto& config);
class Fluxmap;
class AesLanierDecoderProto;
class AesLanierDecoder : public AbstractDecoder
{
public:
AesLanierDecoder(const AesLanierDecoderProto&) {}
virtual ~AesLanierDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
#endif #endif

View File

@@ -24,7 +24,14 @@ static Bytes reverse_bits(const Bytes& input)
return output; return output;
} }
AbstractDecoder::RecordType AesLanierDecoder::advanceToNextRecord() class AesLanierDecoder : public AbstractDecoder
{
public:
AesLanierDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); _sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock) if (_fmr->eof() || !_sector->clock)
@@ -32,7 +39,7 @@ AbstractDecoder::RecordType AesLanierDecoder::advanceToNextRecord()
return SECTOR_RECORD; return SECTOR_RECORD;
} }
void AesLanierDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
/* Skip ID mark. */ /* Skip ID mark. */
@@ -63,3 +70,11 @@ void AesLanierDecoder::decodeSectorRecord()
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data); uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createAesLanierDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new AesLanierDecoder(config));
}

View File

@@ -9,37 +9,8 @@
#define AMIGA_SECTORS_PER_TRACK 11 #define AMIGA_SECTORS_PER_TRACK 11
#define AMIGA_RECORD_SIZE 0x21f #define AMIGA_RECORD_SIZE 0x21f
class Sector; extern std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config);
class Fluxmap; extern std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config);
class SectorSet;
class AmigaDecoderProto;
class AmigaEncoderProto;
class AmigaDecoder : public AbstractDecoder
{
public:
AmigaDecoder(const AmigaDecoderProto&) {}
virtual ~AmigaDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
std::set<unsigned> requiredSectors(FluxTrackProto& track) const;
};
class AmigaEncoder : public AbstractEncoder
{
public:
AmigaEncoder(const AmigaEncoderProto& config):
_config(config) {}
virtual ~AmigaEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
const AmigaEncoderProto& _config;
};
extern uint32_t amigaChecksum(const Bytes& bytes); extern uint32_t amigaChecksum(const Bytes& bytes);
extern Bytes amigaInterleave(const Bytes& input); extern Bytes amigaInterleave(const Bytes& input);

View File

@@ -8,6 +8,7 @@
#include "amiga.h" #include "amiga.h"
#include "bytes.h" #include "bytes.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "lib/decoders/decoders.pb.h"
#include <string.h> #include <string.h>
#include <algorithm> #include <algorithm>
@@ -21,7 +22,15 @@
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD); static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord() class AmigaDecoder : public AbstractDecoder
{
public:
AmigaDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.amiga())
{}
RecordType advanceToNextRecord()
{ {
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); _sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock) if (_fmr->eof() || !_sector->clock)
@@ -29,7 +38,7 @@ AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
return SECTOR_RECORD; return SECTOR_RECORD;
} }
void AmigaDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16); const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16)) if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
@@ -59,9 +68,18 @@ void AmigaDecoder::decodeSectorRecord()
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
std::set<unsigned> AmigaDecoder::requiredSectors(Track& track) const std::set<unsigned> requiredSectors(Track& track) const
{ {
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
return sectors; return sectors;
} }
private:
const AmigaDecoderProto& _config;
};
std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new AmigaDecoder(config));
}

View File

@@ -7,6 +7,7 @@
#include "sectorset.h" #include "sectorset.h"
#include "writer.h" #include "writer.h"
#include "arch/amiga/amiga.pb.h" #include "arch/amiga/amiga.pb.h"
#include "lib/encoders/encoders.pb.h"
static bool lastBit; static bool lastBit;
@@ -96,8 +97,15 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
write_interleaved_bytes(data); write_interleaved_bytes(data);
} }
std::unique_ptr<Fluxmap> AmigaEncoder::encode( class AmigaEncoder : public AbstractEncoder
int physicalTrack, int physicalSide, const SectorSet& allSectors) {
public:
AmigaEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.amiga()) {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{ {
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK)) if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>(); return std::unique_ptr<Fluxmap>();
@@ -124,3 +132,13 @@ std::unique_ptr<Fluxmap> AmigaEncoder::encode(
return fluxmap; return fluxmap;
} }
private:
const AmigaEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
}

View File

@@ -7,21 +7,7 @@
#define APPLE2_SECTOR_LENGTH 256 #define APPLE2_SECTOR_LENGTH 256
#define APPLE2_ENCODED_SECTOR_LENGTH 342 #define APPLE2_ENCODED_SECTOR_LENGTH 342
class Sector; extern std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config);
class Fluxmap;
class Apple2DecoderProto;
class Apple2Decoder : public AbstractDecoder
{
public:
Apple2Decoder(const Apple2DecoderProto&) {}
virtual ~Apple2Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
#endif #endif

View File

@@ -60,12 +60,19 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
return output; return output;
} }
uint8_t combine(uint16_t word) static uint8_t combine(uint16_t word)
{ {
return word & (word >> 7); return word & (word >> 7);
} }
AbstractDecoder::RecordType Apple2Decoder::advanceToNextRecord() class Apple2Decoder : public AbstractDecoder
{
public:
Apple2Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -76,7 +83,7 @@ AbstractDecoder::RecordType Apple2Decoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD; return RecordType::UNKNOWN_RECORD;
} }
void Apple2Decoder::decodeSectorRecord() void decodeSectorRecord()
{ {
/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */ /* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */
readRawBits(24); readRawBits(24);
@@ -94,7 +101,7 @@ void Apple2Decoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ _sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
} }
void Apple2Decoder::decodeDataRecord() void decodeDataRecord()
{ {
/* Check ID. */ /* Check ID. */
@@ -110,3 +117,11 @@ void Apple2Decoder::decodeDataRecord()
_sector->status = Sector::BAD_CHECKSUM; _sector->status = Sector::BAD_CHECKSUM;
_sector->data = decode_crazy_data(&bytes[0], _sector->status); _sector->data = decode_crazy_data(&bytes[0], _sector->status);
} }
};
std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Apple2Decoder(config));
}

View File

@@ -13,37 +13,7 @@
#define BROTHER_TRACKS_PER_120KB_DISK 39 #define BROTHER_TRACKS_PER_120KB_DISK 39
#define BROTHER_SECTORS_PER_TRACK 12 #define BROTHER_SECTORS_PER_TRACK 12
class Sector; extern std::unique_ptr<AbstractDecoder> createBrotherDecoder(const DecoderProto& config);
class SectorSet; extern std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config);
class Fluxmap;
class BrotherDecoderProto;
class BrotherEncoderProto;
class BrotherDecoder : public AbstractDecoder
{
public:
BrotherDecoder(const BrotherDecoderProto& config) {}
virtual ~BrotherDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
class BrotherEncoder : public AbstractEncoder
{
public:
BrotherEncoder(const BrotherEncoderProto& config):
_config(config)
{}
virtual ~BrotherEncoder() {}
private:
const BrotherEncoderProto& _config;
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
#endif #endif

View File

@@ -54,7 +54,14 @@ static int decode_header_gcr(uint16_t word)
return -1; return -1;
}; };
AbstractDecoder::RecordType BrotherDecoder::advanceToNextRecord() class BrotherDecoder : public AbstractDecoder
{
public:
BrotherDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -65,7 +72,7 @@ AbstractDecoder::RecordType BrotherDecoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD; return RecordType::UNKNOWN_RECORD;
} }
void BrotherDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
readRawBits(32); readRawBits(32);
const auto& rawbits = readRawBits(32); const auto& rawbits = readRawBits(32);
@@ -85,7 +92,7 @@ void BrotherDecoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; _sector->status = Sector::DATA_MISSING;
} }
void BrotherDecoder::decodeDataRecord() void decodeDataRecord()
{ {
readRawBits(32); readRawBits(32);
@@ -107,3 +114,11 @@ void BrotherDecoder::decodeDataRecord()
uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24(); uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_sector->status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createBrotherDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new BrotherDecoder(config));
}

View File

@@ -7,6 +7,7 @@
#include "sectorset.h" #include "sectorset.h"
#include "writer.h" #include "writer.h"
#include "arch/brother/brother.pb.h" #include "arch/brother/brother.pb.h"
#include "lib/encoders/encoders.pb.h"
FlagGroup brotherEncoderFlags; FlagGroup brotherEncoderFlags;
@@ -127,8 +128,16 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a'; return 10 + tolower(c) - 'a';
} }
std::unique_ptr<Fluxmap> BrotherEncoder::encode( class BrotherEncoder : public AbstractEncoder
int physicalTrack, int physicalSide, const SectorSet& allSectors) {
public:
BrotherEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.brother())
{}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{ {
int logicalTrack; int logicalTrack;
if (physicalSide != 0) if (physicalSide != 0)
@@ -182,3 +191,15 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
fluxmap->appendBits(bits, clockRateUs*1e3); fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap; return fluxmap;
} }
private:
const BrotherEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
}

View File

@@ -27,42 +27,7 @@
#define C64_TRACKS_PER_DISK 40 #define C64_TRACKS_PER_DISK 40
#define C64_BAM_TRACK 17 #define C64_BAM_TRACK 17
extern std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config);
class Sector; extern std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config);
class Fluxmap;
class Commodore64DecoderProto;
class Commodore64EncoderProto;
class Commodore64Decoder : public AbstractDecoder
{
public:
Commodore64Decoder(const Commodore64DecoderProto&) {}
virtual ~Commodore64Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
class Commodore64Encoder : public AbstractEncoder
{
public:
Commodore64Encoder(const Commodore64EncoderProto& config):
_config(config)
{}
virtual ~Commodore64Encoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
void writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector) const;
private:
const Commodore64EncoderProto& _config;
uint8_t _formatByte1;
uint8_t _formatByte2;
};
#endif #endif

View File

@@ -52,7 +52,14 @@ static Bytes decode(const std::vector<bool>& bits)
return output; return output;
} }
AbstractDecoder::RecordType Commodore64Decoder::advanceToNextRecord() class Commodore64Decoder : public AbstractDecoder
{
public:
Commodore64Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -63,7 +70,7 @@ AbstractDecoder::RecordType Commodore64Decoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD; return RecordType::UNKNOWN_RECORD;
} }
void Commodore64Decoder::decodeSectorRecord() void decodeSectorRecord()
{ {
readRawBits(20); readRawBits(20);
@@ -78,7 +85,7 @@ void Commodore64Decoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ _sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
} }
void Commodore64Decoder::decodeDataRecord() void decodeDataRecord()
{ {
readRawBits(20); readRawBits(20);
@@ -90,3 +97,11 @@ void Commodore64Decoder::decodeDataRecord()
uint8_t wantChecksum = bytes[256]; uint8_t wantChecksum = bytes[256];
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Commodore64Decoder(config));
}

View File

@@ -9,6 +9,7 @@
#include "writer.h" #include "writer.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "arch/c64/c64.pb.h" #include "arch/c64/c64.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <ctype.h> #include <ctype.h>
#include "bytes.h" #include "bytes.h"
@@ -202,7 +203,71 @@ static std::vector<bool> encode_data(uint8_t input)
return output; return output;
} }
void Commodore64Encoder::writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector) const class Commodore64Encoder : public AbstractEncoder
{
public:
Commodore64Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.c64())
{}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
/* 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.
* it is written in every header of every sector and track. headers are not
* stored in a d64 disk image so we have to get it from track 18 which
* contains the BAM.
*/
const auto& sectorData = allSectors.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
if (sectorData)
{
ByteReader br(sectorData->data);
br.seek(162); //goto position of the first Disk ID Byte
_formatByte1 = br.read_8();
_formatByte2 = br.read_8();
}
else
_formatByte1 = _formatByte2 = 0;
int logicalTrack = physicalTrack / 2;
double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(logicalTrack);
unsigned writtenSectors = 0;
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (sectorData)
{
writeSector(bits, cursor, sectorData);
writtenSectors++;
}
}
if (writtenSectors == 0)
return std::unique_ptr<Fluxmap>();
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:
void writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector) const
{ {
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html /* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
* 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR) * 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
@@ -290,59 +355,15 @@ void Commodore64Encoder::writeSector(std::vector<bool>& bits, unsigned& cursor,
} }
} }
std::unique_ptr<Fluxmap> Commodore64Encoder::encode( private:
int physicalTrack, int physicalSide, const SectorSet& allSectors) const Commodore64EncoderProto& _config;
uint8_t _formatByte1;
uint8_t _formatByte2;
};
std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config)
{ {
/* The format ID Character # 1 and # 2 are in the .d64 image only present return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config));
* in track 18 sector zero which contains the BAM info in byte 162 and 163.
* it is written in every header of every sector and track. headers are not
* stored in a d64 disk image so we have to get it from track 18 which
* contains the BAM.
*/
const auto& sectorData = allSectors.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
if (sectorData)
{
ByteReader br(sectorData->data);
br.seek(162); //goto position of the first Disk ID Byte
_formatByte1 = br.read_8();
_formatByte2 = br.read_8();
}
else
_formatByte1 = _formatByte2 = 0;
int logicalTrack = physicalTrack / 2;
double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(logicalTrack);
unsigned writtenSectors = 0;
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (sectorData)
{
writeSector(bits, cursor, sectorData);
writtenSectors++;
}
}
if (writtenSectors == 0)
return std::unique_ptr<Fluxmap>();
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;
} }
// vim: sw=4 ts=4 et // vim: sw=4 ts=4 et

View File

@@ -52,7 +52,14 @@ static Bytes decode(const std::vector<bool>& bits)
return output; return output;
} }
AbstractDecoder::RecordType DurangoF85Decoder::advanceToNextRecord() class DurangoF85Decoder : public AbstractDecoder
{
public:
DurangoF85Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -63,7 +70,7 @@ AbstractDecoder::RecordType DurangoF85Decoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD; return RecordType::UNKNOWN_RECORD;
} }
void DurangoF85Decoder::decodeSectorRecord() void decodeSectorRecord()
{ {
/* Skip sync bits and ID byte. */ /* Skip sync bits and ID byte. */
@@ -83,7 +90,7 @@ void DurangoF85Decoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ _sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
} }
void DurangoF85Decoder::decodeDataRecord() void decodeDataRecord()
{ {
/* Skip sync bits ID byte. */ /* Skip sync bits ID byte. */
@@ -98,3 +105,10 @@ void DurangoF85Decoder::decodeDataRecord()
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data); uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createDurangoF85Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new DurangoF85Decoder(config));
}

View File

@@ -5,19 +5,6 @@
#define F85_DATA_RECORD 0xffffcb /* 1111 1111 1111 1111 1100 1101 */ #define F85_DATA_RECORD 0xffffcb /* 1111 1111 1111 1111 1100 1101 */
#define F85_SECTOR_LENGTH 512 #define F85_SECTOR_LENGTH 512
class Sector; extern std::unique_ptr<AbstractDecoder> createDurangoF85Decoder(const DecoderProto& config);
class Fluxmap;
class F85DecoderProto;
class DurangoF85Decoder : public AbstractDecoder
{
public:
DurangoF85Decoder(const F85DecoderProto&) {}
virtual ~DurangoF85Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
#endif #endif

View File

@@ -99,7 +99,14 @@ static uint16_t checksum(const Bytes& bytes)
return (crchi << 8) | crclo; return (crchi << 8) | crclo;
} }
AbstractDecoder::RecordType Fb100Decoder::advanceToNextRecord() class Fb100Decoder : public AbstractDecoder
{
public:
Fb100Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher);
@@ -108,7 +115,7 @@ AbstractDecoder::RecordType Fb100Decoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD; return RecordType::UNKNOWN_RECORD;
} }
void Fb100Decoder::decodeSectorRecord() void decodeSectorRecord()
{ {
auto rawbits = readRawBits(FB100_RECORD_SIZE*16); auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
@@ -133,3 +140,11 @@ void Fb100Decoder::decodeSectorRecord()
_sector->status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createFb100Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Fb100Decoder(config));
}

View File

@@ -5,20 +5,7 @@
#define FB100_ID_SIZE 17 #define FB100_ID_SIZE 17
#define FB100_PAYLOAD_SIZE 0x500 #define FB100_PAYLOAD_SIZE 0x500
class Sector; extern std::unique_ptr<AbstractDecoder> createFb100Decoder(const DecoderProto& config);
class Fluxmap;
class Track;
class Fb100DecoderProto;
class Fb100Decoder : public AbstractDecoder
{
public:
Fb100Decoder(const Fb100DecoderProto&) {}
virtual ~Fb100Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
#endif #endif

View File

@@ -91,12 +91,15 @@ const FluxMatchers ANY_RECORD_PATTERN(
} }
); );
std::set<unsigned> IbmDecoder::requiredSectors(Track& track) const class IbmDecoder : public AbstractDecoder
{ {
return iterate(_config.required_sectors()); public:
} IbmDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.ibm())
{}
AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord() RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -127,7 +130,7 @@ AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD; return RecordType::UNKNOWN_RECORD;
} }
void IbmDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN; unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16); auto bits = readRawBits(recordSize*16);
@@ -149,7 +152,7 @@ void IbmDecoder::decodeSectorRecord()
_sector->logicalSide = _sector->physicalSide; _sector->logicalSide = _sector->physicalSide;
} }
void IbmDecoder::decodeDataRecord() void decodeDataRecord()
{ {
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3; unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16); auto bits = readRawBits(recordLength*16);
@@ -164,3 +167,20 @@ void IbmDecoder::decodeDataRecord()
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2)); uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2));
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
std::set<unsigned> requiredSectors(Track& track) const
{
return iterate(_config.required_sectors());
}
private:
const IbmDecoderProto& _config;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new IbmDecoder(config));
}

View File

@@ -7,6 +7,7 @@
#include "sectorset.h" #include "sectorset.h"
#include "writer.h" #include "writer.h"
#include "arch/ibm/ibm.pb.h" #include "arch/ibm/ibm.pb.h"
#include "lib/encoders/encoders.pb.h"
#include "fmt/format.h" #include "fmt/format.h"
#include <ctype.h> #include <ctype.h>
@@ -64,7 +65,24 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a'; return 10 + tolower(c) - 'a';
} }
void IbmEncoder::writeRawBits(uint32_t data, int width) static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
class IbmEncoder : public AbstractEncoder
{
public:
IbmEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.ibm())
{}
private:
void writeRawBits(uint32_t data, int width)
{ {
_cursor += width; _cursor += width;
_lastBit = data & 1; _lastBit = data & 1;
@@ -77,15 +95,7 @@ void IbmEncoder::writeRawBits(uint32_t data, int width)
} }
} }
static uint8_t decodeUint16(uint16_t raw) void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
void IbmEncoder::getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{ {
trackdata.Clear(); trackdata.Clear();
for (const auto& f : _config.trackdata()) for (const auto& f : _config.trackdata())
@@ -99,8 +109,8 @@ void IbmEncoder::getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsi
} }
} }
std::unique_ptr<Fluxmap> IbmEncoder::encode( public:
int physicalTrack, int physicalSide, const SectorSet& allSectors) std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{ {
IbmEncoderProto::TrackdataProto trackdata; IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide); getTrackFormat(trackdata, physicalTrack, physicalSide);
@@ -256,3 +266,16 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
return fluxmap; return fluxmap;
} }
private:
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config));
}

View File

@@ -1,10 +1,6 @@
#ifndef IBM_H #ifndef IBM_H
#define IBM_H #define IBM_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "arch/ibm/ibm.pb.h"
/* IBM format (i.e. ordinary PC floppies). */ /* IBM format (i.e. ordinary PC floppies). */
#define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */ #define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */
@@ -30,48 +26,12 @@ struct IbmIdam
uint8_t crc[2]; uint8_t crc[2];
}; };
class IbmDecoder : public AbstractDecoder class AbstractEncoder;
{ class AbstractDecoder;
public: class DecoderProto;
IbmDecoder(const IbmDecoderProto& config): class EncoderProto;
_config(config)
{}
RecordType advanceToNextRecord(); extern std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config);
void decodeSectorRecord(); extern std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config);
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const;
private:
const IbmDecoderProto& _config;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
class IbmEncoder : public AbstractEncoder
{
public:
IbmEncoder(const IbmEncoderProto& config):
_config(config)
{}
virtual ~IbmEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
void writeRawBits(uint32_t data, int width);
void writeSync();
void getTrackFormat(IbmEncoderProto::TrackdataProto& format, unsigned track, unsigned side);
private:
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
#endif #endif

View File

@@ -124,7 +124,14 @@ uint8_t decode_side(uint8_t side)
return !!(side & 0x20); return !!(side & 0x20);
} }
AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord() class MacintoshDecoder : public AbstractDecoder
{
public:
MacintoshDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -135,7 +142,7 @@ AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()
return UNKNOWN_RECORD; return UNKNOWN_RECORD;
} }
void MacintoshDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */ /* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
readRawBits(24); readRawBits(24);
@@ -164,7 +171,7 @@ void MacintoshDecoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ _sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
} }
void MacintoshDecoder::decodeDataRecord() void decodeDataRecord()
{ {
auto id = toBytes(readRawBits(24)).reader().read_be24(); auto id = toBytes(readRawBits(24)).reader().read_be24();
if (id != MAC_DATA_RECORD) if (id != MAC_DATA_RECORD)
@@ -185,7 +192,7 @@ void MacintoshDecoder::decodeDataRecord()
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12)); _sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
} }
std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const std::set<unsigned> requiredSectors(Track& track) const
{ {
int count; int count;
if (track.physicalTrack < 16) if (track.physicalTrack < 16)
@@ -204,5 +211,10 @@ std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const
sectors.insert(count); sectors.insert(count);
return sectors; return sectors;
} }
};
std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new MacintoshDecoder(config));
}

View File

@@ -7,20 +7,10 @@
#include "sectorset.h" #include "sectorset.h"
#include "writer.h" #include "writer.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
#include "arch/macintosh/macintosh.pb.h"
#include <ctype.h> #include <ctype.h>
FlagGroup macintoshEncoderFlags;
static DoubleFlag postIndexGapUs(
{ "--post-index-gap-us" },
"Post-index gap before first sector header (microseconds).",
0);
static DoubleFlag clockCompensation(
{ "--clock-compensation-factor" },
"Scale the output clock by this much.",
1.0);
static bool lastBit; static bool lastBit;
static double clockRateUsForTrack(unsigned track) static double clockRateUsForTrack(unsigned track)
@@ -210,18 +200,26 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
write_bits(bits, cursor, 0xdeaaff, 3*8); write_bits(bits, cursor, 0xdeaaff, 3*8);
} }
std::unique_ptr<Fluxmap> MacintoshEncoder::encode( class MacintoshEncoder : public AbstractEncoder
int physicalTrack, int physicalSide, const SectorSet& allSectors) {
public:
MacintoshEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.macintosh())
{}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{ {
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK)) if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>(); return std::unique_ptr<Fluxmap>();
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation; double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs; int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution); std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0; unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false }); fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false; lastBit = false;
unsigned numSectors = sectorsForTrack(physicalTrack); unsigned numSectors = sectorsForTrack(physicalTrack);
@@ -240,3 +238,12 @@ std::unique_ptr<Fluxmap> MacintoshEncoder::encode(
return fluxmap; return fluxmap;
} }
private:
const MacintoshEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
}

View File

@@ -1,9 +1,6 @@
#ifndef MACINTOSH_H #ifndef MACINTOSH_H
#define MACINTOSH_H #define MACINTOSH_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */ #define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */ #define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
@@ -13,36 +10,13 @@
#define MAC_TRACKS_PER_DISK 80 #define MAC_TRACKS_PER_DISK 80
class Sector; class AbstractEncoder;
class Fluxmap; class AbstractDecoder;
class MacintoshDecoderProto; class DecoderProto;
class MacintoshEncoderProto; class EncoderProto;
class MacintoshDecoder : public AbstractDecoder
{
public:
MacintoshDecoder(const MacintoshDecoderProto&) {}
virtual ~MacintoshDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const;
};
class MacintoshEncoder : public AbstractEncoder
{
public:
MacintoshEncoder(const MacintoshEncoderProto&) {}
virtual ~MacintoshEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup macintoshEncoderFlags;
extern std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config);
#endif #endif

View File

@@ -1,5 +1,14 @@
syntax = "proto2"; syntax = "proto2";
message MacintoshDecoderProto {} import "lib/common.proto";
message MacintoshEncoderProto {}
message MacintoshDecoderProto {}
message MacintoshEncoderProto {
optional double post_index_gap_us = 1 [default = 0.0,
(help) = "post-index gap before first sector header (microseconds)."];
optional double clock_compensation_factor = 2 [default = 1.0,
(help) = "scale the output clock by this much."];
}

View File

@@ -10,18 +10,6 @@
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern. */ /* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern. */
static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555); static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555);
AbstractDecoder::RecordType MicropolisDecoder::advanceToNextRecord()
{
_fmr->seekToIndexMark();
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_SYNC_PATTERN, matcher);
if (matcher == &SECTOR_SYNC_PATTERN) {
readRawBits(16);
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
}
/* Adds all bytes, with carry. */ /* Adds all bytes, with carry. */
static uint8_t checksum(const Bytes& bytes) { static uint8_t checksum(const Bytes& bytes) {
ByteReader br(bytes); ByteReader br(bytes);
@@ -36,7 +24,26 @@ static uint8_t checksum(const Bytes& bytes) {
return sum & 0xFF; return sum & 0xFF;
} }
void MicropolisDecoder::decodeSectorRecord() class MicropolisDecoder : public AbstractDecoder
{
public:
MicropolisDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
_fmr->seekToIndexMark();
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_SYNC_PATTERN, matcher);
if (matcher == &SECTOR_SYNC_PATTERN) {
readRawBits(16);
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
}
void decodeSectorRecord()
{ {
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16); auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16);
auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE); auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
@@ -59,3 +66,10 @@ void MicropolisDecoder::decodeSectorRecord()
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new MicropolisDecoder(config));
}

View File

@@ -3,18 +3,6 @@
#define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6) #define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6)
class Sector; extern std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config);
class Fluxmap;
class MicropolisDecoderProto;
class MicropolisDecoder : public AbstractDecoder
{
public:
MicropolisDecoder(const MicropolisDecoderProto&) {}
virtual ~MicropolisDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
#endif #endif

View File

@@ -23,13 +23,20 @@ const int SECTOR_SIZE = 256;
*/ */
const FluxPattern ID_PATTERN(32, 0xaaaaffaf); const FluxPattern ID_PATTERN(32, 0xaaaaffaf);
void MxDecoder::beginTrack() class MxDecoder : public AbstractDecoder
{
public:
MxDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
void beginTrack()
{ {
_currentSector = -1; _currentSector = -1;
_clock = 0; _clock = 0;
} }
AbstractDecoder::RecordType MxDecoder::advanceToNextRecord() RecordType advanceToNextRecord()
{ {
if (_currentSector == -1) if (_currentSector == -1)
{ {
@@ -56,7 +63,7 @@ AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
return SECTOR_RECORD; return SECTOR_RECORD;
} }
void MxDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
auto bits = readRawBits((SECTOR_SIZE+2)*16); auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab(); auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
@@ -73,3 +80,16 @@ void MxDecoder::decodeSectorRecord()
_sector->data = bytes.slice(0, SECTOR_SIZE); _sector->data = bytes.slice(0, SECTOR_SIZE);
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new MxDecoder(config));
}

View File

@@ -3,22 +3,6 @@
#include "decoders/decoders.h" #include "decoders/decoders.h"
class MxDecoderProto; extern std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config);
class MxDecoder : public AbstractDecoder
{
public:
MxDecoder(const MxDecoderProto&) {}
virtual ~MxDecoder() {}
void beginTrack();
RecordType advanceToNextRecord();
void decodeSectorRecord();
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
#endif #endif

View File

@@ -18,6 +18,7 @@
#include "sector.h" #include "sector.h"
#include "northstar.h" #include "northstar.h"
#include "bytes.h" #include "bytes.h"
#include "lib/decoders/decoders.pb.h"
#include "fmt/format.h" #include "fmt/format.h"
/* /*
@@ -48,8 +49,32 @@ const FluxMatchers ANY_SECTOR_PATTERN(
} }
); );
/* Checksum is initially 0.
* For each data byte, XOR with the current checksum.
* Rotate checksum left, carrying bit 7 to bit 0.
*/
uint8_t northstarChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
while (!br.eof()) {
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
return checksum;
}
class NorthstarDecoder : public AbstractDecoder
{
public:
NorthstarDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.northstar())
{}
/* Search for FM or MFM sector record */ /* Search for FM or MFM sector record */
AbstractDecoder::RecordType NorthstarDecoder::advanceToNextRecord() RecordType advanceToNextRecord()
{ {
nanoseconds_t now = _fmr->tell().ns(); nanoseconds_t now = _fmr->tell().ns();
@@ -115,23 +140,7 @@ AbstractDecoder::RecordType NorthstarDecoder::advanceToNextRecord()
return UNKNOWN_RECORD; return UNKNOWN_RECORD;
} }
/* Checksum is initially 0. void decodeSectorRecord()
* For each data byte, XOR with the current checksum.
* Rotate checksum left, carrying bit 7 to bit 0.
*/
uint8_t northstarChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
while (!br.eof()) {
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
return checksum;
}
void NorthstarDecoder::decodeSectorRecord()
{ {
unsigned recordSize, payloadSize, headerSize; unsigned recordSize, payloadSize, headerSize;
@@ -168,8 +177,20 @@ void NorthstarDecoder::decodeSectorRecord()
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
std::set<unsigned> NorthstarDecoder::requiredSectors(Track& track) const std::set<unsigned> requiredSectors(Track& track) const
{ {
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
return sectors; return sectors;
} }
private:
const NorthstarDecoderProto& _config;
uint8_t _sectorType = SECTOR_TYPE_MFM;
uint8_t _hardSectorId;
};
std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new NorthstarDecoder(config));
}

View File

@@ -1,6 +1,11 @@
#include "globals.h" #include "globals.h"
#include "northstar.h" #include "northstar.h"
#include "sector.h"
#include "sectorset.h" #include "sectorset.h"
#include "bytes.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "lib/encoders/encoders.pb.h"
#define GAP_FILL_SIZE_SD 30 #define GAP_FILL_SIZE_SD 30
#define PRE_HEADER_GAP_FILL_SIZE_SD 9 #define PRE_HEADER_GAP_FILL_SIZE_SD 9
@@ -95,8 +100,15 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
} }
} }
std::unique_ptr<Fluxmap> NorthstarEncoder::encode( class NorthstarEncoder : public AbstractEncoder
int physicalTrack, int physicalSide, const SectorSet& allSectors) {
public:
NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.northstar())
{}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{ {
int bitsPerRevolution = 100000; int bitsPerRevolution = 100000;
double clockRateUs = 4.00; double clockRateUs = 4.00;
@@ -128,3 +140,13 @@ std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
fluxmap->appendBits(bits, clockRateUs * 1e3); fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap; return fluxmap;
} }
private:
const NorthstarEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
}

View File

@@ -12,9 +12,6 @@
* *
*/ */
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define NORTHSTAR_PREAMBLE_SIZE_SD (16) #define NORTHSTAR_PREAMBLE_SIZE_SD (16)
#define NORTHSTAR_PREAMBLE_SIZE_DD (32) #define NORTHSTAR_PREAMBLE_SIZE_DD (32)
#define NORTHSTAR_HEADER_SIZE_SD (1) #define NORTHSTAR_HEADER_SIZE_SD (1)
@@ -28,45 +25,14 @@
#define SECTOR_TYPE_MFM (0) #define SECTOR_TYPE_MFM (0)
#define SECTOR_TYPE_FM (1) #define SECTOR_TYPE_FM (1)
class NorthstarEncoderProto; class AbstractDecoder;
class NorthstarDecoderProto; class AbstractEncoder;
class EncoderProto;
class DecoderProto;
class NorthstarDecoder : public AbstractDecoder
{
public:
NorthstarDecoder(const NorthstarDecoderProto& config):
_config(config)
{
_sectorType = SECTOR_TYPE_MFM;
}
virtual ~NorthstarDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
std::set<unsigned> requiredSectors(Track& track) const;
private:
const NorthstarDecoderProto& _config;
uint8_t _sectorType;
uint8_t _hardSectorId;
};
class NorthstarEncoder : public AbstractEncoder
{
public:
NorthstarEncoder(const NorthstarEncoderProto& config):
_config(config)
{}
virtual ~NorthstarEncoder() {}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
const NorthstarEncoderProto& _config;
};
extern FlagGroup northstarEncoderFlags;
extern uint8_t northstarChecksum(const Bytes& bytes); extern uint8_t northstarChecksum(const Bytes& bytes);
extern std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config);
#endif /* NORTHSTAR */ #endif /* NORTHSTAR */

View File

@@ -40,7 +40,14 @@ const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN }); const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
AbstractDecoder::RecordType Tids990Decoder::advanceToNextRecord() class Tids990Decoder : public AbstractDecoder
{
public:
Tids990Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -51,7 +58,7 @@ AbstractDecoder::RecordType Tids990Decoder::advanceToNextRecord()
return RecordType::UNKNOWN_RECORD; return RecordType::UNKNOWN_RECORD;
} }
void Tids990Decoder::decodeSectorRecord() void decodeSectorRecord()
{ {
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16); auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE); auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
@@ -71,7 +78,7 @@ void Tids990Decoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */ _sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
} }
void Tids990Decoder::decodeDataRecord() void decodeDataRecord()
{ {
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16); auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE); auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
@@ -84,4 +91,10 @@ void Tids990Decoder::decodeDataRecord()
uint16_t wantChecksum = br.read_be16(); uint16_t wantChecksum = br.read_be16();
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createTids990Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Tids990Decoder(config));
}

View File

@@ -7,6 +7,7 @@
#include "sectorset.h" #include "sectorset.h"
#include "writer.h" #include "writer.h"
#include "arch/tids990/tids990.pb.h" #include "arch/tids990/tids990.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <fmt/format.h> #include <fmt/format.h>
static int charToInt(char c) static int charToInt(char c)
@@ -16,7 +17,24 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a'; return 10 + tolower(c) - 'a';
} }
void Tids990Encoder::writeRawBits(uint32_t data, int width) static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
class Tids990Encoder : public AbstractEncoder
{
public:
Tids990Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.tids990())
{}
private:
void writeRawBits(uint32_t data, int width)
{ {
_cursor += width; _cursor += width;
_lastBit = data & 1; _lastBit = data & 1;
@@ -29,28 +47,20 @@ void Tids990Encoder::writeRawBits(uint32_t data, int width)
} }
} }
void Tids990Encoder::writeBytes(const Bytes& bytes) void writeBytes(const Bytes& bytes)
{ {
encodeMfm(_bits, _cursor, bytes, _lastBit); encodeMfm(_bits, _cursor, bytes, _lastBit);
} }
void Tids990Encoder::writeBytes(int count, uint8_t byte) void writeBytes(int count, uint8_t byte)
{ {
Bytes bytes = { byte }; Bytes bytes = { byte };
for (int i=0; i<count; i++) for (int i=0; i<count; i++)
writeBytes(bytes); writeBytes(bytes);
} }
static uint8_t decodeUint16(uint16_t raw) public:
{ std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> Tids990Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{ {
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0; double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs; int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
@@ -128,3 +138,16 @@ std::unique_ptr<Fluxmap> Tids990Encoder::encode(
return fluxmap; return fluxmap;
} }
private:
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
}

View File

@@ -5,49 +5,12 @@
#define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */ #define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */
#define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */ #define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
class Sector; class AbstractEncoder;
class SectorSet; class AbstractDecoder;
class Fluxmap; class DecoderProto;
class Track; class EncoderProto;
class Tids990DecoderProto;
class Tids990EncoderProto;
class Tids990Decoder : public AbstractDecoder extern std::unique_ptr<AbstractDecoder> createTids990Decoder(const DecoderProto& config);
{
public:
Tids990Decoder(const Tids990DecoderProto&) {}
virtual ~Tids990Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
class Tids990Encoder : public AbstractEncoder
{
public:
Tids990Encoder(const Tids990EncoderProto& config):
_config(config)
{}
virtual ~Tids990Encoder() {}
private:
void writeRawBits(uint32_t data, int width);
void writeBytes(const Bytes& bytes);
void writeBytes(int count, uint8_t value);
void writeSync();
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
extern FlagGroup tids990EncoderFlags;
#endif #endif

View File

@@ -54,7 +54,14 @@ static Bytes decode(const std::vector<bool>& bits)
return output; return output;
} }
AbstractDecoder::RecordType Victor9kDecoder::advanceToNextRecord() class Victor9kDecoder : public AbstractDecoder
{
public:
Victor9kDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); _sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -65,7 +72,7 @@ AbstractDecoder::RecordType Victor9kDecoder::advanceToNextRecord()
return UNKNOWN_RECORD; return UNKNOWN_RECORD;
} }
void Victor9kDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
/* Skip the sync marker bit. */ /* Skip the sync marker bit. */
readRawBits(23); readRawBits(23);
@@ -88,7 +95,7 @@ void Victor9kDecoder::decodeSectorRecord()
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ _sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
} }
void Victor9kDecoder::decodeDataRecord() void decodeDataRecord()
{ {
/* Skip the sync marker bit. */ /* Skip the sync marker bit. */
readRawBits(23); readRawBits(23);
@@ -109,3 +116,11 @@ void Victor9kDecoder::decodeDataRecord()
uint16_t wantChecksum = br.read_le16(); uint16_t wantChecksum = br.read_le16();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Victor9kDecoder(config));
}

View File

@@ -6,19 +6,6 @@
#define VICTOR9K_SECTOR_LENGTH 512 #define VICTOR9K_SECTOR_LENGTH 512
class Sector; extern std::unique_ptr<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config);
class Fluxmap;
class Victor9kDecoderProto;
class Victor9kDecoder : public AbstractDecoder
{
public:
Victor9kDecoder(const Victor9kDecoderProto&) {}
virtual ~Victor9kDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
#endif #endif

View File

@@ -14,7 +14,14 @@
static const FluxPattern SECTOR_START_PATTERN(16, 0xaaab); static const FluxPattern SECTOR_START_PATTERN(16, 0xaaab);
AbstractDecoder::RecordType ZilogMczDecoder::advanceToNextRecord() class ZilogMczDecoder : public AbstractDecoder
{
public:
ZilogMczDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{ {
const FluxMatcher* matcher = nullptr; const FluxMatcher* matcher = nullptr;
_fmr->seekToIndexMark(); _fmr->seekToIndexMark();
@@ -24,7 +31,7 @@ AbstractDecoder::RecordType ZilogMczDecoder::advanceToNextRecord()
return UNKNOWN_RECORD; return UNKNOWN_RECORD;
} }
void ZilogMczDecoder::decodeSectorRecord() void decodeSectorRecord()
{ {
readRawBits(14); readRawBits(14);
@@ -46,3 +53,10 @@ void ZilogMczDecoder::decodeSectorRecord()
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
} }
};
std::unique_ptr<AbstractDecoder> createZilogMczDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new ZilogMczDecoder(config));
}

View File

@@ -1,19 +1,7 @@
#ifndef ZILOGMCZ_H #ifndef ZILOGMCZ_H
#define ZILOGMCZ_H #define ZILOGMCZ_H
class Sector; extern std::unique_ptr<AbstractDecoder> createZilogMczDecoder(const DecoderProto& config);
class Fluxmap;
class ZilogMczDecoderProto;
class ZilogMczDecoder : public AbstractDecoder
{
public:
ZilogMczDecoder(const ZilogMczDecoderProto&) {}
virtual ~ZilogMczDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
#endif #endif

View File

@@ -5,18 +5,19 @@
#include "encoders/encoders.h" #include "encoders/encoders.h"
//#include "arch/aeslanier/aeslanier.h" //#include "arch/aeslanier/aeslanier.h"
#include "arch/amiga/amiga.h" #include "arch/amiga/amiga.h"
//#include "arch/apple2/apple2.h" #include "arch/apple2/apple2.h"
//#include "arch/brother/brother.h" #include "arch/brother/brother.h"
//#include "arch/c64/c64.h" #include "arch/c64/c64.h"
//#include "arch/f85/f85.h" #include "arch/f85/f85.h"
//#include "arch/ibm/ibm.h" #include "arch/fb100/fb100.h"
//#include "arch/macintosh/macintosh.h" #include "arch/ibm/ibm.h"
//#include "arch/micropolis/micropolis.h" #include "arch/macintosh/macintosh.h"
//#include "arch/mx/mx.h" #include "arch/micropolis/micropolis.h"
//#include "arch/northstar/northstar.h" #include "arch/mx/mx.h"
//#include "arch/tids990/tids990.h" #include "arch/northstar/northstar.h"
//#include "arch/victor9k/victor9k.h" #include "arch/tids990/tids990.h"
//#include "arch/zilogmcz/zilogmcz.h" #include "arch/victor9k/victor9k.h"
#include "arch/zilogmcz/zilogmcz.h"
#include "decoders/fluxmapreader.h" #include "decoders/fluxmapreader.h"
#include "record.h" #include "record.h"
#include "protocol.h" #include "protocol.h"
@@ -33,7 +34,21 @@ std::unique_ptr<AbstractDecoder> AbstractDecoder::create(const DecoderProto& con
static const std::map<int, static const std::map<int,
std::function<std::unique_ptr<AbstractDecoder>(const DecoderProto&)>> decoders = std::function<std::unique_ptr<AbstractDecoder>(const DecoderProto&)>> decoders =
{ {
{ DecoderProto::kAmiga, constructor }, { DecoderProto::kAeslanier, createAesLanierDecoder },
{ DecoderProto::kAmiga, createAmigaDecoder },
{ DecoderProto::kApple2, createApple2Decoder },
{ DecoderProto::kBrother, createBrotherDecoder },
{ DecoderProto::kC64, createCommodore64Decoder },
{ DecoderProto::kF85, createDurangoF85Decoder },
{ DecoderProto::kFb100, createFb100Decoder },
{ DecoderProto::kIbm, createIbmDecoder },
{ DecoderProto::kMacintosh, createMacintoshDecoder },
{ DecoderProto::kMicropolis, createMicropolisDecoder },
{ DecoderProto::kMx, createMxDecoder },
{ DecoderProto::kNorthstar, createNorthstarDecoder },
{ DecoderProto::kTids990, createTids990Decoder },
{ DecoderProto::kVictor9K, createVictor9kDecoder },
{ DecoderProto::kZilogmcz, createZilogMczDecoder },
}; };
auto decoder = decoders.find(config.format_case()); auto decoder = decoders.find(config.format_case());

View File

@@ -33,6 +33,9 @@ static inline Bytes decodeFmMfm(const std::vector<bool> bits)
class AbstractDecoder class AbstractDecoder
{ {
public: public:
AbstractDecoder() {} // REMOVE ME
AbstractDecoder(const DecoderProto& config) {}
virtual ~AbstractDecoder() {} virtual ~AbstractDecoder() {}
static std::unique_ptr<AbstractDecoder> create(const DecoderProto& config); static std::unique_ptr<AbstractDecoder> create(const DecoderProto& config);

View File

@@ -14,32 +14,23 @@
std::unique_ptr<AbstractEncoder> AbstractEncoder::create(const EncoderProto& config) std::unique_ptr<AbstractEncoder> AbstractEncoder::create(const EncoderProto& config)
{ {
switch (config.format_case()) static const std::map<int,
std::function<std::unique_ptr<AbstractEncoder>(const EncoderProto&)>> encoders =
{ {
case EncoderProto::kAmiga: { EncoderProto::kAmiga, createAmigaEncoder },
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config.amiga())); { EncoderProto::kBrother, createBrotherEncoder },
{ EncoderProto::kC64, createCommodore64Encoder },
{ EncoderProto::kIbm, createIbmEncoder },
{ EncoderProto::kMacintosh, createMacintoshEncoder },
{ EncoderProto::kNorthstar, createNorthstarEncoder },
};
case EncoderProto::kIbm: auto encoder = encoders.find(config.format_case());
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config.ibm())); if (encoder == encoders.end())
Error() << "no encoder specified";
case EncoderProto::kBrother: return (encoder->second)(config);
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config.brother()));
case EncoderProto::kMacintosh:
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config.macintosh()));
case EncoderProto::kC64:
return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config.c64()));
case EncoderProto::kNorthstar:
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config.northstar()));
default:
Error() << "no input disk format specified";
} }
return std::unique_ptr<AbstractEncoder>();
}
Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock) Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock)
{ {

View File

@@ -9,7 +9,7 @@ class EncoderProto;
class AbstractEncoder class AbstractEncoder
{ {
public: public:
virtual ~AbstractEncoder() {} AbstractEncoder(const EncoderProto& config) {}
static std::unique_ptr<AbstractEncoder> create(const EncoderProto& config); static std::unique_ptr<AbstractEncoder> create(const EncoderProto& config);

View File

@@ -24,10 +24,16 @@ public:
unsigned autoSides; unsigned autoSides;
unsigned autoSectors; unsigned autoSectors;
unsigned autoBytes; unsigned autoBytes;
bool mixedDensity = false;
sectors.calculateSize(autoTracks, autoSides, autoSectors, autoBytes); sectors.calculateSize(autoTracks, autoSides, autoSectors, autoBytes);
size_t trackSize = autoSectors * autoBytes; size_t trackSize = autoSectors * autoBytes;
if (autoTracks * trackSize == 0) {
std::cout << "No sectors in output; skipping .nsi image file generation." << std::endl;
return;
}
std::cout << fmt::format("Writing {} cylinders, {} heads, {} sectors, {} ({} bytes/sector), {} kB total", std::cout << fmt::format("Writing {} cylinders, {} heads, {} sectors, {} ({} bytes/sector), {} kB total",
autoTracks, autoSides, autoTracks, autoSides,
autoSectors, autoBytes == 256 ? "SD" : "DD", autoBytes, autoSectors, autoBytes == 256 ? "SD" : "DD", autoBytes,
@@ -56,11 +62,29 @@ public:
(sectorId * autoBytes); /* Sector offset from beginning of track. */ (sectorId * autoBytes); /* Sector offset from beginning of track. */
} }
outputFile.seekp(sectorFileOffset, std::ios::beg); outputFile.seekp(sectorFileOffset, std::ios::beg);
if ((autoBytes == 512) && (sector->data.size() == 256)) {
/* North Star DOS provided an upgrade path for disks formatted as single-
* density to hold double-density data without reformatting. In this
* case, the four directory blocks will be single-density but other areas
* of the disk are double-density. This cannot be accurately represented
* using a .nsi file, so in these cases, we pad the sector to 512-bytes,
* filling with spaces.
*/
char fill[256];
memset(fill, ' ', sizeof(fill));
if (mixedDensity == false) {
std::cout << "Warning: Disk contains mixed single/double-density sectors." << std::endl;
}
mixedDensity = true;
sector->data.slice(0, 256).writeTo(outputFile);
outputFile.write(fill, sizeof(fill));
} else {
sector->data.slice(0, autoBytes).writeTo(outputFile); sector->data.slice(0, autoBytes).writeTo(outputFile);
} }
} }
} }
} }
}
}; };
std::unique_ptr<ImageWriter> ImageWriter::createNsiImageWriter( std::unique_ptr<ImageWriter> ImageWriter::createNsiImageWriter(