Refactor the encoders the same way that's been done for the decoders.

This commit is contained in:
David Given
2021-07-11 16:55:18 +02:00
parent e226773066
commit b423a71b38
18 changed files with 689 additions and 706 deletions

View File

@@ -9,27 +9,8 @@
#define AMIGA_SECTORS_PER_TRACK 11
#define AMIGA_RECORD_SIZE 0x21f
class Sector;
class Fluxmap;
class SectorSet;
class AmigaDecoderProto;
class AmigaEncoderProto;
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

@@ -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,31 +97,48 @@ 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
{
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
public:
AmigaEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.amiga()) {}
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false });
lastBit = false;
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us()*1e3);
return fluxmap;
fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false });
lastBit = false;
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
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

@@ -13,28 +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 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

@@ -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,58 +128,78 @@ 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
{
int logicalTrack;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _config.bias();
switch (_config.format())
{
case BROTHER120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack/2;
break;
public:
BrotherEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.brother())
{}
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack;
break;
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
int logicalTrack;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _config.bias();
switch (_config.format())
{
case BROTHER120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack/2;
break;
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack;
break;
}
int bitsPerRevolution = 200000.0 / clockRateUs;
const std::string& skew = sectorSkew.get();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
double headerMs = postIndexGapMs + sectorCount*sectorSpacingMs;
unsigned headerCursor = headerMs*1e3 / clockRateUs;
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, logicalTrack, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
write_sector_data(bits, cursor, sectorData->data);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
// The pre-index gap is not normally reported.
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
int bitsPerRevolution = 200000.0 / clockRateUs;
const std::string& skew = sectorSkew.get();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
private:
const BrotherEncoderProto& _config;
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
double headerMs = postIndexGapMs + sectorCount*sectorSpacingMs;
unsigned headerCursor = headerMs*1e3 / clockRateUs;
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
};
const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, logicalTrack, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
write_sector_data(bits, cursor, sectorData->data);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
// The pre-index gap is not normally reported.
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
}

View File

@@ -27,33 +27,7 @@
#define C64_TRACKS_PER_DISK 40
#define C64_BAM_TRACK 17
class Sector;
class Fluxmap;
class Commodore64DecoderProto;
class Commodore64EncoderProto;
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

