mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-12 15:31:09 +00:00
Merge pull request #422 from TomHarte/DiskIIAnalyser
Introduces an analyser for Disk II-esque files.
This commit is contained in:
commit
9ff34d90f4
@ -16,7 +16,7 @@ MultiConfigurable::MultiConfigurable(const std::vector<std::unique_ptr<::Machine
|
||||
for(const auto &machine: machines) {
|
||||
Configurable::Device *device = machine->configurable_device();
|
||||
if(device) devices_.push_back(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> MultiConfigurable::get_options() {
|
||||
|
@ -14,7 +14,7 @@ MultiConfigurationTarget::MultiConfigurationTarget(const std::vector<std::unique
|
||||
for(const auto &machine: machines) {
|
||||
ConfigurationTarget::Machine *configuration_target = machine->configuration_target();
|
||||
if(configuration_target) targets_.push_back(configuration_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiConfigurationTarget::configure_as_target(const Analyser::Static::Target *target) {
|
||||
|
@ -66,7 +66,7 @@ MultiJoystickMachine::MultiJoystickMachine(const std::vector<std::unique_ptr<::M
|
||||
joystick_machines.push_back(joystick_machine);
|
||||
total_joysticks = std::max(total_joysticks, joystick_machine->get_joysticks().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(std::size_t index = 0; index < total_joysticks; ++index) {
|
||||
joysticks_.emplace_back(new MultiJoystick(joystick_machines, index));
|
||||
|
@ -14,30 +14,30 @@ MultiKeyboardMachine::MultiKeyboardMachine(const std::vector<std::unique_ptr<::M
|
||||
for(const auto &machine: machines) {
|
||||
KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine();
|
||||
if(keyboard_machine) machines_.push_back(keyboard_machine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::clear_all_keys() {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->clear_all_keys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::set_key_state(uint16_t key, bool is_pressed) {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->set_key_state(key, is_pressed);
|
||||
}
|
||||
for(const auto &machine: machines_) {
|
||||
machine->set_key_state(key, is_pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::type_string(const std::string &string) {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->type_string(string);
|
||||
}
|
||||
for(const auto &machine: machines_) {
|
||||
machine->type_string(string);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
|
||||
for(const auto &machine: machines_) {
|
||||
for(const auto &machine: machines_) {
|
||||
uint16_t mapped_key = machine->get_keyboard_mapper()->mapped_key_for_key(key);
|
||||
if(mapped_key != KeyNotMapped) machine->set_key_state(mapped_key, is_pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,11 +82,11 @@ void MultiMachine::multi_crt_did_run_machines() {
|
||||
|
||||
DynamicMachine *front = machines_.front().get();
|
||||
std::stable_sort(machines_.begin(), machines_.end(),
|
||||
[] (const std::unique_ptr<DynamicMachine> &lhs, const std::unique_ptr<DynamicMachine> &rhs){
|
||||
CRTMachine::Machine *lhs_crt = lhs->crt_machine();
|
||||
CRTMachine::Machine *rhs_crt = rhs->crt_machine();
|
||||
return lhs_crt->get_confidence() > rhs_crt->get_confidence();
|
||||
});
|
||||
[] (const std::unique_ptr<DynamicMachine> &lhs, const std::unique_ptr<DynamicMachine> &rhs){
|
||||
CRTMachine::Machine *lhs_crt = lhs->crt_machine();
|
||||
CRTMachine::Machine *rhs_crt = rhs->crt_machine();
|
||||
return lhs_crt->get_confidence() > rhs_crt->get_confidence();
|
||||
});
|
||||
|
||||
if(machines_.front().get() != front) {
|
||||
crt_machine_.did_change_machine_order();
|
||||
|
@ -35,8 +35,8 @@ static bool is_implied_extension(const std::string &extension) {
|
||||
|
||||
static void right_trim(std::string &string) {
|
||||
string.erase(std::find_if(string.rbegin(), string.rend(), [](int ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), string.end());
|
||||
return !std::isspace(ch);
|
||||
}).base(), string.end());
|
||||
}
|
||||
|
||||
static std::string RunCommandFor(const Storage::Disk::CPM::File &file) {
|
||||
|
123
Analyser/Static/DiskII/StaticAnalyser.cpp
Normal file
123
Analyser/Static/DiskII/StaticAnalyser.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
//
|
||||
// StaticAnalyser.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "../AppleII/Target.hpp"
|
||||
#include "../Oric/Target.hpp"
|
||||
#include "../Disassembler/6502.hpp"
|
||||
#include "../Disassembler/AddressMapper.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
Analyser::Static::Target *AppleTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::AppleII;
|
||||
|
||||
if(sector_zero && sector_zero->encoding == Storage::Encodings::AppleGCR::Sector::Encoding::FiveAndThree) {
|
||||
target->disk_controller = Target::DiskController::ThirteenSector;
|
||||
} else {
|
||||
target->disk_controller = Target::DiskController::SixteenSector;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
Analyser::Static::Target *OricTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
|
||||
using Target = Analyser::Static::Oric::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::Oric;
|
||||
|
||||
// TODO: configure the Oric as a Pravetz 8D with 8DOS.
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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: the boot sector.
|
||||
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)));
|
||||
|
||||
const Storage::Encodings::AppleGCR::Sector *sector_zero = nullptr;
|
||||
for(const auto &pair: sector_map) {
|
||||
if(!pair.second.address.sector) {
|
||||
sector_zero = &pair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no boot sector then if there are also no sectors at all,
|
||||
// decline to nominate a machine. Otherwise go with an Apple as the default.
|
||||
TargetList targets;
|
||||
if(!sector_zero) {
|
||||
if(sector_map.empty()) {
|
||||
return targets;
|
||||
} else {
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleTarget(nullptr)));
|
||||
return targets;
|
||||
}
|
||||
}
|
||||
|
||||
// If the boot sector looks like it's intended for the Oric, create an Oric.
|
||||
// Otherwise go with the Apple II.
|
||||
|
||||
auto disassembly = Analyser::Static::MOS6502::Disassemble(sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800});
|
||||
|
||||
bool did_read_shift_register = false;
|
||||
bool is_oric = false;
|
||||
|
||||
// Look for a tight BPL loop reading the Oric's shift register address of 0x31c. The Apple II just has RAM there,
|
||||
// so the probability of such a loop is infinitesimal.
|
||||
for(const auto &instruction: disassembly.instructions_by_address) {
|
||||
// Is this a read of the shift register?
|
||||
if(
|
||||
(
|
||||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDA) ||
|
||||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDX) ||
|
||||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDY)
|
||||
) &&
|
||||
instruction.second.addressing_mode == Analyser::Static::MOS6502::Instruction::Absolute &&
|
||||
instruction.second.address == 0x031c) {
|
||||
did_read_shift_register = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(did_read_shift_register) {
|
||||
if(
|
||||
instruction.second.operation == Analyser::Static::MOS6502::Instruction::BPL &&
|
||||
instruction.second.address == 0xfb) {
|
||||
is_oric = true;
|
||||
break;
|
||||
}
|
||||
|
||||
did_read_shift_register = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check also for calls into the 0x3xx page above 0x320, as that's where the Oric's boot ROM is.
|
||||
for(const auto address: disassembly.outward_calls) {
|
||||
is_oric |= address >= 0x320 && address < 0x400;
|
||||
}
|
||||
|
||||
if(is_oric) {
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(OricTarget(sector_zero)));
|
||||
} else {
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleTarget(sector_zero)));
|
||||
}
|
||||
return targets;
|
||||
}
|
27
Analyser/Static/DiskII/StaticAnalyser.hpp
Normal file
27
Analyser/Static/DiskII/StaticAnalyser.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// StaticAnalyser.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/05/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_DiskII_StaticAnalyser_hpp
|
||||
#define Analyser_Static_DiskII_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace DiskII {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* Analyser_Static_DiskII_StaticAnalyser_hpp */
|
@ -20,6 +20,7 @@
|
||||
#include "Atari/StaticAnalyser.hpp"
|
||||
#include "Coleco/StaticAnalyser.hpp"
|
||||
#include "Commodore/StaticAnalyser.hpp"
|
||||
#include "DiskII/StaticAnalyser.hpp"
|
||||
#include "MSX/StaticAnalyser.hpp"
|
||||
#include "Oric/StaticAnalyser.hpp"
|
||||
#include "ZX8081/StaticAnalyser.hpp"
|
||||
@ -94,10 +95,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
|
||||
Format("d64", result.disks, Disk::DiskImageHolder<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
|
||||
Format("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
|
||||
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::AppleII) // DO
|
||||
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DO
|
||||
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::AppleII) // DSK (Apple)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MSXDSK>, TargetPlatform::MSX) // DSK (MSX)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
|
||||
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
|
||||
@ -106,10 +107,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Disk::DiskImageHolder<Storage::Disk::HFE>,
|
||||
TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric)
|
||||
// HFE (TODO: switch to AllDisk once the MSX stops being so greedy)
|
||||
Format("nib", result.disks, Disk::DiskImageHolder<Storage::Disk::NIB>, TargetPlatform::AppleII) // NIB
|
||||
Format("nib", result.disks, Disk::DiskImageHolder<Storage::Disk::NIB>, TargetPlatform::DiskII) // NIB
|
||||
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
|
||||
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
|
||||
Format("po", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::AppleII) // PO
|
||||
Format("po", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // PO
|
||||
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
|
||||
|
||||
// PRG
|
||||
@ -134,7 +135,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Format("tsx", result.tapes, Tape::TZX, TargetPlatform::MSX) // TSX
|
||||
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX
|
||||
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
|
||||
Format("woz", result.disks, Disk::DiskImageHolder<Storage::Disk::WOZ>, TargetPlatform::AppleII) // WOZ
|
||||
Format("woz", result.disks, Disk::DiskImageHolder<Storage::Disk::WOZ>, TargetPlatform::DiskII) // WOZ
|
||||
|
||||
#undef Format
|
||||
#undef Insert
|
||||
@ -168,6 +169,7 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
if(potential_platforms & TargetPlatform::Atari2600) Append(Atari);
|
||||
if(potential_platforms & TargetPlatform::ColecoVision) Append(Coleco);
|
||||
if(potential_platforms & TargetPlatform::Commodore) Append(Commodore);
|
||||
if(potential_platforms & TargetPlatform::DiskII) Append(DiskII);
|
||||
if(potential_platforms & TargetPlatform::MSX) Append(MSX);
|
||||
if(potential_platforms & TargetPlatform::Oric) Append(Oric);
|
||||
if(potential_platforms & TargetPlatform::ZX8081) Append(ZX8081);
|
||||
@ -183,9 +185,9 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
// Sort by initial confidence. Use a stable sort in case any of the machine-specific analysers
|
||||
// picked their insertion order carefully.
|
||||
std::stable_sort(targets.begin(), targets.end(),
|
||||
[] (const std::unique_ptr<Target> &a, const std::unique_ptr<Target> &b) {
|
||||
return a->confidence > b->confidence;
|
||||
});
|
||||
[] (const std::unique_ptr<Target> &a, const std::unique_ptr<Target> &b) {
|
||||
return a->confidence > b->confidence;
|
||||
});
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
@ -587,8 +587,6 @@
|
||||
4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; };
|
||||
4BB299F91B587D8400A49093 /* tyan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298ED1B587D8400A49093 /* tyan */; };
|
||||
4BB2A9AF1E13367E001A5C23 /* CRCTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */; };
|
||||
4BB2CB2A208BDDCF00FD192E /* AppleGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */; };
|
||||
4BB2CB2B208BDDCF00FD192E /* AppleGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */; };
|
||||
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; };
|
||||
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */; };
|
||||
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; };
|
||||
@ -626,6 +624,10 @@
|
||||
4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; };
|
||||
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */; };
|
||||
4BD61664206B2AC800236112 /* QuickLoadOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BD61662206B2AC700236112 /* QuickLoadOptions.xib */; };
|
||||
4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; };
|
||||
4BD67DCC209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; };
|
||||
4BD67DD0209BF27B00AB2146 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; };
|
||||
4BD67DD1209BF27B00AB2146 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; };
|
||||
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; };
|
||||
4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */; };
|
||||
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; };
|
||||
@ -642,6 +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 /* 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 */; };
|
||||
@ -1296,8 +1300,6 @@
|
||||
4BB298EC1B587D8400A49093 /* txsn */ = {isa = PBXFileReference; lastKnownFileType = file; path = txsn; sourceTree = "<group>"; };
|
||||
4BB298ED1B587D8400A49093 /* tyan */ = {isa = PBXFileReference; lastKnownFileType = file; path = tyan; sourceTree = "<group>"; };
|
||||
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CRCTests.mm; sourceTree = "<group>"; };
|
||||
4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AppleGCR.cpp; path = Encodings/AppleGCR.cpp; sourceTree = "<group>"; };
|
||||
4BB2CB29208BDDCF00FD192E /* AppleGCR.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = AppleGCR.hpp; path = Encodings/AppleGCR.hpp; sourceTree = "<group>"; };
|
||||
4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.hpp; sourceTree = "<group>"; };
|
||||
4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = "<group>"; };
|
||||
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = "<group>"; };
|
||||
@ -1370,6 +1372,10 @@
|
||||
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
|
||||
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSBestEffortUpdater.mm; path = Updater/CSBestEffortUpdater.mm; sourceTree = "<group>"; };
|
||||
4BD61663206B2AC700236112 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/QuickLoadOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4BD67DC9209BE4D600AB2146 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||
4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
4BD67DCE209BF27B00AB2146 /* Encoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Encoder.cpp; sourceTree = "<group>"; };
|
||||
4BD67DCF209BF27B00AB2146 /* Encoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Encoder.hpp; sourceTree = "<group>"; };
|
||||
4BD9137D1F311BC5009BCF85 /* i8255.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8255.hpp; path = 8255/i8255.hpp; sourceTree = "<group>"; };
|
||||
4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMMachine.hpp; sourceTree = "<group>"; };
|
||||
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MachineCycleTests.swift; sourceTree = "<group>"; };
|
||||
@ -1416,6 +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 /* 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>"; };
|
||||
@ -2239,6 +2248,7 @@
|
||||
4B7A90EA20410A85008514A2 /* Coleco */,
|
||||
4B8944FB201967B4007DE474 /* Commodore */,
|
||||
4B894507201967B4007DE474 /* Disassembler */,
|
||||
4BD67DC8209BE4D600AB2146 /* DiskII */,
|
||||
4B89450F201967B4007DE474 /* MSX */,
|
||||
4B8944F6201967B4007DE474 /* Oric */,
|
||||
4B894504201967B4007DE474 /* ZX8081 */,
|
||||
@ -2675,10 +2685,9 @@
|
||||
4BB697CF1D4BA44900248BDF /* Encodings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */,
|
||||
4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */,
|
||||
4BB2CB29208BDDCF00FD192E /* AppleGCR.hpp */,
|
||||
4BB697CD1D4BA44400248BDF /* CommodoreGCR.hpp */,
|
||||
4BD67DCD209BF27B00AB2146 /* AppleGCR */,
|
||||
4B7136831F78724F008B8ED9 /* MFM */,
|
||||
);
|
||||
name = Encodings;
|
||||
@ -2993,6 +3002,28 @@
|
||||
name = Updater;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD67DC8209BE4D600AB2146 /* DiskII */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BD67DC9209BE4D600AB2146 /* StaticAnalyser.hpp */,
|
||||
4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */,
|
||||
);
|
||||
path = DiskII;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD67DCD209BF27B00AB2146 /* AppleGCR */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BD67DCE209BF27B00AB2146 /* Encoder.cpp */,
|
||||
4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */,
|
||||
4BD67DCF209BF27B00AB2146 /* Encoder.hpp */,
|
||||
4BF437F0209D112F008CBD6B /* Sector.hpp */,
|
||||
4BF437ED209D0F7E008CBD6B /* SegmentParser.hpp */,
|
||||
);
|
||||
name = AppleGCR;
|
||||
path = Encodings/AppleGCR;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BD9137C1F3115AC009BCF85 /* 8255 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3569,6 +3600,7 @@
|
||||
4B055ADA1FAE9B460060FFFF /* 1770.cpp in Sources */,
|
||||
4B055ADC1FAE9B460060FFFF /* AY38910.cpp in Sources */,
|
||||
4B055AD71FAE9B180060FFFF /* Keyboard.cpp in Sources */,
|
||||
4BD67DCC209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */,
|
||||
4B055AB61FAE860F0060FFFF /* TapeUEF.cpp in Sources */,
|
||||
4B055A9D1FAE85DA0060FFFF /* D64.cpp in Sources */,
|
||||
4B055ABB1FAE86170060FFFF /* Oric.cpp in Sources */,
|
||||
@ -3599,6 +3631,7 @@
|
||||
4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */,
|
||||
4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */,
|
||||
4B1B88BC202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */,
|
||||
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
|
||||
4B055AD11FAE9B030060FFFF /* Video.cpp in Sources */,
|
||||
4B055AA21FAE85DA0060FFFF /* SSD.cpp in Sources */,
|
||||
4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */,
|
||||
@ -3639,6 +3672,7 @@
|
||||
4B1B88BD202E3D3D00B67DFF /* MultiMachine.cpp in Sources */,
|
||||
4B055A971FAE85BB0060FFFF /* ZX8081.cpp in Sources */,
|
||||
4B055AAD1FAE85FD0060FFFF /* PCMTrack.cpp in Sources */,
|
||||
4BD67DD1209BF27B00AB2146 /* Encoder.cpp in Sources */,
|
||||
4B055AC61FAE9AEE0060FFFF /* TIASound.cpp in Sources */,
|
||||
4B89451F201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
4B055AA81FAE85EF0060FFFF /* Shifter.cpp in Sources */,
|
||||
@ -3648,7 +3682,6 @@
|
||||
4B894525201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
4B055ACD1FAE9B030060FFFF /* Keyboard.cpp in Sources */,
|
||||
4B055AB21FAE860F0060FFFF /* CommodoreTAP.cpp in Sources */,
|
||||
4BB2CB2B208BDDCF00FD192E /* AppleGCR.cpp in Sources */,
|
||||
4B055ADF1FAE9B4C0060FFFF /* IRQDelegatePortHandler.cpp in Sources */,
|
||||
4B055AB51FAE860F0060FFFF /* TapePRG.cpp in Sources */,
|
||||
4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */,
|
||||
@ -3708,13 +3741,13 @@
|
||||
4B4518A01F75FD1C00926311 /* CPCDSK.cpp in Sources */,
|
||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
||||
4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */,
|
||||
4BB2CB2A208BDDCF00FD192E /* AppleGCR.cpp in Sources */,
|
||||
4B894530201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4B4518A31F75FD1C00926311 /* HFE.cpp in Sources */,
|
||||
4B1B88BB202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */,
|
||||
4B4518A11F75FD1C00926311 /* D64.cpp in Sources */,
|
||||
4B1558C01F844ECD006E9A97 /* BitReverse.cpp in Sources */,
|
||||
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
|
||||
4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */,
|
||||
4B9BE400203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */,
|
||||
4B894538201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
4B54C0CB1F8D92590050900F /* Keyboard.cpp in Sources */,
|
||||
@ -3766,6 +3799,7 @@
|
||||
4B89451C201967B4007DE474 /* Disk.cpp in Sources */,
|
||||
4B302184208A550100773308 /* DiskII.cpp in Sources */,
|
||||
4BEA52631DF339D7007E74F2 /* SoundGenerator.cpp in Sources */,
|
||||
4BD67DD0209BF27B00AB2146 /* Encoder.cpp in Sources */,
|
||||
4BAE495920328897004BE78E /* ZX8081OptionsPanel.swift in Sources */,
|
||||
4B89451A201967B4007DE474 /* ConfidenceSummary.cpp in Sources */,
|
||||
4B54C0C51F8D91D90050900F /* Keyboard.cpp in Sources */,
|
||||
@ -3840,6 +3874,7 @@
|
||||
4B54C0C81F8D91E50050900F /* Keyboard.cpp in Sources */,
|
||||
4B79A5011FC913C900EEDAD5 /* MSX.cpp in Sources */,
|
||||
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
|
||||
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
|
||||
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */,
|
||||
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
|
||||
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */,
|
||||
|
@ -68,7 +68,7 @@
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
|
@ -49,7 +49,7 @@ Gw
|
||||
<action selector="cancelCreateMachine:" target="-2" id="lf8-PM-c0m"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="9YM-5x-pc0">
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9YM-5x-pc0">
|
||||
<rect key="frame" x="20" y="14" width="398" height="34"/>
|
||||
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" id="xTm-Oy-oz5">
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -22,6 +22,7 @@ SOURCES += glob.glob('../../Analyser/Static/Atari/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Coleco/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Commodore/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Disassembler/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/DiskII/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/MSX/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Oric/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/ZX8081/*.cpp')
|
||||
@ -78,6 +79,7 @@ SOURCES += glob.glob('../../Storage/Disk/DiskImage/Formats/*.cpp')
|
||||
SOURCES += glob.glob('../../Storage/Disk/DiskImage/Formats/Utility/*.cpp')
|
||||
SOURCES += glob.glob('../../Storage/Disk/DPLL/*.cpp')
|
||||
SOURCES += glob.glob('../../Storage/Disk/Encodings/*.cpp')
|
||||
SOURCES += glob.glob('../../Storage/Disk/Encodings/AppleGCR/*.cpp')
|
||||
SOURCES += glob.glob('../../Storage/Disk/Encodings/MFM/*.cpp')
|
||||
SOURCES += glob.glob('../../Storage/Disk/Parsers/*.cpp')
|
||||
SOURCES += glob.glob('../../Storage/Disk/Track/*.cpp')
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "AppleDSK.hpp"
|
||||
|
||||
#include "../../Track/PCMTrack.hpp"
|
||||
#include "../../Encodings/AppleGCR.hpp"
|
||||
#include "../../Encodings/AppleGCR/Encoder.hpp"
|
||||
|
||||
using namespace Storage::Disk;
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "NIB.hpp"
|
||||
|
||||
#include "../../Track/PCMTrack.hpp"
|
||||
#include "../../Encodings/AppleGCR.hpp"
|
||||
#include "../../Encodings/AppleGCR/Encoder.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -1,73 +0,0 @@
|
||||
//
|
||||
// AppleGCR.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/04/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef AppleGCR_hpp
|
||||
#define AppleGCR_hpp
|
||||
|
||||
#include <cstdint>
|
||||
#include "../../Disk/Track/PCMSegment.hpp"
|
||||
|
||||
namespace Storage {
|
||||
namespace Encodings {
|
||||
|
||||
namespace AppleGCR {
|
||||
|
||||
/*!
|
||||
@returns the eight-bit 13-sector GCR encoding for the low five bits of @c value.
|
||||
*/
|
||||
// unsigned int five_and_three_encoding_for_value(int value);
|
||||
|
||||
/*!
|
||||
@returns the eight-bit 16-sector GCR encoding for the low six bits of @c value.
|
||||
*/
|
||||
// unsigned int six_and_two_encoding_for_value(int value);
|
||||
|
||||
/*!
|
||||
A block is defined to be five source bytes, which encodes to eight GCR bytes.
|
||||
*/
|
||||
// void encode_five_and_three_block(uint8_t *destination, uint8_t *source);
|
||||
|
||||
/*!
|
||||
A block is defined to be three source bytes, which encodes to four GCR bytes.
|
||||
*/
|
||||
// void encode_six_and_two_block(uint8_t *destination, uint8_t *source);
|
||||
|
||||
/*!
|
||||
@returns the four bit nibble for the five-bit GCR @c quintet if a valid GCR value; INT_MAX otherwise.
|
||||
*/
|
||||
// unsigned int decoding_from_quintet(unsigned int quintet);
|
||||
|
||||
/*!
|
||||
@returns the byte composed by splitting the dectet into two qintets, decoding each and composing the resulting nibbles.
|
||||
*/
|
||||
// unsigned int decoding_from_dectet(unsigned int dectet);
|
||||
|
||||
/// Describes the standard three-byte prologue that begins a header.
|
||||
const uint8_t header_prologue[3] = {0xd5, 0xaa, 0x96};
|
||||
/// Describes the standard three-byte prologue that begins a data section.
|
||||
const uint8_t data_prologue[3] = {0xd5, 0xaa, 0xad};
|
||||
/// Describes the epilogue that ends both data sections and headers.
|
||||
const uint8_t epilogue[3] = {0xde, 0xaa, 0xeb};
|
||||
|
||||
/*!
|
||||
Produces the Apple-standard '4 and 4' per-sector header. This is the same
|
||||
for both the 13- and 16-sector formats, and is 112 bits long.
|
||||
*/
|
||||
Storage::Disk::PCMSegment header(uint8_t volume, uint8_t track, uint8_t sector);
|
||||
|
||||
Storage::Disk::PCMSegment six_and_two_data(const uint8_t *source);
|
||||
Storage::Disk::PCMSegment six_and_two_sync(int length);
|
||||
|
||||
Storage::Disk::PCMSegment five_and_three_data(const uint8_t *source);
|
||||
Storage::Disk::PCMSegment five_and_three_sync(int length);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* AppleGCR_hpp */
|
@ -6,11 +6,11 @@
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "AppleGCR.hpp"
|
||||
#include "Encoder.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
const unsigned int five_and_three_mapping[] = {
|
||||
const uint8_t five_and_three_mapping[] = {
|
||||
0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba,
|
||||
0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb,
|
||||
0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef,
|
||||
@ -53,26 +53,6 @@ Storage::Disk::PCMSegment sync(int length, int bit_size) {
|
||||
|
||||
using namespace Storage::Encodings;
|
||||
|
||||
|
||||
/*void AppleGCR::encode_five_and_three_block(uint8_t *destination, uint8_t *source) {
|
||||
destination[0] = static_cast<uint8_t>(five_and_three_encoding_for_value( source[0] >> 3 ));
|
||||
destination[1] = static_cast<uint8_t>(five_and_three_encoding_for_value( (source[0] << 2) | (source[1] >> 6) ));
|
||||
destination[2] = static_cast<uint8_t>(five_and_three_encoding_for_value( source[1] >> 1 ));
|
||||
destination[3] = static_cast<uint8_t>(five_and_three_encoding_for_value( (source[1] << 4) | (source[2] >> 4) ));
|
||||
destination[4] = static_cast<uint8_t>(five_and_three_encoding_for_value( (source[2] << 1) | (source[3] >> 7) ));
|
||||
destination[5] = static_cast<uint8_t>(five_and_three_encoding_for_value( source[3] >> 2 ));
|
||||
destination[6] = static_cast<uint8_t>(five_and_three_encoding_for_value( (source[3] << 3) | (source[4] >> 5) ));
|
||||
destination[7] = static_cast<uint8_t>(five_and_three_encoding_for_value( source[4] ));
|
||||
}*/
|
||||
|
||||
|
||||
/*void AppleGCR::encode_six_and_two_block(uint8_t *destination, uint8_t *source) {
|
||||
destination[0] = static_cast<uint8_t>(six_and_two_encoding_for_value( source[0] >> 2 ));
|
||||
destination[1] = static_cast<uint8_t>(six_and_two_encoding_for_value( (source[0] << 4) | (source[1] >> 4) ));
|
||||
destination[2] = static_cast<uint8_t>(six_and_two_encoding_for_value( (source[1] << 2) | (source[2] >> 6) ));
|
||||
destination[3] = static_cast<uint8_t>(six_and_two_encoding_for_value( source[2] ));
|
||||
}*/
|
||||
|
||||
Storage::Disk::PCMSegment AppleGCR::six_and_two_sync(int length) {
|
||||
return sync(length, 10);
|
||||
}
|
||||
@ -132,6 +112,11 @@ Storage::Disk::PCMSegment AppleGCR::five_and_three_data(const uint8_t *source) {
|
||||
// destination_pointer += 8;
|
||||
// }
|
||||
|
||||
// Map five-bit values up to full bytes.
|
||||
for(std::size_t c = 0; c < 410; ++c) {
|
||||
segment.data[3 + c] = five_and_three_mapping[segment.data[3 + c]];
|
||||
}
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
@ -154,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) {
|
60
Storage/Disk/Encodings/AppleGCR/Encoder.hpp
Normal file
60
Storage/Disk/Encodings/AppleGCR/Encoder.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// AppleGCR.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/04/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef AppleGCR_hpp
|
||||
#define AppleGCR_hpp
|
||||
|
||||
#include <cstdint>
|
||||
#include "../../../Disk/Track/PCMSegment.hpp"
|
||||
|
||||
namespace Storage {
|
||||
namespace Encodings {
|
||||
namespace AppleGCR {
|
||||
|
||||
/// Describes the standard three-byte prologue that begins a header.
|
||||
const uint8_t header_prologue[3] = {0xd5, 0xaa, 0x96};
|
||||
/// Describes the standard three-byte prologue that begins a data section.
|
||||
const uint8_t data_prologue[3] = {0xd5, 0xaa, 0xad};
|
||||
/// Describes the epilogue that ends both data sections and headers.
|
||||
const uint8_t epilogue[3] = {0xde, 0xaa, 0xeb};
|
||||
|
||||
/*!
|
||||
Produces the Apple-standard four-and-four per-sector header. This is the same
|
||||
for both the 13- and 16-sector formats, and is 112 bits long.
|
||||
*/
|
||||
Storage::Disk::PCMSegment header(uint8_t volume, uint8_t track, uint8_t sector);
|
||||
|
||||
/*!
|
||||
Produces the data section of a six-and-two format sector; the segment returned
|
||||
will be 2,792 bits long, encoding the first 256 bytes from @c source.
|
||||
*/
|
||||
Storage::Disk::PCMSegment six_and_two_data(const uint8_t *source);
|
||||
|
||||
/*!
|
||||
Produces @c length sync six-and-two format sync bytes. The segment returned
|
||||
is @c 10*length bits long.
|
||||
*/
|
||||
Storage::Disk::PCMSegment six_and_two_sync(int length);
|
||||
|
||||
/*!
|
||||
Produces the data section of a five-and-three format sector; the segment returned
|
||||
will be 3,336 bits long, encoding the first 256 bytes from @c source.
|
||||
*/
|
||||
Storage::Disk::PCMSegment five_and_three_data(const uint8_t *source);
|
||||
|
||||
/*!
|
||||
Produces @c length sync five-and-three format sync bytes. The segment returned
|
||||
is @c 9*length bits long.
|
||||
*/
|
||||
Storage::Disk::PCMSegment five_and_three_sync(int length);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* AppleGCR_hpp */
|
63
Storage/Disk/Encodings/AppleGCR/Sector.hpp
Normal file
63
Storage/Disk/Encodings/AppleGCR/Sector.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
Sector() {}
|
||||
|
||||
Sector(Sector &&rhs) :
|
||||
address(rhs.address),
|
||||
data(std::move(rhs.data)),
|
||||
has_data_checksum_error(rhs.has_data_checksum_error),
|
||||
has_header_checksum_error(rhs.has_header_checksum_error),
|
||||
encoding(rhs.encoding) {}
|
||||
|
||||
Sector(const Sector &rhs) :
|
||||
address(rhs.address),
|
||||
data(rhs.data),
|
||||
has_data_checksum_error(rhs.has_data_checksum_error),
|
||||
has_header_checksum_error(rhs.has_header_checksum_error),
|
||||
encoding(rhs.encoding) {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Sector_h */
|
172
Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp
Normal file
172
Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
//
|
||||
// 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 sector_location = 0;
|
||||
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 {
|
||||
sector_location = static_cast<std::size_t>(bit);
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
|
||||
// Add this sector to the map.
|
||||
result.insert(std::make_pair(sector_location, std::move(*sector)));
|
||||
} 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 */
|
@ -33,7 +33,8 @@ struct Sector {
|
||||
};
|
||||
|
||||
Address address;
|
||||
uint8_t size = 0;
|
||||
uint8_t size = 0; // Size is stored in ordinary MFM form — the number of bytes included in this sector
|
||||
// is 2^(7 + size), or 128 << size.
|
||||
|
||||
// Multiple samplings of the underlying data are accepted, to allow weak and fuzzy data to be communicated.
|
||||
std::vector<std::vector<uint8_t>> samples;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -17,6 +17,30 @@ namespace Storage {
|
||||
namespace Encodings {
|
||||
namespace MFM {
|
||||
|
||||
/*!
|
||||
The MFM shifter parses a stream of bits as input in order to produce
|
||||
a stream of MFM tokens as output. So e.g. it is suitable for use in parsing
|
||||
the output of a PLL windowing of disk events.
|
||||
|
||||
It supports both FM and MFM parsing; see @c set_is_double_density.
|
||||
|
||||
It will ordinarily honour sync patterns; that should be turned off when within
|
||||
a sector because false syncs can occur. See @c set_should_obey_syncs.
|
||||
|
||||
Bits should be fed in with @c add_input_bit.
|
||||
|
||||
The current output token can be read with @c get_token. It will usually be None but
|
||||
may indicate that an index, ID, data or deleted data mark was found, that an
|
||||
MFM sync mark was found, or that an ordinary byte has been decoded.
|
||||
|
||||
It will properly reset and/or seed a CRC generator based on the data and ID marks,
|
||||
and feed it with incoming bytes. You can access that CRC generator to query its
|
||||
value via @c get_crc_generator(). An easy way to check whether the disk contained
|
||||
a proper CRC is to read bytes until you've just read whatever CRC was on the disk,
|
||||
then check that the generator has a value of zero.
|
||||
|
||||
A specific instance of the CRC generator can be supplied at construction if preferred.
|
||||
*/
|
||||
class Shifter {
|
||||
public:
|
||||
Shifter();
|
||||
|
@ -46,11 +46,11 @@ class CAS: public Tape {
|
||||
void get_next(Storage::FileHolder &file, uint8_t (&buffer)[10], std::size_t quantity);
|
||||
|
||||
// Storage for the array of data blobs to transcribe into audio;
|
||||
// each chunk is preceded by a header which may be long, and is optionally
|
||||
// also preceded by a gap.
|
||||
// each chunk is preceded by a header which may be long, and is optionally
|
||||
// also preceded by a gap.
|
||||
struct Chunk {
|
||||
bool has_gap;
|
||||
bool long_header;
|
||||
bool long_header;
|
||||
std::vector<std::uint8_t> data;
|
||||
};
|
||||
std::vector<Chunk> chunks_;
|
||||
|
@ -23,10 +23,11 @@ enum Type: IntType {
|
||||
BBCModelB = 1 << 8,
|
||||
ColecoVision = 1 << 9,
|
||||
Commodore = 1 << 10,
|
||||
MSX = 1 << 11,
|
||||
Oric = 1 << 12,
|
||||
ZX80 = 1 << 13,
|
||||
ZX81 = 1 << 14,
|
||||
DiskII = 1 << 11,
|
||||
MSX = 1 << 12,
|
||||
Oric = 1 << 13,
|
||||
ZX80 = 1 << 14,
|
||||
ZX81 = 1 << 15,
|
||||
|
||||
Acorn = AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB,
|
||||
ZX8081 = ZX80 | ZX81,
|
||||
|
Loading…
x
Reference in New Issue
Block a user