mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-31 11:17:01 -07:00 
			
		
		
		
	Add initial support for the Basis-108.
This commit is contained in:
		@@ -106,7 +106,7 @@ people who've had it work).
 | 
			
		||||
| [Acorn DFS](doc/disk-acorndfs.md)         |  🦄   |   🦖*  |                                     |
 | 
			
		||||
| [Ampro Little Board](doc/disk-ampro.md)   |  🦖   |   🦖*  |                                     |
 | 
			
		||||
| [Agat](doc/disk-agat.md)                  |  🦖   |        | Soviet Union Apple-II-like computer |
 | 
			
		||||
| [Apple II](doc/disk-apple2.md)            |  🦄   |   🦄   |                                     |
 | 
			
		||||
| [Apple II](doc/disk-apple2.md)            |  🦄   |   🦄   | also the Basis-108 clone            |
 | 
			
		||||
| [Amiga](doc/disk-amiga.md)                |  🦄   |   🦄   |                                     |
 | 
			
		||||
| [Commodore 64 1541/1581](doc/disk-c64.md) |  🦄   |   🦄   | and probably the other formats      |
 | 
			
		||||
| [Brother 120kB](doc/disk-brother.md)      |  🦄   |   🦄   |                                     |
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,10 @@ syntax = "proto2";
 | 
			
		||||
 | 
			
		||||
import "lib/common.proto";
 | 
			
		||||
 | 
			
		||||
message Apple2DecoderProto {}
 | 
			
		||||
