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_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5)
class Sector;
class Fluxmap;
class AesLanierDecoderProto;
class AesLanierDecoder : public AbstractDecoder
{
public:
AesLanierDecoder(const AesLanierDecoderProto&) {}
virtual ~AesLanierDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<AbstractDecoder> createAesLanierDecoder(const DecoderProto& config);
#endif

View File

@@ -24,16 +24,23 @@ static Bytes reverse_bits(const Bytes& input)
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);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
}
}
void AesLanierDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
/* Skip ID mark. */
readRawBits(16);
@@ -62,4 +69,12 @@ void AesLanierDecoder::decodeSectorRecord()
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_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_RECORD_SIZE 0x21f
class Sector;
class Fluxmap;
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 std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config);
extern uint32_t amigaChecksum(const Bytes& bytes);
extern Bytes amigaInterleave(const Bytes& input);

View File

@@ -8,6 +8,7 @@
#include "amiga.h"
#include "bytes.h"
#include "fmt/format.h"
#include "lib/decoders/decoders.pb.h"
#include <string.h>
#include <algorithm>
@@ -21,16 +22,24 @@
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);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
}
}
void AmigaDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
return;
@@ -57,11 +66,20 @@ void AmigaDecoder::decodeSectorRecord()
_sector->data.clear();
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_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 };
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 "writer.h"
#include "arch/amiga/amiga.pb.h"
#include "lib/encoders/encoders.pb.h"
static bool lastBit;
@@ -96,9 +97,16 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
write_interleaved_bytes(data);
}
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class AmigaEncoder : public AbstractEncoder
{
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))
return std::unique_ptr<Fluxmap>();
@@ -122,5 +130,15 @@ std::unique_ptr<Fluxmap> AmigaEncoder::encode(
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us()*1e3);
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_ENCODED_SECTOR_LENGTH 342
class Sector;
class Fluxmap;
class Apple2DecoderProto;
class Apple2Decoder : public AbstractDecoder
{
public:
Apple2Decoder(const Apple2DecoderProto&) {}
virtual ~Apple2Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config);
#endif

View File

@@ -60,13 +60,20 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
return output;
}
uint8_t combine(uint16_t word)
static uint8_t combine(uint16_t word)
{
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;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
@@ -74,10 +81,10 @@ AbstractDecoder::RecordType Apple2Decoder::advanceToNextRecord()
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
}
void Apple2Decoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */
readRawBits(24);
@@ -92,10 +99,10 @@ void Apple2Decoder::decodeSectorRecord()
uint8_t checksum = combine(br.read_be16());
if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
}
void Apple2Decoder::decodeDataRecord()
{
void decodeDataRecord()
{
/* Check ID. */
Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3);
@@ -109,4 +116,12 @@ void Apple2Decoder::decodeDataRecord()
_sector->status = Sector::BAD_CHECKSUM;
_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_SECTORS_PER_TRACK 12
class Sector;
class SectorSet;
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);
};
extern std::unique_ptr<AbstractDecoder> createBrotherDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config);
#endif

View File

@@ -54,8 +54,15 @@ static int decode_header_gcr(uint16_t word)
return -1;
};
AbstractDecoder::RecordType BrotherDecoder::advanceToNextRecord()
class BrotherDecoder : public AbstractDecoder
{
public:
BrotherDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
@@ -63,10 +70,10 @@ AbstractDecoder::RecordType BrotherDecoder::advanceToNextRecord()
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
}
void BrotherDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(32);
const auto& bytes = toBytes(rawbits).slice(0, 4);
@@ -83,10 +90,10 @@ void BrotherDecoder::decodeSectorRecord()
return;
_sector->status = Sector::DATA_MISSING;
}
}
void BrotherDecoder::decodeDataRecord()
{
void decodeDataRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8);
@@ -106,4 +113,12 @@ void BrotherDecoder::decodeDataRecord()
uint32_t realCrc = crcbrother(_sector->data);
uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_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 "writer.h"
#include "arch/brother/brother.pb.h"
#include "lib/encoders/encoders.pb.h"
FlagGroup brotherEncoderFlags;
@@ -127,9 +128,17 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a';
}
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class BrotherEncoder : public AbstractEncoder
{
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;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
@@ -181,4 +190,16 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
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_BAM_TRACK 17
class Sector;
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;
};
extern std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config);
#endif

View File

