mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Massive refactor to work in logical cylinders everywhere. The logical/physical
mapping is now done at the last stage and should, ideally, be automatic. I'm sure there are bugs everywhere.
This commit is contained in:
@@ -68,7 +68,7 @@ public:
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location&) const override
|
||||
{
|
||||
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
return sectors;
|
||||
|
||||
@@ -12,152 +12,156 @@ static bool lastBit;
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
lastBit = data & 1;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
cursor += width;
|
||||
lastBit = data & 1;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 528))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 528";
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 528))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 528";
|
||||
|
||||
uint32_t checksum = 0;
|
||||
uint32_t checksum = 0;
|
||||
|
||||
auto write_interleaved_bytes = [&](const Bytes& bytes)
|
||||
{
|
||||
Bytes interleaved = amigaInterleave(bytes);
|
||||
Bytes mfm = encodeMfm(interleaved, lastBit);
|
||||
checksum ^= amigaChecksum(mfm);
|
||||
checksum &= 0x55555555;
|
||||
write_bits(bits, cursor, mfm);
|
||||
};
|
||||
auto write_interleaved_bytes = [&](const Bytes& bytes)
|
||||
{
|
||||
Bytes interleaved = amigaInterleave(bytes);
|
||||
Bytes mfm = encodeMfm(interleaved, lastBit);
|
||||
checksum ^= amigaChecksum(mfm);
|
||||
checksum &= 0x55555555;
|
||||
write_bits(bits, cursor, mfm);
|
||||
};
|
||||
|
||||
auto write_interleaved_word = [&](uint32_t word)
|
||||
{
|
||||
Bytes b(4);
|
||||
b.writer().write_be32(word);
|
||||
write_interleaved_bytes(b);
|
||||
};
|
||||
auto write_interleaved_word = [&](uint32_t word)
|
||||
{
|
||||
Bytes b(4);
|
||||
b.writer().write_be32(word);
|
||||
write_interleaved_bytes(b);
|
||||
};
|
||||
|
||||
write_bits(bits, cursor, 0xaaaa, 2*8);
|
||||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
|
||||
write_bits(bits, cursor, 0xaaaa, 2 * 8);
|
||||
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6 * 8);
|
||||
|
||||
checksum = 0;
|
||||
Bytes header =
|
||||
{
|
||||
0xff, /* Amiga 1.0 format byte */
|
||||
(uint8_t) ((sector->logicalTrack<<1) | sector->logicalSide),
|
||||
(uint8_t) sector->logicalSector,
|
||||
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
|
||||
};
|
||||
write_interleaved_bytes(header);
|
||||
Bytes recoveryInfo(16);
|
||||
if (sector->data.size() == 528)
|
||||
recoveryInfo = sector->data.slice(512, 16);
|
||||
write_interleaved_bytes(recoveryInfo);
|
||||
write_interleaved_word(checksum);
|
||||
checksum = 0;
|
||||
Bytes header = {0xff, /* Amiga 1.0 format byte */
|
||||
(uint8_t)((sector->logicalTrack << 1) | sector->logicalSide),
|
||||
(uint8_t)sector->logicalSector,
|
||||
(uint8_t)(AMIGA_SECTORS_PER_TRACK - sector->logicalSector)};
|
||||
write_interleaved_bytes(header);
|
||||
Bytes recoveryInfo(16);
|
||||
if (sector->data.size() == 528)
|
||||
recoveryInfo = sector->data.slice(512, 16);
|
||||
write_interleaved_bytes(recoveryInfo);
|
||||
write_interleaved_word(checksum);
|
||||
|
||||
Bytes data = sector->data.slice(0, 512);
|
||||
write_interleaved_word(amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
|
||||
write_interleaved_bytes(data);
|
||||
Bytes data = sector->data.slice(0, 512);
|
||||
write_interleaved_word(
|
||||
amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
|
||||
write_interleaved_bytes(data);
|
||||
}
|
||||
|
||||
class AmigaEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
AmigaEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.amiga()) {}
|
||||
AmigaEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.amiga())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < AMIGA_TRACKS_PER_DISK))
|
||||
{
|
||||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
if ((location.logicalCylinder >= 0) &&
|
||||
(location.logicalCylinder < AMIGA_TRACKS_PER_DISK))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < AMIGA_SECTORS_PER_TRACK;
|
||||
sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(
|
||||
location.logicalCylinder, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
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;
|
||||
|
||||
fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false });
|
||||
lastBit = false;
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, sector);
|
||||
|
||||
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
|
||||
{
|
||||
const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sectorData)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
}
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), {true, false});
|
||||
|
||||
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;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const AmigaEncoderProto& _config;
|
||||
const AmigaEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,13 +10,15 @@
|
||||
#include <ctype.h>
|
||||
#include "bytes.h"
|
||||
|
||||
static int encode_data_gcr(uint8_t data) {
|
||||
switch(data)
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -24,162 +26,178 @@ static int encode_data_gcr(uint8_t data) {
|
||||
class Apple2Encoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
Apple2Encoder(const EncoderProto& config):
|
||||
AbstractEncoder(config)
|
||||
{}
|
||||
Apple2Encoder(const EncoderProto& config): AbstractEncoder(config) {}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
constexpr auto numSectors = 16;
|
||||
if (physicalSide == 0)
|
||||
{
|
||||
int logicalTrack = physicalTrack / 2;
|
||||
unsigned numSectors = 16;
|
||||
for (int sectorId=0; sectorId<numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(logicalTrack, 0, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
constexpr auto numSectors = 16;
|
||||
if (location.head == 0)
|
||||
{
|
||||
unsigned numSectors = 16;
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(location.logicalCylinder, 0, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
std::unique_ptr<Fluxmap> encode(
|
||||
const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
if (physicalSide != 0)
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
double clockRateUs = 4.;
|
||||
|
||||
int logicalTrack = physicalTrack / 2;
|
||||
double clockRateUs = 4.;
|
||||
// apple2 drives spin at 300rpm. 300/minute in us = 200000.
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
|
||||
// apple2 drives spin at 300rpm. 300/minute in us = 200000.
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
for (const auto& sector : sectors)
|
||||
writeSector(bits, cursor, *sector);
|
||||
|
||||
for (const auto& sector : sectors) {
|
||||
if(sector) {
|
||||
writeSector(bits, cursor, *sector);
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
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:
|
||||
uint8_t volume_id = 254;
|
||||
|
||||
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
|
||||
* and R. Belmont: https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
|
||||
/* This is extremely inspired by the MESS implementation, written by Nathan
|
||||
* Woods and R. Belmont:
|
||||
* https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
|
||||
* as well as Understanding the Apple II (1983) Chapter 9
|
||||
* https://archive.org/details/Understanding_the_Apple_II_1983_Quality_Software/page/n230/mode/1up?view=theater
|
||||
*/
|
||||
|
||||
void writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector& sector) const
|
||||
void writeSector(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Sector& sector) const
|
||||
{
|
||||
if ((sector.status == Sector::OK) or (sector.status == Sector::BAD_CHECKSUM))
|
||||
{
|
||||
auto write_bit = [&](bool val) {
|
||||
if(cursor <= bits.size()) { bits[cursor] = val; }
|
||||
cursor++;
|
||||
};
|
||||
if ((sector.status == Sector::OK) or
|
||||
(sector.status == Sector::BAD_CHECKSUM))
|
||||
{
|
||||
auto write_bit = [&](bool val)
|
||||
{
|
||||
if (cursor <= bits.size())
|
||||
{
|
||||
bits[cursor] = val;
|
||||
}
|
||||
cursor++;
|
||||
};
|
||||
|
||||
auto write_bits = [&](uint32_t bits, int width) {
|
||||
for(int i=width; i--;) {
|
||||
write_bit(bits & (1u << i));
|
||||
}
|
||||
};
|
||||
auto write_bits = [&](uint32_t bits, int width)
|
||||
{
|
||||
for (int i = width; i--;)
|
||||
{
|
||||
write_bit(bits & (1u << i));
|
||||
}
|
||||
};
|
||||
|
||||
auto write_gcr44 = [&](uint8_t value) {
|
||||
write_bits((value << 7) | value | 0xaaaa, 16);
|
||||
};
|
||||
auto write_gcr44 = [&](uint8_t value)
|
||||
{
|
||||
write_bits((value << 7) | value | 0xaaaa, 16);
|
||||
};
|
||||
|
||||
auto write_gcr6 = [&](uint8_t value) {
|
||||
write_bits(encode_data_gcr(value), 8);
|
||||
};
|
||||
auto write_gcr6 = [&](uint8_t value)
|
||||
{
|
||||
write_bits(encode_data_gcr(value), 8);
|
||||
};
|
||||
|
||||
// The special "FF40" sequence is used to synchronize the receiving
|
||||
// shift register. It's written as "1111 1111 00"; FF indicates the
|
||||
// 8 consecutive 1-bits, while "40" indicates the total number of
|
||||
// microseconds.
|
||||
auto write_ff40 = [&](int n=1) {
|
||||
for(;n--;) {
|
||||
write_bits(0xff << 2, 10);
|
||||
}
|
||||
};
|
||||
// The special "FF40" sequence is used to synchronize the receiving
|
||||
// shift register. It's written as "1111 1111 00"; FF indicates the
|
||||
// 8 consecutive 1-bits, while "40" indicates the total number of
|
||||
// microseconds.
|
||||
auto write_ff40 = [&](int n = 1)
|
||||
{
|
||||
for (; n--;)
|
||||
{
|
||||
write_bits(0xff << 2, 10);
|
||||
}
|
||||
};
|
||||
|
||||
// There is data to encode to disk.
|
||||
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
|
||||
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector.data.size());
|
||||
// There is data to encode to disk.
|
||||
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
|
||||
Error() << fmt::format(
|
||||
"unsupported sector size {} --- you must pick 256",
|
||||
sector.data.size());
|
||||
|
||||
// Write address syncing leader : A sequence of "FF40"s; 5 of them
|
||||
// are said to suffice to synchronize the decoder.
|
||||
// "FF40" indicates that the actual data written is "1111
|
||||
// 1111 00" i.e., 8 1s and a total of 40 microseconds
|
||||
//
|
||||
// In standard formatting, the first logical sector apparently gets
|
||||
// extra padding.
|
||||
write_ff40(sector.logicalSector == 0 ? 32 : 8);
|
||||
// Write address syncing leader : A sequence of "FF40"s; 5 of them
|
||||
// are said to suffice to synchronize the decoder.
|
||||
// "FF40" indicates that the actual data written is "1111
|
||||
// 1111 00" i.e., 8 1s and a total of 40 microseconds
|
||||
//
|
||||
// In standard formatting, the first logical sector apparently gets
|
||||
// extra padding.
|
||||
write_ff40(sector.logicalSector == 0 ? 32 : 8);
|
||||
|
||||
// Write address field: APPLE2_SECTOR_RECORD + sector identifier + DE AA EB
|
||||
write_bits(APPLE2_SECTOR_RECORD, 24);
|
||||
write_gcr44(volume_id);
|
||||
write_gcr44(sector.logicalTrack);
|
||||
write_gcr44(sector.logicalSector);
|
||||
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
|
||||
write_bits(0xDEAAEB, 24);
|
||||
// Write address field: APPLE2_SECTOR_RECORD + sector identifier +
|
||||
// DE AA EB
|
||||
write_bits(APPLE2_SECTOR_RECORD, 24);
|
||||
write_gcr44(volume_id);
|
||||
write_gcr44(sector.logicalTrack);
|
||||
write_gcr44(sector.logicalSector);
|
||||
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
|
||||
write_bits(0xDEAAEB, 24);
|
||||
|
||||
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector data + sum + DE AA EB (+ mystery bits cut off of the scan?)
|
||||
write_ff40(8);
|
||||
write_bits(APPLE2_DATA_RECORD, 24);
|
||||
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector
|
||||
// data + sum + DE AA EB (+ mystery bits cut off of the scan?)
|
||||
write_ff40(8);
|
||||
write_bits(APPLE2_DATA_RECORD, 24);
|
||||
|
||||
// Convert the sector data to GCR, append the checksum, and write it out
|
||||
constexpr auto TWOBIT_COUNT = 0x56; // Size of the 'twobit' area at the start of the GCR data
|
||||
uint8_t checksum = 0;
|
||||
for(int i=0; i<APPLE2_ENCODED_SECTOR_LENGTH; i++) {
|
||||
int value;
|
||||
if(i >= TWOBIT_COUNT) {
|
||||
value = sector.data[i - TWOBIT_COUNT] >> 2;
|
||||
} else {
|
||||
uint8_t tmp = sector.data[i];
|
||||
value = ((tmp & 1) << 1) | ((tmp & 2) >> 1);
|
||||
// Convert the sector data to GCR, append the checksum, and write it
|
||||
// out
|
||||
constexpr auto TWOBIT_COUNT =
|
||||
0x56; // Size of the 'twobit' area at the start of the GCR data
|
||||
uint8_t checksum = 0;
|
||||
for (int i = 0; i < APPLE2_ENCODED_SECTOR_LENGTH; i++)
|
||||
{
|
||||
int value;
|
||||
if (i >= TWOBIT_COUNT)
|
||||
{
|
||||
value = sector.data[i - TWOBIT_COUNT] >> 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t tmp = sector.data[i];
|
||||
value = ((tmp & 1) << 1) | ((tmp & 2) >> 1);
|
||||
|
||||
tmp = sector.data[i + TWOBIT_COUNT];
|
||||
value |= ((tmp & 1) << 3) | ((tmp & 2) << 1);
|
||||
tmp = sector.data[i + TWOBIT_COUNT];
|
||||
value |= ((tmp & 1) << 3) | ((tmp & 2) << 1);
|
||||
|
||||
if(i + 2*TWOBIT_COUNT < APPLE2_SECTOR_LENGTH) {
|
||||
tmp = sector.data[i + 2*TWOBIT_COUNT];
|
||||
value |= ((tmp & 1) << 5) | ((tmp & 2) << 3);
|
||||
}
|
||||
}
|
||||
checksum ^= value;
|
||||
// assert(checksum & ~0x3f == 0);
|
||||
write_gcr6(checksum);
|
||||
checksum = value;
|
||||
}
|
||||
if(sector.status == Sector::BAD_CHECKSUM) checksum ^= 0x3f;
|
||||
write_gcr6(checksum);
|
||||
write_bits(0xDEAAEB, 24);
|
||||
}
|
||||
if (i + 2 * TWOBIT_COUNT < APPLE2_SECTOR_LENGTH)
|
||||
{
|
||||
tmp = sector.data[i + 2 * TWOBIT_COUNT];
|
||||
value |= ((tmp & 1) << 5) | ((tmp & 2) << 3);
|
||||
}
|
||||
}
|
||||
checksum ^= value;
|
||||
// assert(checksum & ~0x3f == 0);
|
||||
write_gcr6(checksum);
|
||||
checksum = value;
|
||||
}
|
||||
if (sector.status == Sector::BAD_CHECKSUM)
|
||||
checksum ^= 0x3f;
|
||||
write_gcr6(checksum);
|
||||
write_bits(0xDEAAEB, 24);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createApple2Encoder(const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new Apple2Encoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new Apple2Encoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,5 @@ message BrotherEncoderProto {
|
||||
optional string sector_skew = 5 [default = "05a3816b4927"];
|
||||
|
||||
optional BrotherFormat format = 6 [default = BROTHER240];
|
||||
optional int32 bias = 7 [default = 0];
|
||||
}
|
||||
|
||||
|
||||
@@ -10,84 +10,88 @@
|
||||
|
||||
static int encode_header_gcr(uint16_t word)
|
||||
{
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
switch (word)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "header_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
switch (data)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
cursor += width;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_sector_header(std::vector<bool>& bits, unsigned& cursor,
|
||||
int track, int sector)
|
||||
static void write_sector_header(
|
||||
std::vector<bool>& bits, unsigned& cursor, int track, int sector)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 31);
|
||||
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
|
||||
write_bits(bits, cursor, encode_header_gcr(track), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(sector), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
|
||||
write_bits(bits, cursor, 0xffffffff, 31);
|
||||
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
|
||||
write_bits(bits, cursor, encode_header_gcr(track), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(sector), 16);
|
||||
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
|
||||
}
|
||||
|
||||
static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
|
||||
static void write_sector_data(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
|
||||
{
|
||||
write_bits(bits, cursor, 0xffffffff, 32);
|
||||
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
|
||||
write_bits(bits, cursor, 0xffffffff, 32);
|
||||
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
|
||||
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
uint16_t fifo = 0;
|
||||
int width = 0;
|
||||
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
Error() << "unsupported sector size";
|
||||
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
|
||||
Error() << "unsupported sector size";
|
||||
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
auto write_byte = [&](uint8_t byte)
|
||||
{
|
||||
fifo |= (byte << (8 - width));
|
||||
width += 8;
|
||||
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
while (width >= 5)
|
||||
{
|
||||
uint8_t quintet = fifo >> 11;
|
||||
fifo <<= 5;
|
||||
width -= 5;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
|
||||
}
|
||||
};
|
||||
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
for (uint8_t byte : data)
|
||||
write_byte(byte);
|
||||
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc>>16);
|
||||
write_byte(realCrc>>8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
uint32_t realCrc = crcbrother(data);
|
||||
write_byte(realCrc >> 16);
|
||||
write_byte(realCrc >> 8);
|
||||
write_byte(realCrc);
|
||||
write_byte(0x58); /* magic */
|
||||
write_byte(0xd4);
|
||||
while (width != 0)
|
||||
write_byte(0);
|
||||
@@ -95,112 +99,98 @@ static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const B
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
class BrotherEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
BrotherEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.brother())
|
||||
{}
|
||||
BrotherEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.brother())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location,
|
||||
const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if (physicalSide != 0)
|
||||
return sectors;
|
||||
physicalTrack -= _config.bias();
|
||||
switch (_config.format())
|
||||
{
|
||||
case BROTHER120:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|
||||
|| (physicalTrack & 1))
|
||||
return sectors;
|
||||
break;
|
||||
if (location.head != 0)
|
||||
return sectors;
|
||||
|
||||
case BROTHER240:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
|
||||
return sectors;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int sectorId=0; sectorId<BROTHER_SECTORS_PER_TRACK; sectorId++)
|
||||
switch (_config.format())
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, 0, sectorId);
|
||||
case BROTHER120:
|
||||
if (location.logicalCylinder >= BROTHER_TRACKS_PER_120KB_DISK)
|
||||
return sectors;
|
||||
break;
|
||||
|
||||
case BROTHER240:
|
||||
if (location.logicalCylinder >= BROTHER_TRACKS_PER_240KB_DISK)
|
||||
return sectors;
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string& skew = _config.sector_skew();
|
||||
for (int sectorCount = 0; sectorCount < BROTHER_SECTORS_PER_TRACK;
|
||||
sectorCount++)
|
||||
{
|
||||
int sectorId = charToInt(skew.at(sectorCount));
|
||||
const auto& sector = image.get(location.logicalCylinder, 0, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
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>();
|
||||
break;
|
||||
std::unique_ptr<Fluxmap> encode(
|
||||
const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
case BROTHER240:
|
||||
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
break;
|
||||
}
|
||||
int sectorCount = 0;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
double headerMs = _config.post_index_gap_ms() +
|
||||
sectorCount * _config.sector_spacing_ms();
|
||||
unsigned headerCursor = headerMs * 1e3 / _config.clock_rate_us();
|
||||
double dataMs = headerMs + _config.post_header_spacing_ms();
|
||||
unsigned dataCursor = dataMs * 1e3 / _config.clock_rate_us();
|
||||
|
||||
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
|
||||
const std::string& skew = _config.sector_skew();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
fillBitmapTo(bits, cursor, headerCursor, {true, false});
|
||||
write_sector_header(
|
||||
bits, cursor, sectorData->logicalTrack, sectorData->logicalSector);
|
||||
fillBitmapTo(bits, cursor, dataCursor, {true, false});
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
|
||||
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
|
||||
{
|
||||
int sectorId = charToInt(skew.at(sectorCount));
|
||||
double headerMs = _config.post_index_gap_ms() + sectorCount*_config.sector_spacing_ms();
|
||||
unsigned headerCursor = headerMs*1e3 / _config.clock_rate_us();
|
||||
double dataMs = headerMs + _config.post_header_spacing_ms();
|
||||
unsigned dataCursor = dataMs*1e3 / _config.clock_rate_us();
|
||||
sectorCount++;
|
||||
}
|
||||
|
||||
const auto& sectorData = image.get(physicalTrack, 0, sectorId);
|
||||
if (cursor >= bits.size())
|
||||
Error() << "track data overrun";
|
||||
fillBitmapTo(bits, cursor, bits.size(), {true, false});
|
||||
|
||||
fillBitmapTo(bits, cursor, headerCursor, { true, false });
|
||||
write_sector_header(bits, cursor, sectorData->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, _config.clock_rate_us()*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const BrotherEncoderProto& _config;
|
||||
|
||||
const BrotherEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createBrotherEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -211,17 +211,16 @@ public:
|
||||
{}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if (physicalSide == 0)
|
||||
if (location.head == 0)
|
||||
{
|
||||
int logicalTrack = physicalTrack / 2;
|
||||
unsigned numSectors = sectorsForTrack(logicalTrack);
|
||||
unsigned numSectors = sectorsForTrack(location.logicalCylinder);
|
||||
for (int sectorId=0; sectorId<numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(logicalTrack, 0, sectorId);
|
||||
const auto& sector = image.get(location.logicalCylinder, 0, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
@@ -230,7 +229,7 @@ public:
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
/* The format ID Character # 1 and # 2 are in the .d64 image only present
|
||||
@@ -240,9 +239,6 @@ public:
|
||||
* contains the BAM.
|
||||
*/
|
||||
|
||||
if (physicalSide != 0)
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
const auto& sectorData = image.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
|
||||
if (sectorData)
|
||||
{
|
||||
@@ -254,8 +250,7 @@ public:
|
||||
else
|
||||
_formatByte1 = _formatByte2 = 0;
|
||||
|
||||
int logicalTrack = physicalTrack / 2;
|
||||
double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor();
|
||||
double clockRateUs = clockRateUsForTrack(location.logicalCylinder) * _config.clock_compensation_factor();
|
||||
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
|
||||
|
||||
@@ -203,10 +203,10 @@ public:
|
||||
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
IbmDecoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, cylinder, head);
|
||||
getTrackFormat(trackdata, location.logicalCylinder, location.head);
|
||||
|
||||
std::set<unsigned> s;
|
||||
if (trackdata.has_sectors())
|
||||
|
||||
@@ -39,9 +39,9 @@
|
||||
* ^^^^^
|
||||
* When shifted out of phase, the special 0xa1 byte becomes an illegal
|
||||
* encoding (you can't do 10 00). So this can't be spoofed by user data.
|
||||
*
|
||||
*
|
||||
* shifted: 10 00 10 01 00 01 00 1
|
||||
*
|
||||
*
|
||||
* It's repeated three times.
|
||||
*/
|
||||
#define MFM_RECORD_SEPARATOR 0x4489
|
||||
@@ -59,255 +59,256 @@
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
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())
|
||||
{}
|
||||
IbmEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.ibm())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void writeRawBits(uint32_t data, int width)
|
||||
{
|
||||
_cursor += width;
|
||||
_lastBit = data & 1;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = _cursor - i - 1;
|
||||
if (pos < _bits.size())
|
||||
_bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
void writeRawBits(uint32_t data, int width)
|
||||
{
|
||||
_cursor += width;
|
||||
_lastBit = data & 1;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = _cursor - i - 1;
|
||||
if (pos < _bits.size())
|
||||
_bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_cylinder() && (f.cylinder() != cylinder))
|
||||
continue;
|
||||
if (f.has_head() && (f.head() != head))
|
||||
continue;
|
||||
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata,
|
||||
unsigned cylinder,
|
||||
unsigned head)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static std::set<unsigned> getSectorIds(const IbmEncoderProto::TrackdataProto& trackdata)
|
||||
{
|
||||
std::set<unsigned> s;
|
||||
if (trackdata.has_sectors())
|
||||
{
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
}
|
||||
else if (trackdata.has_sector_range())
|
||||
{
|
||||
int sectorId = trackdata.sector_range().min_sector();
|
||||
while (sectorId <= trackdata.sector_range().max_sector())
|
||||
{
|
||||
s.insert(sectorId);
|
||||
sectorId++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
static std::set<unsigned> getSectorIds(
|
||||
const IbmEncoderProto::TrackdataProto& trackdata)
|
||||
{
|
||||
std::set<unsigned> s;
|
||||
if (trackdata.has_sectors())
|
||||
{
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
s.insert(sectorId);
|
||||
}
|
||||
else if (trackdata.has_sector_range())
|
||||
{
|
||||
int sectorId = trackdata.sector_range().min_sector();
|
||||
while (sectorId <= trackdata.sector_range().max_sector())
|
||||
{
|
||||
s.insert(sectorId);
|
||||
sectorId++;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalCylinder, location.head);
|
||||
|
||||
int logicalSide = physicalSide ^ trackdata.swap_sides();
|
||||
for (int sectorId : getSectorIds(trackdata))
|
||||
int logicalSide = location.head ^ trackdata.swap_sides();
|
||||
for (int sectorId : getSectorIds(trackdata))
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, logicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
const auto& sector = image.get(location.logicalCylinder, logicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
std::unique_ptr<Fluxmap> encode(
|
||||
const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalCylinder, location.head);
|
||||
|
||||
auto writeBytes = [&](const Bytes& bytes)
|
||||
{
|
||||
if (trackdata.use_fm())
|
||||
encodeFm(_bits, _cursor, bytes);
|
||||
else
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
};
|
||||
auto writeBytes = [&](const Bytes& bytes)
|
||||
{
|
||||
if (trackdata.use_fm())
|
||||
encodeFm(_bits, _cursor, bytes);
|
||||
else
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
};
|
||||
|
||||
auto writeFillerRawBytes = [&](int count, uint16_t byte)
|
||||
{
|
||||
for (int i=0; i<count; i++)
|
||||
writeRawBits(byte, 16);
|
||||
};
|
||||
auto writeFillerRawBytes = [&](int count, uint16_t byte)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
writeRawBits(byte, 16);
|
||||
};
|
||||
|
||||
auto writeFillerBytes = [&](int count, uint8_t byte)
|
||||
{
|
||||
Bytes b { byte };
|
||||
for (int i=0; i<count; i++)
|
||||
writeBytes(b);
|
||||
};
|
||||
auto writeFillerBytes = [&](int count, uint8_t byte)
|
||||
{
|
||||
Bytes b{byte};
|
||||
for (int i = 0; i < count; i++)
|
||||
writeBytes(b);
|
||||
};
|
||||
|
||||
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;
|
||||
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 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)
|
||||
{
|
||||
s >>= 1;
|
||||
sectorSize += 1;
|
||||
}
|
||||
}
|
||||
uint8_t sectorSize = 0;
|
||||
{
|
||||
int s = trackdata.sector_size() >> 7;
|
||||
while (s > 1)
|
||||
{
|
||||
s >>= 1;
|
||||
sectorSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t gapFill = trackdata.gap_fill_byte();
|
||||
uint16_t gapFill = trackdata.gap_fill_byte();
|
||||
|
||||
writeFillerRawBytes(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);
|
||||
writeFillerRawBytes(trackdata.gap1(), gapFill);
|
||||
}
|
||||
writeFillerRawBytes(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);
|
||||
writeFillerRawBytes(trackdata.gap1(), gapFill);
|
||||
}
|
||||
|
||||
int logicalSide = physicalSide ^ trackdata.swap_sides();
|
||||
bool first = true;
|
||||
for (int sectorId : trackdata.sectors().sector())
|
||||
{
|
||||
if (!first)
|
||||
writeFillerRawBytes(trackdata.gap3(), gapFill);
|
||||
first = false;
|
||||
bool first = true;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
if (!first)
|
||||
writeFillerRawBytes(trackdata.gap3(), gapFill);
|
||||
first = false;
|
||||
|
||||
const auto& sectorData = image.get(physicalTrack, logicalSide, sectorId);
|
||||
if (!sectorData)
|
||||
continue;
|
||||
/* 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. */
|
||||
|
||||
/* 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);
|
||||
|
||||
{
|
||||
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);
|
||||
bw.write_8(sectorSize);
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
bw.write_be16(crc);
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
writeBytes(header.slice(conventionalHeaderStart));
|
||||
}
|
||||
|
||||
}
|
||||
writeRawBits(trackdata.idam_byte(), 16);
|
||||
conventionalHeaderStart += 1;
|
||||
writeFillerRawBytes(trackdata.gap2(), gapFill);
|
||||
|
||||
writeBytes(header.slice(conventionalHeaderStart));
|
||||
}
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
writeFillerRawBytes(trackdata.gap2(), gapFill);
|
||||
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 data;
|
||||
ByteWriter bw(data);
|
||||
Bytes truncatedData =
|
||||
sectorData->data.slice(0, trackdata.sector_size());
|
||||
bw += truncatedData;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
Bytes truncatedData = sectorData->data.slice(0, trackdata.sector_size());
|
||||
bw += truncatedData;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
writeBytes(data.slice(conventionalHeaderStart));
|
||||
}
|
||||
}
|
||||
|
||||
int conventionalHeaderStart = 0;
|
||||
if (!trackdata.use_fm())
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
writeRawBits(MFM_RECORD_SEPARATOR, 16);
|
||||
conventionalHeaderStart += 3;
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeFillerRawBytes(1, gapFill);
|
||||
|
||||
}
|
||||
writeRawBits(trackdata.dam_byte(), 16);
|
||||
conventionalHeaderStart += 1;
|
||||
|
||||
writeBytes(data.slice(conventionalHeaderStart));
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeFillerRawBytes(1, gapFill);
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs*1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const IbmEncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
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));
|
||||
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -183,8 +183,10 @@ public:
|
||||
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
unsigned cylinder = location.logicalCylinder;
|
||||
|
||||
int count;
|
||||
if (cylinder < 16)
|
||||
count = 12;
|
||||
|
||||
@@ -14,44 +14,46 @@ static bool lastBit;
|
||||
|
||||
static double clockRateUsForTrack(unsigned track)
|
||||
{
|
||||
if (track < 16)
|
||||
return 2.623;
|
||||
if (track < 32)
|
||||
return 2.861;
|
||||
if (track < 48)
|
||||
return 3.148;
|
||||
if (track < 64)
|
||||
return 3.497;
|
||||
return 3.934;
|
||||
if (track < 16)
|
||||
return 2.623;
|
||||
if (track < 32)
|
||||
return 2.861;
|
||||
if (track < 48)
|
||||
return 3.148;
|
||||
if (track < 64)
|
||||
return 3.497;
|
||||
return 3.934;
|
||||
}
|
||||
|
||||
static unsigned sectorsForTrack(unsigned track)
|
||||
{
|
||||
if (track < 16)
|
||||
return 12;
|
||||
if (track < 32)
|
||||
return 11;
|
||||
if (track < 48)
|
||||
return 10;
|
||||
if (track < 64)
|
||||
return 9;
|
||||
return 8;
|
||||
if (track < 16)
|
||||
return 12;
|
||||
if (track < 32)
|
||||
return 11;
|
||||
if (track < 48)
|
||||
return 10;
|
||||
if (track < 64)
|
||||
return 9;
|
||||
return 8;
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t gcr)
|
||||
{
|
||||
switch (gcr)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
|
||||
* and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
|
||||
/* This is extremely inspired by the MESS implementation, written by Nathan
|
||||
* Woods and R. Belmont:
|
||||
* https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
|
||||
*/
|
||||
static Bytes encode_crazy_data(const Bytes& input)
|
||||
{
|
||||
@@ -59,7 +61,7 @@ static Bytes encode_crazy_data(const Bytes& input)
|
||||
ByteWriter bw(output);
|
||||
ByteReader br(input);
|
||||
|
||||
uint8_t w1, w2, w3, w4;
|
||||
uint8_t w1, w2, w3, w4;
|
||||
|
||||
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
|
||||
|
||||
@@ -67,92 +69,94 @@ static Bytes encode_crazy_data(const Bytes& input)
|
||||
uint8_t b2[LOOKUP_LEN + 1];
|
||||
uint8_t b3[LOOKUP_LEN + 1];
|
||||
|
||||
uint32_t c1 = 0;
|
||||
uint32_t c2 = 0;
|
||||
uint32_t c3 = 0;
|
||||
for (int j=0;; j++)
|
||||
{
|
||||
c1 = (c1 & 0xff) << 1;
|
||||
if (c1 & 0x0100)
|
||||
c1++;
|
||||
uint32_t c1 = 0;
|
||||
uint32_t c2 = 0;
|
||||
uint32_t c3 = 0;
|
||||
for (int j = 0;; j++)
|
||||
{
|
||||
c1 = (c1 & 0xff) << 1;
|
||||
if (c1 & 0x0100)
|
||||
c1++;
|
||||
|
||||
uint8_t val = br.read_8();
|
||||
c3 += val;
|
||||
if (c1 & 0x0100)
|
||||
{
|
||||
c3++;
|
||||
c1 &= 0xff;
|
||||
}
|
||||
b1[j] = (val ^ c1) & 0xff;
|
||||
uint8_t val = br.read_8();
|
||||
c3 += val;
|
||||
if (c1 & 0x0100)
|
||||
{
|
||||
c3++;
|
||||
c1 &= 0xff;
|
||||
}
|
||||
b1[j] = (val ^ c1) & 0xff;
|
||||
|
||||
val = br.read_8();
|
||||
c2 += val;
|
||||
if (c3 > 0xff)
|
||||
{
|
||||
c2++;
|
||||
c3 &= 0xff;
|
||||
}
|
||||
b2[j] = (val ^ c3) & 0xff;
|
||||
val = br.read_8();
|
||||
c2 += val;
|
||||
if (c3 > 0xff)
|
||||
{
|
||||
c2++;
|
||||
c3 &= 0xff;
|
||||
}
|
||||
b2[j] = (val ^ c3) & 0xff;
|
||||
|
||||
if (br.pos == 524)
|
||||
break;
|
||||
if (br.pos == 524)
|
||||
break;
|
||||
|
||||
val = br.read_8();
|
||||
c1 += val;
|
||||
if (c2 > 0xff)
|
||||
{
|
||||
c1++;
|
||||
c2 &= 0xff;
|
||||
}
|
||||
b3[j] = (val ^ c2) & 0xff;
|
||||
}
|
||||
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
|
||||
b3[LOOKUP_LEN] = 0;
|
||||
val = br.read_8();
|
||||
c1 += val;
|
||||
if (c2 > 0xff)
|
||||
{
|
||||
c1++;
|
||||
c2 &= 0xff;
|
||||
}
|
||||
b3[j] = (val ^ c2) & 0xff;
|
||||
}
|
||||
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
|
||||
b3[LOOKUP_LEN] = 0;
|
||||
|
||||
for (int i = 0; i <= LOOKUP_LEN; i++)
|
||||
{
|
||||
w1 = b1[i] & 0x3f;
|
||||
w2 = b2[i] & 0x3f;
|
||||
w3 = b3[i] & 0x3f;
|
||||
w4 = ((b1[i] & 0xc0) >> 2);
|
||||
w4 |= ((b2[i] & 0xc0) >> 4);
|
||||
w4 |= ((b3[i] & 0xc0) >> 6);
|
||||
for (int i = 0; i <= LOOKUP_LEN; i++)
|
||||
{
|
||||
w1 = b1[i] & 0x3f;
|
||||
w2 = b2[i] & 0x3f;
|
||||
w3 = b3[i] & 0x3f;
|
||||
w4 = ((b1[i] & 0xc0) >> 2);
|
||||
w4 |= ((b2[i] & 0xc0) >> 4);
|
||||
w4 |= ((b3[i] & 0xc0) >> 6);
|
||||
|
||||
bw.write_8(w4);
|
||||
bw.write_8(w1);
|
||||
bw.write_8(w2);
|
||||
bw.write_8(w4);
|
||||
bw.write_8(w1);
|
||||
bw.write_8(w2);
|
||||
|
||||
if (i != LOOKUP_LEN)
|
||||
bw.write_8(w3);
|
||||
}
|
||||
if (i != LOOKUP_LEN)
|
||||
bw.write_8(w3);
|
||||
}
|
||||
|
||||
bw.write_8(c4 & 0x3f);
|
||||
bw.write_8(c3 & 0x3f);
|
||||
bw.write_8(c2 & 0x3f);
|
||||
bw.write_8(c1 & 0x3f);
|
||||
bw.write_8(c4 & 0x3f);
|
||||
bw.write_8(c3 & 0x3f);
|
||||
bw.write_8(c2 & 0x3f);
|
||||
bw.write_8(c1 & 0x3f);
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
cursor += width;
|
||||
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 encode_side(uint8_t track, uint8_t side)
|
||||
@@ -161,103 +165,116 @@ static uint8_t encode_side(uint8_t track, uint8_t side)
|
||||
* bit 5) and also whether we're above track 0x3f (in bit 0).
|
||||
*/
|
||||
|
||||
return (side ? 0x20 : 0x00) | ((track>0x3f) ? 0x01 : 0x00);
|
||||
return (side ? 0x20 : 0x00) | ((track > 0x3f) ? 0x01 : 0x00);
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 524))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 524";
|
||||
if ((sector->data.size() != 512) && (sector->data.size() != 524))
|
||||
Error() << "unsupported sector size --- you must pick 512 or 524";
|
||||
|
||||
write_bits(bits, cursor, 0xff, 1*8); /* pad byte */
|
||||
for (int i=0; i<7; i++)
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
|
||||
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3*8);
|
||||
write_bits(bits, cursor, 0xff, 1 * 8); /* pad byte */
|
||||
for (int i = 0; i < 7; i++)
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
|
||||
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3 * 8);
|
||||
|
||||
uint8_t encodedTrack = sector->logicalTrack & 0x3f;
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
uint8_t encodedSide = encode_side(sector->logicalTrack, sector->logicalSide);
|
||||
uint8_t formatByte = MAC_FORMAT_BYTE;
|
||||
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
uint8_t encodedSide =
|
||||
encode_side(sector->logicalTrack, sector->logicalSide);
|
||||
uint8_t formatByte = MAC_FORMAT_BYTE;
|
||||
uint8_t headerChecksum =
|
||||
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(formatByte), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(formatByte), 1 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1 * 8);
|
||||
|
||||
write_bits(bits, cursor, 0xdeaaff, 3*8);
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
|
||||
write_bits(bits, cursor, MAC_DATA_RECORD, 3*8);
|
||||
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1*8);
|
||||
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
|
||||
write_bits(bits, cursor, MAC_DATA_RECORD, 3 * 8);
|
||||
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1 * 8);
|
||||
|
||||
Bytes wireData;
|
||||
wireData.writer().append(sector->data.slice(512, 12)).append(sector->data.slice(0, 512));
|
||||
for (uint8_t b : encode_crazy_data(wireData))
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 1*8);
|
||||
Bytes wireData;
|
||||
wireData.writer()
|
||||
.append(sector->data.slice(512, 12))
|
||||
.append(sector->data.slice(0, 512));
|
||||
for (uint8_t b : encode_crazy_data(wireData))
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 1 * 8);
|
||||
|
||||
write_bits(bits, cursor, 0xdeaaff, 3*8);
|
||||
write_bits(bits, cursor, 0xdeaaff, 3 * 8);
|
||||
}
|
||||
|
||||
class MacintoshEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
MacintoshEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.macintosh())
|
||||
{}
|
||||
MacintoshEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.macintosh())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < MAC_TRACKS_PER_DISK))
|
||||
if ((location.logicalCylinder >= 0) &&
|
||||
(location.logicalCylinder < MAC_TRACKS_PER_DISK))
|
||||
{
|
||||
unsigned numSectors = sectorsForTrack(physicalTrack);
|
||||
for (int sectorId=0; sectorId<numSectors; sectorId++)
|
||||
unsigned numSectors = sectorsForTrack(location.logicalCylinder);
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
const auto& sector = image.get(
|
||||
location.logicalCylinder, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
double clockRateUs = clockRateUsForTrack(location.logicalCylinder) *
|
||||
_config.clock_compensation_factor();
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
double clockRateUs = clockRateUsForTrack(physicalTrack) * _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;
|
||||
|
||||
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
|
||||
lastBit = false;
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, sector);
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, sector);
|
||||
if (cursor >= bits.size())
|
||||
Error() << fmt::format(
|
||||
"track data overrun by {} bits", cursor - bits.size());
|
||||
fillBitmapTo(bits, cursor, bits.size(), {true, false});
|
||||
|
||||
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<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const MacintoshEncoderProto& _config;
|
||||
const MacintoshEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location& location) const override
|
||||
{
|
||||
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
return sectors;
|
||||
|
||||
@@ -6,116 +6,123 @@
|
||||
#include "image.h"
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
if ((sector->data.size() != 256) && (sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
|
||||
Error() << "unsupported sector size --- you must pick 256 or 275";
|
||||
if ((sector->data.size() != 256) &&
|
||||
(sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
|
||||
Error() << "unsupported sector size --- you must pick 256 or 275";
|
||||
|
||||
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
/* sector preamble */
|
||||
for (int i=0; i<40; i++)
|
||||
fullSector->push_back(0);
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
|
||||
{
|
||||
if (sector->data[0] != 0xFF)
|
||||
Error() << "275 byte sector doesn't start with sync byte 0xFF. Corrupted sector";
|
||||
uint8_t wantChecksum = sector->data[1+2+266];
|
||||
uint8_t gotChecksum = micropolisChecksum(sector->data.slice(1, 2+266));
|
||||
if (wantChecksum != gotChecksum)
|
||||
std::cerr << "Warning: checksum incorrect. Sector: " << sector->logicalSector << std::endl;
|
||||
sectorData = sector->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xff); /* Sync */
|
||||
writer.write_8(sector->logicalTrack);
|
||||
writer.write_8(sector->logicalSector);
|
||||
for (int i=0; i<10; i++)
|
||||
writer.write_8(0); /* Padding */
|
||||
writer += sector->data;
|
||||
writer.write_8(micropolisChecksum(sectorData.slice(1)));
|
||||
for (int i=0; i<5; i++)
|
||||
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
/* sector postamble */
|
||||
for (int i=0; i<40; i++)
|
||||
fullSector->push_back(0);
|
||||
/* filler */
|
||||
for (int i=0; i<35; i++)
|
||||
fullSector->push_back(0);
|
||||
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < 40; i++)
|
||||
fullSector->push_back(0);
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
|
||||
{
|
||||
if (sector->data[0] != 0xFF)
|
||||
Error() << "275 byte sector doesn't start with sync byte 0xFF. "
|
||||
"Corrupted sector";
|
||||
uint8_t wantChecksum = sector->data[1 + 2 + 266];
|
||||
uint8_t gotChecksum =
|
||||
micropolisChecksum(sector->data.slice(1, 2 + 266));
|
||||
if (wantChecksum != gotChecksum)
|
||||
std::cerr << "Warning: checksum incorrect. Sector: "
|
||||
<< sector->logicalSector << std::endl;
|
||||
sectorData = sector->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xff); /* Sync */
|
||||
writer.write_8(sector->logicalTrack);
|
||||
writer.write_8(sector->logicalSector);
|
||||
for (int i = 0; i < 10; i++)
|
||||
writer.write_8(0); /* Padding */
|
||||
writer += sector->data;
|
||||
writer.write_8(micropolisChecksum(sectorData.slice(1)));
|
||||
for (int i = 0; i < 5; i++)
|
||||
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < 40; i++)
|
||||
fullSector->push_back(0);
|
||||
/* filler */
|
||||
for (int i = 0; i < 35; i++)
|
||||
fullSector->push_back(0);
|
||||
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length";
|
||||
bool lastBit = false;
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
/* filler */
|
||||
for (int i=0; i<5; i++)
|
||||
{
|
||||
bits[cursor++] = 1;
|
||||
bits[cursor++] = 0;
|
||||
}
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length";
|
||||
bool lastBit = false;
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
/* filler */
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
bits[cursor++] = 1;
|
||||
bits[cursor++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class MicropolisEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
MicropolisEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.micropolis())
|
||||
{}
|
||||
MicropolisEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.micropolis())
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < 77))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 16; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
if ((location.logicalCylinder >= 0) && (location.logicalCylinder < 77))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 16; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(
|
||||
location.logicalCylinder, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 2.00;
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 2.00;
|
||||
|
||||
if ((physicalTrack < 0) || (physicalTrack >= 77) || sectors.empty())
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
if (cursor != bits.size())
|
||||
Error() << "track data mismatched length";
|
||||
|
||||
if (cursor != bits.size())
|
||||
Error() << "track data mismatched length";
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const MicropolisEncoderProto& _config;
|
||||
const MicropolisEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createMicropolisEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createMicropolisEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
|
||||
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
|
||||
std::set<unsigned> requiredSectors(const Location&) const override
|
||||
{
|
||||
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
return sectors;
|
||||
|
||||
@@ -12,155 +12,178 @@
|
||||
#define GAP_FILL_SIZE_DD 62
|
||||
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
|
||||
|
||||
#define GAP1_FILL_BYTE (0x4F)
|
||||
#define GAP2_FILL_BYTE (0x4F)
|
||||
#define GAP1_FILL_BYTE (0x4F)
|
||||
#define GAP2_FILL_BYTE (0x4F)
|
||||
|
||||
#define TOTAL_SECTOR_BYTES ()
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const std::shared_ptr<const Sector>& sector)
|
||||
{
|
||||
int preambleSize = 0;
|
||||
int encodedSectorSize = 0;
|
||||
int gapFillSize = 0;
|
||||
int preHeaderGapFillSize = 0;
|
||||
int preambleSize = 0;
|
||||
int encodedSectorSize = 0;
|
||||
int gapFillSize = 0;
|
||||
int preHeaderGapFillSize = 0;
|
||||
|
||||
bool doubleDensity;
|
||||
bool doubleDensity;
|
||||
|
||||
switch (sector->data.size()) {
|
||||
case NORTHSTAR_PAYLOAD_SIZE_SD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD;
|
||||
gapFillSize = GAP_FILL_SIZE_SD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
|
||||
doubleDensity = false;
|
||||
break;
|
||||
case NORTHSTAR_PAYLOAD_SIZE_DD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
|
||||
gapFillSize = GAP_FILL_SIZE_DD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
|
||||
doubleDensity = true;
|
||||
break;
|
||||
default:
|
||||
Error() << "unsupported sector size --- you must pick 256 or 512";
|
||||
break;
|
||||
}
|
||||
switch (sector->data.size())
|
||||
{
|
||||
case NORTHSTAR_PAYLOAD_SIZE_SD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD +
|
||||
NORTHSTAR_ENCODED_SECTOR_SIZE_SD +
|
||||
GAP_FILL_SIZE_SD;
|
||||
gapFillSize = GAP_FILL_SIZE_SD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
|
||||
doubleDensity = false;
|
||||
break;
|
||||
case NORTHSTAR_PAYLOAD_SIZE_DD:
|
||||
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
|
||||
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD +
|
||||
NORTHSTAR_ENCODED_SECTOR_SIZE_DD +
|
||||
GAP_FILL_SIZE_DD;
|
||||
gapFillSize = GAP_FILL_SIZE_DD;
|
||||
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
|
||||
doubleDensity = true;
|
||||
break;
|
||||
default:
|
||||
Error() << "unsupported sector size --- you must pick 256 or 512";
|
||||
break;
|
||||
}
|
||||
|
||||
int fullSectorSize = preambleSize + encodedSectorSize;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
int fullSectorSize = preambleSize + encodedSectorSize;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
|
||||
/* sector gap after index pulse */
|
||||
for (int i = 0; i < preHeaderGapFillSize; i++)
|
||||
fullSector->push_back(GAP1_FILL_BYTE);
|
||||
/* sector gap after index pulse */
|
||||
for (int i = 0; i < preHeaderGapFillSize; i++)
|
||||
fullSector->push_back(GAP1_FILL_BYTE);
|
||||
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < preambleSize; i++)
|
||||
fullSector->push_back(0);
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < preambleSize; i++)
|
||||
fullSector->push_back(0);
|
||||
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == encodedSectorSize)
|
||||
sectorData = sector->data;
|
||||
else {
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xFB); /* sync character */
|
||||
if (doubleDensity == true) {
|
||||
writer.write_8(0xFB); /* Double-density has two sync characters */
|
||||
}
|
||||
writer += sector->data;
|
||||
if (doubleDensity == true) {
|
||||
writer.write_8(northstarChecksum(sectorData.slice(2)));
|
||||
} else {
|
||||
writer.write_8(northstarChecksum(sectorData.slice(1)));
|
||||
}
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
Bytes sectorData;
|
||||
if (sector->data.size() == encodedSectorSize)
|
||||
sectorData = sector->data;
|
||||
else
|
||||
{
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xFB); /* sync character */
|
||||
if (doubleDensity == true)
|
||||
{
|
||||
writer.write_8(0xFB); /* Double-density has two sync characters */
|
||||
}
|
||||
writer += sector->data;
|
||||
if (doubleDensity == true)
|
||||
{
|
||||
writer.write_8(northstarChecksum(sectorData.slice(2)));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.write_8(northstarChecksum(sectorData.slice(1)));
|
||||
}
|
||||
}
|
||||
for (uint8_t b : sectorData)
|
||||
fullSector->push_back(b);
|
||||
|
||||
if (sector->logicalSector != 9) {
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
if (sector->logicalSector != 9)
|
||||
{
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize;
|
||||
} else {
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
}
|
||||
if (fullSector->size() != fullSectorSize)
|
||||
Error() << "sector mismatched length (" << sector->data.size()
|
||||
<< ") expected: " << fullSector->size() << " got "
|
||||
<< fullSectorSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
}
|
||||
|
||||
bool lastBit = false;
|
||||
bool lastBit = false;
|
||||
|
||||
if (doubleDensity == true) {
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
}
|
||||
else {
|
||||
encodeFm(bits, cursor, fullSector);
|
||||
}
|
||||
if (doubleDensity == true)
|
||||
{
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
}
|
||||
else
|
||||
{
|
||||
encodeFm(bits, cursor, fullSector);
|
||||
}
|
||||
}
|
||||
|
||||
class NorthstarEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
NorthstarEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.northstar())
|
||||
{}
|
||||
NorthstarEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.northstar())
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
if ((physicalTrack >= 0) && (physicalTrack < 35))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 10; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
if ((location.logicalCylinder >= 0) && (location.logicalCylinder < 35))
|
||||
{
|
||||
for (int sectorId = 0; sectorId < 10; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(
|
||||
location.logicalCylinder, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 4.00;
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 4.00;
|
||||
|
||||
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
const auto& sector = *sectors.begin();
|
||||
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD)
|
||||
{
|
||||
bitsPerRevolution /= 2; // FM
|
||||
}
|
||||
else
|
||||
{
|
||||
clockRateUs /= 2.00;
|
||||
}
|
||||
|
||||
const auto& sector = *sectors.begin();
|
||||
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
|
||||
bitsPerRevolution /= 2; // FM
|
||||
} else {
|
||||
clockRateUs /= 2.00;
|
||||
}
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
if (cursor > bits.size())
|
||||
Error() << "track data overrun";
|
||||
|
||||
if (cursor > bits.size())
|
||||
Error() << "track data overrun";
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const NorthstarEncoderProto& _config;
|
||||
const NorthstarEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,158 +11,157 @@
|
||||
|
||||
static int charToInt(char c)
|
||||
{
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
if (isdigit(c))
|
||||
return c - '0';
|
||||
return 10 + tolower(c) - 'a';
|
||||
}
|
||||
|
||||
static uint8_t decodeUint16(uint16_t raw)
|
||||
{
|
||||
Bytes b;
|
||||
ByteWriter bw(b);
|
||||
bw.write_be16(raw);
|
||||
return decodeFmMfm(b.toBits())[0];
|
||||
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())
|
||||
{}
|
||||
Tids990Encoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.tids990())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void writeRawBits(uint32_t data, int width)
|
||||
{
|
||||
_cursor += width;
|
||||
_lastBit = data & 1;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = _cursor - i - 1;
|
||||
if (pos < _bits.size())
|
||||
_bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
void writeRawBits(uint32_t data, int width)
|
||||
{
|
||||
_cursor += width;
|
||||
_lastBit = data & 1;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = _cursor - i - 1;
|
||||
if (pos < _bits.size())
|
||||
_bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
void writeBytes(const Bytes& bytes)
|
||||
{
|
||||
encodeMfm(_bits, _cursor, bytes, _lastBit);
|
||||
}
|
||||
|
||||
void writeBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = { byte };
|
||||
for (int i=0; i<count; i++)
|
||||
writeBytes(bytes);
|
||||
}
|
||||
void writeBytes(int count, uint8_t byte)
|
||||
{
|
||||
Bytes bytes = {byte};
|
||||
for (int i = 0; i < count; i++)
|
||||
writeBytes(bytes);
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
for (char sectorChar : _config.sector_skew())
|
||||
for (char sectorChar : _config.sector_skew())
|
||||
{
|
||||
int sectorId = charToInt(sectorChar);
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
int sectorId = charToInt(sectorChar);
|
||||
const auto& sector =
|
||||
image.get(location.logicalCylinder, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
|
||||
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
|
||||
_bits.resize(bitsPerRevolution);
|
||||
_cursor = 0;
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
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());
|
||||
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
|
||||
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
|
||||
|
||||
writeBytes(_config.gap1_bytes(), 0x55);
|
||||
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;
|
||||
bool first = true;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
if (!first)
|
||||
writeBytes(_config.gap3_bytes(), 0x55);
|
||||
first = false;
|
||||
|
||||
const auto& sectorData = image.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. */
|
||||
|
||||
/* 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);
|
||||
|
||||
{
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
writeRawBits(_config.am1_byte(), 16);
|
||||
writeBytes(header.slice(1));
|
||||
}
|
||||
writeBytes(_config.gap2_bytes(), 0x55);
|
||||
|
||||
writeBytes(_config.gap2_bytes(), 0x55);
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
|
||||
{
|
||||
Bytes data;
|
||||
ByteWriter bw(data);
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am2Unencoded);
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am2Unencoded);
|
||||
bw += sectorData->data;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
bw += sectorData->data;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
writeRawBits(_config.am2_byte(), 16);
|
||||
writeBytes(data.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
writeRawBits(_config.am2_byte(), 16);
|
||||
writeBytes(data.slice(1));
|
||||
}
|
||||
}
|
||||
if (_cursor >= _bits.size())
|
||||
Error() << "track data overrun";
|
||||
while (_cursor < _bits.size())
|
||||
writeBytes(1, 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;
|
||||
}
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(_bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const Tids990EncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
const Tids990EncoderProto& _config;
|
||||
std::vector<bool> _bits;
|
||||
unsigned _cursor;
|
||||
bool _lastBit;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createTids990Encoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,77 +14,84 @@
|
||||
|
||||
static bool lastBit;
|
||||
|
||||
static void write_zero_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
static void write_zero_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 0;
|
||||
}
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_one_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
static void write_one_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, unsigned count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 1;
|
||||
}
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
|
||||
{
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
for (bool bit : src)
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
lastBit = bits[cursor++] = bit;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
|
||||
{
|
||||
cursor += width;
|
||||
lastBit = data & 1;
|
||||
for (int i=0; i<width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
cursor += width;
|
||||
lastBit = data & 1;
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
unsigned pos = cursor - i - 1;
|
||||
if (pos < bits.size())
|
||||
bits[pos] = data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
static void write_bits(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
ByteReader br(bytes);
|
||||
BitReader bitr(br);
|
||||
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
while (!bitr.eof())
|
||||
{
|
||||
if (cursor < bits.size())
|
||||
bits[cursor++] = bitr.get();
|
||||
}
|
||||
}
|
||||
|
||||
static int encode_data_gcr(uint8_t data)
|
||||
{
|
||||
switch (data & 0x0f)
|
||||
{
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
#define GCR_ENTRY(gcr, data) \
|
||||
case data: \
|
||||
return gcr;
|
||||
#include "data_gcr.h"
|
||||
#undef GCR_ENTRY
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b)
|
||||
{
|
||||
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b >> 4), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 5);
|
||||
}
|
||||
|
||||
static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
static void write_bytes(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
for (uint8_t b : bytes)
|
||||
write_byte(bits, cursor, b);
|
||||
@@ -92,24 +99,27 @@ static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes&
|
||||
|
||||
static void write_gap(std::vector<bool>& bits, unsigned& cursor, int length)
|
||||
{
|
||||
for (int i = 0; i < length/10; i++)
|
||||
for (int i = 0; i < length / 10; i++)
|
||||
write_byte(bits, cursor, '0');
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor,
|
||||
const Victor9kEncoderProto::TrackdataProto& trackdata,
|
||||
const Sector& sector)
|
||||
static void write_sector(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const Victor9kEncoderProto::TrackdataProto& trackdata,
|
||||
const Sector& sector)
|
||||
{
|
||||
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
|
||||
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
|
||||
|
||||
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide<<7);
|
||||
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide << 7);
|
||||
uint8_t encodedSector = sector.logicalSector;
|
||||
write_bytes(bits, cursor, Bytes {
|
||||
encodedTrack,
|
||||
encodedSector,
|
||||
(uint8_t)(encodedTrack + encodedSector),
|
||||
});
|
||||
write_bytes(bits,
|
||||
cursor,
|
||||
Bytes{
|
||||
encodedTrack,
|
||||
encodedSector,
|
||||
(uint8_t)(encodedTrack + encodedSector),
|
||||
});
|
||||
|
||||
write_gap(bits, cursor, trackdata.post_header_gap_bits());
|
||||
|
||||
@@ -127,82 +137,92 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor,
|
||||
class Victor9kEncoder : public AbstractEncoder
|
||||
{
|
||||
public:
|
||||
Victor9kEncoder(const EncoderProto& config):
|
||||
Victor9kEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_config(config.victor9k())
|
||||
{}
|
||||
_config(config.victor9k())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata,
|
||||
unsigned cylinder,
|
||||
unsigned head)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_min_cylinder() && (cylinder < f.min_cylinder()))
|
||||
continue;
|
||||
if (f.has_max_cylinder() && (cylinder > f.max_cylinder()))
|
||||
continue;
|
||||
if (f.has_head() && (head != f.head()))
|
||||
continue;
|
||||
|
||||
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const auto& f : _config.trackdata())
|
||||
{
|
||||
if (f.has_min_cylinder() && (cylinder < f.min_cylinder()))
|
||||
continue;
|
||||
if (f.has_max_cylinder() && (cylinder > f.max_cylinder()))
|
||||
continue;
|
||||
if (f.has_head() && (head != f.head()))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
const Location& location, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalCylinder, location.head);
|
||||
|
||||
for (int i = 0; i < trackdata.sector_range().sector_count(); i++)
|
||||
{
|
||||
int sectorId = trackdata.sector_range().start_sector() + i;
|
||||
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
const auto& sector =
|
||||
image.get(location.logicalCylinder, location.head, sectorId);
|
||||
if (sector)
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
||||
return sectors;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, physicalTrack, physicalSide);
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(trackdata, location.logicalCylinder, location.head);
|
||||
|
||||
unsigned bitsPerRevolution = trackdata.original_data_rate_khz() * trackdata.original_period_ms();
|
||||
unsigned bitsPerRevolution =
|
||||
trackdata.original_data_rate_khz() * trackdata.original_period_ms();
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
double clockRateUs = 166666.0 / bitsPerRevolution;
|
||||
unsigned cursor = 0;
|
||||
|
||||
fillBitmapTo(bits, cursor, trackdata.post_index_gap_us() / clockRateUs, { true, false });
|
||||
fillBitmapTo(bits,
|
||||
cursor,
|
||||
trackdata.post_index_gap_us() / clockRateUs,
|
||||
{true, false});
|
||||
lastBit = false;
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
write_sector(bits, cursor, trackdata, *sector);
|
||||
|
||||
if (cursor >= bits.size())
|
||||
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
|
||||
fillBitmapTo(bits, cursor, bits.size(), { true, false });
|
||||
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);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const Victor9kEncoderProto& _config;
|
||||
const Victor9kEncoderProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
|
||||
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
|
||||
}
|
||||
|
||||
// vim: sw=4 ts=4 et
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import "lib/drive.proto";
|
||||
import "lib/mapper.proto";
|
||||
import "lib/common.proto";
|
||||
|
||||
// NEXT_TAG: 16
|
||||
// NEXT_TAG: 17
|
||||
message ConfigProto {
|
||||
optional string comment = 8;
|
||||
optional bool is_extension = 13;
|
||||
@@ -29,6 +29,7 @@ message ConfigProto {
|
||||
|
||||
optional RangeProto cylinders = 6;
|
||||
optional RangeProto heads = 7;
|
||||
optional int32 tpi = 16 [ (help) = "TPI of image; if 0, use TPI of drive" ];
|
||||
|
||||
optional SectorMappingProto sector_mapping = 14;
|
||||
}
|
||||
|
||||
@@ -60,12 +60,11 @@ std::unique_ptr<AbstractDecoder> AbstractDecoder::create(const DecoderProto& con
|
||||
}
|
||||
|
||||
std::shared_ptr<const TrackDataFlux> AbstractDecoder::decodeToSectors(
|
||||
std::shared_ptr<const Fluxmap> fluxmap, unsigned physicalCylinder, unsigned physicalHead)
|
||||
std::shared_ptr<const Fluxmap> fluxmap, const Location& location)
|
||||
{
|
||||
_trackdata = std::make_shared<TrackDataFlux>();
|
||||
_trackdata->fluxmap = fluxmap;
|
||||
_trackdata->physicalCylinder = physicalCylinder;
|
||||
_trackdata->physicalHead = physicalHead;
|
||||
_trackdata->location = location;
|
||||
|
||||
FluxmapReader fmr(*fluxmap);
|
||||
_fmr = &fmr;
|
||||
@@ -73,8 +72,8 @@ std::shared_ptr<const TrackDataFlux> AbstractDecoder::decodeToSectors(
|
||||
auto newSector = [&] {
|
||||
_sector = std::make_shared<Sector>();
|
||||
_sector->status = Sector::MISSING;
|
||||
_sector->physicalCylinder = physicalCylinder;
|
||||
_sector->physicalHead = physicalHead;
|
||||
_sector->physicalCylinder = location.physicalCylinder;
|
||||
_sector->physicalHead = location.head;
|
||||
};
|
||||
|
||||
newSector();
|
||||
@@ -218,7 +217,7 @@ uint64_t AbstractDecoder::readRaw64()
|
||||
}
|
||||
|
||||
|
||||
std::set<unsigned> AbstractDecoder::requiredSectors(unsigned cylinder, unsigned head) const
|
||||
std::set<unsigned> AbstractDecoder::requiredSectors(const Location& location) const
|
||||
{
|
||||
static std::set<unsigned> set;
|
||||
return set;
|
||||
|
||||
@@ -18,23 +18,27 @@ extern void setDecoderManualClockRate(double clockrate_us);
|
||||
|
||||
extern Bytes decodeFmMfm(std::vector<bool>::const_iterator start,
|
||||
std::vector<bool>::const_iterator end);
|
||||
extern void encodeMfm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input, bool& lastBit);
|
||||
extern void encodeFm(std::vector<bool>& bits, unsigned& cursor, const Bytes& input);
|
||||
extern void encodeMfm(std::vector<bool>& bits,
|
||||
unsigned& cursor,
|
||||
const Bytes& input,
|
||||
bool& lastBit);
|
||||
extern void encodeFm(
|
||||
std::vector<bool>& bits, unsigned& cursor, const Bytes& input);
|
||||
extern Bytes encodeMfm(const Bytes& input, bool& lastBit);
|
||||
|
||||
static inline Bytes decodeFmMfm(const std::vector<bool> bits)
|
||||
{ return decodeFmMfm(bits.begin(), bits.end()); }
|
||||
{
|
||||
return decodeFmMfm(bits.begin(), bits.end());
|
||||
}
|
||||
|
||||
class AbstractDecoder
|
||||
{
|
||||
public:
|
||||
AbstractDecoder(const DecoderProto& config):
|
||||
_config(config)
|
||||
{}
|
||||
AbstractDecoder(const DecoderProto& config): _config(config) {}
|
||||
|
||||
virtual ~AbstractDecoder() {}
|
||||
|
||||
static std::unique_ptr<AbstractDecoder> create(const DecoderProto& config);
|
||||
static std::unique_ptr<AbstractDecoder> create(const DecoderProto& config);
|
||||
|
||||
public:
|
||||
enum RecordType
|
||||
@@ -45,47 +49,59 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
std::shared_ptr<const TrackDataFlux> decodeToSectors(std::shared_ptr<const Fluxmap> fluxmap, unsigned cylinder, unsigned head);
|
||||
void pushRecord(const Fluxmap::Position& start, const Fluxmap::Position& end);
|
||||
std::shared_ptr<const TrackDataFlux> decodeToSectors(
|
||||
std::shared_ptr<const Fluxmap> fluxmap,
|
||||
const Location& location);
|
||||
|
||||
void resetFluxDecoder();
|
||||
void pushRecord(
|
||||
const Fluxmap::Position& start, const Fluxmap::Position& end);
|
||||
|
||||
void resetFluxDecoder();
|
||||
std::vector<bool> readRawBits(unsigned count);
|
||||
uint8_t readRaw8();
|
||||
uint16_t readRaw16();
|
||||
uint32_t readRaw20();
|
||||
uint32_t readRaw24();
|
||||
uint32_t readRaw32();
|
||||
uint64_t readRaw48();
|
||||
uint64_t readRaw64();
|
||||
uint8_t readRaw8();
|
||||
uint16_t readRaw16();
|
||||
uint32_t readRaw20();
|
||||
uint32_t readRaw24();
|
||||
uint32_t readRaw32();
|
||||
uint64_t readRaw48();
|
||||
uint64_t readRaw64();
|
||||
|
||||
Fluxmap::Position tell()
|
||||
{ return _fmr->tell(); }
|
||||
{
|
||||
return _fmr->tell();
|
||||
}
|
||||
|
||||
void seek(const Fluxmap::Position& pos)
|
||||
{ return _fmr->seek(pos); }
|
||||
{
|
||||
return _fmr->seek(pos);
|
||||
}
|
||||
|
||||
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
|
||||
void seekToIndexMark();
|
||||
nanoseconds_t seekToPattern(const FluxMatcher& pattern);
|
||||
void seekToIndexMark();
|
||||
|
||||
bool eof() const
|
||||
{ return _fmr->eof(); }
|
||||
{
|
||||
return _fmr->eof();
|
||||
}
|
||||
|
||||
nanoseconds_t getFluxmapDuration() const
|
||||
{ return _fmr->getDuration(); }
|
||||
nanoseconds_t getFluxmapDuration() const
|
||||
{
|
||||
return _fmr->getDuration();
|
||||
}
|
||||
|
||||
virtual std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const;
|
||||
virtual std::set<unsigned> requiredSectors(const Location& location) const;
|
||||
|
||||
protected:
|
||||
virtual void beginTrack() {};
|
||||
virtual void beginTrack(){};
|
||||
virtual nanoseconds_t advanceToNextRecord() = 0;
|
||||
virtual void decodeSectorRecord() = 0;
|
||||
virtual void decodeDataRecord() {};
|
||||
virtual void decodeDataRecord(){};
|
||||
|
||||
const DecoderProto& _config;
|
||||
std::shared_ptr<TrackDataFlux> _trackdata;
|
||||
const DecoderProto& _config;
|
||||
std::shared_ptr<TrackDataFlux> _trackdata;
|
||||
std::shared_ptr<Sector> _sector;
|
||||
std::unique_ptr<FluxDecoder> _decoder;
|
||||
std::vector<bool> _recordBits;
|
||||
std::unique_ptr<FluxDecoder> _decoder;
|
||||
std::vector<bool> _recordBits;
|
||||
|
||||
private:
|
||||
FluxmapReader* _fmr = nullptr;
|
||||
|
||||
@@ -2,13 +2,33 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message DriveProto {
|
||||
optional int32 drive = 1 [default = 0, (help) = "which drive to write to (0 or 1)"];
|
||||
optional IndexMode index_mode = 2 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
|
||||
optional int32 hard_sector_count = 3 [default = 0, (help) = "number of hard sectors on disk"];
|
||||
optional bool high_density = 4 [default = true, (help) = "set if this is a high density disk"];
|
||||
optional bool sync_with_index = 5 [default = false, (help) = "start reading at index mark"];
|
||||
optional double revolutions = 6 [default = 1.2, (help) = "number of revolutions to read"];
|
||||
// Next: 13
|
||||
message DriveProto
|
||||
{
|
||||
optional int32 drive = 1
|
||||
[ default = 0, (help) = "which drive to write to (0 or 1)" ];
|
||||
optional IndexMode index_mode = 2
|
||||
[ default = INDEXMODE_DRIVE, (help) = "index pulse source" ];
|
||||
optional int32 hard_sector_count = 3
|
||||
[ default = 0, (help) = "number of hard sectors on disk" ];
|
||||
optional bool high_density = 4
|
||||
[ default = true, (help) = "set if this is a high density disk" ];
|
||||
optional bool sync_with_index = 5
|
||||
[ default = false, (help) = "start reading at index mark" ];
|
||||
optional double revolutions = 6
|
||||
[ default = 1.2, (help) = "number of revolutions to read" ];
|
||||
|
||||
optional int32 cylinders = 7
|
||||
[ default = 81, (help) = "Number of cylinders supported by drive" ];
|
||||
optional int32 heads = 8
|
||||
[ default = 2, (help) = "Number of heads supported by drive" ];
|
||||
optional int32 head_bias = 10 [
|
||||
default = 0,
|
||||
(help) = "Bias to apply to the head position (in cylinders)"
|
||||
];
|
||||
optional int32 head_width = 11
|
||||
[ default = 1, (help) = "Width of the head (in cylinders)" ];
|
||||
optional int32 tpi = 9 [ default = 96, (help) = "TPI of drive" ];
|
||||
}
|
||||
|
||||
|
||||
// vim: ts=4 sw=4 et
|
||||
|
||||
@@ -15,45 +15,44 @@
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "protocol.h"
|
||||
|
||||
std::unique_ptr<AbstractEncoder> AbstractEncoder::create(const EncoderProto& config)
|
||||
std::unique_ptr<AbstractEncoder> AbstractEncoder::create(
|
||||
const EncoderProto& config)
|
||||
{
|
||||
static const std::map<int,
|
||||
std::function<std::unique_ptr<AbstractEncoder>(const EncoderProto&)>> encoders =
|
||||
{
|
||||
{ EncoderProto::kAmiga, createAmigaEncoder },
|
||||
{ EncoderProto::kApple2, createApple2Encoder },
|
||||
{ EncoderProto::kBrother, createBrotherEncoder },
|
||||
{ EncoderProto::kC64, createCommodore64Encoder },
|
||||
{ EncoderProto::kIbm, createIbmEncoder },
|
||||
{ EncoderProto::kMacintosh, createMacintoshEncoder },
|
||||
{ EncoderProto::kMicropolis,createMicropolisEncoder },
|
||||
{ EncoderProto::kNorthstar, createNorthstarEncoder },
|
||||
{ EncoderProto::kTids990, createTids990Encoder },
|
||||
{ EncoderProto::kVictor9K, createVictor9kEncoder },
|
||||
};
|
||||
static const std::map<int,
|
||||
std::function<std::unique_ptr<AbstractEncoder>(const EncoderProto&)>>
|
||||
encoders = {
|
||||
{EncoderProto::kAmiga, createAmigaEncoder },
|
||||
{EncoderProto::kApple2, createApple2Encoder },
|
||||
{EncoderProto::kBrother, createBrotherEncoder },
|
||||
{EncoderProto::kC64, createCommodore64Encoder},
|
||||
{EncoderProto::kIbm, createIbmEncoder },
|
||||
{EncoderProto::kMacintosh, createMacintoshEncoder },
|
||||
{EncoderProto::kMicropolis, createMicropolisEncoder },
|
||||
{EncoderProto::kNorthstar, createNorthstarEncoder },
|
||||
{EncoderProto::kTids990, createTids990Encoder },
|
||||
{EncoderProto::kVictor9K, createVictor9kEncoder },
|
||||
};
|
||||
|
||||
auto encoder = encoders.find(config.format_case());
|
||||
if (encoder == encoders.end())
|
||||
Error() << "no encoder specified";
|
||||
auto encoder = encoders.find(config.format_case());
|
||||
if (encoder == encoders.end())
|
||||
Error() << "no encoder specified";
|
||||
|
||||
return (encoder->second)(config);
|
||||
return (encoder->second)(config);
|
||||
}
|
||||
|
||||
Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock)
|
||||
{
|
||||
nanoseconds_t now = duration();
|
||||
for (unsigned i=0; i<bits.size(); i++)
|
||||
{
|
||||
now += clock;
|
||||
if (bits[i])
|
||||
{
|
||||
unsigned delta = (now - duration()) / NS_PER_TICK;
|
||||
nanoseconds_t now = duration();
|
||||
for (unsigned i = 0; i < bits.size(); i++)
|
||||
{
|
||||
now += clock;
|
||||
if (bits[i])
|
||||
{
|
||||
unsigned delta = (now - duration()) / NS_PER_TICK;
|
||||
appendInterval(delta);
|
||||
appendPulse();
|
||||
}
|
||||
}
|
||||
appendPulse();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#ifndef ENCODERS_H
|
||||
#define ENCODERS_H
|
||||
|
||||
class Fluxmap;
|
||||
class EncoderProto;
|
||||
class Fluxmap;
|
||||
class Image;
|
||||
class Location;
|
||||
class Sector;
|
||||
|
||||
class AbstractEncoder
|
||||
@@ -16,10 +17,9 @@ public:
|
||||
|
||||
public:
|
||||
virtual std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
int physicalCylinder, int physicalHead, const Image& image) = 0;
|
||||
const Location& location, const Image& image) = 0;
|
||||
|
||||
virtual std::unique_ptr<Fluxmap> encode(int physicalCylinder,
|
||||
int physicalHead,
|
||||
virtual std::unique_ptr<Fluxmap> encode(const Location& location,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) = 0;
|
||||
};
|
||||
|
||||
22
lib/flux.h
22
lib/flux.h
@@ -12,10 +12,25 @@ struct Record
|
||||
Bytes rawData;
|
||||
};
|
||||
|
||||
struct Location
|
||||
{
|
||||
unsigned physicalCylinder;
|
||||
unsigned logicalCylinder;
|
||||
unsigned head;
|
||||
unsigned groupSize;
|
||||
|
||||
std::strong_ordering operator<=>(const Location& other) const
|
||||
{
|
||||
auto i = physicalCylinder <=> other.physicalCylinder;
|
||||
if (i == std::strong_ordering::equal)
|
||||
i = head <=> other.head;
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
struct TrackDataFlux
|
||||
{
|
||||
unsigned physicalCylinder;
|
||||
unsigned physicalHead;
|
||||
Location location;
|
||||
std::shared_ptr<const Fluxmap> fluxmap;
|
||||
std::vector<std::shared_ptr<const Record>> records;
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
@@ -23,8 +38,7 @@ struct TrackDataFlux
|
||||
|
||||
struct TrackFlux
|
||||
{
|
||||
unsigned physicalCylinder;
|
||||
unsigned physicalHead;
|
||||
Location location;
|
||||
std::vector<std::shared_ptr<const TrackDataFlux>> trackDatas;
|
||||
std::set<std::shared_ptr<const Sector>> sectors;
|
||||
};
|
||||
|
||||
@@ -32,9 +32,10 @@ public:
|
||||
appendBytes((const uint8_t*) s.c_str(), s.size());
|
||||
}
|
||||
|
||||
Fluxmap(const Bytes bytes):
|
||||
_bytes(bytes)
|
||||
{}
|
||||
Fluxmap(const Bytes bytes)
|
||||
{
|
||||
appendBytes(bytes);
|
||||
}
|
||||
|
||||
nanoseconds_t duration() const { return _duration; }
|
||||
unsigned ticks() const { return _ticks; }
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
|
||||
#if defined(_WIN32) || defined(__WIN32__)
|
||||
#include <direct.h>
|
||||
|
||||
@@ -34,8 +34,6 @@ message ImgInputOutputProto {
|
||||
repeated TrackdataProto trackdata = 4 [(help) = "per-track format information (repeatable)"];
|
||||
optional int32 tracks = 5 [default=0, (help) = "number of tracks in image"];
|
||||
optional int32 sides = 6 [default=0, (help) = "number of sides in image"];
|
||||
optional int32 physical_offset = 7 [default=0, (help) = "logical:physical track offset"];
|
||||
optional int32 physical_step = 8 [default=1, (help) = "logical:physical track step"];
|
||||
optional Order order = 9 [default=CHS, (help) = "the order in which to emit tracks in the image"];
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,6 @@ public:
|
||||
|
||||
if (inputFile.eof())
|
||||
break;
|
||||
int physicalCylinder = track * _config.img().physical_step() +
|
||||
_config.img().physical_offset();
|
||||
|
||||
ImgInputOutputProto::TrackdataProto trackdata;
|
||||
getTrackFormat(_config.img(), trackdata, track, side);
|
||||
@@ -46,11 +44,10 @@ public:
|
||||
Bytes data(trackdata.sector_size());
|
||||
inputFile.read((char*)data.begin(), data.size());
|
||||
|
||||
const auto& sector =
|
||||
image->put(physicalCylinder, side, sectorId);
|
||||
const auto& sector = image->put(track, side, sectorId);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = track;
|
||||
sector->physicalCylinder = physicalCylinder;
|
||||
sector->physicalCylinder = track;
|
||||
sector->logicalSide = sector->physicalHead = side;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
|
||||
176
lib/mapper.cc
176
lib/mapper.cc
@@ -3,89 +3,143 @@
|
||||
#include "image.h"
|
||||
#include "fmt/format.h"
|
||||
#include "logger.h"
|
||||
#include "proto.h"
|
||||
#include "mapper.h"
|
||||
#include "flux.h"
|
||||
#include "lib/mapper.pb.h"
|
||||
|
||||
typedef std::function<void(std::map<int, int>&, const SectorMappingProto::MappingProto&)> insertercb_t;
|
||||
typedef std::function<void(
|
||||
std::map<int, int>&, const SectorMappingProto::MappingProto&)>
|
||||
insertercb_t;
|
||||
|
||||
static void getTrackFormat(const SectorMappingProto& proto,
|
||||
SectorMappingProto::TrackdataProto& trackdata, unsigned track, unsigned side)
|
||||
SectorMappingProto::TrackdataProto& trackdata,
|
||||
unsigned track,
|
||||
unsigned side)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const SectorMappingProto::TrackdataProto& f : proto.trackdata())
|
||||
{
|
||||
if (f.has_track() && f.has_up_to_track() && ((track < f.track()) || (track > f.up_to_track())))
|
||||
continue;
|
||||
if (f.has_track() && !f.has_up_to_track() && (track != f.track()))
|
||||
continue;
|
||||
if (f.has_side() && (f.side() != side))
|
||||
continue;
|
||||
trackdata.Clear();
|
||||
for (const SectorMappingProto::TrackdataProto& f : proto.trackdata())
|
||||
{
|
||||
if (f.has_track() && f.has_up_to_track() &&
|
||||
((track < f.track()) || (track > f.up_to_track())))
|
||||
continue;
|
||||
if (f.has_track() && !f.has_up_to_track() && (track != f.track()))
|
||||
continue;
|
||||
if (f.has_side() && (f.side() != side))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<Image> remapImpl(const Image& source, const SectorMappingProto& mapping,
|
||||
insertercb_t inserter_cb)
|
||||
static std::unique_ptr<Image> remapImpl(const Image& source,
|
||||
const SectorMappingProto& mapping,
|
||||
insertercb_t inserter_cb)
|
||||
{
|
||||
typedef std::pair<int, int> tracksidekey_t;
|
||||
std::map<tracksidekey_t, std::map<int, int>> cache;
|
||||
typedef std::pair<int, int> tracksidekey_t;
|
||||
std::map<tracksidekey_t, std::map<int, int>> cache;
|
||||
|
||||
auto getTrackdata =
|
||||
[&](const tracksidekey_t& key) -> const std::map<int, int>& {
|
||||
auto it = cache.find(key);
|
||||
if (it != cache.end())
|
||||
return it->second;
|
||||
auto getTrackdata =
|
||||
[&](const tracksidekey_t& key) -> const std::map<int, int>&
|
||||
{
|
||||
auto it = cache.find(key);
|
||||
if (it != cache.end())
|
||||
return it->second;
|
||||
|
||||
SectorMappingProto::TrackdataProto trackdata;
|
||||
getTrackFormat(mapping, trackdata, key.first, key.second);
|
||||
SectorMappingProto::TrackdataProto trackdata;
|
||||
getTrackFormat(mapping, trackdata, key.first, key.second);
|
||||
|
||||
auto& map = cache[key];
|
||||
for (const auto mappingsit : trackdata.mapping())
|
||||
inserter_cb(map, mappingsit);
|
||||
auto& map = cache[key];
|
||||
for (const auto mappingsit : trackdata.mapping())
|
||||
inserter_cb(map, mappingsit);
|
||||
|
||||
return map;
|
||||
};
|
||||
return map;
|
||||
};
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> destSectors;
|
||||
for (const auto& sector : source)
|
||||
{
|
||||
tracksidekey_t key = { sector->logicalTrack, sector->logicalSide };
|
||||
const auto& trackdata = getTrackdata(key);
|
||||
if (trackdata.empty())
|
||||
destSectors.insert(sector);
|
||||
else
|
||||
{
|
||||
auto it = trackdata.find(sector->logicalSector);
|
||||
if (it == trackdata.end())
|
||||
Error() << fmt::format("mapping requested but mapping table has no entry for sector {}", sector->logicalSector);
|
||||
std::set<std::shared_ptr<const Sector>> destSectors;
|
||||
for (const auto& sector : source)
|
||||
{
|
||||
tracksidekey_t key = {sector->logicalTrack, sector->logicalSide};
|
||||
const auto& trackdata = getTrackdata(key);
|
||||
if (trackdata.empty())
|
||||
destSectors.insert(sector);
|
||||
else
|
||||
{
|
||||
auto it = trackdata.find(sector->logicalSector);
|
||||
if (it == trackdata.end())
|
||||
Error() << fmt::format(
|
||||
"mapping requested but mapping table has no entry for "
|
||||
"sector {}",
|
||||
sector->logicalSector);
|
||||
|
||||
auto newSector = std::make_shared<Sector>(*sector);
|
||||
newSector->logicalSector = it->second;
|
||||
destSectors.insert(newSector);
|
||||
}
|
||||
}
|
||||
auto newSector = std::make_shared<Sector>(*sector);
|
||||
newSector->logicalSector = it->second;
|
||||
destSectors.insert(newSector);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<Image>(destSectors);
|
||||
return std::make_unique<Image>(destSectors);
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> Mapper::remapPhysicalToLogical(const Image& source, const SectorMappingProto& mapping)
|
||||
std::unique_ptr<const Image> Mapper::remapSectorsPhysicalToLogical(
|
||||
const Image& source, const SectorMappingProto& mapping)
|
||||
{
|
||||
Logger() << "remapping sectors from physical IDs to logical IDs";
|
||||
return remapImpl(source, mapping,
|
||||
[](auto& map, const auto& pair)
|
||||
{
|
||||
map.insert({ pair.physical(), pair.logical() });
|
||||
});
|
||||
Logger() << "remapping sectors from physical IDs to logical IDs";
|
||||
return remapImpl(source,
|
||||
mapping,
|
||||
[](auto& map, const auto& pair)
|
||||
{
|
||||
map.insert({pair.physical(), pair.logical()});
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> Mapper::remapLogicalToPhysical(const Image& source, const SectorMappingProto& mapping)
|
||||
std::unique_ptr<const Image> Mapper::remapSectorsLogicalToPhysical(
|
||||
const Image& source, const SectorMappingProto& mapping)
|
||||
{
|
||||
Logger() << "remapping sectors from logical IDs to physical IDs";
|
||||
return remapImpl(source, mapping,
|
||||
[](auto& map, const auto& pair)
|
||||
{
|
||||
map.insert({ pair.logical(), pair.physical() });
|
||||
});
|
||||
Logger() << "remapping sectors from logical IDs to physical IDs";
|
||||
return remapImpl(source,
|
||||
mapping,
|
||||
[](auto& map, const auto& pair)
|
||||
{
|
||||
map.insert({pair.logical(), pair.physical()});
|
||||
});
|
||||
}
|
||||
|
||||
unsigned Mapper::remapCylinderPhysicalToLogical(unsigned pcylinder)
|
||||
{
|
||||
return (pcylinder - config.drive().head_bias()) /
|
||||
config.drive().head_width();
|
||||
}
|
||||
|
||||
unsigned Mapper::remapCylinderLogicalToPhysical(unsigned lcylinder)
|
||||
{
|
||||
Error() << "not working yet";
|
||||
return config.drive().head_bias() + lcylinder * config.drive().head_width();
|
||||
}
|
||||
|
||||
std::set<Location> Mapper::computeLocations()
|
||||
{
|
||||
std::set<Location> locations;
|
||||
|
||||
unsigned track_step =
|
||||
(config.tpi() == 0) ? 1 : (config.drive().tpi() / config.tpi());
|
||||
|
||||
if (track_step == 0)
|
||||
Error()
|
||||
<< "this drive can't write this image, because the head is too big";
|
||||
|
||||
for (unsigned logicalCylinder : iterate(config.cylinders()))
|
||||
{
|
||||
for (unsigned head : iterate(config.heads()))
|
||||
{
|
||||
unsigned physicalCylinder = config.drive().head_bias() + logicalCylinder * track_step;
|
||||
|
||||
locations.insert({.physicalCylinder = physicalCylinder,
|
||||
.logicalCylinder = logicalCylinder,
|
||||
.head = head,
|
||||
.groupSize = track_step});
|
||||
}
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
14
lib/mapper.h
14
lib/mapper.h
@@ -2,13 +2,21 @@
|
||||
#define MAPPER_H
|
||||
|
||||
class SectorMappingProto;
|
||||
class DriveProto;
|
||||
class Location;
|
||||
|
||||
class Mapper
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<Image> remapPhysicalToLogical(const Image& source, const SectorMappingProto& mapping);
|
||||
static std::unique_ptr<Image> remapLogicalToPhysical(const Image& source, const SectorMappingProto& mapping);
|
||||
static std::unique_ptr<const Image> remapSectorsPhysicalToLogical(
|
||||
const Image& source, const SectorMappingProto& mapping);
|
||||
static std::unique_ptr<const Image> remapSectorsLogicalToPhysical(
|
||||
const Image& source, const SectorMappingProto& mapping);
|
||||
|
||||
static unsigned remapCylinderPhysicalToLogical(unsigned cylinder);
|
||||
static unsigned remapCylinderLogicalToPhysical(unsigned cylinder);
|
||||
|
||||
static std::set<Location> computeLocations();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
311
lib/reader.cc
311
lib/reader.cc
@@ -23,13 +23,17 @@
|
||||
|
||||
static std::unique_ptr<FluxSink> outputFluxSink;
|
||||
|
||||
static std::shared_ptr<const Fluxmap> readFluxmap(FluxSourceIterator& fluxsourceIterator, unsigned cylinder, unsigned head)
|
||||
static std::shared_ptr<const Fluxmap> readFluxmap(
|
||||
FluxSourceIterator& fluxsourceIterator, unsigned cylinder, unsigned head)
|
||||
{
|
||||
Logger() << BeginReadOperationLogMessage { cylinder, head };
|
||||
auto fluxmap = fluxsourceIterator.next()->rescale(1.0/config.flux_source().rescale());
|
||||
Logger() << EndReadOperationLogMessage()
|
||||
<< fmt::format("{0:.0} ms in {1} bytes", fluxmap->duration()/1e6, fluxmap->bytes());
|
||||
return fluxmap;
|
||||
Logger() << BeginReadOperationLogMessage{cylinder, head};
|
||||
auto fluxmap = fluxsourceIterator.next()->rescale(
|
||||
1.0 / config.flux_source().rescale());
|
||||
Logger() << EndReadOperationLogMessage()
|
||||
<< fmt::format("{0:.0} ms in {1} bytes",
|
||||
fluxmap->duration() / 1e6,
|
||||
fluxmap->bytes());
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
static bool conflictable(Sector::Status status)
|
||||
@@ -38,7 +42,8 @@ static bool conflictable(Sector::Status status)
|
||||
}
|
||||
|
||||
static std::set<std::shared_ptr<const Sector>> collect_sectors(
|
||||
std::set<std::shared_ptr<const Sector>>& track_sectors, bool collapse_conflicts = true)
|
||||
std::set<std::shared_ptr<const Sector>>& track_sectors,
|
||||
bool collapse_conflicts = true)
|
||||
{
|
||||
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
|
||||
std::multimap<key_t, std::shared_ptr<const Sector>> sectors;
|
||||
@@ -65,12 +70,12 @@ static std::set<std::shared_ptr<const Sector>> collect_sectors(
|
||||
(right->status == Sector::OK) &&
|
||||
(left->data != right->data))
|
||||
{
|
||||
if (!collapse_conflicts)
|
||||
{
|
||||
auto s = std::make_shared<Sector>(*right);
|
||||
s->status = Sector::CONFLICT;
|
||||
sector_set.insert(s);
|
||||
}
|
||||
if (!collapse_conflicts)
|
||||
{
|
||||
auto s = std::make_shared<Sector>(*right);
|
||||
s->status = Sector::CONFLICT;
|
||||
sector_set.insert(s);
|
||||
}
|
||||
auto s = std::make_shared<Sector>(*left);
|
||||
s->status = Sector::CONFLICT;
|
||||
return s;
|
||||
@@ -91,164 +96,172 @@ static std::set<std::shared_ptr<const Sector>> collect_sectors(
|
||||
return sector_set;
|
||||
}
|
||||
|
||||
std::shared_ptr<const DiskFlux> readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder)
|
||||
std::shared_ptr<const DiskFlux> readDiskCommand(
|
||||
FluxSource& fluxsource, AbstractDecoder& decoder)
|
||||
{
|
||||
if (config.decoder().has_copy_flux_to())
|
||||
outputFluxSink = FluxSink::create(config.decoder().copy_flux_to());
|
||||
|
||||
auto diskflux = std::make_shared<DiskFlux>();
|
||||
bool failures = false;
|
||||
for (int cylinder : iterate(config.cylinders()))
|
||||
|
||||
for (const auto& location : Mapper::computeLocations())
|
||||
{
|
||||
for (int head : iterate(config.heads()))
|
||||
testForEmergencyStop();
|
||||
|
||||
auto track = std::make_shared<TrackFlux>();
|
||||
track->location = location;
|
||||
diskflux->tracks.push_back(track);
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> track_sectors;
|
||||
std::set<std::shared_ptr<const Record>> track_records;
|
||||
Fluxmap totalFlux;
|
||||
|
||||
auto fluxsourceIterator =
|
||||
fluxsource.readFlux(location.physicalCylinder, location.head);
|
||||
int retriesRemaining = config.decoder().retries();
|
||||
while (fluxsourceIterator->hasNext())
|
||||
{
|
||||
testForEmergencyStop();
|
||||
auto fluxmap = readFluxmap(
|
||||
*fluxsourceIterator, location.physicalCylinder, location.head);
|
||||
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
|
||||
|
||||
auto track = std::make_shared<TrackFlux>();
|
||||
track->physicalCylinder = cylinder;
|
||||
track->physicalHead = head;
|
||||
diskflux->tracks.push_back(track);
|
||||
auto trackdataflux = decoder.decodeToSectors(fluxmap, location);
|
||||
track->trackDatas.push_back(trackdataflux);
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> track_sectors;
|
||||
std::set<std::shared_ptr<const Record>> track_records;
|
||||
Fluxmap totalFlux;
|
||||
track_sectors.insert(
|
||||
trackdataflux->sectors.begin(), trackdataflux->sectors.end());
|
||||
track_records.insert(
|
||||
trackdataflux->records.begin(), trackdataflux->records.end());
|
||||
|
||||
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
|
||||
int retriesRemaining = config.decoder().retries();
|
||||
while (fluxsourceIterator->hasNext())
|
||||
bool hasBadSectors = false;
|
||||
std::set<unsigned> required_sectors =
|
||||
decoder.requiredSectors(location);
|
||||
std::set<std::shared_ptr<const Sector>> result_sectors;
|
||||
for (const auto& sector : collect_sectors(track_sectors))
|
||||
{
|
||||
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
|
||||
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
|
||||
|
||||
auto trackdataflux =
|
||||
decoder.decodeToSectors(fluxmap, cylinder, head);
|
||||
track->trackDatas.push_back(trackdataflux);
|
||||
|
||||
track_sectors.insert(trackdataflux->sectors.begin(),
|
||||
trackdataflux->sectors.end());
|
||||
track_records.insert(trackdataflux->records.begin(),
|
||||
trackdataflux->records.end());
|
||||
|
||||
bool hasBadSectors = false;
|
||||
std::set<unsigned> required_sectors =
|
||||
decoder.requiredSectors(cylinder, head);
|
||||
std::set<std::shared_ptr<const Sector>> result_sectors;
|
||||
for (const auto& sector : collect_sectors(track_sectors))
|
||||
{
|
||||
result_sectors.insert(sector);
|
||||
required_sectors.erase(sector->logicalSector);
|
||||
|
||||
if (sector->status != Sector::OK)
|
||||
hasBadSectors = true;
|
||||
}
|
||||
for (unsigned logical_sector : required_sectors)
|
||||
{
|
||||
auto sector = std::make_shared<Sector>();
|
||||
sector->logicalSector = logical_sector;
|
||||
sector->status = Sector::MISSING;
|
||||
result_sectors.insert(sector);
|
||||
result_sectors.insert(sector);
|
||||
required_sectors.erase(sector->logicalSector);
|
||||
|
||||
if (sector->status != Sector::OK)
|
||||
hasBadSectors = true;
|
||||
}
|
||||
|
||||
track->sectors = collect_sectors(result_sectors);
|
||||
|
||||
/* track can't be modified below this point. */
|
||||
Logger() << TrackReadLogMessage { track };
|
||||
|
||||
if (hasBadSectors)
|
||||
failures = false;
|
||||
|
||||
if (!hasBadSectors)
|
||||
break;
|
||||
|
||||
if (!fluxsourceIterator->hasNext())
|
||||
break;
|
||||
if (fluxsource.isHardware())
|
||||
{
|
||||
retriesRemaining--;
|
||||
if (retriesRemaining == 0)
|
||||
{
|
||||
Logger() << fmt::format("giving up");
|
||||
break;
|
||||
}
|
||||
else
|
||||
Logger()
|
||||
<< fmt::format("retrying; {} retries remaining", retriesRemaining);
|
||||
}
|
||||
}
|
||||
|
||||
if (outputFluxSink)
|
||||
outputFluxSink->writeFlux(cylinder, head, totalFlux);
|
||||
|
||||
if (config.decoder().dump_records())
|
||||
for (unsigned logical_sector : required_sectors)
|
||||
{
|
||||
std::vector<std::shared_ptr<const Record>> sorted_records(track_records.begin(), track_records.end());
|
||||
std::sort(sorted_records.begin(), sorted_records.end(),
|
||||
[](const auto& o1, const auto& o2) {
|
||||
return o1->startTime < o2->startTime;
|
||||
});
|
||||
auto sector = std::make_shared<Sector>();
|
||||
sector->logicalSector = logical_sector;
|
||||
sector->status = Sector::MISSING;
|
||||
result_sectors.insert(sector);
|
||||
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
for (const auto& record : sorted_records)
|
||||
{
|
||||
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
|
||||
record->startTime / 1000.0,
|
||||
record->clock / 1000.0);
|
||||
hexdump(std::cout, record->rawData);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
hasBadSectors = true;
|
||||
}
|
||||
|
||||
if (config.decoder().dump_sectors())
|
||||
track->sectors = collect_sectors(result_sectors);
|
||||
|
||||
/* track can't be modified below this point. */
|
||||
Logger() << TrackReadLogMessage{track};
|
||||
|
||||
if (hasBadSectors)
|
||||
failures = false;
|
||||
|
||||
if (!hasBadSectors)
|
||||
break;
|
||||
|
||||
if (!fluxsourceIterator->hasNext())
|
||||
break;
|
||||
if (fluxsource.isHardware())
|
||||
{
|
||||
auto collected_sectors = collect_sectors(track_sectors, false);
|
||||
std::vector<std::shared_ptr<const Sector>> sorted_sectors(collected_sectors.begin(), collected_sectors.end());
|
||||
std::sort(sorted_sectors.begin(), sorted_sectors.end(),
|
||||
[](const auto& o1, const auto& o2) {
|
||||
return *o1 < *o2;
|
||||
});
|
||||
|
||||
std::cout << "\nDecoded sectors follow:\n\n";
|
||||
for (const auto& sector : sorted_sectors)
|
||||
retriesRemaining--;
|
||||
if (retriesRemaining == 0)
|
||||
{
|
||||
std::cout << fmt::format(
|
||||
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
|
||||
"status {}\n",
|
||||
sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
sector->logicalSector,
|
||||
sector->headerStartTime / 1000.0,
|
||||
sector->clock / 1000.0,
|
||||
Sector::statusToString(sector->status));
|
||||
hexdump(std::cout, sector->data);
|
||||
std::cout << std::endl;
|
||||
Logger() << fmt::format("giving up");
|
||||
break;
|
||||
}
|
||||
else
|
||||
Logger() << fmt::format(
|
||||
"retrying; {} retries remaining", retriesRemaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outputFluxSink)
|
||||
outputFluxSink->writeFlux(
|
||||
location.physicalCylinder, location.head, totalFlux);
|
||||
|
||||
if (config.decoder().dump_records())
|
||||
{
|
||||
std::vector<std::shared_ptr<const Record>> sorted_records(
|
||||
track_records.begin(), track_records.end());
|
||||
std::sort(sorted_records.begin(),
|
||||
sorted_records.end(),
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return o1->startTime < o2->startTime;
|
||||
});
|
||||
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
for (const auto& record : sorted_records)
|
||||
{
|
||||
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
|
||||
record->startTime / 1000.0,
|
||||
record->clock / 1000.0);
|
||||
hexdump(std::cout, record->rawData);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.decoder().dump_sectors())
|
||||
{
|
||||
auto collected_sectors = collect_sectors(track_sectors, false);
|
||||
std::vector<std::shared_ptr<const Sector>> sorted_sectors(
|
||||
collected_sectors.begin(), collected_sectors.end());
|
||||
std::sort(sorted_sectors.begin(),
|
||||
sorted_sectors.end(),
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return *o1 < *o2;
|
||||
});
|
||||
|
||||
std::cout << "\nDecoded sectors follow:\n\n";
|
||||
for (const auto& sector : sorted_sectors)
|
||||
{
|
||||
std::cout << fmt::format(
|
||||
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
|
||||
"status {}\n",
|
||||
sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
sector->logicalSector,
|
||||
sector->headerStartTime / 1000.0,
|
||||
sector->clock / 1000.0,
|
||||
Sector::statusToString(sector->status));
|
||||
hexdump(std::cout, sector->data);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failures)
|
||||
Logger() << "Warning: some sectors could not be decoded.";
|
||||
if (failures)
|
||||
Logger() << "Warning: some sectors could not be decoded.";
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> all_sectors;
|
||||
for (auto& track : diskflux->tracks)
|
||||
for (auto& sector : track->sectors)
|
||||
all_sectors.insert(sector);
|
||||
all_sectors = collect_sectors(all_sectors);
|
||||
diskflux->image = std::make_shared<Image>(all_sectors);
|
||||
std::set<std::shared_ptr<const Sector>> all_sectors;
|
||||
for (auto& track : diskflux->tracks)
|
||||
for (auto& sector : track->sectors)
|
||||
all_sectors.insert(sector);
|
||||
all_sectors = collect_sectors(all_sectors);
|
||||
diskflux->image = std::make_shared<Image>(all_sectors);
|
||||
|
||||
if (config.has_sector_mapping())
|
||||
diskflux->image = std::move(Mapper::remapPhysicalToLogical(*diskflux->image, config.sector_mapping()));
|
||||
if (config.has_sector_mapping())
|
||||
diskflux->image = std::move(Mapper::remapSectorsPhysicalToLogical(
|
||||
*diskflux->image, config.sector_mapping()));
|
||||
|
||||
/* diskflux can't be modified below this point. */
|
||||
Logger() << DiskReadLogMessage { diskflux };
|
||||
return diskflux;
|
||||
/* diskflux can't be modified below this point. */
|
||||
Logger() << DiskReadLogMessage{diskflux};
|
||||
return diskflux;
|
||||
}
|
||||
|
||||
void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWriter& writer)
|
||||
void readDiskCommand(
|
||||
FluxSource& fluxsource, AbstractDecoder& decoder, ImageWriter& writer)
|
||||
{
|
||||
auto diskflux = readDiskCommand(fluxsource, decoder);
|
||||
auto diskflux = readDiskCommand(fluxsource, decoder);
|
||||
|
||||
writer.printMap(*diskflux->image);
|
||||
if (config.decoder().has_write_csv_to())
|
||||
@@ -258,14 +271,14 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
|
||||
|
||||
void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
|
||||
{
|
||||
for (int cylinder : iterate(config.cylinders()))
|
||||
{
|
||||
for (int head : iterate(config.heads()))
|
||||
{
|
||||
testForEmergencyStop();
|
||||
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
|
||||
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
|
||||
fluxsink.writeFlux(cylinder, head, *fluxmap);
|
||||
}
|
||||
for (int cylinder : iterate(config.cylinders()))
|
||||
{
|
||||
for (int head : iterate(config.heads()))
|
||||
{
|
||||
testForEmergencyStop();
|
||||
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
|
||||
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
|
||||
fluxsink.writeFlux(cylinder, head, *fluxmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,14 @@ Sector::Status Sector::stringToStatus(const std::string& value)
|
||||
return Status::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
bool sectorPointerSortPredicate(
|
||||
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs)
|
||||
bool sectorPointerSortPredicate(const std::shared_ptr<const Sector>& lhs,
|
||||
const std::shared_ptr<const Sector>& rhs)
|
||||
{
|
||||
return *lhs < *rhs;
|
||||
}
|
||||
|
||||
bool sectorPointerEqualsPredicate(
|
||||
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs)
|
||||
bool sectorPointerEqualsPredicate(const std::shared_ptr<const Sector>& lhs,
|
||||
const std::shared_ptr<const Sector>& rhs)
|
||||
{
|
||||
if (!lhs && !rhs)
|
||||
return true;
|
||||
|
||||
@@ -66,8 +66,10 @@ public:
|
||||
};
|
||||
|
||||
extern bool sectorPointerSortPredicate(
|
||||
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs);
|
||||
const std::shared_ptr<const Sector>& lhs,
|
||||
const std::shared_ptr<const Sector>& rhs);
|
||||
extern bool sectorPointerEqualsPredicate(
|
||||
std::shared_ptr<const Sector>& lhs, std::shared_ptr<const Sector>& rhs);
|
||||
const std::shared_ptr<const Sector>& lhs,
|
||||
const std::shared_ptr<const Sector>& rhs);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,7 @@ message GreaseWeazleProto {
|
||||
BUSTYPE_INVALID = 0;
|
||||
IBMPC = 1;
|
||||
SHUGART = 2;
|
||||
APPLE2 = 3;
|
||||
};
|
||||
|
||||
optional string port = 1
|
||||
|
||||
518
lib/writer.cc
518
lib/writer.cc
@@ -9,6 +9,7 @@
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include "sector.h"
|
||||
#include "image.h"
|
||||
@@ -17,129 +18,418 @@
|
||||
#include "utils.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "proto.h"
|
||||
#include <optional>
|
||||
|
||||
static std::set<std::shared_ptr<const Sector>> collectSectors(
|
||||
std::set<std::shared_ptr<const Sector>>& track_sectors,
|
||||
bool collapse_conflicts = true)
|
||||
{
|
||||
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
|
||||
std::multimap<key_t, std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
for (const auto& sector : track_sectors)
|
||||
{
|
||||
key_t sectorid = {
|
||||
sector->logicalTrack, sector->logicalSide, sector->logicalSector};
|
||||
sectors.insert({sectorid, sector});
|
||||
}
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> sector_set;
|
||||
auto it = sectors.begin();
|
||||
while (it != sectors.end())
|
||||
{
|
||||
auto ub = sectors.upper_bound(it->first);
|
||||
auto new_sector = std::accumulate(it,
|
||||
ub,
|
||||
it->second,
|
||||
[&](auto left, auto& rightit) -> std::shared_ptr<const Sector>
|
||||
{
|
||||
auto& right = rightit.second;
|
||||
if ((left->status == Sector::OK) &&
|
||||
(right->status == Sector::OK) &&
|
||||
(left->data != right->data))
|
||||
{
|
||||
if (!collapse_conflicts)
|
||||
{
|
||||
auto s = std::make_shared<Sector>(*right);
|
||||
s->status = Sector::CONFLICT;
|
||||
sector_set.insert(s);
|
||||
}
|
||||
auto s = std::make_shared<Sector>(*left);
|
||||
s->status = Sector::CONFLICT;
|
||||
return s;
|
||||
}
|
||||
if (left->status == Sector::CONFLICT)
|
||||
return left;
|
||||
if (right->status == Sector::CONFLICT)
|
||||
return right;
|
||||
if (left->status == Sector::OK)
|
||||
return left;
|
||||
if (right->status == Sector::OK)
|
||||
return right;
|
||||
return left;
|
||||
});
|
||||
sector_set.insert(new_sector);
|
||||
it = ub;
|
||||
}
|
||||
return sector_set;
|
||||
}
|
||||
|
||||
/* Returns true if the result contains bad sectors. */
|
||||
bool combineRecordAndSectors(TrackFlux& trackFlux, AbstractDecoder& decoder)
|
||||
{
|
||||
std::set<std::shared_ptr<const Sector>> track_sectors;
|
||||
|
||||
for (auto& trackdataflux : trackFlux.trackDatas)
|
||||
track_sectors.insert(
|
||||
trackdataflux->sectors.begin(), trackdataflux->sectors.end());
|
||||
|
||||
for (unsigned logical_sector : decoder.requiredSectors(trackFlux.location))
|
||||
{
|
||||
auto sector = std::make_shared<Sector>();
|
||||
sector->logicalSector = logical_sector;
|
||||
sector->status = Sector::MISSING;
|
||||
track_sectors.insert(sector);
|
||||
}
|
||||
|
||||
trackFlux.sectors = collectSectors(track_sectors);
|
||||
if (trackFlux.sectors.empty())
|
||||
return true;
|
||||
for (const auto& sector : trackFlux.sectors)
|
||||
if (sector->status != Sector::OK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns true if the result contains bad sectors. */
|
||||
bool readGroup(FluxSource& fluxSource,
|
||||
const Location& location,
|
||||
TrackFlux& trackFlux,
|
||||
AbstractDecoder& decoder)
|
||||
{
|
||||
for (unsigned offset = 0; offset < location.groupSize;
|
||||
offset += config.drive().head_width())
|
||||
{
|
||||
auto fluxSourceIterator = fluxSource.readFlux(
|
||||
location.physicalCylinder + offset, location.head);
|
||||
|
||||
Logger() << BeginReadOperationLogMessage{
|
||||
location.physicalCylinder + offset, location.head};
|
||||
std::shared_ptr<const Fluxmap> fluxmap =
|
||||
fluxSourceIterator->next()->rescale(
|
||||
1.0 / config.flux_source().rescale());
|
||||
Logger() << EndReadOperationLogMessage()
|
||||
<< fmt::format("{0:.0} ms in {1} bytes",
|
||||
fluxmap->duration() / 1e6,
|
||||
fluxmap->bytes());
|
||||
|
||||
auto trackdataflux = decoder.decodeToSectors(fluxmap, location);
|
||||
trackFlux.trackDatas.push_back(trackdataflux);
|
||||
if (!combineRecordAndSectors(trackFlux, decoder))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void writeTracks(FluxSink& fluxSink,
|
||||
const std::function<std::unique_ptr<const Fluxmap>(int track, int side)> producer)
|
||||
std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
|
||||
producer,
|
||||
std::function<bool(const Location& location)> verifier)
|
||||
{
|
||||
for (unsigned cylinder : iterate(config.cylinders()))
|
||||
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
|
||||
|
||||
for (const auto& location : Mapper::computeLocations())
|
||||
{
|
||||
for (unsigned head : iterate(config.heads()))
|
||||
testForEmergencyStop();
|
||||
|
||||
int retriesRemaining = config.decoder().retries();
|
||||
for (;;)
|
||||
{
|
||||
testForEmergencyStop();
|
||||
Logger() << BeginWriteOperationLogMessage{cylinder, head};
|
||||
|
||||
auto fluxmap = producer(cylinder, head);
|
||||
if (!fluxmap)
|
||||
for (unsigned offset = 0; offset < location.groupSize;
|
||||
offset += config.drive().head_width())
|
||||
{
|
||||
/* Erase this track rather than writing. */
|
||||
unsigned physicalCylinder = location.physicalCylinder + offset;
|
||||
|
||||
fluxmap.reset(new Fluxmap());
|
||||
fluxSink.writeFlux(cylinder, head, *fluxmap);
|
||||
Logger() << "erased";
|
||||
Logger() << BeginWriteOperationLogMessage{physicalCylinder, location.head};
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
auto fluxmap = producer(location);
|
||||
if (!fluxmap)
|
||||
goto erase;
|
||||
|
||||
auto scaled =
|
||||
fluxmap->rescale(config.flux_sink().rescale());
|
||||
/* Precompensation actually seems to make things worse, so
|
||||
* let's leave it disabled for now. */
|
||||
// fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS,
|
||||
// 2);
|
||||
fluxSink.writeFlux(physicalCylinder, location.head, *scaled);
|
||||
Logger() << fmt::format("writing {0} ms in {1} bytes",
|
||||
int(fluxmap->duration() / 1e6),
|
||||
fluxmap->bytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
erase:
|
||||
/* Erase this track rather than writing. */
|
||||
|
||||
Fluxmap blank;
|
||||
fluxSink.writeFlux(physicalCylinder, location.head, blank);
|
||||
Logger() << "erased";
|
||||
}
|
||||
|
||||
Logger() << EndWriteOperationLogMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto scaled = fluxmap->rescale(config.flux_sink().rescale());
|
||||
/* Precompensation actually seems to make things worse, so let's
|
||||
* leave it disabled for now. */
|
||||
// fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
|
||||
fluxSink.writeFlux(cylinder, head, *scaled);
|
||||
Logger() << fmt::format("{0} ms in {1} bytes",
|
||||
int(fluxmap->duration() / 1e6),
|
||||
fluxmap->bytes());
|
||||
}
|
||||
Logger() << EndWriteOperationLogMessage();
|
||||
|
||||
if (verifier(location))
|
||||
break;
|
||||
|
||||
if (retriesRemaining == 0)
|
||||
Error() << "fatal error on write";
|
||||
|
||||
Logger() << fmt::format(
|
||||
"retrying; {} retries remaining", retriesRemaining);
|
||||
retriesRemaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool dontVerify(const Location&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void writeTracks(
|
||||
FluxSink& fluxSink, AbstractEncoder& encoder, const Image& image)
|
||||
{
|
||||
writeTracks(
|
||||
fluxSink,
|
||||
[&](const Location& location)
|
||||
{
|
||||
auto sectors = encoder.collectSectors(location, image);
|
||||
return encoder.encode(location, sectors, image);
|
||||
},
|
||||
dontVerify);
|
||||
}
|
||||
|
||||
void writeTracksAndVerify(FluxSink& fluxSink,
|
||||
AbstractEncoder& encoder,
|
||||
FluxSource& fluxSource,
|
||||
AbstractDecoder& decoder,
|
||||
const Image& image)
|
||||
{
|
||||
Logger() << fmt::format("Writing to: {}", (std::string)fluxSink);
|
||||
writeTracks(
|
||||
fluxSink,
|
||||
[&](const Location& location)
|
||||
{
|
||||
auto sectors = encoder.collectSectors(location, image);
|
||||
return encoder.encode(location, sectors, image);
|
||||
},
|
||||
[&](const Location& location)
|
||||
{
|
||||
auto trackFlux = std::make_shared<TrackFlux>();
|
||||
readGroup(fluxSource, location, *trackFlux, decoder);
|
||||
Logger() << TrackReadLogMessage{ trackFlux };
|
||||
|
||||
auto wantedSectors = encoder.collectSectors(location, image);
|
||||
std::sort(wantedSectors.begin(), wantedSectors.end(),
|
||||
sectorPointerSortPredicate);
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> gotSectors(
|
||||
trackFlux->sectors.begin(), trackFlux->sectors.end());
|
||||
std::sort(gotSectors.begin(), gotSectors.end(),
|
||||
sectorPointerSortPredicate);
|
||||
|
||||
return std::equal(gotSectors.begin(),
|
||||
gotSectors.end(),
|
||||
wantedSectors.begin(),
|
||||
wantedSectors.end(),
|
||||
sectorPointerEqualsPredicate);
|
||||
});
|
||||
}
|
||||
|
||||
void writeDiskCommand(std::shared_ptr<const Image> image,
|
||||
AbstractEncoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
AbstractDecoder* decoder,
|
||||
FluxSource* fluxSource)
|
||||
{
|
||||
if (config.has_sector_mapping())
|
||||
image = std::move(Mapper::remapSectorsLogicalToPhysical(
|
||||
*image, config.sector_mapping()));
|
||||
|
||||
if (fluxSource && decoder)
|
||||
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *image);
|
||||
else
|
||||
writeTracks(fluxSink, encoder, *image);
|
||||
}
|
||||
|
||||
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
||||
{
|
||||
writeTracks(
|
||||
fluxSink,
|
||||
[&](const Location& location)
|
||||
{
|
||||
return fluxSource
|
||||
.readFlux(location.physicalCylinder, location.head)
|
||||
->next();
|
||||
},
|
||||
dontVerify);
|
||||
}
|
||||
|
||||
std::shared_ptr<const DiskFlux> readDiskCommand(
|
||||
FluxSource& fluxSource, AbstractDecoder& decoder)
|
||||
{
|
||||
std::unique_ptr<FluxSink> outputFluxSink;
|
||||
if (config.decoder().has_copy_flux_to())
|
||||
outputFluxSink = FluxSink::create(config.decoder().copy_flux_to());
|
||||
|
||||
auto diskflux = std::make_shared<DiskFlux>();
|
||||
bool failures = false;
|
||||
|
||||
for (const auto& location : Mapper::computeLocations())
|
||||
{
|
||||
testForEmergencyStop();
|
||||
|
||||
auto track = std::make_shared<TrackFlux>();
|
||||
diskflux->tracks.push_back(track);
|
||||
|
||||
int retriesRemaining = config.decoder().retries();
|
||||
for (;;)
|
||||
{
|
||||
if (!readGroup(fluxSource, location, *track, decoder))
|
||||
break;
|
||||
|
||||
if (retriesRemaining == 0)
|
||||
{
|
||||
failures = true;
|
||||
Logger() << fmt::format("giving up");
|
||||
break;
|
||||
}
|
||||
|
||||
Logger() << fmt::format(
|
||||
"retrying; {} retries remaining", retriesRemaining);
|
||||
retriesRemaining--;
|
||||
}
|
||||
|
||||
if (outputFluxSink)
|
||||
{
|
||||
for (const auto& data : track->trackDatas)
|
||||
outputFluxSink->writeFlux(
|
||||
location.physicalCylinder, location.head, *data->fluxmap);
|
||||
}
|
||||
|
||||
if (config.decoder().dump_records())
|
||||
{
|
||||
std::vector<std::shared_ptr<const Record>> sorted_records;
|
||||
|
||||
for (const auto& data : track->trackDatas)
|
||||
sorted_records.insert(sorted_records.end(),
|
||||
data->records.begin(),
|
||||
data->records.end());
|
||||
|
||||
std::sort(sorted_records.begin(),
|
||||
sorted_records.end(),
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return o1->startTime < o2->startTime;
|
||||
});
|
||||
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
for (const auto& record : sorted_records)
|
||||
{
|
||||
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
|
||||
record->startTime / 1000.0,
|
||||
record->clock / 1000.0);
|
||||
hexdump(std::cout, record->rawData);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.decoder().dump_sectors())
|
||||
{
|
||||
auto collected_sectors = collectSectors(track->sectors, false);
|
||||
std::vector<std::shared_ptr<const Sector>> sorted_sectors(
|
||||
collected_sectors.begin(), collected_sectors.end());
|
||||
std::sort(sorted_sectors.begin(),
|
||||
sorted_sectors.end(),
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return *o1 < *o2;
|
||||
});
|
||||
|
||||
std::cout << "\nDecoded sectors follow:\n\n";
|
||||
for (const auto& sector : sorted_sectors)
|
||||
{
|
||||
std::cout << fmt::format(
|
||||
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
|
||||
"status {}\n",
|
||||
sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
sector->logicalSector,
|
||||
sector->headerStartTime / 1000.0,
|
||||
sector->clock / 1000.0,
|
||||
Sector::statusToString(sector->status));
|
||||
hexdump(std::cout, sector->data);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* track can't be modified below this point. */
|
||||
Logger() << TrackReadLogMessage{track};
|
||||
}
|
||||
|
||||
if (failures)
|
||||
Logger() << "Warning: some sectors could not be decoded.";
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> all_sectors;
|
||||
for (auto& track : diskflux->tracks)
|
||||
for (auto& sector : track->sectors)
|
||||
all_sectors.insert(sector);
|
||||
all_sectors = collectSectors(all_sectors);
|
||||
diskflux->image = std::make_shared<Image>(all_sectors);
|
||||
|
||||
if (config.has_sector_mapping())
|
||||
diskflux->image = std::move(Mapper::remapSectorsPhysicalToLogical(
|
||||
*diskflux->image, config.sector_mapping()));
|
||||
|
||||
/* diskflux can't be modified below this point. */
|
||||
Logger() << DiskReadLogMessage{diskflux};
|
||||
return diskflux;
|
||||
}
|
||||
|
||||
void readDiskCommand(
|
||||
FluxSource& fluxsource, AbstractDecoder& decoder, ImageWriter& writer)
|
||||
{
|
||||
auto diskflux = readDiskCommand(fluxsource, decoder);
|
||||
|
||||
writer.printMap(*diskflux->image);
|
||||
if (config.decoder().has_write_csv_to())
|
||||
writer.writeCsv(*diskflux->image, config.decoder().write_csv_to());
|
||||
writer.writeImage(*diskflux->image);
|
||||
}
|
||||
|
||||
void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
|
||||
{
|
||||
for (unsigned cylinder : iterate(config.cylinders()))
|
||||
{
|
||||
for (unsigned head : iterate(config.heads()))
|
||||
{
|
||||
testForEmergencyStop();
|
||||
auto fluxSourceIterator = fluxsource.readFlux(cylinder, head);
|
||||
|
||||
auto sectors = encoder.collectSectors(cylinder, head, image);
|
||||
std::unique_ptr<Fluxmap> fluxmap =
|
||||
encoder.encode(cylinder, head, sectors, image);
|
||||
if (!fluxmap)
|
||||
{
|
||||
/* Erase this track rather than writing. */
|
||||
Logger() << BeginReadOperationLogMessage{cylinder, head};
|
||||
auto fluxmap = fluxSourceIterator->next()->rescale(
|
||||
1.0 / config.flux_source().rescale());
|
||||
Logger() << EndReadOperationLogMessage()
|
||||
<< fmt::format("{0:.0} ms in {1} bytes",
|
||||
fluxmap->duration() / 1e6,
|
||||
fluxmap->bytes());
|
||||
|
||||
Logger() << BeginWriteOperationLogMessage{cylinder, head};
|
||||
fluxmap.reset(new Fluxmap());
|
||||
fluxSink.writeFlux(cylinder, head, *fluxmap);
|
||||
Logger() << EndWriteOperationLogMessage()
|
||||
<< fmt::format("erased");
|
||||
}
|
||||
else
|
||||
{
|
||||
fluxmap->rescale(config.flux_sink().rescale());
|
||||
std::sort(
|
||||
sectors.begin(), sectors.end(), sectorPointerSortPredicate);
|
||||
|
||||
for (int retry = 0;; retry++)
|
||||
{
|
||||
/* Precompensation actually seems to make things worse, so
|
||||
* let's leave it disabled for now. */
|
||||
// fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS,
|
||||
// 2);
|
||||
Logger() << BeginWriteOperationLogMessage{cylinder, head};
|
||||
fluxSink.writeFlux(cylinder, head, *fluxmap);
|
||||
Logger() << EndWriteOperationLogMessage()
|
||||
<< fmt::format("writing {0} ms in {1} bytes",
|
||||
int(fluxmap->duration() / 1e6),
|
||||
fluxmap->bytes());
|
||||
|
||||
Logger() << BeginReadOperationLogMessage{cylinder, head};
|
||||
std::shared_ptr<const Fluxmap> writtenFluxmap = fluxSource.readFlux(cylinder, head)->next();
|
||||
Logger() << EndReadOperationLogMessage()
|
||||
<< fmt::format("verifying {0} ms in {1} bytes",
|
||||
int(writtenFluxmap->duration() / 1e6),
|
||||
writtenFluxmap->bytes());
|
||||
|
||||
const auto trackdata =
|
||||
decoder.decodeToSectors(writtenFluxmap, cylinder, head);
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> gotSectors(
|
||||
trackdata->sectors.begin(), trackdata->sectors.end());
|
||||
gotSectors.erase(std::remove_if(gotSectors.begin(),
|
||||
gotSectors.end(),
|
||||
[](const auto& s)
|
||||
{
|
||||
return s->status != Sector::OK;
|
||||
}),
|
||||
gotSectors.end());
|
||||
std::sort(gotSectors.begin(),
|
||||
gotSectors.end(),
|
||||
sectorPointerSortPredicate);
|
||||
gotSectors.erase(std::unique(gotSectors.begin(),
|
||||
gotSectors.end(),
|
||||
sectorPointerEqualsPredicate),
|
||||
gotSectors.end());
|
||||
|
||||
if (std::equal(gotSectors.begin(),
|
||||
gotSectors.end(),
|
||||
sectors.begin(),
|
||||
sectors.end(),
|
||||
sectorPointerEqualsPredicate))
|
||||
break;
|
||||
|
||||
if (retry == config.decoder().retries())
|
||||
Error() << "Write failed; uncorrectable error during "
|
||||
"write.";
|
||||
|
||||
Logger() << "retrying";
|
||||
}
|
||||
}
|
||||
fluxsink.writeFlux(cylinder, head, *fluxmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,37 +448,3 @@ void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void writeDiskCommand(
|
||||
std::shared_ptr<const Image> image,
|
||||
AbstractEncoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
AbstractDecoder* decoder,
|
||||
FluxSource* fluxSource)
|
||||
{
|
||||
if (config.has_sector_mapping())
|
||||
image = std::move(Mapper::remapLogicalToPhysical(*image, config.sector_mapping()));
|
||||
|
||||
if (fluxSource && decoder)
|
||||
writeTracksAndVerify(fluxSink, encoder, *fluxSource, *decoder, *image);
|
||||
else
|
||||
writeTracks(fluxSink,
|
||||
[&](int physicalTrack, int physicalSide) -> std::unique_ptr<Fluxmap>
|
||||
{
|
||||
const auto& sectors =
|
||||
encoder.collectSectors(physicalTrack, physicalSide, *image);
|
||||
if (sectors.empty())
|
||||
return std::make_unique<Fluxmap>();
|
||||
return encoder.encode(
|
||||
physicalTrack, physicalSide, sectors, *image);
|
||||
});
|
||||
}
|
||||
|
||||
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
||||
{
|
||||
writeTracks(fluxSink,
|
||||
[&](int track, int side) -> std::unique_ptr<const Fluxmap>
|
||||
{
|
||||
return fluxSource.readFlux(track, side)->next();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
#ifndef WRITER_H
|
||||
#define WRITER_H
|
||||
|
||||
class Fluxmap;
|
||||
class AbstractDecoder;
|
||||
class AbstractEncoder;
|
||||
class ImageReader;
|
||||
class FluxSource;
|
||||
class FluxSink;
|
||||
class FluxSource;
|
||||
class Fluxmap;
|
||||
class Image;
|
||||
class ImageReader;
|
||||
class Location;
|
||||
|
||||
extern void writeTracks(FluxSink& fluxSink,
|
||||
const std::function<std::unique_ptr<const Fluxmap>(int track, int side)>
|
||||
const std::function<std::unique_ptr<const Fluxmap>(const Location& location)>
|
||||
producer);
|
||||
|
||||
extern void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
|
||||
@@ -477,7 +477,6 @@ buildlibrary libbackend.a \
|
||||
lib/logger.cc \
|
||||
lib/mapper.cc \
|
||||
lib/proto.cc \
|
||||
lib/reader.cc \
|
||||
lib/sector.cc \
|
||||
lib/usb/fluxengineusb.cc \
|
||||
lib/usb/greaseweazle.cc \
|
||||
@@ -489,13 +488,15 @@ buildlibrary libbackend.a \
|
||||
lib/writer.cc \
|
||||
|
||||
FORMATS="\
|
||||
40trackdrive \
|
||||
acornadfs \
|
||||
acorndfs \
|
||||
agat840 \
|
||||
aeslanier \
|
||||
agat840 \
|
||||
amiga \
|
||||
ampro \
|
||||
apple2 \
|
||||
apple2drive \
|
||||
appledos \
|
||||
atarist360 \
|
||||
atarist370 \
|
||||
@@ -535,10 +536,11 @@ FORMATS="\
|
||||
northstar87 \
|
||||
prodos \
|
||||
rx50 \
|
||||
shugartdrive \
|
||||
tids990 \
|
||||
vgi \
|
||||
victor9k_ss \
|
||||
victor9k_ds \
|
||||
victor9k_ss \
|
||||
zilogmcz \
|
||||
"
|
||||
|
||||
|
||||
10
src/formats/40trackdrive.textpb
Normal file
10
src/formats/40trackdrive.textpb
Normal file
@@ -0,0 +1,10 @@
|
||||
comment: 'Adjust configuration for a 40-track drive'
|
||||
is_extension: true
|
||||
|
||||
drive {
|
||||
cylinders: 40
|
||||
head_width: 1
|
||||
tpi: 48
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,7 @@ encoder {
|
||||
|
||||
cylinders {
|
||||
start: 0
|
||||
end: 79
|
||||
step: 2
|
||||
end: 39
|
||||
}
|
||||
|
||||
heads {
|
||||
@@ -39,3 +38,5 @@ heads {
|
||||
end: 0
|
||||
}
|
||||
|
||||
tpi: 48
|
||||
|
||||
|
||||
17
src/formats/apple2drive.textpb
Normal file
17
src/formats/apple2drive.textpb
Normal file
@@ -0,0 +1,17 @@
|
||||
comment: 'Adjust configuration for a 40-track Apple II drive'
|
||||
is_extension: true
|
||||
|
||||
usb {
|
||||
greaseweazle {
|
||||
bus_type: APPLE2
|
||||
}
|
||||
}
|
||||
|
||||
drive {
|
||||
cylinders: 160
|
||||
heads: 1
|
||||
head_width: 4
|
||||
tpi: 48
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ image_reader {
|
||||
img {
|
||||
tracks: 39
|
||||
sides: 1
|
||||
physical_step: 2
|
||||
trackdata {
|
||||
sector_size: 256
|
||||
sector_range {
|
||||
@@ -21,7 +20,6 @@ image_writer {
|
||||
img {
|
||||
tracks: 39
|
||||
sides: 1
|
||||
physical_step: 2
|
||||
trackdata {
|
||||
sector_size: 256
|
||||
sector_range {
|
||||
@@ -39,13 +37,16 @@ encoder {
|
||||
}
|
||||
|
||||
decoder {
|
||||
retries: 1
|
||||
brother {}
|
||||
}
|
||||
|
||||
drive {
|
||||
head_bias: 1
|
||||
}
|
||||
|
||||
cylinders {
|
||||
start: 0
|
||||
end: 77
|
||||
end: 38
|
||||
}
|
||||
|
||||
heads {
|
||||
@@ -53,3 +54,4 @@ heads {
|
||||
end: 0
|
||||
}
|
||||
|
||||
tpi: 48
|
||||
|
||||
@@ -38,6 +38,10 @@ decoder {
|
||||
brother {}
|
||||
}
|
||||
|
||||
drive {
|
||||
head_bias: 1
|
||||
}
|
||||
|
||||
cylinders {
|
||||
start: 0
|
||||
end: 77
|
||||
|
||||
@@ -20,7 +20,7 @@ decoder {
|
||||
|
||||
cylinders {
|
||||
start: 0
|
||||
end: 79
|
||||
end: 39
|
||||
}
|
||||
|
||||
heads {
|
||||
@@ -28,3 +28,5 @@ heads {
|
||||
end: 0
|
||||
}
|
||||
|
||||
tpi: 48
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ image_reader {
|
||||
img {
|
||||
tracks: 40
|
||||
sides: 1
|
||||
physical_step: 2
|
||||
trackdata {
|
||||
sector_size: 512
|
||||
sector_range {
|
||||
@@ -54,7 +53,7 @@ decoder {
|
||||
|
||||
cylinders {
|
||||
start: 0
|
||||
end: 79
|
||||
end: 39
|
||||
}
|
||||
|
||||
heads {
|
||||
@@ -62,3 +61,4 @@ heads {
|
||||
end: 0
|
||||
}
|
||||
|
||||
tpi: 48
|
||||
|
||||
@@ -5,7 +5,6 @@ image_reader {
|
||||
img {
|
||||
tracks: 40
|
||||
sides: 2
|
||||
physical_step: 2
|
||||
trackdata {
|
||||
sector_size: 512
|
||||
sector_range {
|
||||
@@ -54,7 +53,7 @@ decoder {
|
||||
|
||||
cylinders {
|
||||
start: 0
|
||||
end: 79
|
||||
end: 39
|
||||
}
|
||||
|
||||
heads {
|
||||
@@ -62,3 +61,5 @@ heads {
|
||||
end: 1
|
||||
}
|
||||
|
||||
tpi: 48
|
||||
|
||||
|
||||
9
src/formats/shugartdrive.textpb
Normal file
9
src/formats/shugartdrive.textpb
Normal file
@@ -0,0 +1,9 @@
|
||||
comment: 'Adjust configuration for a Shugart drive'
|
||||
is_extension: true
|
||||
|
||||
usb {
|
||||
greaseweazle {
|
||||
bus_type: SHUGART
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "decoders/decoders.h"
|
||||
#include "lib/usb/usbfinder.h"
|
||||
#include "fmt/format.h"
|
||||
#include "mapper.h"
|
||||
#include "utils.h"
|
||||
#include "mainwindow.h"
|
||||
#include <google/protobuf/text_format.h>
|
||||
@@ -86,6 +87,7 @@ void MainWindow::OnReadFluxButton(wxCommandEvent&)
|
||||
auto fluxSource = FluxSource::create(config.flux_source());
|
||||
auto decoder = AbstractDecoder::create(config.decoder());
|
||||
auto diskflux = readDiskCommand(*fluxSource, *decoder);
|
||||
|
||||
runOnUiThread(
|
||||
[&]()
|
||||
{
|
||||
|
||||
@@ -158,7 +158,7 @@ void VisualisationControl::Clear()
|
||||
|
||||
void VisualisationControl::SetTrackData(std::shared_ptr<const TrackFlux> track)
|
||||
{
|
||||
key_t key = {track->physicalCylinder, track->physicalHead};
|
||||
key_t key = {track->location.physicalCylinder, track->location.head};
|
||||
_sectors.erase(key);
|
||||
for (auto& sector : track->sectors)
|
||||
_sectors.insert({key, sector});
|
||||
|
||||
Reference in New Issue
Block a user