From d303067deb54db5792609c9dbd0b261053329593 Mon Sep 17 00:00:00 2001 From: David Given Date: Fri, 25 Mar 2022 00:22:28 +0100 Subject: [PATCH] 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. --- arch/amiga/decoder.cc | 2 +- arch/amiga/encoder.cc | 222 ++++++------- arch/apple2/encoder.cc | 270 ++++++++-------- arch/brother/brother.proto | 1 - arch/brother/encoder.cc | 274 ++++++++-------- arch/c64/encoder.cc | 17 +- arch/ibm/decoder.cc | 4 +- arch/ibm/encoder.cc | 415 ++++++++++++------------ arch/macintosh/decoder.cc | 4 +- arch/macintosh/encoder.cc | 337 ++++++++++--------- arch/micropolis/decoder.cc | 2 +- arch/micropolis/encoder.cc | 189 +++++------ arch/northstar/decoder.cc | 2 +- arch/northstar/encoder.cc | 259 ++++++++------- arch/tids990/encoder.cc | 229 +++++++------ arch/victor9k/encoder.cc | 204 ++++++------ lib/config.proto | 3 +- lib/decoders/decoders.cc | 11 +- lib/decoders/decoders.h | 78 +++-- lib/drive.proto | 36 ++- lib/encoders/encoders.cc | 63 ++-- lib/encoders/encoders.h | 8 +- lib/flux.h | 22 +- lib/fluxmap.h | 7 +- lib/globals.h | 1 + lib/imagereader/imagereader.proto | 2 - lib/imagereader/imgimagereader.cc | 7 +- lib/mapper.cc | 176 ++++++---- lib/mapper.h | 14 +- lib/reader.cc | 311 +++++++++--------- lib/sector.cc | 8 +- lib/sector.h | 6 +- lib/usb/usb.proto | 1 + lib/writer.cc | 518 ++++++++++++++++++++++-------- lib/writer.h | 9 +- mkninja.sh | 8 +- src/formats/40trackdrive.textpb | 10 + src/formats/apple2.textpb | 5 +- src/formats/apple2drive.textpb | 17 + src/formats/brother120.textpb | 10 +- src/formats/brother240.textpb | 4 + src/formats/commodore1541.textpb | 4 +- src/formats/ibm180_525.textpb | 4 +- src/formats/ibm360_525.textpb | 5 +- src/formats/shugartdrive.textpb | 9 + src/gui/mainwindow.cc | 2 + src/gui/visualisation.cc | 2 +- 47 files changed, 2149 insertions(+), 1643 deletions(-) create mode 100644 src/formats/40trackdrive.textpb create mode 100644 src/formats/apple2drive.textpb create mode 100644 src/formats/shugartdrive.textpb diff --git a/arch/amiga/decoder.cc b/arch/amiga/decoder.cc index d099dfb6..6af128d7 100644 --- a/arch/amiga/decoder.cc +++ b/arch/amiga/decoder.cc @@ -68,7 +68,7 @@ public: _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; } - std::set requiredSectors(unsigned cylinder, unsigned head) const override + std::set requiredSectors(const Location&) const override { static std::set sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; return sectors; diff --git a/arch/amiga/encoder.cc b/arch/amiga/encoder.cc index cf5ac3e4..16a8d2b0 100644 --- a/arch/amiga/encoder.cc +++ b/arch/amiga/encoder.cc @@ -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& bits, unsigned& cursor, const std::vector& src) +static void write_bits( + std::vector& bits, unsigned& cursor, const std::vector& 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& bits, unsigned& cursor, uint64_t data, int width) +static void write_bits( + std::vector& bits, unsigned& cursor, uint64_t data, int width) { - cursor += width; - lastBit = data & 1; - for (int i=0; i>= 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& bits, unsigned& cursor, const Bytes& bytes) +static void write_bits( + std::vector& 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& bits, unsigned& cursor, const std::shared_ptr& sector) +static void write_sector(std::vector& bits, + unsigned& cursor, + const std::shared_ptr& 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; + std::vector> collectSectors( + const Location& location, const Image& image) override + { + std::vector> sectors; - if ((physicalTrack >= 0) && (physicalTrack < AMIGA_TRACKS_PER_DISK)) - { - for (int sectorId=0; sectorId= 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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override - { - if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK)) - return std::unique_ptr(); + std::unique_ptr encode(const Location& location, + const std::vector>& sectors, + const Image& image) override + { + int bitsPerRevolution = 200000.0 / _config.clock_rate_us(); + std::vector bits(bitsPerRevolution); + unsigned cursor = 0; - int bitsPerRevolution = 200000.0 / _config.clock_rate_us(); - std::vector 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= 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(new Fluxmap); - fluxmap->appendBits(bits, _config.clock_rate_us()*1e3); - return fluxmap; - } + std::unique_ptr fluxmap(new Fluxmap); + fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3); + return fluxmap; + } private: - const AmigaEncoderProto& _config; + const AmigaEncoderProto& _config; }; std::unique_ptr createAmigaEncoder(const EncoderProto& config) { - return std::unique_ptr(new AmigaEncoder(config)); + return std::unique_ptr(new AmigaEncoder(config)); } - - diff --git a/arch/apple2/encoder.cc b/arch/apple2/encoder.cc index f177baa5..0569d9af 100644 --- a/arch/apple2/encoder.cc +++ b/arch/apple2/encoder.cc @@ -10,13 +10,15 @@ #include #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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override + std::vector> collectSectors( + const Location& location, const Image& image) override { - std::vector> sectors; - constexpr auto numSectors = 16; - if (physicalSide == 0) - { - int logicalTrack = physicalTrack / 2; - unsigned numSectors = 16; - for (int sectorId=0; sectorId> 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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override + std::unique_ptr encode( + const Location& location, + const std::vector>& sectors, + const Image& image) override { - if (physicalSide != 0) - return std::unique_ptr(); + 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 bits(bitsPerRevolution); + unsigned cursor = 0; - std::vector 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(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(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& bits, unsigned& cursor, const Sector& sector) const + void writeSector( + std::vector& 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= 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 createApple2Encoder(const EncoderProto& config) { - return std::unique_ptr(new Apple2Encoder(config)); + return std::unique_ptr(new Apple2Encoder(config)); } - - diff --git a/arch/brother/brother.proto b/arch/brother/brother.proto index 15818c47..dfa606b1 100644 --- a/arch/brother/brother.proto +++ b/arch/brother/brother.proto @@ -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]; } diff --git a/arch/brother/encoder.cc b/arch/brother/encoder.cc index 65b096d7..379c8085 100644 --- a/arch/brother/encoder.cc +++ b/arch/brother/encoder.cc @@ -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& bits, unsigned& cursor, uint32_t data, int width) +static void write_bits( + std::vector& bits, unsigned& cursor, uint32_t data, int width) { - cursor += width; - for (int i=0; i>= 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& bits, unsigned& cursor, - int track, int sector) +static void write_sector_header( + std::vector& 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& bits, unsigned& cursor, const Bytes& data) +static void write_sector_data( + std::vector& 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& 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; + std::vector> collectSectors( + const Location& location, + const Image& image) override + { + std::vector> 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_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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override - { - if (physicalSide != 0) - return std::unique_ptr(); - physicalTrack -= _config.bias(); - switch (_config.format()) - { - case BROTHER120: - if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2)) - || (physicalTrack & 1)) - return std::unique_ptr(); - break; + std::unique_ptr encode( + const Location& location, + const std::vector>& sectors, + const Image& image) override + { + int bitsPerRevolution = 200000.0 / _config.clock_rate_us(); + std::vector bits(bitsPerRevolution); + unsigned cursor = 0; - case BROTHER240: - if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK)) - return std::unique_ptr(); - 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 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= 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(new Fluxmap); - fluxmap->appendBits(bits, _config.clock_rate_us()*1e3); - return fluxmap; - } + std::unique_ptr fluxmap(new Fluxmap); + fluxmap->appendBits(bits, _config.clock_rate_us() * 1e3); + return fluxmap; + } private: - const BrotherEncoderProto& _config; - + const BrotherEncoderProto& _config; }; -std::unique_ptr createBrotherEncoder(const EncoderProto& config) +std::unique_ptr createBrotherEncoder( + const EncoderProto& config) { - return std::unique_ptr(new BrotherEncoder(config)); + return std::unique_ptr(new BrotherEncoder(config)); } - - diff --git a/arch/c64/encoder.cc b/arch/c64/encoder.cc index ac1c451f..99c7d84a 100644 --- a/arch/c64/encoder.cc +++ b/arch/c64/encoder.cc @@ -211,17 +211,16 @@ public: {} public: - std::vector> collectSectors(int physicalTrack, int physicalSide, const Image& image) override + std::vector> collectSectors(const Location& location, const Image& image) override { std::vector> 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 encode(int physicalTrack, int physicalSide, + std::unique_ptr encode(const Location& location, const std::vector>& 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(); - 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; diff --git a/arch/ibm/decoder.cc b/arch/ibm/decoder.cc index fb7fbe5b..30a9e19b 100644 --- a/arch/ibm/decoder.cc +++ b/arch/ibm/decoder.cc @@ -203,10 +203,10 @@ public: _sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM; } - std::set requiredSectors(unsigned cylinder, unsigned head) const override + std::set requiredSectors(const Location& location) const override { IbmDecoderProto::TrackdataProto trackdata; - getTrackFormat(trackdata, cylinder, head); + getTrackFormat(trackdata, location.logicalCylinder, location.head); std::set s; if (trackdata.has_sectors()) diff --git a/arch/ibm/encoder.cc b/arch/ibm/encoder.cc index 9a188c98..e4848877 100644 --- a/arch/ibm/encoder.cc +++ b/arch/ibm/encoder.cc @@ -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>= 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 getSectorIds(const IbmEncoderProto::TrackdataProto& trackdata) - { - std::set 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 getSectorIds( + const IbmEncoderProto::TrackdataProto& trackdata) + { + std::set 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; - IbmEncoderProto::TrackdataProto trackdata; - getTrackFormat(trackdata, physicalTrack, physicalSide); + std::vector> collectSectors( + const Location& location, const Image& image) override + { + std::vector> 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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override - { - IbmEncoderProto::TrackdataProto trackdata; - getTrackFormat(trackdata, physicalTrack, physicalSide); + std::unique_ptr encode( + const Location& location, + const std::vector>& 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> 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(new Fluxmap); - fluxmap->appendBits(_bits, clockRateUs*1e3); - return fluxmap; - } + std::unique_ptr fluxmap(new Fluxmap); + fluxmap->appendBits(_bits, clockRateUs * 1e3); + return fluxmap; + } private: - const IbmEncoderProto& _config; - std::vector _bits; - unsigned _cursor; - bool _lastBit; + const IbmEncoderProto& _config; + std::vector _bits; + unsigned _cursor; + bool _lastBit; }; std::unique_ptr createIbmEncoder(const EncoderProto& config) { - return std::unique_ptr(new IbmEncoder(config)); + return std::unique_ptr(new IbmEncoder(config)); } - - diff --git a/arch/macintosh/decoder.cc b/arch/macintosh/decoder.cc index e20c6b37..db446f44 100644 --- a/arch/macintosh/decoder.cc +++ b/arch/macintosh/decoder.cc @@ -183,8 +183,10 @@ public: _sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12)); } - std::set requiredSectors(unsigned cylinder, unsigned head) const override + std::set requiredSectors(const Location& location) const override { + unsigned cylinder = location.logicalCylinder; + int count; if (cylinder < 16) count = 12; diff --git a/arch/macintosh/encoder.cc b/arch/macintosh/encoder.cc index 7b4a9e38..d93349ab 100644 --- a/arch/macintosh/encoder.cc +++ b/arch/macintosh/encoder.cc @@ -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& bits, unsigned& cursor, const std::vector& src) +static void write_bits( + std::vector& bits, unsigned& cursor, const std::vector& 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& bits, unsigned& cursor, uint64_t data, int width) +static void write_bits( + std::vector& bits, unsigned& cursor, uint64_t data, int width) { - cursor += width; - for (int i=0; i>= 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& bits, unsigned& cursor, const std::shared_ptr& sector) +static void write_sector(std::vector& bits, + unsigned& cursor, + const std::shared_ptr& 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; + std::vector> collectSectors( + const Location& location, const Image& image) override + { + std::vector> 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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override - { - if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK)) - return std::unique_ptr(); + std::unique_ptr encode(const Location& location, + const std::vector>& sectors, + const Image& image) override + { + double clockRateUs = clockRateUsForTrack(location.logicalCylinder) * + _config.clock_compensation_factor(); + int bitsPerRevolution = 200000.0 / clockRateUs; + std::vector bits(bitsPerRevolution); + unsigned cursor = 0; - double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor(); - int bitsPerRevolution = 200000.0 / clockRateUs; - std::vector 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(new Fluxmap); - fluxmap->appendBits(bits, clockRateUs*1e3); - return fluxmap; - } + std::unique_ptr fluxmap(new Fluxmap); + fluxmap->appendBits(bits, clockRateUs * 1e3); + return fluxmap; + } private: - const MacintoshEncoderProto& _config; + const MacintoshEncoderProto& _config; }; -std::unique_ptr createMacintoshEncoder(const EncoderProto& config) +std::unique_ptr createMacintoshEncoder( + const EncoderProto& config) { - return std::unique_ptr(new MacintoshEncoder(config)); + return std::unique_ptr(new MacintoshEncoder(config)); } - diff --git a/arch/micropolis/decoder.cc b/arch/micropolis/decoder.cc index 930a777b..99b4cfb2 100644 --- a/arch/micropolis/decoder.cc +++ b/arch/micropolis/decoder.cc @@ -173,7 +173,7 @@ public: _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; } - std::set requiredSectors(unsigned cylinder, unsigned head) const override + std::set requiredSectors(const Location& location) const override { static std::set sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; return sectors; diff --git a/arch/micropolis/encoder.cc b/arch/micropolis/encoder.cc index e81b76b9..1e9ada42 100644 --- a/arch/micropolis/encoder.cc +++ b/arch/micropolis/encoder.cc @@ -6,116 +6,123 @@ #include "image.h" #include "lib/encoders/encoders.pb.h" -static void write_sector(std::vector& bits, unsigned& cursor, const std::shared_ptr& sector) +static void write_sector(std::vector& bits, + unsigned& cursor, + const std::shared_ptr& 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>(); - 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>(); + 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; + std::vector> collectSectors( + const Location& location, const Image& image) override + { + std::vector> 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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override - { - int bitsPerRevolution = 100000; - double clockRateUs = 2.00; + std::unique_ptr encode(const Location& location, + const std::vector>& sectors, + const Image& image) override + { + int bitsPerRevolution = 100000; + double clockRateUs = 2.00; - if ((physicalTrack < 0) || (physicalTrack >= 77) || sectors.empty()) - return std::unique_ptr(); + std::vector bits(bitsPerRevolution); + unsigned cursor = 0; - std::vector 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(new Fluxmap); - fluxmap->appendBits(bits, clockRateUs * 1e3); - return fluxmap; - } + std::unique_ptr fluxmap(new Fluxmap); + fluxmap->appendBits(bits, clockRateUs * 1e3); + return fluxmap; + } private: - const MicropolisEncoderProto& _config; + const MicropolisEncoderProto& _config; }; -std::unique_ptr createMicropolisEncoder(const EncoderProto& config) +std::unique_ptr createMicropolisEncoder( + const EncoderProto& config) { - return std::unique_ptr(new MicropolisEncoder(config)); + return std::unique_ptr(new MicropolisEncoder(config)); } - diff --git a/arch/northstar/decoder.cc b/arch/northstar/decoder.cc index ffc99faf..bbdad7cb 100644 --- a/arch/northstar/decoder.cc +++ b/arch/northstar/decoder.cc @@ -164,7 +164,7 @@ public: _sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM; } - std::set requiredSectors(unsigned cylinder, unsigned head) const override + std::set requiredSectors(const Location&) const override { static std::set sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; return sectors; diff --git a/arch/northstar/encoder.cc b/arch/northstar/encoder.cc index 4172ba72..2bfe31fb 100644 --- a/arch/northstar/encoder.cc +++ b/arch/northstar/encoder.cc @@ -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& bits, unsigned& cursor, const std::shared_ptr& sector) +static void write_sector(std::vector& bits, + unsigned& cursor, + const std::shared_ptr& 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>(); - fullSector->reserve(fullSectorSize); + int fullSectorSize = preambleSize + encodedSectorSize; + auto fullSector = std::make_shared>(); + 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; + std::vector> collectSectors( + const Location& location, const Image& image) override + { + std::vector> 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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override - { - int bitsPerRevolution = 100000; - double clockRateUs = 4.00; + std::unique_ptr encode(const Location& location, + const std::vector>& sectors, + const Image& image) override + { + int bitsPerRevolution = 100000; + double clockRateUs = 4.00; - if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty()) - return std::unique_ptr(); + 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 bits(bitsPerRevolution); + unsigned cursor = 0; - std::vector 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(new Fluxmap); - fluxmap->appendBits(bits, clockRateUs * 1e3); - return fluxmap; - } + std::unique_ptr fluxmap(new Fluxmap); + fluxmap->appendBits(bits, clockRateUs * 1e3); + return fluxmap; + } private: - const NorthstarEncoderProto& _config; + const NorthstarEncoderProto& _config; }; -std::unique_ptr createNorthstarEncoder(const EncoderProto& config) +std::unique_ptr createNorthstarEncoder( + const EncoderProto& config) { - return std::unique_ptr(new NorthstarEncoder(config)); + return std::unique_ptr(new NorthstarEncoder(config)); } - diff --git a/arch/tids990/encoder.cc b/arch/tids990/encoder.cc index 915f03b1..eac2620a 100644 --- a/arch/tids990/encoder.cc +++ b/arch/tids990/encoder.cc @@ -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>= 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; + std::vector> collectSectors( + const Location& location, const Image& image) override + { + std::vector> 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 encode(int physicalTrack, int physicalSide, - const std::vector>& 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 encode(const Location& location, + const std::vector>& 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(new Fluxmap); - fluxmap->appendBits(_bits, clockRateUs*1e3); - return fluxmap; - } + std::unique_ptr fluxmap(new Fluxmap); + fluxmap->appendBits(_bits, clockRateUs * 1e3); + return fluxmap; + } private: - const Tids990EncoderProto& _config; - std::vector _bits; - unsigned _cursor; - bool _lastBit; + const Tids990EncoderProto& _config; + std::vector _bits; + unsigned _cursor; + bool _lastBit; }; -std::unique_ptr createTids990Encoder(const EncoderProto& config) +std::unique_ptr createTids990Encoder( + const EncoderProto& config) { - return std::unique_ptr(new Tids990Encoder(config)); + return std::unique_ptr(new Tids990Encoder(config)); } - - diff --git a/arch/victor9k/encoder.cc b/arch/victor9k/encoder.cc index ce41f51d..14429c2c 100644 --- a/arch/victor9k/encoder.cc +++ b/arch/victor9k/encoder.cc @@ -14,77 +14,84 @@ static bool lastBit; -static void write_zero_bits(std::vector& bits, unsigned& cursor, unsigned count) +static void write_zero_bits( + std::vector& 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& bits, unsigned& cursor, unsigned count) +static void write_one_bits( + std::vector& 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& bits, unsigned& cursor, const std::vector& src) +static void write_bits( + std::vector& bits, unsigned& cursor, const std::vector& 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& bits, unsigned& cursor, uint64_t data, int width) +static void write_bits( + std::vector& bits, unsigned& cursor, uint64_t data, int width) { - cursor += width; - lastBit = data & 1; - for (int i=0; i>= 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& bits, unsigned& cursor, const Bytes& bytes) +static void write_bits( + std::vector& 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& 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& bits, unsigned& cursor, const Bytes& bytes) +static void write_bytes( + std::vector& 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& bits, unsigned& cursor, const Bytes& static void write_gap(std::vector& 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& bits, unsigned& cursor, - const Victor9kEncoderProto::TrackdataProto& trackdata, - const Sector& sector) +static void write_sector(std::vector& 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& 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> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; + std::vector> collectSectors( + const Location& location, const Image& image) override + { + std::vector> 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 encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override + std::unique_ptr encode(const Location& location, + const std::vector>& 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 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(new Fluxmap); - fluxmap->appendBits(bits, clockRateUs*1e3); + fluxmap->appendBits(bits, clockRateUs * 1e3); return fluxmap; } private: - const Victor9kEncoderProto& _config; + const Victor9kEncoderProto& _config; }; -std::unique_ptr createVictor9kEncoder(const EncoderProto& config) +std::unique_ptr createVictor9kEncoder( + const EncoderProto& config) { - return std::unique_ptr(new Victor9kEncoder(config)); + return std::unique_ptr(new Victor9kEncoder(config)); } // vim: sw=4 ts=4 et - diff --git a/lib/config.proto b/lib/config.proto index b15035c1..6e7ee93a 100644 --- a/lib/config.proto +++ b/lib/config.proto @@ -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; } diff --git a/lib/decoders/decoders.cc b/lib/decoders/decoders.cc index d0fbfa98..3ee1c293 100644 --- a/lib/decoders/decoders.cc +++ b/lib/decoders/decoders.cc @@ -60,12 +60,11 @@ std::unique_ptr AbstractDecoder::create(const DecoderProto& con } std::shared_ptr AbstractDecoder::decodeToSectors( - std::shared_ptr fluxmap, unsigned physicalCylinder, unsigned physicalHead) + std::shared_ptr fluxmap, const Location& location) { _trackdata = std::make_shared(); _trackdata->fluxmap = fluxmap; - _trackdata->physicalCylinder = physicalCylinder; - _trackdata->physicalHead = physicalHead; + _trackdata->location = location; FluxmapReader fmr(*fluxmap); _fmr = &fmr; @@ -73,8 +72,8 @@ std::shared_ptr AbstractDecoder::decodeToSectors( auto newSector = [&] { _sector = std::make_shared(); _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 AbstractDecoder::requiredSectors(unsigned cylinder, unsigned head) const +std::set AbstractDecoder::requiredSectors(const Location& location) const { static std::set set; return set; diff --git a/lib/decoders/decoders.h b/lib/decoders/decoders.h index f559539c..90e1c544 100644 --- a/lib/decoders/decoders.h +++ b/lib/decoders/decoders.h @@ -18,23 +18,27 @@ extern void setDecoderManualClockRate(double clockrate_us); extern Bytes decodeFmMfm(std::vector::const_iterator start, std::vector::const_iterator end); -extern void encodeMfm(std::vector& bits, unsigned& cursor, const Bytes& input, bool& lastBit); -extern void encodeFm(std::vector& bits, unsigned& cursor, const Bytes& input); +extern void encodeMfm(std::vector& bits, + unsigned& cursor, + const Bytes& input, + bool& lastBit); +extern void encodeFm( + std::vector& bits, unsigned& cursor, const Bytes& input); extern Bytes encodeMfm(const Bytes& input, bool& lastBit); static inline Bytes decodeFmMfm(const std::vector 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 create(const DecoderProto& config); + static std::unique_ptr create(const DecoderProto& config); public: enum RecordType @@ -45,47 +49,59 @@ public: }; public: - std::shared_ptr decodeToSectors(std::shared_ptr fluxmap, unsigned cylinder, unsigned head); - void pushRecord(const Fluxmap::Position& start, const Fluxmap::Position& end); + std::shared_ptr decodeToSectors( + std::shared_ptr fluxmap, + const Location& location); - void resetFluxDecoder(); + void pushRecord( + const Fluxmap::Position& start, const Fluxmap::Position& end); + + void resetFluxDecoder(); std::vector 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 requiredSectors(unsigned cylinder, unsigned head) const; + virtual std::set 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 _trackdata; + const DecoderProto& _config; + std::shared_ptr _trackdata; std::shared_ptr _sector; - std::unique_ptr _decoder; - std::vector _recordBits; + std::unique_ptr _decoder; + std::vector _recordBits; private: FluxmapReader* _fmr = nullptr; diff --git a/lib/drive.proto b/lib/drive.proto index 74d3e3bd..6d9b04eb 100644 --- a/lib/drive.proto +++ b/lib/drive.proto @@ -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 diff --git a/lib/encoders/encoders.cc b/lib/encoders/encoders.cc index e2094581..30faf3b7 100644 --- a/lib/encoders/encoders.cc +++ b/lib/encoders/encoders.cc @@ -15,45 +15,44 @@ #include "lib/encoders/encoders.pb.h" #include "protocol.h" -std::unique_ptr AbstractEncoder::create(const EncoderProto& config) +std::unique_ptr AbstractEncoder::create( + const EncoderProto& config) { - static const std::map(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(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& bits, nanoseconds_t clock) { - nanoseconds_t now = duration(); - for (unsigned i=0; i> collectSectors( - int physicalCylinder, int physicalHead, const Image& image) = 0; + const Location& location, const Image& image) = 0; - virtual std::unique_ptr encode(int physicalCylinder, - int physicalHead, + virtual std::unique_ptr encode(const Location& location, const std::vector>& sectors, const Image& image) = 0; }; diff --git a/lib/flux.h b/lib/flux.h index 12000d7c..cee4df51 100644 --- a/lib/flux.h +++ b/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 fluxmap; std::vector> records; std::vector> sectors; @@ -23,8 +38,7 @@ struct TrackDataFlux struct TrackFlux { - unsigned physicalCylinder; - unsigned physicalHead; + Location location; std::vector> trackDatas; std::set> sectors; }; diff --git a/lib/fluxmap.h b/lib/fluxmap.h index fb389b01..f4b85c6c 100644 --- a/lib/fluxmap.h +++ b/lib/fluxmap.h @@ -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; } diff --git a/lib/globals.h b/lib/globals.h index 92e427c8..79b6fc16 100644 --- a/lib/globals.h +++ b/lib/globals.h @@ -14,6 +14,7 @@ #include #include #include +#include #if defined(_WIN32) || defined(__WIN32__) #include diff --git a/lib/imagereader/imagereader.proto b/lib/imagereader/imagereader.proto index dd5002f8..0f3efd29 100644 --- a/lib/imagereader/imagereader.proto +++ b/lib/imagereader/imagereader.proto @@ -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"]; } diff --git a/lib/imagereader/imgimagereader.cc b/lib/imagereader/imgimagereader.cc index db37060c..4f25d770 100644 --- a/lib/imagereader/imgimagereader.cc +++ b/lib/imagereader/imgimagereader.cc @@ -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; diff --git a/lib/mapper.cc b/lib/mapper.cc index 23717a25..f393d32d 100644 --- a/lib/mapper.cc +++ b/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&, const SectorMappingProto::MappingProto&)> insertercb_t; +typedef std::function&, 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 remapImpl(const Image& source, const SectorMappingProto& mapping, - insertercb_t inserter_cb) +static std::unique_ptr remapImpl(const Image& source, + const SectorMappingProto& mapping, + insertercb_t inserter_cb) { - typedef std::pair tracksidekey_t; - std::map> cache; + typedef std::pair tracksidekey_t; + std::map> cache; - auto getTrackdata = - [&](const tracksidekey_t& key) -> const std::map& { - auto it = cache.find(key); - if (it != cache.end()) - return it->second; + auto getTrackdata = + [&](const tracksidekey_t& key) -> const std::map& + { + 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> 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> 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); - newSector->logicalSector = it->second; - destSectors.insert(newSector); - } - } + auto newSector = std::make_shared(*sector); + newSector->logicalSector = it->second; + destSectors.insert(newSector); + } + } - return std::make_unique(destSectors); + return std::make_unique(destSectors); } -std::unique_ptr Mapper::remapPhysicalToLogical(const Image& source, const SectorMappingProto& mapping) +std::unique_ptr 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 Mapper::remapLogicalToPhysical(const Image& source, const SectorMappingProto& mapping) +std::unique_ptr 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 Mapper::computeLocations() +{ + std::set 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; +} diff --git a/lib/mapper.h b/lib/mapper.h index 643da115..b73a590a 100644 --- a/lib/mapper.h +++ b/lib/mapper.h @@ -2,13 +2,21 @@ #define MAPPER_H class SectorMappingProto; +class DriveProto; +class Location; class Mapper { public: - static std::unique_ptr remapPhysicalToLogical(const Image& source, const SectorMappingProto& mapping); - static std::unique_ptr remapLogicalToPhysical(const Image& source, const SectorMappingProto& mapping); + static std::unique_ptr remapSectorsPhysicalToLogical( + const Image& source, const SectorMappingProto& mapping); + static std::unique_ptr remapSectorsLogicalToPhysical( + const Image& source, const SectorMappingProto& mapping); + + static unsigned remapCylinderPhysicalToLogical(unsigned cylinder); + static unsigned remapCylinderLogicalToPhysical(unsigned cylinder); + + static std::set computeLocations(); }; #endif - diff --git a/lib/reader.cc b/lib/reader.cc index 678cfa16..9999b907 100644 --- a/lib/reader.cc +++ b/lib/reader.cc @@ -23,13 +23,17 @@ static std::unique_ptr outputFluxSink; -static std::shared_ptr readFluxmap(FluxSourceIterator& fluxsourceIterator, unsigned cylinder, unsigned head) +static std::shared_ptr 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> collect_sectors( - std::set>& track_sectors, bool collapse_conflicts = true) + std::set>& track_sectors, + bool collapse_conflicts = true) { typedef std::tuple key_t; std::multimap> sectors; @@ -65,12 +70,12 @@ static std::set> collect_sectors( (right->status == Sector::OK) && (left->data != right->data)) { - if (!collapse_conflicts) - { - auto s = std::make_shared(*right); - s->status = Sector::CONFLICT; - sector_set.insert(s); - } + if (!collapse_conflicts) + { + auto s = std::make_shared(*right); + s->status = Sector::CONFLICT; + sector_set.insert(s); + } auto s = std::make_shared(*left); s->status = Sector::CONFLICT; return s; @@ -91,164 +96,172 @@ static std::set> collect_sectors( return sector_set; } -std::shared_ptr readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder) +std::shared_ptr 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(); 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(); + track->location = location; + diskflux->tracks.push_back(track); + + std::set> track_sectors; + std::set> 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(); - track->physicalCylinder = cylinder; - track->physicalHead = head; - diskflux->tracks.push_back(track); + auto trackdataflux = decoder.decodeToSectors(fluxmap, location); + track->trackDatas.push_back(trackdataflux); - std::set> track_sectors; - std::set> 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 required_sectors = + decoder.requiredSectors(location); + std::set> 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 required_sectors = - decoder.requiredSectors(cylinder, head); - std::set> 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->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> 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->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> 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> 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> 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> 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(all_sectors); + std::set> 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(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); + } } } diff --git a/lib/sector.cc b/lib/sector.cc index c499bf83..4d6460a0 100644 --- a/lib/sector.cc +++ b/lib/sector.cc @@ -55,14 +55,14 @@ Sector::Status Sector::stringToStatus(const std::string& value) return Status::INTERNAL_ERROR; } -bool sectorPointerSortPredicate( - std::shared_ptr& lhs, std::shared_ptr& rhs) +bool sectorPointerSortPredicate(const std::shared_ptr& lhs, + const std::shared_ptr& rhs) { return *lhs < *rhs; } -bool sectorPointerEqualsPredicate( - std::shared_ptr& lhs, std::shared_ptr& rhs) +bool sectorPointerEqualsPredicate(const std::shared_ptr& lhs, + const std::shared_ptr& rhs) { if (!lhs && !rhs) return true; diff --git a/lib/sector.h b/lib/sector.h index 8061ceae..9c42551d 100644 --- a/lib/sector.h +++ b/lib/sector.h @@ -66,8 +66,10 @@ public: }; extern bool sectorPointerSortPredicate( - std::shared_ptr& lhs, std::shared_ptr& rhs); + const std::shared_ptr& lhs, + const std::shared_ptr& rhs); extern bool sectorPointerEqualsPredicate( - std::shared_ptr& lhs, std::shared_ptr& rhs); + const std::shared_ptr& lhs, + const std::shared_ptr& rhs); #endif diff --git a/lib/usb/usb.proto b/lib/usb/usb.proto index 38ab5a9b..ab46f269 100644 --- a/lib/usb/usb.proto +++ b/lib/usb/usb.proto @@ -7,6 +7,7 @@ message GreaseWeazleProto { BUSTYPE_INVALID = 0; IBMPC = 1; SHUGART = 2; + APPLE2 = 3; }; optional string port = 1 diff --git a/lib/writer.cc b/lib/writer.cc index 8cbd54bb..eeabfec9 100644 --- a/lib/writer.cc +++ b/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 + +static std::set> collectSectors( + std::set>& track_sectors, + bool collapse_conflicts = true) +{ + typedef std::tuple key_t; + std::multimap> sectors; + + for (const auto& sector : track_sectors) + { + key_t sectorid = { + sector->logicalTrack, sector->logicalSide, sector->logicalSector}; + sectors.insert({sectorid, sector}); + } + + std::set> 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 + { + 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(*right); + s->status = Sector::CONFLICT; + sector_set.insert(s); + } + auto s = std::make_shared(*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> 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->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 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(int track, int side)> producer) + std::function(const Location& location)> + producer, + std::function 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(); + readGroup(fluxSource, location, *trackFlux, decoder); + Logger() << TrackReadLogMessage{ trackFlux }; + auto wantedSectors = encoder.collectSectors(location, image); + std::sort(wantedSectors.begin(), wantedSectors.end(), + sectorPointerSortPredicate); + + std::vector> 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 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 readDiskCommand( + FluxSource& fluxSource, AbstractDecoder& decoder) +{ + std::unique_ptr outputFluxSink; + if (config.decoder().has_copy_flux_to()) + outputFluxSink = FluxSink::create(config.decoder().copy_flux_to()); + + auto diskflux = std::make_shared(); + bool failures = false; + + for (const auto& location : Mapper::computeLocations()) + { + testForEmergencyStop(); + + auto track = std::make_shared(); + 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> 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> 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> 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(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 = - 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 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> 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& bitmap, } } } - -void writeDiskCommand( - std::shared_ptr 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 - { - const auto& sectors = - encoder.collectSectors(physicalTrack, physicalSide, *image); - if (sectors.empty()) - return std::make_unique(); - return encoder.encode( - physicalTrack, physicalSide, sectors, *image); - }); -} - -void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink) -{ - writeTracks(fluxSink, - [&](int track, int side) -> std::unique_ptr - { - return fluxSource.readFlux(track, side)->next(); - }); -} diff --git a/lib/writer.h b/lib/writer.h index 3edf6840..461e0674 100644 --- a/lib/writer.h +++ b/lib/writer.h @@ -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(int track, int side)> + const std::function(const Location& location)> producer); extern void fillBitmapTo(std::vector& bitmap, diff --git a/mkninja.sh b/mkninja.sh index 904fbd83..7383aa01 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -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 \ " diff --git a/src/formats/40trackdrive.textpb b/src/formats/40trackdrive.textpb new file mode 100644 index 00000000..b2dc2a71 --- /dev/null +++ b/src/formats/40trackdrive.textpb @@ -0,0 +1,10 @@ +comment: 'Adjust configuration for a 40-track drive' +is_extension: true + +drive { + cylinders: 40 + head_width: 1 + tpi: 48 +} + + diff --git a/src/formats/apple2.textpb b/src/formats/apple2.textpb index cae22a4f..cf72a326 100644 --- a/src/formats/apple2.textpb +++ b/src/formats/apple2.textpb @@ -30,8 +30,7 @@ encoder { cylinders { start: 0 - end: 79 - step: 2 + end: 39 } heads { @@ -39,3 +38,5 @@ heads { end: 0 } +tpi: 48 + diff --git a/src/formats/apple2drive.textpb b/src/formats/apple2drive.textpb new file mode 100644 index 00000000..2bfad40b --- /dev/null +++ b/src/formats/apple2drive.textpb @@ -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 +} + + diff --git a/src/formats/brother120.textpb b/src/formats/brother120.textpb index 56f865ea..e63052cc 100644 --- a/src/formats/brother120.textpb +++ b/src/formats/brother120.textpb @@ -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 diff --git a/src/formats/brother240.textpb b/src/formats/brother240.textpb index fdbcd196..e0427528 100644 --- a/src/formats/brother240.textpb +++ b/src/formats/brother240.textpb @@ -38,6 +38,10 @@ decoder { brother {} } +drive { + head_bias: 1 +} + cylinders { start: 0 end: 77 diff --git a/src/formats/commodore1541.textpb b/src/formats/commodore1541.textpb index db88c4d8..19aec957 100644 --- a/src/formats/commodore1541.textpb +++ b/src/formats/commodore1541.textpb @@ -20,7 +20,7 @@ decoder { cylinders { start: 0 - end: 79 + end: 39 } heads { @@ -28,3 +28,5 @@ heads { end: 0 } +tpi: 48 + diff --git a/src/formats/ibm180_525.textpb b/src/formats/ibm180_525.textpb index efd99306..58cb8c21 100644 --- a/src/formats/ibm180_525.textpb +++ b/src/formats/ibm180_525.textpb @@ -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 diff --git a/src/formats/ibm360_525.textpb b/src/formats/ibm360_525.textpb index e98c012c..de5cb723 100644 --- a/src/formats/ibm360_525.textpb +++ b/src/formats/ibm360_525.textpb @@ -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 + diff --git a/src/formats/shugartdrive.textpb b/src/formats/shugartdrive.textpb new file mode 100644 index 00000000..4a2611ad --- /dev/null +++ b/src/formats/shugartdrive.textpb @@ -0,0 +1,9 @@ +comment: 'Adjust configuration for a Shugart drive' +is_extension: true + +usb { + greaseweazle { + bus_type: SHUGART + } +} + diff --git a/src/gui/mainwindow.cc b/src/gui/mainwindow.cc index 66f8e5f5..a0705ca4 100644 --- a/src/gui/mainwindow.cc +++ b/src/gui/mainwindow.cc @@ -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 @@ -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( [&]() { diff --git a/src/gui/visualisation.cc b/src/gui/visualisation.cc index f187c03f..bf190c43 100644 --- a/src/gui/visualisation.cc +++ b/src/gui/visualisation.cc @@ -158,7 +158,7 @@ void VisualisationControl::Clear() void VisualisationControl::SetTrackData(std::shared_ptr 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});