@@ -52,8 +52,15 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType Commodore64Decoder::advanceToNextRecord()
class Commodore64Decoder : public AbstractDecoder
{
public:
Commodore64Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
@@ -61,10 +68,10 @@ AbstractDecoder::RecordType Commodore64Decoder::advanceToNextRecord()
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
}
void Commodore64Decoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
readRawBits(20);
const auto& bits = readRawBits(5*10);
@@ -76,10 +83,10 @@ void Commodore64Decoder::decodeSectorRecord()
_sector->logicalTrack = bytes[2] - 1;
if (checksum == xorBytes(bytes.slice(1, 4)))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
}
void Commodore64Decoder::decodeDataRecord()
{
void decodeDataRecord()
{
readRawBits(20);
const auto& bits = readRawBits(259*10);
@@ -89,4 +96,12 @@ void Commodore64Decoder::decodeDataRecord()
uint8_t gotChecksum = xorBytes(_sector->data);
uint8_t wantChecksum = bytes[256];
_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 "fmt/format.h"
#include "arch/c64/c64.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <ctype.h>
#include "bytes.h"
@@ -202,8 +203,72 @@ static std::vector<bool> encode_data(uint8_t input)
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
* 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
* 2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
@@ -288,61 +353,17 @@ void Commodore64Encoder::writeSector(std::vector<bool>& bits, unsigned& cursor,
write_bits(bits, cursor, C64_INTER_SECTOR_GAP, 1*8); /* sync */
}
}
}
std::unique_ptr<Fluxmap> Commodore64Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
private:
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
* 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;
return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config));
}
// vim: sw=4 ts=4 et

View File

@@ -52,8 +52,15 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType DurangoF85Decoder::advanceToNextRecord()
class DurangoF85Decoder : public AbstractDecoder
{
public:
DurangoF85Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
@@ -61,10 +68,10 @@ AbstractDecoder::RecordType DurangoF85Decoder::advanceToNextRecord()
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
}
void DurangoF85Decoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
/* Skip sync bits and ID byte. */
readRawBits(24);
@@ -81,10 +88,10 @@ void DurangoF85Decoder::decodeSectorRecord()
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
}
void DurangoF85Decoder::decodeDataRecord()
{
void decodeDataRecord()
{
/* Skip sync bits ID byte. */
readRawBits(24);
@@ -97,4 +104,11 @@ void DurangoF85Decoder::decodeDataRecord()
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_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_SECTOR_LENGTH 512
class Sector;
class Fluxmap;
class F85DecoderProto;
class DurangoF85Decoder : public AbstractDecoder
{
public:
DurangoF85Decoder(const F85DecoderProto&) {}
virtual ~DurangoF85Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<AbstractDecoder> createDurangoF85Decoder(const DecoderProto& config);
#endif

View File

@@ -99,17 +99,24 @@ static uint16_t checksum(const Bytes& bytes)
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;
_sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher);
if (matcher == &SECTOR_ID_PATTERN)
return RecordType::SECTOR_RECORD;
return RecordType::UNKNOWN_RECORD;
}
}
void Fb100Decoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
@@ -132,4 +139,12 @@ void Fb100Decoder::decodeSectorRecord()
_sector->data.writer().append(id.slice(5, 12)).append(payload);
_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_PAYLOAD_SIZE 0x500
class Sector;
class Fluxmap;
class Track;
class Fb100DecoderProto;
class Fb100Decoder : public AbstractDecoder
{
public:
Fb100Decoder(const Fb100DecoderProto&) {}
virtual ~Fb100Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<AbstractDecoder> createFb100Decoder(const DecoderProto& config);
#endif

View File

@@ -91,13 +91,16 @@ 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;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
@@ -125,10 +128,10 @@ AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
return RecordType::DATA_RECORD;
}
return RecordType::UNKNOWN_RECORD;
}
}
void IbmDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16);
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
@@ -147,10 +150,10 @@ void IbmDecoder::decodeSectorRecord()
if (_config.ignore_side_byte())
_sector->logicalSide = _sector->physicalSide;
}
}
void IbmDecoder::decodeDataRecord()
{
void decodeDataRecord()
{
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16);
auto bytes = decodeFmMfm(bits).slice(0, recordLength);
@@ -163,4 +166,21 @@ void IbmDecoder::decodeDataRecord()
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2));
_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 "writer.h"
#include "arch/ibm/ibm.pb.h"
#include "lib/encoders/encoders.pb.h"
#include "fmt/format.h"
#include <ctype.h>
@@ -64,8 +65,25 @@ static int charToInt(char c)
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;
_lastBit = data & 1;
for (int i=0; i<width; i++)
@@ -75,18 +93,10 @@ void IbmEncoder::writeRawBits(uint32_t data, int width)
_bits[pos] = data & 1;
data >>= 1;
}
}
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
void IbmEncoder::getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
@@ -97,11 +107,11 @@ void IbmEncoder::getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsi
trackdata.MergeFrom(f);
}
}
}
std::unique_ptr<Fluxmap> IbmEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
@@ -254,5 +264,18 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
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
#define IBM_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "arch/ibm/ibm.pb.h"
/* IBM format (i.e. ordinary PC floppies). */
#define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */
@@ -30,48 +26,12 @@ struct IbmIdam
uint8_t crc[2];
};
class IbmDecoder : public AbstractDecoder
{
public:
IbmDecoder(const IbmDecoderProto& config):
_config(config)
{}
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
RecordType advanceToNextRecord();
void decodeSectorRecord();
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;
};
extern std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config);
#endif

