1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 03:32:32 +00:00

Makes first attempt at 6 and 2 decoder.

This commit is contained in:
Thomas Harte 2018-05-04 23:11:12 -04:00
parent 1c0b5bb02b
commit c36d7b4972
9 changed files with 275 additions and 42 deletions

View File

@ -9,14 +9,24 @@
#include "StaticAnalyser.hpp"
#include "../AppleII/Target.hpp"
#include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
#include "../../../Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
// This analyser can comprehend disks only.
if(media.disks.empty()) return {};
// Grab track 0, sector 0.
auto track_zero = media.disks.front()->get_track_at_position(Storage::Disk::Track::Address(0, 0));
auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000)));
using Target = Analyser::Static::AppleII::Target;
auto target = std::unique_ptr<Target>(new Target);
target->machine = Machine::AppleII;
target->media = media;
if(!target->media.disks.empty())
target->disk_controller = Target::DiskController::SixteenSector;
target->disk_controller = Target::DiskController::SixteenSector;
TargetList targets;
targets.push_back(std::move(target));

View File

@ -644,8 +644,8 @@
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; };
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; };
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; };
4BF437EE209D0F7E008CBD6B /* TrackParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* TrackParser.cpp */; };
4BF437EF209D0F7E008CBD6B /* TrackParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* TrackParser.cpp */; };
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; };
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; };
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; };
@ -1422,8 +1422,9 @@
4BEF6AA81D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigitalPhaseLockedLoopBridge.h; sourceTree = "<group>"; };
4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DigitalPhaseLockedLoopBridge.mm; sourceTree = "<group>"; };
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPLLTests.swift; sourceTree = "<group>"; };
4BF437EC209D0F7E008CBD6B /* TrackParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TrackParser.cpp; sourceTree = "<group>"; };
4BF437ED209D0F7E008CBD6B /* TrackParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackParser.hpp; sourceTree = "<group>"; };
4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentParser.cpp; sourceTree = "<group>"; };
4BF437ED209D0F7E008CBD6B /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = "<group>"; };
4BF437F0209D112F008CBD6B /* Sector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sector.hpp; sourceTree = "<group>"; };
4BF4A2D91F534DB300B171F4 /* TargetPlatforms.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TargetPlatforms.hpp; sourceTree = "<group>"; };
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClockReceiver.hpp; sourceTree = "<group>"; };
4BF8295F1D8F3C87001BAE39 /* CRC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRC.hpp; path = ../../NumberTheory/CRC.hpp; sourceTree = "<group>"; };
@ -3014,9 +3015,10 @@
isa = PBXGroup;
children = (
4BD67DCE209BF27B00AB2146 /* Encoder.cpp */,
4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */,
4BD67DCF209BF27B00AB2146 /* Encoder.hpp */,
4BF437EC209D0F7E008CBD6B /* TrackParser.cpp */,
4BF437ED209D0F7E008CBD6B /* TrackParser.hpp */,
4BF437F0209D112F008CBD6B /* Sector.hpp */,
4BF437ED209D0F7E008CBD6B /* SegmentParser.hpp */,
);
name = AppleGCR;
path = Encodings/AppleGCR;
@ -3629,7 +3631,7 @@
4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */,
4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */,
4B1B88BC202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */,
4BF437EF209D0F7E008CBD6B /* TrackParser.cpp in Sources */,
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
4B055AD11FAE9B030060FFFF /* Video.cpp in Sources */,
4B055AA21FAE85DA0060FFFF /* SSD.cpp in Sources */,
4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */,
@ -3872,7 +3874,7 @@
4B54C0C81F8D91E50050900F /* Keyboard.cpp in Sources */,
4B79A5011FC913C900EEDAD5 /* MSX.cpp in Sources */,
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
4BF437EE209D0F7E008CBD6B /* TrackParser.cpp in Sources */,
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */,
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */,

View File

