Files
fluxengine/arch/macintosh/decoder.cc
David Given 0933dc1afa Partially complete rework of all the decoders to avoid seeking inside the
fluxmap. This requires resetting the FluxDecoder, which loses any pending
state, resulting in bad reads for (some) formats which don't have gaps between
sectors --- the DVK MX is the main victim.
2022-02-12 00:55:09 +01:00

213 lines
5.2 KiB
C++

#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "macintosh.h"
#include "bytes.h"
#include "fmt/format.h"
#include <string.h>
#include <algorithm>
const FluxPattern SECTOR_RECORD_PATTERN(24, MAC_SECTOR_RECORD);
const FluxPattern DATA_RECORD_PATTERN(24, MAC_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/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
*/
static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
{
Bytes output;
ByteWriter bw(output);
ByteReader br(input);
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
uint8_t b1[LOOKUP_LEN + 1];
uint8_t b2[LOOKUP_LEN + 1];
uint8_t b3[LOOKUP_LEN + 1];
for (int i=0; i<=LOOKUP_LEN; i++)
{
uint8_t w4 = br.read_8();
uint8_t w1 = br.read_8();
uint8_t w2 = br.read_8();
uint8_t w3 = (i != 174) ? br.read_8() : 0;
b1[i] = (w1 & 0x3F) | ((w4 << 2) & 0xC0);
b2[i] = (w2 & 0x3F) | ((w4 << 4) & 0xC0);
b3[i] = (w3 & 0x3F) | ((w4 << 6) & 0xC0);
}
/* Copy from the user's buffer to our buffer, while computing
* the three-byte data checksum. */
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t c3 = 0;
unsigned count = 0;
for (;;)
{
c1 = (c1 & 0xFF) << 1;
if (c1 & 0x0100)
c1++;
uint8_t val = b1[count] ^ c1;
c3 += val;
if (c1 & 0x0100)
{
c3++;
c1 &= 0xFF;
}
bw.write_8(val);
val = b2[count] ^ c3;
c2 += val;
if (c3 > 0xFF)
{
c2++;
c3 &= 0xFF;
}
bw.write_8(val);
if (output.size() == 524)
break;
val = b3[count] ^ c2;
c1 += val;
if (c2 > 0xFF)
{
c1++;
c2 &= 0xFF;
}
bw.write_8(val);
count++;
}
uint8_t c4 = ((c1 & 0xC0) >> 6) | ((c2 & 0xC0) >> 4) | ((c3 & 0xC0) >> 2);
c1 &= 0x3f;
c2 &= 0x3f;
c3 &= 0x3f;
c4 &= 0x3f;
uint8_t g4 = br.read_8();
uint8_t g3 = br.read_8();
uint8_t g2 = br.read_8();
uint8_t g1 = br.read_8();
if ((g4 == c4) && (g3 == c3) && (g2 == c2) && (g1 == c1))
status = Sector::OK;
return output;
}
uint8_t decode_side(uint8_t side)
{
/* Mac disks, being weird, use the side byte to encode both the side (in
* bit 5) and also whether we're above track 0x3f (in bit 0).
*/
return !!(side & 0x20);
}
class MacintoshDecoder : public AbstractDecoder
{
public:
MacintoshDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord()
{
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
readRawBits(24);
/* Read header. */
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_sector->physicalCylinder & 0x3f))
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
uint8_t encodedSide = decode_data_gcr(header[2]);
uint8_t formatByte = decode_data_gcr(header[3]);
uint8_t wantedsum = decode_data_gcr(header[4]);
if (encodedSector > 11)
return;
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord()
{
auto id = toBytes(readRawBits(24)).reader().read_be24();
if (id != MAC_DATA_RECORD)
return;
/* Read data. */
readRawBits(8); /* skip spare byte */
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH*8))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
for (unsigned i=0; i<inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const
{
int count;
if (cylinder < 16)
count = 12;
else if (cylinder < 32)
count = 11;
else if (cylinder < 48)
count = 10;
else if (cylinder < 64)
count = 9;
else
count = 8;
std::set<unsigned> sectors;
while (count--)
sectors.insert(count);
return sectors;
}
};
std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new MacintoshDecoder(config));
}