@@ -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,147 +203,167 @@ 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
{
/* 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)
* 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
* 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
* 5. Data block 55...4A (325 GCR bytes)
* 6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
* 1. Header sync (SYNC for the next sector)
*/
if ((sector->status == Sector::OK) or (sector->status == Sector::BAD_CHECKSUM))
public:
Commodore64Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.c64())
{}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
// There is data to encode to disk.
if ((sector->data.size() != C64_SECTOR_LENGTH))
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector->data.size());
/* 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.
*/
// 1. Write header Sync (not GCR)
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 2. Write Header info 10 GCR bytes
/*
* The 10 byte header info (#2) is GCR encoded and must be decoded to
* it's normal 8 bytes to be understood. Once decoded, its breakdown is
* as follows:
*
* Byte $00 - header block ID ($08)
* 01 - header block checksum 16 (EOR of $02-$05)
* 02 - Sector
* 03 - Track
* 04 - Format ID byte #2
* 05 - Format ID byte #1
* 06-07 - $0F ("off" bytes)
*/
uint8_t encodedTrack = ((sector->logicalTrack) + 1); // C64 track numbering starts with 1. Fluxengine with 0.
uint8_t encodedSector = sector->logicalSector;
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
// uint8_t formatByte2 = C64_FORMAT_ID_BYTE2;
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ _formatByte1 ^ _formatByte2);
write_bits(bits, cursor, encode_data(C64_HEADER_BLOCK_ID));
write_bits(bits, cursor, encode_data(headerChecksum));
write_bits(bits, cursor, encode_data(encodedSector));
write_bits(bits, cursor, encode_data(encodedTrack));
write_bits(bits, cursor, encode_data(_formatByte2));
write_bits(bits, cursor, encode_data(_formatByte1));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
// 3. Write header GAP not GCR
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_HEADER_GAP, 1*8); /* header gap */
// 4. Write Data sync not GCR
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 5. Write data block 325 GCR bytes
/*
* The 325 byte data block (#5) is GCR encoded and must be decoded to its
* normal 260 bytes to be understood. The data block is made up of the following:
*
* Byte $00 - data block ID ($07)
* 01-100 - 256 bytes data
* 101 - data block checksum (EOR of $01-100)
* 102-103 - $00 ("off" bytes, to make the sector size a multiple of 5)
*/
write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID));
uint8_t dataChecksum = xorBytes(sector->data);
ByteReader br(sector->data);
int i = 0;
for (i = 0; i < C64_SECTOR_LENGTH; i++)
{
uint8_t val = br.read_8();
write_bits(bits, cursor, encode_data(val));
}
write_bits(bits, cursor, encode_data(dataChecksum));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
//6. Write inter-sector gap 9 - 12 bytes nor gcr
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_INTER_SECTOR_GAP, 1*8); /* sync */
}
}
std::unique_ptr<Fluxmap> Commodore64Encoder::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);
const auto& sectorData = allSectors.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
if (sectorData)
{
writeSector(bits, cursor, sectorData);
writtenSectors++;
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)
* 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
* 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
* 5. Data block 55...4A (325 GCR bytes)
* 6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
* 1. Header sync (SYNC for the next sector)
*/
if ((sector->status == Sector::OK) or (sector->status == Sector::BAD_CHECKSUM))
{
// There is data to encode to disk.
if ((sector->data.size() != C64_SECTOR_LENGTH))
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector->data.size());
// 1. Write header Sync (not GCR)
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 2. Write Header info 10 GCR bytes
/*
* The 10 byte header info (#2) is GCR encoded and must be decoded to
* it's normal 8 bytes to be understood. Once decoded, its breakdown is
* as follows:
*
* Byte $00 - header block ID ($08)
* 01 - header block checksum 16 (EOR of $02-$05)
* 02 - Sector
* 03 - Track
* 04 - Format ID byte #2
* 05 - Format ID byte #1
* 06-07 - $0F ("off" bytes)
*/
uint8_t encodedTrack = ((sector->logicalTrack) + 1); // C64 track numbering starts with 1. Fluxengine with 0.
uint8_t encodedSector = sector->logicalSector;
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
// uint8_t formatByte2 = C64_FORMAT_ID_BYTE2;
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ _formatByte1 ^ _formatByte2);
write_bits(bits, cursor, encode_data(C64_HEADER_BLOCK_ID));
write_bits(bits, cursor, encode_data(headerChecksum));
write_bits(bits, cursor, encode_data(encodedSector));
write_bits(bits, cursor, encode_data(encodedTrack));
write_bits(bits, cursor, encode_data(_formatByte2));
write_bits(bits, cursor, encode_data(_formatByte1));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
// 3. Write header GAP not GCR
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_HEADER_GAP, 1*8); /* header gap */
// 4. Write Data sync not GCR
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 5. Write data block 325 GCR bytes
/*
* The 325 byte data block (#5) is GCR encoded and must be decoded to its
* normal 260 bytes to be understood. The data block is made up of the following:
*
* Byte $00 - data block ID ($07)
* 01-100 - 256 bytes data
* 101 - data block checksum (EOR of $01-100)
* 102-103 - $00 ("off" bytes, to make the sector size a multiple of 5)
*/
write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID));
uint8_t dataChecksum = xorBytes(sector->data);
ByteReader br(sector->data);
int i = 0;
for (i = 0; i < C64_SECTOR_LENGTH; i++)
{
uint8_t val = br.read_8();
write_bits(bits, cursor, encode_data(val));
}
write_bits(bits, cursor, encode_data(dataChecksum));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
//6. Write inter-sector gap 9 - 12 bytes nor gcr
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_INTER_SECTOR_GAP, 1*8); /* sync */
}
}
if (writtenSectors == 0)
return std::unique_ptr<Fluxmap>();
private:
const Commodore64EncoderProto& _config;
uint8_t _formatByte1;
uint8_t _formatByte2;
};
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;
std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config));
}
// vim: sw=4 ts=4 et

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,19 +65,6 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a';
}
void IbmEncoder::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;
}
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
@@ -85,174 +73,209 @@ static uint8_t decodeUint16(uint16_t raw)
return decodeFmMfm(b.toBits())[0];
}
void IbmEncoder::getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
class IbmEncoder : public AbstractEncoder
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
public:
IbmEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.ibm())
{}
private:
void writeRawBits(uint32_t data, int width)
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
}
std::unique_ptr<Fluxmap> IbmEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
auto writeBytes = [&](const Bytes& bytes)
{
if (trackdata.use_fm())
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
};
auto writeFillerBytes = [&](int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
};
if (trackdata.swap_sides())
physicalSide = 1 - physicalSide;
double clockRateUs = 1e3 / trackdata.clock_rate_khz();
if (!trackdata.use_fm())
clockRateUs /= 2.0;
int bitsPerRevolution = (trackdata.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
uint8_t sectorSize = 0;
{
int s = trackdata.sector_size() >> 7;
while (s > 1)
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
s >>= 1;
sectorSize += 1;
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
uint8_t gapFill = trackdata.use_fm() ? 0x00 : 0x4e;
writeFillerBytes(trackdata.gap0(), gapFill);
if (trackdata.emit_iam())
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
auto writeBytes = [&](const Bytes& bytes)
{
if (trackdata.use_fm())
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
};
auto writeFillerBytes = [&](int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
};
if (trackdata.swap_sides())
physicalSide = 1 - physicalSide;
double clockRateUs = 1e3 / trackdata.clock_rate_khz();
if (!trackdata.use_fm())
clockRateUs /= 2.0;
int bitsPerRevolution = (trackdata.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
uint8_t sectorSize = 0;
{
for (int i=0; i<3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
int s = trackdata.sector_size() >> 7;
while (s > 1)
{
s >>= 1;
sectorSize += 1;
}
}
writeRawBits(trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeFillerBytes(trackdata.gap1(), gapFill);
uint8_t gapFill = trackdata.use_fm() ? 0x00 : 0x4e;
writeFillerBytes(trackdata.gap0(), gapFill);
if (trackdata.emit_iam())
{
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
writeRawBits(trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeFillerBytes(trackdata.gap1(), gapFill);
}
bool first = true;
for (char sectorChar : trackdata.sector_skew())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeFillerBytes(trackdata.gap3(), gapFill);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
{
/* If there are any missing sectors, this is an empty track. */
return std::unique_ptr<Fluxmap>();
}
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector + trackdata.start_sector_id());
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.idam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeFillerBytes(trackdata.gap2(), gapFill);
{
Bytes data;
ByteWriter bw(data);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
Bytes truncatedData = sectorData->data.slice(0, trackdata.sector_size());
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.dam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeFillerBytes(1, gapFill);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}
bool first = true;
for (char sectorChar : trackdata.sector_skew())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeFillerBytes(trackdata.gap3(), gapFill);
first = false;
private:
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
{
/* If there are any missing sectors, this is an empty track. */
return std::unique_ptr<Fluxmap>();
}
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector + trackdata.start_sector_id());
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.idam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeFillerBytes(trackdata.gap2(), gapFill);
{
Bytes data;
ByteWriter bw(data);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
Bytes truncatedData = sectorData->data.slice(0, trackdata.sector_size());
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.dam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeFillerBytes(1, gapFill);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
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,31 +26,12 @@ struct IbmIdam
uint8_t crc[2];
};
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;
};
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
extern std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config);
#endif

View File

@@ -218,4 +218,3 @@ std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& conf
return std::unique_ptr<AbstractDecoder>(new MacintoshDecoder(config));
}

View File

@@ -210,33 +210,46 @@ 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
{
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
public:
MacintoshEncoder(const EncoderProto& config):
AbstractEncoder(config)
{}
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation;
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
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 });
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation;
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
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;
}
};
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,24 +10,13 @@
#define MAC_TRACKS_PER_DISK 80
class Sector;
class Fluxmap;
class MacintoshDecoderProto;
class MacintoshEncoderProto;
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

