From 6a215c35ee132220fe1d76a7ff3158fd66ac7b9f Mon Sep 17 00:00:00 2001 From: David Given Date: Fri, 12 Apr 2019 21:10:15 +0200 Subject: [PATCH] Add a reverse-engineered checksum routine, and some documentation. --- README.md | 2 + doc/disk-fb100.md | 32 ++++++++++++++++ lib/fb100/decoder.cc | 90 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 doc/disk-fb100.md diff --git a/README.md b/README.md index b01c40e1..bfb4d3f5 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ physical disks with these formats and so know they work. | [Commodore 64 1541](doc/disk-c64.md) | 🦖 | | and probably the other GCR formats | | [Brother 120kB](doc/disk-brother.md) | 🦄 | | | | [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | | +| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines | | [Macintosh 800kB](doc/disk-macintosh.md) | 🦖 | | and probably the 400kB too | | [TRS-80](doc/disk-trs80.md) | 🦖 | | a minor variation of the IBM scheme | {: .datatable } @@ -100,6 +101,7 @@ at least, check the CRC so what data's there is probably good. | [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8-inch | | [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8-inch _and_ hard sectors | {: .datatable } + ### Notes - IBM PC disks are the lowest-common-denominator standard. A number of other diff --git a/doc/disk-fb100.md b/doc/disk-fb100.md new file mode 100644 index 00000000..4de5bd3a --- /dev/null +++ b/doc/disk-fb100.md @@ -0,0 +1,32 @@ +Disk: Brother FB-100 +==================== + +The Brother FB-100 is a serial-attached smart floppy drive used by a several +different machines for mass storage, including the Tandy Model 100 and +clones, the Husky Hunter 2, and (bizarrely) several knitting machines. It was +usually rebadged, sometimes with a cheap paper label stuck over the Brother +logo: the most common variant appears to be the Tandy Portable Disk Drive or +TPDD: + + A Tandy Portable Disk Drive + +It's a bit of an oddball: the disk encoding is FM with a very custom record +scheme: 40-track single-sided 3.5" disks storing 100kB or so each. Each track +had only _two_ sectors, each 1280 bytes, but with an additional 17 bytes of +ID data used for filesystem management. + +There was also apparently a TPDD-2 which could store twice as much data, but +I don't have access to one of those disks. + +Reading discs +------------- + +Just do: + +``` +.obj/fe-readfb100 +``` + +You should end up with an `fb11.img` of the appropriate size. It's a simple +array of 80 1297-byte sectors (17 bytes for the ID record plus 1280 bytes for +the data). \ No newline at end of file diff --git a/lib/fb100/decoder.cc b/lib/fb100/decoder.cc index d3dada56..d20fe9f7 100644 --- a/lib/fb100/decoder.cc +++ b/lib/fb100/decoder.cc @@ -30,6 +30,89 @@ static bool search(const RawBits& rawbits, size_t& cursor) return false; } +/* + * Reverse engineered from a dump of the floppy drive's ROM. I have no idea how + * it works. + * + * LF8BA: + * clra + * staa X00B0 + * staa X00B1 + * ldx #$8000 + * LF8C2: ldaa $00,x + * inx + * bsr LF8CF + * cpx #$8011 + * bne LF8C2 + * ldd X00B0 + * rts + * LF8CF: + * eora X00B0 + * staa X00CF + * asla + * asla + * asla + * asla + * eora X00CF + * staa X00CF + * rola + * rola + * rola + * tab + * anda #$F8 + * eora X00B1 + * staa X00B0 + * rolb + * rolb + * andb #$0F + * eorb X00B0 + * stab X00B0 + * rolb + * eorb X00CF + * stab X00B1 + * rts + */ + +static void rol(uint8_t& b, bool& c) +{ + bool newc = b & 0x80; + b <<= 1; + b |= c; + c = newc; +} + +static uint16_t checksum(const Bytes& bytes) +{ + uint8_t crclo = 0; + uint8_t crchi = 0; + for (uint8_t a : bytes) + { + a ^= crchi; + uint8_t t1 = a; + a <<= 4; + bool c = a & 0x10; + a ^= t1; + t1 = a; + rol(a, c); + rol(a, c); + rol(a, c); + uint8_t b = a; + a &= 0xf8; + a ^= crclo; + crchi = a; + rol(b, c); + rol(b, c); + b &= 0x0f; + b ^= crchi; + crchi = b; + rol(b, c); + b ^= t1; + crclo = b; + } + + return (crchi << 8) | crclo; +} + void Fb100Decoder::decodeToSectors(const RawBits& rawbits, unsigned, RawRecordVector& rawrecords, SectorVector& sectors) { @@ -58,14 +141,19 @@ void Fb100Decoder::decodeToSectors(const RawBits& rawbits, unsigned, br.seek(1); const Bytes id = br.read(FB100_ID_SIZE); uint16_t wantIdCrc = br.read_be16(); + uint16_t gotIdCrc = checksum(id); const Bytes payload = br.read(FB100_PAYLOAD_SIZE); uint16_t wantPayloadCrc = br.read_be16(); + uint16_t gotPayloadCrc = checksum(payload); + + if (wantIdCrc != gotIdCrc) + continue; uint8_t abssector = id[2]; uint8_t track = abssector >> 1; uint8_t sectorid = abssector & 1; - int status = Sector::BAD_CHECKSUM; + int status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM; auto sector = std::unique_ptr( new Sector(status, track, 0, sectorid, payload)); sectors.push_back(std::move(sector));