mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			187 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			4.9 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 6).
 | |
|      */
 | |
| 
 | |
|     return !!(side & 0x40);
 | |
| }
 | |
| 
 | |
| AbstractDecoder::RecordType MacintoshDecoder::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 MacintoshDecoder::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 MacintoshDecoder::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));
 | |
| }
 |