From 792cc881929e21ed16d60bdafb3c94f1daee1c71 Mon Sep 17 00:00:00 2001 From: David Given Date: Sat, 14 Dec 2019 20:44:48 +0100 Subject: [PATCH] The Amiga writer now generates valid flux files --- but it looks like the writer's broken (both the Amiga and the Brother have failed). --- arch/amiga/amiga.cc | 101 ++++++++++++++++++++++++++++++++++++++++ arch/amiga/amiga.h | 5 ++ arch/amiga/decoder.cc | 55 +++------------------- arch/amiga/encoder.cc | 68 +++++++++++++++++++-------- arch/brother/encoder.cc | 2 +- lib/decoders/decoders.h | 1 + lib/decoders/fmmfm.cc | 22 +++++++++ mkninja.sh | 2 + tests/amiga.cc | 38 +++++++++++++++ 9 files changed, 226 insertions(+), 68 deletions(-) create mode 100644 arch/amiga/amiga.cc create mode 100644 tests/amiga.cc diff --git a/arch/amiga/amiga.cc b/arch/amiga/amiga.cc new file mode 100644 index 00000000..2ca89957 --- /dev/null +++ b/arch/amiga/amiga.cc @@ -0,0 +1,101 @@ +#include "globals.h" +#include "record.h" +#include "decoders/decoders.h" +#include "amiga.h" +#include "bytes.h" +#include "fmt/format.h" + +uint32_t amigaChecksum(const Bytes& bytes) +{ + ByteReader br(bytes); + uint32_t checksum = 0; + + assert((bytes.size() & 3) == 0); + while (!br.eof()) + checksum ^= br.read_be32(); + + return checksum & 0x55555555; +} + +static uint8_t everyother(uint16_t x) +{ + /* aabb ccdd eeff gghh */ + x &= 0x6666; /* 0ab0 0cd0 0ef0 0gh0 */ + x >>= 1; /* 00ab 00cd 00ef 00gh */ + x |= x << 2; /* abab cdcd efef ghgh */ + x &= 0x3c3c; /* 00ab cd00 00ef gh00 */ + x >>= 2; /* 0000 abcd 0000 efgh */ + x |= x >> 4; /* 0000 abcd abcd efgh */ + return x; +} + +Bytes amigaInterleave(const Bytes& input) +{ + Bytes output; + ByteWriter bw(output); + + /* Write all odd bits. (Numbering starts at 0...) */ + + { + ByteReader br(input); + while (!br.eof()) + { + uint16_t x = br.read_be16(); + x &= 0xaaaa; /* a0b0 c0d0 e0f0 g0h0 */ + x |= x >> 1; /* aabb ccdd eeff gghh */ + x = everyother(x); /* 0000 0000 abcd efgh */ + bw.write_8(x); + } + } + + /* Write all even bits. */ + + { + ByteReader br(input); + while (!br.eof()) + { + uint16_t x = br.read_be16(); + x &= 0x5555; /* 0a0b 0c0d 0e0f 0g0h */ + x |= x << 1; /* aabb ccdd eeff gghh */ + x = everyother(x); /* 0000 0000 abcd efgh */ + bw.write_8(x); + } + } + + return output; +} + +Bytes amigaDeinterleave(const uint8_t*& input, size_t len) +{ + assert(!(len & 1)); + const uint8_t* odds = &input[0]; + const uint8_t* evens = &input[len/2]; + Bytes output; + ByteWriter bw(output); + + for (size_t i=0; i> 49) & 0x5555) | + (((o * 0x0101010101010101ULL & 0x8040201008040201ULL) + * 0x0102040810204081ULL >> 48) & 0xAAAA); + + bw.write_be16(result); + } + + input += len; + return output; +} + +Bytes amigaDeinterleave(const Bytes& input) +{ + const uint8_t* ptr = input.cbegin(); + return amigaDeinterleave(ptr, input.size()); +} diff --git a/arch/amiga/amiga.h b/arch/amiga/amiga.h index efb753a3..82fb441e 100644 --- a/arch/amiga/amiga.h +++ b/arch/amiga/amiga.h @@ -33,4 +33,9 @@ public: extern FlagGroup amigaEncoderFlags; +extern uint32_t amigaChecksum(const Bytes& bytes); +extern Bytes amigaInterleave(const Bytes& input); +extern Bytes amigaDeinterleave(const uint8_t*& input, size_t len); +extern Bytes amigaDeinterleave(const Bytes& input); + #endif diff --git a/arch/amiga/decoder.cc b/arch/amiga/decoder.cc index 8ae3b896..4e5a9f01 100644 --- a/arch/amiga/decoder.cc +++ b/arch/amiga/decoder.cc @@ -21,47 +21,6 @@ static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD); -static Bytes deinterleave(const uint8_t*& input, size_t len) -{ - assert(!(len & 1)); - const uint8_t* odds = &input[0]; - const uint8_t* evens = &input[len/2]; - Bytes output; - ByteWriter bw(output); - - for (size_t i=0; i> 49) & 0x5555) | - (((o * 0x0101010101010101ULL & 0x8040201008040201ULL) - * 0x0102040810204081ULL >> 48) & 0xAAAA); - - bw.write_be16(result); - } - - input += len; - return output; -} - -static uint32_t checksum(const Bytes& bytes) -{ - ByteReader br(bytes); - uint32_t checksum = 0; - - assert((bytes.size() & 3) == 0); - while (!br.eof()) - checksum ^= br.read_be32(); - - return checksum & 0x55555555; -} - AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord() { _sector->clock = _fmr->seekToPattern(SECTOR_PATTERN); @@ -78,22 +37,22 @@ void AmigaDecoder::decodeSectorRecord() const uint8_t* ptr = bytes.begin() + 3; - Bytes header = deinterleave(ptr, 4); - Bytes recoveryinfo = deinterleave(ptr, 16); + Bytes header = amigaDeinterleave(ptr, 4); + Bytes recoveryinfo = amigaDeinterleave(ptr, 16); _sector->logicalTrack = header[1] >> 1; _sector->logicalSide = header[1] & 1; _sector->logicalSector = header[2]; - uint32_t wantedheaderchecksum = deinterleave(ptr, 4).reader().read_be32(); - uint32_t gotheaderchecksum = checksum(rawbytes.slice(6, 40)); + uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); + uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40)); if (gotheaderchecksum != wantedheaderchecksum) return; - uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32(); - uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024)); + uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32(); + uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024)); _sector->data.clear(); - _sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo); + _sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo); _sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM; } diff --git a/arch/amiga/encoder.cc b/arch/amiga/encoder.cc index eade7f1b..8155b2b3 100644 --- a/arch/amiga/encoder.cc +++ b/arch/amiga/encoder.cc @@ -17,7 +17,7 @@ static DoubleFlag clockRateUs( static DoubleFlag postIndexGapMs( { "--post-index-gap" }, "Post-index gap before first sector header (milliseconds).", - 20.0); + 5.0); static int charToInt(char c) { @@ -26,16 +26,13 @@ static int charToInt(char c) return 10 + tolower(c) - 'a'; } -static uint32_t checksum(const Bytes& bytes) +static void write_bits(std::vector& bits, unsigned& cursor, const std::vector& src) { - ByteReader br(bytes); - uint32_t checksum = 0; - - assert((bytes.size() & 3) == 0); - while (!br.eof()) - checksum ^= br.read_be32(); - - return checksum & 0x55555555; + 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) @@ -50,16 +47,49 @@ static void write_bits(std::vector& bits, unsigned& cursor, uint64_t data, } } -static void write_sector(std::vector bits, unsigned& cursor, const Sector* sector) +static void write_interleaved_bytes(std::vector& bits, unsigned& cursor, const Bytes& bytes) +{ + assert(!(bytes.size() & 3)); + Bytes interleaved = amigaInterleave(bytes); + encodeMfm(bits, cursor, interleaved); +} + +static void write_interleaved_bytes(std::vector& bits, unsigned& cursor, uint32_t data) +{ + Bytes b(4); + ByteWriter bw(b); + bw.write_be32(data); + write_interleaved_bytes(bits, cursor, b); +} + +static void write_sector(std::vector& bits, unsigned& cursor, const Sector* sector) { write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8); - Bytes header(4); - ByteWriter bw(header); - bw.write_8(0xff); /* Amiga 1.0 format byte */ - bw.write_8((sector->logicalTrack<<1) | sector->logicalSide); - bw.write_8(sector->logicalSector); - bw.write_8(AMIGA_SECTORS_PER_TRACK - sector->logicalSector); + std::vector headerBits(20*16); + unsigned headerCursor = 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(headerBits, headerCursor, header); + Bytes recoveryInfo(16); + write_interleaved_bytes(headerBits, headerCursor, recoveryInfo); + + std::vector dataBits(512*16); + unsigned dataCursor = 0; + write_interleaved_bytes(dataBits, dataCursor, sector->data); + + write_bits(bits, cursor, headerBits); + uint32_t headerChecksum = amigaChecksum(toBytes(headerBits)); + write_interleaved_bytes(bits, cursor, headerChecksum); + uint32_t dataChecksum = amigaChecksum(toBytes(dataBits)); + write_interleaved_bytes(bits, cursor, dataChecksum); + write_bits(bits, cursor, dataBits); } std::unique_ptr AmigaEncoder::encode( @@ -76,11 +106,11 @@ std::unique_ptr AmigaEncoder::encode( for (int sectorId=0; sectorId bits.size()) + if (cursor >= bits.size()) Error() << "track data overrun"; fillBitmapTo(bits, cursor, bits.size(), { true, false }); diff --git a/arch/brother/encoder.cc b/arch/brother/encoder.cc index 20911866..3037db01 100644 --- a/arch/brother/encoder.cc +++ b/arch/brother/encoder.cc @@ -154,7 +154,7 @@ std::unique_ptr BrotherEncoder::encode( write_sector_data(bits, cursor, sectorData->data); } - if (cursor > bits.size()) + if (cursor >= bits.size()) Error() << "track data overrun"; fillBitmapTo(bits, cursor, bits.size(), { true, false }); diff --git a/lib/decoders/decoders.h b/lib/decoders/decoders.h index f1f68b3c..d622e4a0 100644 --- a/lib/decoders/decoders.h +++ b/lib/decoders/decoders.h @@ -20,6 +20,7 @@ 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); static inline Bytes decodeFmMfm(const std::vector bits) { return decodeFmMfm(bits.begin(), bits.end()); } diff --git a/lib/decoders/fmmfm.cc b/lib/decoders/fmmfm.cc index 097214bf..cba3aa42 100644 --- a/lib/decoders/fmmfm.cc +++ b/lib/decoders/fmmfm.cc @@ -51,3 +51,25 @@ Bytes decodeFmMfm( return bytes; } + +void encodeMfm(std::vector& bits, unsigned& cursor, const Bytes& input) +{ + bool lastBit = false; + unsigned len = bits.size()-1; + + for (uint8_t b : input) + { + for (int i=0; i<8; i++) + { + bool bit = b & 0x80; + b <<= 1; + + if (cursor >= len) + return; + + bits[cursor++] = !lastBit && !bit; + bits[cursor++] = bit; + lastBit = bit; + } + } +} diff --git a/mkninja.sh b/mkninja.sh index c755cd9f..689fea4c 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -146,6 +146,7 @@ buildlibrary libbackend.a \ arch/aeslanier/decoder.cc \ arch/amiga/decoder.cc \ arch/amiga/encoder.cc \ + arch/amiga/amiga.cc \ arch/apple2/decoder.cc \ arch/brother/decoder.cc \ arch/brother/encoder.cc \ @@ -253,3 +254,4 @@ runtest fluxpattern-test tests/fluxpattern.cc runtest fmmfm-test tests/fmmfm.cc runtest kryoflux-test tests/kryoflux.cc runtest ldbs-test tests/ldbs.cc +runtest amiga-test tests/amiga.cc diff --git a/tests/amiga.cc b/tests/amiga.cc new file mode 100644 index 00000000..db6af12f --- /dev/null +++ b/tests/amiga.cc @@ -0,0 +1,38 @@ +#include "globals.h" +#include "bytes.h" +#include "record.h" +#include "decoders/decoders.h" +#include "arch/amiga/amiga.h" +#include + +static const Bytes testData = { + 0x52, /* 0101 0010 */ + 0xff, /* 1111 1111 */ + 0x4a, /* 0100 1010 */ + 0x22, /* 0010 0010 */ +}; +static const Bytes testDataInterleaved = { + 0x1f, /* 0001 1111 */ + 0x35, /* 0011 0101 */ + 0xcf, /* 1100 1111 */ + 0x80, /* 1000 0000 */ +}; + +static void testInterleave(void) +{ + Bytes interleaved = amigaInterleave(testData); + assert(interleaved == testDataInterleaved); +} + +static void testDeinterleave(void) +{ + Bytes deinterleaved = amigaDeinterleave(testDataInterleaved); + assert(deinterleaved == testData); +} + +int main(int argc, const char* argv[]) +{ + testDeinterleave(); + testInterleave(); + return 0; +}