mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge from master.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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."];
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
18
arch/mx/mx.h
18
arch/mx/mx.h
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user