View File

@@ -124,8 +124,15 @@ uint8_t decode_side(uint8_t side)
return !!(side & 0x20);
}
AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()
class MacintoshDecoder : public AbstractDecoder
{
public:
MacintoshDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
@@ -133,10 +140,10 @@ AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
}
}
void MacintoshDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
readRawBits(24);
@@ -162,10 +169,10 @@ void MacintoshDecoder::decodeSectorRecord()
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
}
void MacintoshDecoder::decodeDataRecord()
{
void decodeDataRecord()
{
auto id = toBytes(readRawBits(24)).reader().read_be24();
if (id != MAC_DATA_RECORD)
return;
@@ -183,10 +190,10 @@ void MacintoshDecoder::decodeDataRecord()
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_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;
if (track.physicalTrack < 16)
count = 12;
@@ -203,6 +210,11 @@ std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const
while (count--)
sectors.insert(count);
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 "writer.h"
#include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
#include "arch/macintosh/macintosh.pb.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 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);
}
std::unique_ptr<Fluxmap> MacintoshEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class MacintoshEncoder : public AbstractEncoder
{
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))
return std::unique_ptr<Fluxmap>();
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation;
double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false });
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(physicalTrack);
@@ -238,5 +236,14 @@ std::unique_ptr<Fluxmap> MacintoshEncoder::encode(
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
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
#define MACINTOSH_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define MAC_SECTOR_RECORD 0xd5aa96 /* 1101 0101 1010 1010 1001 0110 */
#define MAC_DATA_RECORD 0xd5aaad /* 1101 0101 1010 1010 1010 1101 */
@@ -13,36 +10,13 @@
#define MAC_TRACKS_PER_DISK 80
class Sector;
class Fluxmap;
class MacintoshDecoderProto;
class MacintoshEncoderProto;
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;
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
extern std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config);
#endif

View File

