#include "globals.h" #include "fluxmap.h" #include "decoders/fluxmapreader.h" #include "protocol.h" #include "decoders/decoders.h" #include "sector.h" #include "apple2.h" #include "bytes.h" #include "fmt/format.h" #include #include const FluxPattern SECTOR_RECORD_PATTERN(24, APPLE2_SECTOR_RECORD); const FluxPattern DATA_RECORD_PATTERN(24, APPLE2_DATA_RECORD); const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN }); static int decode_data_gcr(uint8_t gcr) { switch (gcr) { #define GCR_ENTRY(gcr, data) \ case gcr: return data; #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/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp */ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status) { Bytes output(APPLE2_SECTOR_LENGTH); uint8_t checksum = 0; for (unsigned i = 0; i < APPLE2_ENCODED_SECTOR_LENGTH; i++) { checksum ^= decode_data_gcr(*inp++); if (i >= 86) { /* 6 bit */ output[i - 86] |= (checksum << 2); } else { /* 3 * 2 bit */ output[i + 0] = ((checksum >> 1) & 0x01) | ((checksum << 1) & 0x02); output[i + 86] = ((checksum >> 3) & 0x01) | ((checksum >> 1) & 0x02); if ((i + 172) < APPLE2_SECTOR_LENGTH) output[i + 172] = ((checksum >> 5) & 0x01) | ((checksum >> 3) & 0x02); } } checksum &= 0x3f; uint8_t wantedchecksum = decode_data_gcr(*inp); status = (checksum == wantedchecksum) ? Sector::OK : Sector::BAD_CHECKSUM; return output; } static uint8_t combine(uint16_t word) { return word & (word >> 7); } class Apple2Decoder : public AbstractDecoder { public: Apple2Decoder(const DecoderProto& config): AbstractDecoder(config) {} RecordType advanceToNextRecord() { const FluxMatcher* matcher = nullptr; _sector->clock = _sector->bitcell = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher); if (matcher == &SECTOR_RECORD_PATTERN) return RecordType::SECTOR_RECORD; if (matcher == &DATA_RECORD_PATTERN) return RecordType::DATA_RECORD; return RecordType::UNKNOWN_RECORD; } void decodeSectorRecord() { /* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */ readRawBits(24); /* Read header. */ auto header = toBytes(readRawBits(8*8)).slice(0, 8); ByteReader br(header); uint8_t volume = combine(br.read_be16()); _sector->logicalTrack = combine(br.read_be16()); _sector->logicalSector = combine(br.read_be16()); uint8_t checksum = combine(br.read_be16()); if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector)) _sector->status = Sector::DATA_MISSING; /* unintuitive but correct */ } void decodeDataRecord() { /* Check ID. */ Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3); if (bytes.reader().read_be24() != APPLE2_DATA_RECORD) return; /* Read and decode data. */ unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2; bytes = toBytes(readRawBits(recordLength*8)).slice(0, recordLength); _sector->status = Sector::BAD_CHECKSUM; _sector->data = decode_crazy_data(&bytes[0], _sector->status); } }; std::unique_ptr createApple2Decoder(const DecoderProto& config) { return std::unique_ptr(new Apple2Decoder(config)); }