mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Makes first attempt at 6 and 2 decoder.
This commit is contained in:
parent
1c0b5bb02b
commit
c36d7b4972
@ -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));
|
||||
|
@ -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 */,
|
||||
|
@ -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) {
|
||||
|
47
Storage/Disk/Encodings/AppleGCR/Sector.hpp
Normal file
47
Storage/Disk/Encodings/AppleGCR/Sector.hpp
Normal 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 */
|
166
Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp
Normal file
166
Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp
Normal 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;
|
||||
}
|
30
Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp
Normal file
30
Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp
Normal 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 */
|
@ -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"
|
@ -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 */
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user