@@ -194,5 +194,3 @@ std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& conf
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,36 +100,53 @@ 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
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
public:
NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.northstar())
{}
if ((physicalTrack < 0) || (physicalTrack >= 35))
return std::unique_ptr<Fluxmap>();
const auto& sector = allSectors.get(physicalTrack, physicalSide, 0);
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
} else {
clockRateUs /= 2.00;
}
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorId = 0; sectorId < 10; sectorId++)
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
if ((physicalTrack < 0) || (physicalTrack >= 35))
return std::unique_ptr<Fluxmap>();
const auto& sector = allSectors.get(physicalTrack, physicalSide, 0);
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
} else {
clockRateUs /= 2.00;
}
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
if (cursor > bits.size())
Error() << "track data overrun";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
if (cursor > bits.size())
Error() << "track data overrun";
private:
const NorthstarEncoderProto& _config;
};
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
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,26 +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 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

@@ -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,82 +25,129 @@ 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
{
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
public:
Tids990Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.tids990())
{}
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
writeBytes(_config.gap1_bytes(), 0x55);
bool first = true;
for (char sectorChar : _config.sector_skew())
private:
void writeRawBits(uint32_t data, int width)
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
Bytes header;
ByteWriter bw(header);
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
void writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
writeRawBits(_config.am1_byte(), 16);
writeBytes(header.slice(1));
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);
_cursor = 0;
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
writeBytes(_config.gap1_bytes(), 0x55);
bool first = true;
for (char sectorChar : _config.sector_skew())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeRawBits(_config.am1_byte(), 16);
writeBytes(header.slice(1));
}
writeBytes(_config.gap2_bytes(), 0x55);
{
Bytes data;
ByteWriter bw(data);
writeBytes(12, 0x55);
bw.write_8(am2Unencoded);
bw += sectorData->data;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeRawBits(_config.am2_byte(), 16);
writeBytes(data.slice(1));
}
}
writeBytes(_config.gap2_bytes(), 0x55);
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, 0x55);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}
{
Bytes data;
ByteWriter bw(data);
private:
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
writeBytes(12, 0x55);
bw.write_8(am2Unencoded);
bw += sectorData->data;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeRawBits(_config.am2_byte(), 16);
writeBytes(data.slice(1));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, 0x55);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
}

View File

@@ -5,38 +5,10 @@
#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 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;
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
extern std::unique_ptr<AbstractDecoder> createTids990Decoder(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);