@@ -1,5 +1,14 @@
syntax = "proto2";
message MacintoshDecoderProto {}
message MacintoshEncoderProto {}
import "lib/common.proto";
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. */
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. */
static uint8_t checksum(const Bytes& bytes) {
ByteReader br(bytes);
@@ -36,8 +24,27 @@ static uint8_t checksum(const Bytes& bytes) {
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 bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
ByteReader br(bytes);
@@ -58,4 +65,11 @@ void MicropolisDecoder::decodeSectorRecord()
br.read(5); /* 4 byte ECC and ECC-present flag */
_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)
class Sector;
class Fluxmap;
class MicropolisDecoderProto;
class MicropolisDecoder : public AbstractDecoder
{
public:
MicropolisDecoder(const MicropolisDecoderProto&) {}
virtual ~MicropolisDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config);
#endif

View File

@@ -23,14 +23,21 @@ const int SECTOR_SIZE = 256;
*/
const FluxPattern ID_PATTERN(32, 0xaaaaffaf);
void MxDecoder::beginTrack()
class MxDecoder : public AbstractDecoder
{
public:
MxDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
void beginTrack()
{
_currentSector = -1;
_clock = 0;
}
}
AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
{
RecordType advanceToNextRecord()
{
if (_currentSector == -1)
{
/* First sector in the track: look for the sync marker. */
@@ -54,10 +61,10 @@ AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
_currentSector++;
return SECTOR_RECORD;
}
}
void MxDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
@@ -72,4 +79,17 @@ void MxDecoder::decodeSectorRecord()
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE);
_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"
class MxDecoderProto;
class MxDecoder : public AbstractDecoder
{
public:
MxDecoder(const MxDecoderProto&) {}
virtual ~MxDecoder() {}
void beginTrack();
RecordType advanceToNextRecord();
void decodeSectorRecord();
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
extern std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config);
#endif

View File

@@ -18,6 +18,7 @@
#include "sector.h"
#include "northstar.h"
#include "bytes.h"
#include "lib/decoders/decoders.pb.h"
#include "fmt/format.h"
/*
@@ -48,9 +49,33 @@ const FluxMatchers ANY_SECTOR_PATTERN(
}
);
/* Search for FM or MFM sector record */
AbstractDecoder::RecordType NorthstarDecoder::advanceToNextRecord()
/* 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 */
RecordType advanceToNextRecord()
{
nanoseconds_t now = _fmr->tell().ns();
/* For all but the first sector, seek to the next sector pulse.
@@ -96,9 +121,9 @@ AbstractDecoder::RecordType NorthstarDecoder::advanceToNextRecord()
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
// std::cout << fmt::format(
// "Sector ID {}: hole at {}ms, sector start at {}ms",
// _hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl;
// std::cout << fmt::format(
// "Sector ID {}: hole at {}ms, sector start at {}ms",
// _hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl;
if (matcher == &MFM_PATTERN) {
_sectorType = SECTOR_TYPE_MFM;
@@ -113,26 +138,10 @@ AbstractDecoder::RecordType NorthstarDecoder::advanceToNextRecord()
}
return UNKNOWN_RECORD;
}
/* 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;
}
void NorthstarDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
unsigned recordSize, payloadSize, headerSize;
if (_sectorType == SECTOR_TYPE_MFM) {
@@ -166,10 +175,22 @@ void NorthstarDecoder::decodeSectorRecord()
uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize, payloadSize));
_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 };
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 "northstar.h"
#include "sector.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 PRE_HEADER_GAP_FILL_SIZE_SD 9
@@ -95,9 +100,16 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
}
}
std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class NorthstarEncoder : public AbstractEncoder
{
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;
double clockRateUs = 4.00;
@@ -127,4 +139,14 @@ std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
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_DD (32)
#define NORTHSTAR_HEADER_SIZE_SD (1)
@@ -28,45 +25,14 @@
#define SECTOR_TYPE_MFM (0)
#define SECTOR_TYPE_FM (1)
class NorthstarEncoderProto;
class NorthstarDecoderProto;
class AbstractDecoder;
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 std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config);
#endif /* NORTHSTAR */

View File

@@ -40,8 +40,15 @@ const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
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;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
@@ -49,10 +56,10 @@ AbstractDecoder::RecordType Tids990Decoder::advanceToNextRecord()
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
}
void Tids990Decoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
@@ -69,10 +76,10 @@ void Tids990Decoder::decodeSectorRecord()
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
}
}
void Tids990Decoder::decodeDataRecord()
{
void decodeDataRecord()
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
@@ -83,5 +90,11 @@ void Tids990Decoder::decodeDataRecord()
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
uint16_t wantChecksum = br.read_be16();
_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 "writer.h"
#include "arch/tids990/tids990.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <fmt/format.h>
static int charToInt(char c)
@@ -16,31 +17,6 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a';
}
void Tids990Encoder::writeRawBits(uint32_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;
}
}
void Tids990Encoder::writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void Tids990Encoder::writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
@@ -49,9 +25,43 @@ static uint8_t decodeUint16(uint16_t raw)
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> Tids990Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class Tids990Encoder : public AbstractEncoder
{
public:
Tids990Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.tids990())
{}
private:
void writeRawBits(uint32_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;
}
}
void writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
@@ -126,5 +136,18 @@ std::unique_ptr<Fluxmap> Tids990Encoder::encode(
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
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_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
class Sector;
class SectorSet;
class Fluxmap;
class Track;
class Tids990DecoderProto;
class Tids990EncoderProto;
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
class Tids990Decoder : public AbstractDecoder
{
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;
extern std::unique_ptr<AbstractDecoder> createTids990Decoder(const DecoderProto& config);
#endif

View File

@@ -54,8 +54,15 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType Victor9kDecoder::advanceToNextRecord()
class Victor9kDecoder : public AbstractDecoder
{
public:
Victor9kDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
@@ -63,10 +70,10 @@ AbstractDecoder::RecordType Victor9kDecoder::advanceToNextRecord()
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
}
}
void Victor9kDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
/* Skip the sync marker bit. */
readRawBits(23);
@@ -86,10 +93,10 @@ void Victor9kDecoder::decodeSectorRecord()
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
}
void Victor9kDecoder::decodeDataRecord()
{
void decodeDataRecord()
{
/* Skip the sync marker bit. */
readRawBits(23);
@@ -108,4 +115,12 @@ void Victor9kDecoder::decodeDataRecord()
uint16_t gotChecksum = sumBytes(_sector->data);
uint16_t wantChecksum = br.read_le16();
_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
class Sector;
class Fluxmap;
class Victor9kDecoderProto;
class Victor9kDecoder : public AbstractDecoder
{
public:
Victor9kDecoder(const Victor9kDecoderProto&) {}
virtual ~Victor9kDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config);
#endif

View File

@@ -14,18 +14,25 @@
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;
_fmr->seekToIndexMark();
_sector->clock = _fmr->seekToPattern(SECTOR_START_PATTERN, matcher);
if (matcher == &SECTOR_START_PATTERN)
return SECTOR_RECORD;
return UNKNOWN_RECORD;
}
}
void ZilogMczDecoder::decodeSectorRecord()
{
void decodeSectorRecord()
{
readRawBits(14);
auto rawbits = readRawBits(140*16);
@@ -45,4 +52,11 @@ void ZilogMczDecoder::decodeSectorRecord()
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
_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
#define ZILOGMCZ_H
class Sector;
class Fluxmap;
class ZilogMczDecoderProto;
class ZilogMczDecoder : public AbstractDecoder
{
public:
ZilogMczDecoder(const ZilogMczDecoderProto&) {}
virtual ~ZilogMczDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<AbstractDecoder> createZilogMczDecoder(const DecoderProto& config);
#endif

View File

@@ -5,18 +5,19 @@
#include "encoders/encoders.h"
//#include "arch/aeslanier/aeslanier.h"
#include "arch/amiga/amiga.h"
//#include "arch/apple2/apple2.h"
//#include "arch/brother/brother.h"
//#include "arch/c64/c64.h"
//#include "arch/f85/f85.h"
//#include "arch/ibm/ibm.h"
//#include "arch/macintosh/macintosh.h"
//#include "arch/micropolis/micropolis.h"
//#include "arch/mx/mx.h"
//#include "arch/northstar/northstar.h"
//#include "arch/tids990/tids990.h"
//#include "arch/victor9k/victor9k.h"
//#include "arch/zilogmcz/zilogmcz.h"
#include "arch/apple2/apple2.h"
#include "arch/brother/brother.h"
#include "arch/c64/c64.h"
#include "arch/f85/f85.h"
#include "arch/fb100/fb100.h"
#include "arch/ibm/ibm.h"
#include "arch/macintosh/macintosh.h"
#include "arch/micropolis/micropolis.h"
#include "arch/mx/mx.h"
#include "arch/northstar/northstar.h"
#include "arch/tids990/tids990.h"
#include "arch/victor9k/victor9k.h"
#include "arch/zilogmcz/zilogmcz.h"
#include "decoders/fluxmapreader.h"
#include "record.h"
#include "protocol.h"
@@ -33,7 +34,21 @@ std::unique_ptr<AbstractDecoder> AbstractDecoder::create(const DecoderProto& con
static const std::map<int,
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());

View File

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

View File

@@ -14,33 +14,24 @@
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:
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config.amiga()));
{ EncoderProto::kAmiga, createAmigaEncoder },
{ EncoderProto::kBrother, createBrotherEncoder },
{ EncoderProto::kC64, createCommodore64Encoder },
{ EncoderProto::kIbm, createIbmEncoder },
{ EncoderProto::kMacintosh, createMacintoshEncoder },
{ EncoderProto::kNorthstar, createNorthstarEncoder },
};
case EncoderProto::kIbm:
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config.ibm()));
auto encoder = encoders.find(config.format_case());
if (encoder == encoders.end())
Error() << "no encoder specified";
case EncoderProto::kBrother:
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>();
return (encoder->second)(config);
}
Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock)
{
nanoseconds_t now = duration();

View File

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

View File

@@ -24,10 +24,16 @@ public:
unsigned autoSides;
unsigned autoSectors;
unsigned autoBytes;
bool mixedDensity = false;
sectors.calculateSize(autoTracks, autoSides, 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",
autoTracks, autoSides,
autoSectors, autoBytes == 256 ? "SD" : "DD", autoBytes,
@@ -56,11 +62,29 @@ public:
(sectorId * autoBytes); /* Sector offset from beginning of track. */
}
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);
}
}
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createNsiImageWriter(