@ -139,24 +139,24 @@ Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) {
// and combined copies of the bottom two bits of the sector
// contents; the 256 bytes afterwards are the remaining
// six bits.
const uint8_t bit_shuffle[] = {0, 2, 1, 3};
const uint8_t bit_reverse[] = {0, 2, 1, 3};
for(std::size_t c = 0; c < 84; ++c) {
segment.data[3 + c] =
static_cast<uint8_t>(
bit_shuffle[source[c]&3] |
(bit_shuffle[source[c + 86]&3] << 2) |
(bit_shuffle[source[c + 172]&3] << 4)
bit_reverse[source[c]&3] |
(bit_reverse[source[c + 86]&3] << 2) |
(bit_reverse[source[c + 172]&3] << 4)
);
}
segment.data[87] =
static_cast<uint8_t>(
(bit_shuffle[source[84]&3] << 0) |
(bit_shuffle[source[170]&3] << 2)
(bit_reverse[source[84]&3] << 0) |
(bit_reverse[source[170]&3] << 2)
);
segment.data[88] =
static_cast<uint8_t>(
(bit_shuffle[source[85]&3] << 0) |
(bit_shuffle[source[171]&3] << 2)
(bit_reverse[source[85]&3] << 0) |
(bit_reverse[source[171]&3] << 2)
);
for(std::size_t c = 0; c < 256; ++c) {

View File

@ -0,0 +1,47 @@
//
// Sector.hpp
// Clock Signal
//
// Created by Thomas Harte on 04/05/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#ifndef Sector_h
#define Sector_h
#include <cstdint>
#include <vector>
namespace Storage {
namespace Encodings {
namespace AppleGCR {
struct Sector {
/*!
Describes the location of a sector, implementing < to allow for use as a set key.
*/
struct Address {
uint_fast8_t volume = 0, track = 0, sector = 0;
bool operator < (const Address &rhs) const {
return ((volume << 16) | (track << 8) | sector) < ((rhs.volume << 16) | (rhs.track << 8) | rhs.sector);
}
};
Address address;
std::vector<uint8_t> data;
bool has_data_checksum_error = false;
bool has_header_checksum_error = false;
enum class Encoding {
FiveAndThree, SixAndTwo
};
Encoding encoding = Encoding::SixAndTwo;
};
}
}
}
#endif /* Sector_h */

View File

@ -0,0 +1,166 @@
//
// SegmentParser.cpp
// Clock Signal
//
// Created by Thomas Harte on 04/05/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#include "SegmentParser.hpp"
#include "Encoder.hpp"
#include <array>
namespace {
const uint8_t six_and_two_unmapping[] = {
0x00, 0x01, 0xff, 0xff,
0x02, 0x03, 0xff, 0x04, 0x05, 0x06, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x07, 0x08, 0xff, 0xff,
0xff, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0xff, 0xff,
0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0xff, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1b, 0xff, 0x1c, 0x1d, 0x1e, 0xff, 0xff,
0xff, 0x1f, 0xff, 0xff, 0x20, 0x21, 0xff, 0x22,
0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0xff, 0xff,
0xff, 0xff, 0xff, 0x29, 0x2a, 0x2b, 0xff, 0x2c,
0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0xff, 0xff,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0xff, 0x39,
0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0xff, 0xff
};
}
using namespace Storage::Encodings::AppleGCR;
std::map<std::size_t, Sector> Storage::Encodings::AppleGCR::sectors_from_segment(const Disk::PCMSegment &&segment) {
std::map<std::size_t, Sector> result;
uint_fast8_t shift_register = 0;
const std::size_t scanning_sentinel = std::numeric_limits<std::size_t>::max();
std::unique_ptr<Sector> new_sector;
std::size_t pointer = scanning_sentinel;
std::array<uint_fast8_t, 8> header{{0, 0, 0, 0, 0, 0, 0, 0}};
std::array<uint_fast8_t, 3> scanner;
for(unsigned int bit = 0; bit < segment.number_of_bits; ++bit) {
// Apple GCR parsing: bytes always have the top bit set.
shift_register = static_cast<uint_fast8_t>((shift_register << 1) | segment.bit(bit));
if(!(shift_register&0x80)) continue;
// Grab the byte.
const uint_fast8_t value = shift_register;
shift_register = 0;
if(pointer == scanning_sentinel) {
scanner[0] = scanner[1];
scanner[1] = scanner[2];
scanner[2] = value;
if(
scanner[0] == header_prologue[0] &&
scanner[1] == header_prologue[1] &&
(
scanner[2] == header_prologue[2] ||
scanner[2] == data_prologue[2]
)) {
pointer = 0;
// If this is the start of a data section, and at least
// one header has been witnessed, start a sector.
if(scanner[2] == data_prologue[2]) {
new_sector.reset(new Sector);
new_sector->data.reserve(412);
}
}
} else {
if(new_sector) {
// If this is an epilogue, make sense of this whole sector;
// otherwise just keep the byte for later.
if(value == epilogue[1]) {
std::unique_ptr<Sector> sector = std::move(new_sector);
new_sector.reset();
pointer = scanning_sentinel;
// Check for an expected size; the first byte of the epilogue has been stored, so
// these numbers are one bigger than usual.
if(sector->data.size() != 412 && sector->data.size() != 344) continue;
sector->data.resize(sector->data.size() - 1);
// Check for apparent four and four encoding.
uint_fast8_t header_mask = 0xff;
for(auto c : header) header_mask &= c;
header_mask &= 0xaa;
if(header_mask != 0xaa) continue;
sector->address.volume = ((header[0] << 1) | 1) & header[1];
sector->address.track = ((header[2] << 1) | 1) & header[3];
sector->address.sector = ((header[4] << 1) | 1) & header[5];
// Check the header checksum.
uint_fast8_t checksum = ((header[6] << 1) | 1) & header[7];
if(checksum != (sector->address.volume^sector->address.track^sector->address.sector)) continue;
if(sector->data.size() == 343) {
// Unmap the sector contents as 6 and 2 data.
bool out_of_bounds = false;
for(auto &c : sector->data) {
if(c < 0x96 || six_and_two_unmapping[c - 0x96] == 0xff) {
out_of_bounds = true;
break;
}
c = six_and_two_unmapping[c - 0x96];
}
if(out_of_bounds) continue;
// Undo the XOR step on sector contents and check that checksum.
for(std::size_t c = 1; c < sector->data.size(); ++c) {
sector->data[c] ^= sector->data[c-1];
}
if(sector->data.back()) continue;
// Having checked the checksum, remove it.
sector->data.resize(sector->data.size() - 1);
// Undo the 6 and 2 mapping.
const uint8_t bit_reverse[] = {0, 2, 1, 3};
#define unmap(byte, nibble, shift) \
sector->data[86 + byte] = static_cast<uint8_t>(\
(sector->data[86 + byte] << 2) | bit_reverse[(sector->data[nibble] >> shift)&3]);
for(std::size_t c = 0; c < 84; ++c) {
unmap(c, c, 0);
unmap(c+86, c, 2);
unmap(c+172, c, 4);
}
unmap(84, 84, 0);
unmap(170, 84, 2);
unmap(85, 85, 0);
unmap(171, 85, 2);
#undef unmap
}
// Throw away the collection of two-bit chunks.
sector->data.erase(sector->data.begin(), sector->data.end() - 256);
} else {
new_sector->data.push_back(value);
}
} else {
// Just capture the header in place; it'll be decoded
// once a whole sector has been read.
header[pointer] = value;
++pointer;
if(pointer == 8) {
pointer = scanning_sentinel;
}
}
}
}
return result;
}

View File

@ -0,0 +1,30 @@
//
// SegmentParser.hpp
// Clock Signal
//
// Created by Thomas Harte on 04/05/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#ifndef TrackParser_hpp
#define TrackParser_hpp
#include "Sector.hpp"
#include "../../Track/PCMSegment.hpp"
#include <map>
namespace Storage {
namespace Encodings {
namespace AppleGCR {
/*!
Scans @c segment for all included sectors, returning a set that maps from location within
the segment (counted in bits from the beginning) to sector.
*/
std::map<std::size_t, Sector> sectors_from_segment(const Disk::PCMSegment &&segment);
}
}
}
#endif /* TrackParser_hpp */

View File

@ -1,9 +0,0 @@
//
// TrackParser.cpp
// Clock Signal
//
// Created by Thomas Harte on 04/05/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#include "TrackParser.hpp"

View File

@ -1,14 +0,0 @@
//
// TrackParser.hpp
// Clock Signal
//
// Created by Thomas Harte on 04/05/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#ifndef TrackParser_hpp
#define TrackParser_hpp
#include <stdio.h>
#endif /* TrackParser_hpp */

View File

@ -19,7 +19,8 @@ namespace MFM {
/*!
Scans @c segment for all included sectors, returning a set that maps from location within
the segment (counted in bits from the beginning) to sector.
the segment (counted in bits from the beginning and pointing to the location the disk
had reached upon detection of the ID mark) to sector.
*/
std::map<std::size_t, Sector> sectors_from_segment(const Disk::PCMSegment &&segment, bool is_double_density);