mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
222 lines
5.4 KiB
C++
222 lines
5.4 KiB
C++
#include "globals.h"
|
|
#include "fluxmap.h"
|
|
#include "decoders/fluxmapreader.h"
|
|
#include "protocol.h"
|
|
#include "record.h"
|
|
#include "decoders/decoders.h"
|
|
#include "sector.h"
|
|
#include "track.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)
|
|
{}
|
|
|
|
RecordType advanceToNextRecord()
|
|
{
|
|
const FluxMatcher* matcher = nullptr;
|
|
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
|
|
if (matcher == &SECTOR_RECORD_PATTERN)
|
|
return SECTOR_RECORD;
|
|
if (matcher == &DATA_RECORD_PATTERN)
|
|
return DATA_RECORD;
|
|
return UNKNOWN_RECORD;
|
|
}
|
|
|
|
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 != (_track->physicalTrack & 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 = _track->physicalTrack;
|
|
_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(Track& track) const
|
|
{
|
|
int count;
|
|
if (track.physicalTrack < 16)
|
|
count = 12;
|
|
else if (track.physicalTrack < 32)
|
|
count = 11;
|
|
else if (track.physicalTrack < 48)
|
|
count = 10;
|
|
else if (track.physicalTrack < 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));
|
|
}
|
|
|
|
|