From d93d380c888c16735a6a79ffd9ce9a1869dc58da Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Mar 2021 15:51:25 -0500 Subject: [PATCH] Adds bit-level Spectrum-style tape parsing. More to do, obviously. --- .../Clock Signal.xcodeproj/project.pbxproj | 8 ++ Storage/Tape/Parsers/Spectrum.cpp | 98 +++++++++++++++++++ Storage/Tape/Parsers/Spectrum.hpp | 48 +++++++++ 3 files changed, 154 insertions(+) create mode 100644 Storage/Tape/Parsers/Spectrum.cpp create mode 100644 Storage/Tape/Parsers/Spectrum.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 03d6f0e1b..c671eaf43 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -243,6 +243,8 @@ 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; }; 4B595FAD2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B595FAC2086DFBA0083CAA8 /* AudioToggle.cpp */; }; 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B595FAC2086DFBA0083CAA8 /* AudioToggle.cpp */; }; + 4B5D5C9725F56FC7001B4623 /* Spectrum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */; }; + 4B5D5C9825F56FC7001B4623 /* Spectrum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */; }; 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; }; 4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */; }; 4B622AE5222E0AD5008B59F2 /* DisplayMetrics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */; }; @@ -1252,6 +1254,8 @@ 4B59199B1DAC6C46005BB85C /* OricTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricTAP.hpp; sourceTree = ""; }; 4B595FAB2086DFBA0083CAA8 /* AudioToggle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AudioToggle.hpp; sourceTree = ""; }; 4B595FAC2086DFBA0083CAA8 /* AudioToggle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioToggle.cpp; sourceTree = ""; }; + 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Spectrum.cpp; path = Parsers/Spectrum.cpp; sourceTree = ""; }; + 4B5D5C9625F56FC7001B4623 /* Spectrum.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Spectrum.hpp; path = Parsers/Spectrum.hpp; sourceTree = ""; }; 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FileHolder.cpp; sourceTree = ""; }; 4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = ""; }; 4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Microdisc.cpp; path = Oric/Microdisc.cpp; sourceTree = ""; }; @@ -3021,11 +3025,13 @@ 4B8805F21DCFD22A003085B1 /* Commodore.cpp */, 4B0E61051FF34737002A9DBD /* MSX.cpp */, 4B8805F91DCFF807003085B1 /* Oric.cpp */, + 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */, 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */, 4B8805EF1DCFC99C003085B1 /* Acorn.hpp */, 4B8805F31DCFD22A003085B1 /* Commodore.hpp */, 4B0E61061FF34737002A9DBD /* MSX.hpp */, 4B8805FA1DCFF807003085B1 /* Oric.hpp */, + 4B5D5C9625F56FC7001B4623 /* Spectrum.hpp */, 4B4518A71F76004200926311 /* TapeParser.hpp */, 4BBFBB6B1EE8401E00C01E7A /* ZX8081.hpp */, ); @@ -5050,6 +5056,7 @@ 4B2E86BF25D74F160024F1E9 /* Mouse.cpp in Sources */, 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */, 4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */, + 4B5D5C9825F56FC7001B4623 /* Spectrum.cpp in Sources */, 4B2E86D025D8D8C70024F1E9 /* Keyboard.cpp in Sources */, 4B89452F201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B894531201967B4007DE474 /* StaticAnalyser.cpp in Sources */, @@ -5256,6 +5263,7 @@ 4B894530201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B4518A31F75FD1C00926311 /* HFE.cpp in Sources */, 4B1B88BB202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */, + 4B5D5C9725F56FC7001B4623 /* Spectrum.cpp in Sources */, 4B4518A11F75FD1C00926311 /* D64.cpp in Sources */, 4B1558C01F844ECD006E9A97 /* BitReverse.cpp in Sources */, 4BCE0052227CE8CA000CA200 /* DiskIICard.cpp in Sources */, diff --git a/Storage/Tape/Parsers/Spectrum.cpp b/Storage/Tape/Parsers/Spectrum.cpp new file mode 100644 index 000000000..6b1ff4362 --- /dev/null +++ b/Storage/Tape/Parsers/Spectrum.cpp @@ -0,0 +1,98 @@ +// +// Spectrum.cpp +// Clock Signal +// +// Created by Thomas Harte on 07/03/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#include "Spectrum.hpp" + +// +// Source used for the logic below was primarily https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface +// + +using namespace Storage::Tape::ZXSpectrum; + +void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) { + if(pulse.type == Storage::Tape::Tape::Pulse::Type::Zero) { + push_wave(WaveType::Gap); + return; + } + + // Only pulse duration matters; the ZX Spectrum et al do not rely on polarity. + const float t_states = pulse.length.get() * 3'500'000.0f; + + // Too long => gap. + if(t_states > 2400.0f) { + push_wave(WaveType::Gap); + return; + } + + // 1940–2400 t-states => pilot. + if(t_states > 1940.0f) { + push_wave(WaveType::Pilot); + return; + } + + // 1282–1940 t-states => one. + if(t_states > 1282.0f) { + push_wave(WaveType::One); + return; + } + + // 895–1282 => zero. + if(t_states > 795.0f) { + push_wave(WaveType::Zero); + return; + } + + // 701–895 => sync 2. + if(t_states > 701.0f) { + push_wave(WaveType::Sync2); + return; + } + + // Anything remaining above 600 => sync 1. + if(t_states > 600.0f) { + push_wave(WaveType::Sync1); + return; + } + + // Whatever this was, it's too short. Call it a gap. + push_wave(WaveType::Gap); +} + +void Parser::inspect_waves(const std::vector &waves) { + switch(waves[0]) { + // Gap and Pilot map directly. + case WaveType::Gap: push_symbol(SymbolType::Gap, 1); break; + case WaveType::Pilot: push_symbol(SymbolType::Pilot, 1); break; + + // Encountering a sync 2 on its own is unexpected. + case WaveType::Sync2: + push_symbol(SymbolType::Gap, 1); + break; + + // A sync 1 should be followed by a sync 2 in order to make a sync. + case WaveType::Sync1: + if(waves.size() < 2) return; + if(waves[1] == WaveType::Sync2) { + push_symbol(SymbolType::Sync, 2); + } else { + push_symbol(SymbolType::Gap, 1); + } + break; + + // Both one and zero waves should come in pairs. + case WaveType::One: + case WaveType::Zero: + if(waves.size() < 2) return; + if(waves[1] == waves[0]) { + push_symbol(waves[0] == WaveType::One ? SymbolType::One : SymbolType::Zero, 2); + } else { + push_symbol(SymbolType::Gap, 1); + } + break; + } +} diff --git a/Storage/Tape/Parsers/Spectrum.hpp b/Storage/Tape/Parsers/Spectrum.hpp new file mode 100644 index 000000000..817a064d0 --- /dev/null +++ b/Storage/Tape/Parsers/Spectrum.hpp @@ -0,0 +1,48 @@ +// +// Spectrum.hpp +// Clock Signal +// +// Created by Thomas Harte on 07/03/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#ifndef Storage_Tape_Parsers_Spectrum_hpp +#define Storage_Tape_Parsers_Spectrum_hpp + +#include "TapeParser.hpp" + +namespace Storage { +namespace Tape { +namespace ZXSpectrum { + +enum class WaveType { + // All references to 't-states' below are cycles relative to the + // ZX Spectrum's 3.5Mhz processor. + + Pilot, // Nominally 2168 t-states. + Sync1, // 667 t-states. + Sync2, // 735 t-states. + Zero, // 855 t-states. + One, // 1710 t-states. + Gap, +}; + +enum class SymbolType { + Pilot, + Sync, + Zero, + One, + Gap, +}; + +class Parser: public Storage::Tape::PulseClassificationParser { + private: + void process_pulse(const Storage::Tape::Tape::Pulse &pulse) override; + void inspect_waves(const std::vector &waves) override; +}; + +} +} +} + +#endif /* Spectrum_hpp */