message Apple2DecoderProto {
 | 
			
		||||
	optional uint32 side_one_track_offset = 1
 | 
			
		||||
		[ default = 0, (help) = "offset to apply to track numbers on side 1" ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message Apple2EncoderProto
 | 
			
		||||
{
 | 
			
		||||
@@ -13,4 +16,7 @@ message Apple2EncoderProto
 | 
			
		||||
    /* Apple II disk drives spin at 300rpm. */
 | 
			
		||||
    optional double rotational_period_ms = 2
 | 
			
		||||
        [ default = 200.0, (help) = "rotational period on the real device" ];
 | 
			
		||||
 | 
			
		||||
	optional uint32 side_one_track_offset = 3
 | 
			
		||||
		[ default = 0, (help) = "offset to apply to track numbers on side 1" ];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@
 | 
			
		||||
#include "decoders/decoders.h"
 | 
			
		||||
#include "sector.h"
 | 
			
		||||
#include "apple2.h"
 | 
			
		||||
#include "arch/apple2/apple2.pb.h"
 | 
			
		||||
#include "lib/decoders/decoders.pb.h"
 | 
			
		||||
#include "bytes.h"
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@@ -12,22 +14,25 @@
 | 
			
		||||
 | 
			
		||||
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 });
 | 
			
		||||
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
 | 
			
		||||
#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
 | 
			
		||||
/* 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)
 | 
			
		||||
{
 | 
			
		||||
@@ -47,9 +52,11 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
 | 
			
		||||
        {
 | 
			
		||||
            /* 3 * 2 bit */
 | 
			
		||||
            output[i + 0] = ((checksum >> 1) & 0x01) | ((checksum << 1) & 0x02);
 | 
			
		||||
            output[i + 86] = ((checksum >> 3) & 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);
 | 
			
		||||
                output[i + 172] =
 | 
			
		||||
                    ((checksum >> 5) & 0x01) | ((checksum >> 3) & 0x02);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -67,88 +74,102 @@ static uint8_t combine(uint16_t word)
 | 
			
		||||
class Apple2Decoder : public Decoder
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	Apple2Decoder(const DecoderProto& config):
 | 
			
		||||
		Decoder(config)
 | 
			
		||||
	{}
 | 
			
		||||
    Apple2Decoder(const DecoderProto& config): Decoder(config) {}
 | 
			
		||||
 | 
			
		||||
    nanoseconds_t advanceToNextRecord() override
 | 
			
		||||
	{
 | 
			
		||||
		return seekToPattern(ANY_RECORD_PATTERN);
 | 
			
		||||
	}
 | 
			
		||||
    {
 | 
			
		||||
        return seekToPattern(ANY_RECORD_PATTERN);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void decodeSectorRecord() override
 | 
			
		||||
	{
 | 
			
		||||
		if (readRaw24() != APPLE2_SECTOR_RECORD)
 | 
			
		||||
			return;
 | 
			
		||||
    {
 | 
			
		||||
        if (readRaw24() != APPLE2_SECTOR_RECORD)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
		/* Read header. */
 | 
			
		||||
        /* Read header. */
 | 
			
		||||
 | 
			
		||||
		auto header = toBytes(readRawBits(8*8)).slice(0, 8);
 | 
			
		||||
		ByteReader br(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());
 | 
			
		||||
        uint8_t volume = combine(br.read_be16());
 | 
			
		||||
        _sector->logicalTrack = combine(br.read_be16());
 | 
			
		||||
        _sector->logicalSide = _sector->physicalSide;
 | 
			
		||||
        _sector->logicalSector = combine(br.read_be16());
 | 
			
		||||
        uint8_t checksum = combine(br.read_be16());
 | 
			
		||||
 | 
			
		||||
		// If the checksum is correct, upgrade the sector from MISSING
 | 
			
		||||
		// to DATA_MISSING in anticipation of its data record
 | 
			
		||||
		if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
 | 
			
		||||
			_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
 | 
			
		||||
	}
 | 
			
		||||
        // If the checksum is correct, upgrade the sector from MISSING
 | 
			
		||||
        // to DATA_MISSING in anticipation of its data record
 | 
			
		||||
        if (checksum ==
 | 
			
		||||
            (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
 | 
			
		||||
            _sector->status =
 | 
			
		||||
                Sector::DATA_MISSING; /* unintuitive but correct */
 | 
			
		||||
 | 
			
		||||
        if (_sector->logicalSide == 1)
 | 
			
		||||
            _sector->logicalTrack -= _config.apple2().side_one_track_offset();
 | 
			
		||||
 | 
			
		||||
        /* Sanity check. */
 | 
			
		||||
 | 
			
		||||
        if (_sector->logicalTrack > 100)
 | 
			
		||||
        {
 | 
			
		||||
            _sector->status = Sector::MISSING;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void decodeDataRecord() override
 | 
			
		||||
	{
 | 
			
		||||
		/* Check ID. */
 | 
			
		||||
    {
 | 
			
		||||
        /* Check ID. */
 | 
			
		||||
 | 
			
		||||
		if (readRaw24() != APPLE2_DATA_RECORD)
 | 
			
		||||
			return;
 | 
			
		||||
        if (readRaw24() != APPLE2_DATA_RECORD)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
		// Sometimes there's a 1-bit gap between APPLE2_DATA_RECORD and
 | 
			
		||||
		// the data itself.  This has been seen on real world disks
 | 
			
		||||
		// such as the Apple II Operating System Kit from Apple2Online.
 | 
			
		||||
		// However, I haven't seen it described in any of the various
 | 
			
		||||
		// references.
 | 
			
		||||
		//
 | 
			
		||||
		// This extra '0' bit would not affect the real disk interface,
 | 
			
		||||
		// as it was a '1' reaching the top bit of a shift register
 | 
			
		||||
		// that triggered a byte to be available, but it affects the
 | 
			
		||||
		// way the data is read here.
 | 
			
		||||
		//
 | 
			
		||||
		// While the floppies tested only seemed to need this applied
 | 
			
		||||
		// to the first byte of the data record, applying it
 | 
			
		||||
		// consistently to all of them doesn't seem to hurt, and
 | 
			
		||||
		// simplifies the code.
 | 
			
		||||
        // Sometimes there's a 1-bit gap between APPLE2_DATA_RECORD and
 | 
			
		||||
        // the data itself.  This has been seen on real world disks
 | 
			
		||||
        // such as the Apple II Operating System Kit from Apple2Online.
 | 
			
		||||
        // However, I haven't seen it described in any of the various
 | 
			
		||||
        // references.
 | 
			
		||||
        //
 | 
			
		||||
        // This extra '0' bit would not affect the real disk interface,
 | 
			
		||||
        // as it was a '1' reaching the top bit of a shift register
 | 
			
		||||
        // that triggered a byte to be available, but it affects the
 | 
			
		||||
        // way the data is read here.
 | 
			
		||||
        //
 | 
			
		||||
        // While the floppies tested only seemed to need this applied
 | 
			
		||||
        // to the first byte of the data record, applying it
 | 
			
		||||
        // consistently to all of them doesn't seem to hurt, and
 | 
			
		||||
        // simplifies the code.
 | 
			
		||||
 | 
			
		||||
		/* Read and decode data. */
 | 
			
		||||
        /* Read and decode data. */
 | 
			
		||||
 | 
			
		||||
		auto readApple8 = [&]() {
 | 
			
		||||
		    auto result = 0;
 | 
			
		||||
		    while((result & 0x80) == 0) {
 | 
			
		||||
			auto b = readRawBits(1);
 | 
			
		||||
                        if(b.empty()) break;
 | 
			
		||||
			result = (result << 1) | b[0];
 | 
			
		||||
		    }
 | 
			
		||||
		    return result;
 | 
			
		||||
		};
 | 
			
		||||
        auto readApple8 = [&]()
 | 
			
		||||
        {
 | 
			
		||||
            auto result = 0;
 | 
			
		||||
            while ((result & 0x80) == 0)
 | 
			
		||||
            {
 | 
			
		||||
                auto b = readRawBits(1);
 | 
			
		||||
                if (b.empty())
 | 
			
		||||
                    break;
 | 
			
		||||
                result = (result << 1) | b[0];
 | 
			
		||||
            }
 | 
			
		||||
            return result;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
		constexpr unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH+2;
 | 
			
		||||
                uint8_t bytes[recordLength];
 | 
			
		||||
                for(auto &byte : bytes) {
 | 
			
		||||
                    byte = readApple8();
 | 
			
		||||
                }
 | 
			
		||||
        constexpr unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
 | 
			
		||||
        uint8_t bytes[recordLength];
 | 
			
		||||
        for (auto& byte : bytes)
 | 
			
		||||
        {
 | 
			
		||||
            byte = readApple8();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		// Upgrade the sector from MISSING to BAD_CHECKSUM.
 | 
			
		||||
		// If decode_crazy_data succeeds, it upgrades the sector to
 | 
			
		||||
		// OK.
 | 
			
		||||
		_sector->status = Sector::BAD_CHECKSUM;
 | 
			
		||||
		_sector->data = decode_crazy_data(&bytes[0], _sector->status);
 | 
			
		||||
	}
 | 
			
		||||
        // Upgrade the sector from MISSING to BAD_CHECKSUM.
 | 
			
		||||
        // If decode_crazy_data succeeds, it upgrades the sector to
 | 
			
		||||
        // OK.
 | 
			
		||||
        _sector->status = Sector::BAD_CHECKSUM;
 | 
			
		||||
        _sector->data = decode_crazy_data(&bytes[0], _sector->status);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Decoder> createApple2Decoder(const DecoderProto& config)
 | 
			
		||||
{
 | 
			
		||||
	return std::unique_ptr<Decoder>(new Apple2Decoder(config));
 | 
			
		||||
    return std::unique_ptr<Decoder>(new Apple2Decoder(config));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
comment: 'Apple II 140kB DOS 3.3 5.25" 40 track SSSD'
 | 
			
		||||
include: '_apple2'
 | 
			
		||||
 | 
			
		||||
image_reader {
 | 
			
		||||
	filename: "apple2.img"
 | 
			
		||||
@@ -22,94 +23,5 @@ image_writer {
 | 
			
		||||
	type: IMG
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
decoder {
 | 
			
		||||
	apple2 {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
encoder {
 | 
			
		||||
	apple2 {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tpi: 48
 | 
			
		||||
 | 
			
		||||
option {
 | 
			
		||||
	name: "appledos"
 | 
			
		||||
	comment: "specifies AppleDOS soft sector skew for filesystem access and images"
 | 
			
		||||
	message: "compensating for AppleDOS soft sector skew"
 | 
			
		||||
		
 | 
			
		||||
	config {
 | 
			
		||||
		image_reader {
 | 
			
		||||
			filesystem_sector_order: true
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		image_writer {
 | 
			
		||||
			filesystem_sector_order: true
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
		layout {
 | 
			
		||||
			layoutdata {
 | 
			
		||||
				filesystem {
 | 
			
		||||
					sector: 0
 | 
			
		||||
					sector: 14
 | 
			
		||||
					sector: 13
 | 
			
		||||
					sector: 12
 | 
			
		||||
					sector: 11
 | 
			
		||||
					sector: 10
 | 
			
		||||
					sector:  9
 | 
			
		||||
					sector:  8
 | 
			
		||||
					sector:  7
 | 
			
		||||
					sector:  6
 | 
			
		||||
					sector:  5
 | 
			
		||||
					sector:  4
 | 
			
		||||
					sector:  3
 | 
			
		||||
					sector:  2
 | 
			
		||||
					sector:  1
 | 
			
		||||
					sector: 15
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
option {
 | 
			
		||||
	name: "prodos"
 | 
			
		||||
	comment: "specifies ProDOS soft sector skew for filesystem access and images"
 | 
			
		||||
	message: "compensating for ProDOS soft sector skew"
 | 
			
		||||
		
 | 
			
		||||
	config {
 | 
			
		||||
		image_reader {
 | 
			
		||||
			filesystem_sector_order: true
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		image_writer {
 | 
			
		||||
			filesystem_sector_order: true
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
		filesystem {
 | 
			
		||||
			type: PRODOS
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		layout {
 | 
			
		||||
			layoutdata {
 | 
			
		||||
				filesystem {
 | 
			
		||||
					sector:  0
 | 
			
		||||
					sector:  2
 | 
			
		||||
					sector:  4
 | 
			
		||||
					sector:  6
 | 
			
		||||
					sector:  8
 | 
			
		||||
					sector: 10
 | 
			
		||||
					sector: 12
 | 
			
		||||
					sector: 14
 | 
			
		||||
					sector:  1
 | 
			
		||||
					sector:  3
 | 
			
		||||
					sector:  5
 | 
			
		||||
					sector:  7
 | 
			
		||||
					sector:  9
 | 
			
		||||
					sector: 11
 | 
			
		||||
					sector: 13
 | 
			
		||||
					sector: 15
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
FORMATS = \
 | 
			
		||||
	_acornadfs8 \
 | 
			
		||||
	_acornadfs32 \
 | 
			
		||||
	_apple2 \
 | 
			
		||||
	_atari \
 | 
			
		||||
	_micropolis \
 | 
			
		||||
	_northstar \
 | 
			
		||||
@@ -27,6 +28,7 @@ FORMATS = \
 | 
			
		||||
	atarist740 \
 | 
			
		||||
	atarist800 \
 | 
			
		||||
	atarist820 \
 | 
			
		||||
	basis640 \
 | 
			
		||||
	bk800 \
 | 
			
		||||
	brother120 \
 | 
			
		||||
	brother240 \
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user