1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-25 04:29:09 +00:00

Merge pull request #73 from TomHarte/TapeClasses

Clarifies that a tape parser does not own a tape, it is merely lent one
This commit is contained in:
Thomas Harte 2016-11-07 22:04:37 -05:00 committed by GitHub
commit 79dd062941
19 changed files with 1137 additions and 640 deletions

View File

@ -17,7 +17,6 @@ Machine::Machine() :
_typer_delay(2500000)
{
set_clock_rate(1000000);
_via.tape.reset(new TapePlayer);
_via.set_interrupt_delegate(this);
_keyboard.reset(new Keyboard);
_via.keyboard = _keyboard;
@ -171,7 +170,10 @@ void Machine::run_for_cycles(int number_of_cycles)
#pragma mark - The 6522
Machine::VIA::VIA() : MOS::MOS6522<Machine::VIA>(), _cycles_since_ay_update(0) {}
Machine::VIA::VIA() :
MOS::MOS6522<Machine::VIA>(),
_cycles_since_ay_update(0),
tape(new TapePlayer) {}
void Machine::VIA::set_control_line_output(Port port, Line line, bool value)
{
@ -231,79 +233,10 @@ void Machine::VIA::update_ay()
#pragma mark - TapePlayer
Machine::TapePlayer::TapePlayer() :
Storage::Tape::BinaryTapePlayer(1000000),
_is_catching_bytes(false)
Storage::Tape::BinaryTapePlayer(1000000)
{}
uint8_t Machine::TapePlayer::get_next_byte(bool fast)
{
_is_in_fast_mode = fast;
_is_catching_bytes = true;
_was_high = get_input();
_queued_lengths_pointer = 0;
_data_register = 0;
_cycle_length = 0.0f;
_bit_count = 0;
while(_bit_count < 10)
{
process_next_event();
}
_is_catching_bytes = false;
return (uint8_t)(_data_register >> 1);
}
void Machine::TapePlayer::process_input_pulse(Storage::Tape::Tape::Pulse pulse)
{
Storage::Tape::BinaryTapePlayer::process_input_pulse(pulse);
if(_is_catching_bytes)
{
_cycle_length += pulse.length.get_float();
bool is_high = get_input();
if(is_high != _was_high)
{
// queue up the new length
_queued_lengths[_queued_lengths_pointer] = _cycle_length;
_cycle_length = 0.0f;
_queued_lengths_pointer++;
// search for bits
if(_is_in_fast_mode)
{
if(_queued_lengths_pointer >= 2)
{
float first_two = _queued_lengths[0] + _queued_lengths[1];
if(first_two < 0.000512*2.0 && _queued_lengths[0] >= _queued_lengths[1] - 0.000256)
{
int new_bit = (first_two < 0.000512) ? 1 : 0;
if(_bit_count || !new_bit)
{
_data_register |= (new_bit << _bit_count);
_bit_count++;
}
memmove(_queued_lengths, &_queued_lengths[2], sizeof(float)*14);
_queued_lengths_pointer -= 2;
}
else
{
memmove(_queued_lengths, &_queued_lengths[1], sizeof(float)*15);
_queued_lengths_pointer--;
}
}
}
else
{
// TODO
}
}
_was_high = is_high;
}
}
void Machine::TapePlayer::run_for_cycles(int number_of_cycles)
{
if(!_is_catching_bytes) Storage::Tape::BinaryTapePlayer::run_for_cycles(number_of_cycles);
return (uint8_t)_parser.get_next_byte(get_tape(), fast);
}

View File

@ -16,6 +16,7 @@
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Components/6522/6522.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Storage/Tape/Parsers/Oric.hpp"
#include "Video.hpp"
@ -112,22 +113,9 @@ class Machine:
public:
TapePlayer();
uint8_t get_next_byte(bool fast);
void run_for_cycles(int number_of_cycles);
private:
bool _is_catching_bytes; // `true` to enable tape byte parsing, `false` otherwise
bool _is_in_fast_mode; // `true` to indicate that tape byte parsing should use the Oric's fast encoding, `false` otherwise
float _cycle_length; // a counter for the amount of time since the tape input changed
bool _was_high; // a latch to spot when the tape input changes
float _queued_lengths[16]; // a history of previous half-wave lengths
int _queued_lengths_pointer; // a pointer into the history, showing the number of lengths waiting to be parsed
int _data_register; // the accumulation of input bits
int _bit_count; // a counter of accumulated bits
virtual void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
Storage::Tape::Oric::Parser _parser;
};
bool _use_fast_tape_hack;
@ -143,7 +131,7 @@ class Machine:
inline void run_for_cycles(unsigned int number_of_cycles);
std::shared_ptr<GI::AY38910> ay8910;
std::shared_ptr<TapePlayer> tape;
std::unique_ptr<TapePlayer> tape;
std::shared_ptr<Keyboard> keyboard;
void synchronise();

View File

@ -56,6 +56,11 @@
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; };
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */; };
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805EE1DCFC99C003085B1 /* Acorn.cpp */; };
4B8805F41DCFD22A003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F21DCFD22A003085B1 /* Commodore.cpp */; };
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F51DCFF6C9003085B1 /* Commodore.cpp */; };
4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F91DCFF807003085B1 /* Oric.cpp */; };
4B8805FE1DD02552003085B1 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805FC1DD02552003085B1 /* Tape.cpp */; };
4B8FE21B1DA19D5F0090D3CE /* Atari2600Options.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */; };
4B8FE21C1DA19D5F0090D3CE /* MachineDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */; };
4B8FE21D1DA19D5F0090D3CE /* ElectronOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B8FE2171DA19D5F0090D3CE /* ElectronOptions.xib */; };
@ -352,7 +357,6 @@
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; };
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; };
4BC5E4921D7ED365008CF980 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4901D7ED365008CF980 /* StaticAnalyser.cpp */; };
4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */; };
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; };
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; };
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
@ -497,6 +501,16 @@
4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskController.cpp; sourceTree = "<group>"; };
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskController.hpp; sourceTree = "<group>"; };
4B8805EE1DCFC99C003085B1 /* Acorn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Acorn.cpp; path = Parsers/Acorn.cpp; sourceTree = "<group>"; };
4B8805EF1DCFC99C003085B1 /* Acorn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Acorn.hpp; path = Parsers/Acorn.hpp; sourceTree = "<group>"; };
4B8805F21DCFD22A003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Commodore.cpp; path = Parsers/Commodore.cpp; sourceTree = "<group>"; };
4B8805F31DCFD22A003085B1 /* Commodore.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Commodore.hpp; path = Parsers/Commodore.hpp; sourceTree = "<group>"; };
4B8805F51DCFF6C9003085B1 /* Commodore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Commodore.cpp; path = Data/Commodore.cpp; sourceTree = "<group>"; };
4B8805F61DCFF6C9003085B1 /* Commodore.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Commodore.hpp; path = Data/Commodore.hpp; sourceTree = "<group>"; };
4B8805F91DCFF807003085B1 /* Oric.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Oric.cpp; path = Parsers/Oric.cpp; sourceTree = "<group>"; };
4B8805FA1DCFF807003085B1 /* Oric.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Oric.hpp; path = Parsers/Oric.hpp; sourceTree = "<group>"; };
4B8805FC1DD02552003085B1 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Oric/Tape.cpp; sourceTree = "<group>"; };
4B8805FD1DD02552003085B1 /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Oric/Tape.hpp; sourceTree = "<group>"; };
4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = "<group>"; };
4B8FE2141DA19D5F0090D3CE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/Atari2600Options.xib"; sourceTree = SOURCE_ROOT; };
4B8FE2161DA19D5F0090D3CE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MachineDocument.xib"; sourceTree = SOURCE_ROOT; };
@ -826,8 +840,6 @@
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = "<group>"; };
4BC5E4901D7ED365008CF980 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Commodore/StaticAnalyser.cpp; sourceTree = "<group>"; };
4BC5E4911D7ED365008CF980 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Commodore/StaticAnalyser.hpp; sourceTree = "<group>"; };
4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Utilities.cpp; path = ../../StaticAnalyser/Commodore/Utilities.cpp; sourceTree = "<group>"; };
4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Utilities.hpp; path = ../../StaticAnalyser/Commodore/Utilities.hpp; sourceTree = "<group>"; };
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = "<group>"; };
4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = "<group>"; };
@ -854,7 +866,6 @@
4BCF1FAA1DADD41B0039D2E7 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Oric/StaticAnalyser.hpp; sourceTree = "<group>"; };
4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Acorn/StaticAnalyser.cpp; sourceTree = "<group>"; };
4BD14B101D74627C0088EAD6 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Acorn/StaticAnalyser.hpp; sourceTree = "<group>"; };
4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = ../../StaticAnalyser/TapeParser.hpp; sourceTree = "<group>"; };
4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 1770.cpp; path = 1770/1770.cpp; sourceTree = "<group>"; };
4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = "<group>"; };
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
@ -1158,6 +1169,7 @@
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */,
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */,
4BEE0A691D72496600532C7B /* Cartridge */,
4B8805F81DCFF6CD003085B1 /* Data */,
4BAB62AA1D3272D200DF5BA0 /* Disk */,
4B69FB3A1C4D908A00B5F0AA /* Tape */,
);
@ -1169,6 +1181,7 @@
isa = PBXGroup;
children = (
4B69FB411C4D941400B5F0AA /* Formats */,
4B8805F11DCFC9A2003085B1 /* Parsers */,
4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */,
4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */,
);
@ -1191,6 +1204,28 @@
path = Formats;
sourceTree = "<group>";
};
4B8805F11DCFC9A2003085B1 /* Parsers */ = {
isa = PBXGroup;
children = (
4B8805EE1DCFC99C003085B1 /* Acorn.cpp */,
4B8805EF1DCFC99C003085B1 /* Acorn.hpp */,
4B8805F21DCFD22A003085B1 /* Commodore.cpp */,
4B8805F31DCFD22A003085B1 /* Commodore.hpp */,
4B8805F91DCFF807003085B1 /* Oric.cpp */,
4B8805FA1DCFF807003085B1 /* Oric.hpp */,
);
name = Parsers;
sourceTree = "<group>";
};
4B8805F81DCFF6CD003085B1 /* Data */ = {
isa = PBXGroup;
children = (
4B8805F51DCFF6C9003085B1 /* Commodore.cpp */,
4B8805F61DCFF6C9003085B1 /* Commodore.hpp */,
);
name = Data;
sourceTree = "<group>";
};
4BA799961D8B65730045123D /* Atari */ = {
isa = PBXGroup;
children = (
@ -1678,8 +1713,6 @@
4BC5E4911D7ED365008CF980 /* StaticAnalyser.hpp */,
4BC830CF1D6E7C690000A26F /* Tape.cpp */,
4BC830D01D6E7C690000A26F /* Tape.hpp */,
4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */,
4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */,
);
name = Commodore;
sourceTree = "<group>";
@ -1740,6 +1773,8 @@
children = (
4BCF1FA91DADD41B0039D2E7 /* StaticAnalyser.cpp */,
4BCF1FAA1DADD41B0039D2E7 /* StaticAnalyser.hpp */,
4B8805FC1DD02552003085B1 /* Tape.cpp */,
4B8805FD1DD02552003085B1 /* Tape.hpp */,
);
name = Oric;
sourceTree = "<group>";
@ -1812,7 +1847,6 @@
children = (
4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */,
4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */,
4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */,
4BD14B121D7462810088EAD6 /* Acorn */,
4BA799961D8B65730045123D /* Atari */,
4BC830D21D6E7C6D0000A26F /* Commodore */,
@ -2236,7 +2270,6 @@
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */,
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */,
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */,
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
@ -2255,6 +2288,7 @@
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */,
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */,
4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */,
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */,
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */,
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */,
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */,
@ -2294,8 +2328,11 @@
4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */,
4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */,
4B8FE2201DA19D7C0090D3CE /* Atari2600OptionsPanel.swift in Sources */,
4B8805F41DCFD22A003085B1 /* Commodore.cpp in Sources */,
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
4B8805FE1DD02552003085B1 /* Tape.cpp in Sources */,
4B9CCDA11DA279CA0098B625 /* Vic20OptionsPanel.swift in Sources */,
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */,
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */,
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */,
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */,
@ -2304,6 +2341,7 @@
4B4C83701D4F623200CD541F /* D64.cpp in Sources */,
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */,
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */,
4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */,
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */,

View File

@ -9,130 +9,27 @@
#include "Tape.hpp"
#include <deque>
#include "../TapeParser.hpp"
#include "../../NumberTheory/CRC.hpp"
#include "../../Storage/Tape/Parsers/Acorn.hpp"
using namespace StaticAnalyser::Acorn;
enum class WaveType {
Short, Long, Unrecognised
};
enum class SymbolType {
One, Zero
};
class Acorn1200BaudTapeParser: public StaticAnalyer::TapeParser<WaveType, SymbolType> {
public:
Acorn1200BaudTapeParser(const std::shared_ptr<Storage::Tape::Tape> &tape) :
TapeParser(tape),
_crc(0x1021, 0x0000) {}
int get_next_bit()
{
SymbolType symbol = get_next_symbol();
return (symbol == SymbolType::One) ? 1 : 0;
}
int get_next_byte()
{
int value = 0;
int c = 8;
if(get_next_bit())
{
set_error_flag();
return -1;
}
while(c--)
{
value = (value >> 1) | (get_next_bit() << 7);
}
if(!get_next_bit())
{
set_error_flag();
return -1;
}
_crc.add((uint8_t)value);
return value;
}
int get_next_short()
{
int result = get_next_byte();
result |= get_next_byte() << 8;
return result;
}
int get_next_word()
{
int result = get_next_short();
result |= get_next_short() << 8;
return result;
}
void reset_crc() { _crc.reset(); }
uint16_t get_crc() { return _crc.get_value(); }
private:
void process_pulse(Storage::Tape::Tape::Pulse pulse)
{
switch(pulse.type)
{
default: break;
case Storage::Tape::Tape::Pulse::High:
case Storage::Tape::Tape::Pulse::Low:
float pulse_length = pulse.length.get_float();
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) { push_wave(WaveType::Short); return; }
if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) { push_wave(WaveType::Long); return; }
break;
}
push_wave(WaveType::Unrecognised);
}
void inspect_waves(const std::vector<WaveType> &waves)
{
if(waves.size() < 2) return;
if(waves[0] == WaveType::Long && waves[1] == WaveType::Long)
{
push_symbol(SymbolType::Zero, 2);
return;
}
if(waves.size() < 4) return;
if( waves[0] == WaveType::Short &&
waves[1] == WaveType::Short &&
waves[2] == WaveType::Short &&
waves[3] == WaveType::Short)
{
push_symbol(SymbolType::One, 4);
return;
}
remove_waves(1);
}
NumberTheory::CRC16 _crc;
};
static std::unique_ptr<File::Chunk> GetNextChunk(Acorn1200BaudTapeParser &parser)
static std::unique_ptr<File::Chunk> GetNextChunk(const std::shared_ptr<Storage::Tape::Tape> &tape, Storage::Tape::Acorn::Parser &parser)
{
std::unique_ptr<File::Chunk> new_chunk(new File::Chunk);
int shift_register = 0;
// TODO: move this into the parser
#define shift() shift_register = (shift_register >> 1) | (parser.get_next_bit() << 9)
#define shift() shift_register = (shift_register >> 1) | (parser.get_next_bit(tape) << 9)
// find next area of high tone
while(!parser.is_at_end() && (shift_register != 0x3ff))
while(!tape->is_at_end() && (shift_register != 0x3ff))
{
shift();
}
// find next 0x2a (swallowing stop bit)
while(!parser.is_at_end() && (shift_register != 0x254))
while(!tape->is_at_end() && (shift_register != 0x254))
{
shift();
}
@ -145,9 +42,9 @@ static std::unique_ptr<File::Chunk> GetNextChunk(Acorn1200BaudTapeParser &parser
// read out name
char name[11];
int name_ptr = 0;
while(!parser.is_at_end() && name_ptr < sizeof(name))
while(!tape->is_at_end() && name_ptr < sizeof(name))
{
name[name_ptr] = (char)parser.get_next_byte();
name[name_ptr] = (char)parser.get_next_byte(tape);
if(!name[name_ptr]) break;
name_ptr++;
}
@ -155,15 +52,15 @@ static std::unique_ptr<File::Chunk> GetNextChunk(Acorn1200BaudTapeParser &parser
new_chunk->name = name;
// addresses
new_chunk->load_address = (uint32_t)parser.get_next_word();
new_chunk->execution_address = (uint32_t)parser.get_next_word();
new_chunk->block_number = (uint16_t)parser.get_next_short();
new_chunk->block_length = (uint16_t)parser.get_next_short();
new_chunk->block_flag = (uint8_t)parser.get_next_byte();
new_chunk->next_address = (uint32_t)parser.get_next_word();
new_chunk->load_address = (uint32_t)parser.get_next_word(tape);
new_chunk->execution_address = (uint32_t)parser.get_next_word(tape);
new_chunk->block_number = (uint16_t)parser.get_next_short(tape);
new_chunk->block_length = (uint16_t)parser.get_next_short(tape);
new_chunk->block_flag = (uint8_t)parser.get_next_byte(tape);
new_chunk->next_address = (uint32_t)parser.get_next_word(tape);
uint16_t calculated_header_crc = parser.get_crc();
uint16_t stored_header_crc = (uint16_t)parser.get_next_short();
uint16_t stored_header_crc = (uint16_t)parser.get_next_short(tape);
stored_header_crc = (uint16_t)((stored_header_crc >> 8) | (stored_header_crc << 8));
new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc;
@ -171,13 +68,13 @@ static std::unique_ptr<File::Chunk> GetNextChunk(Acorn1200BaudTapeParser &parser
new_chunk->data.reserve(new_chunk->block_length);
for(int c = 0; c < new_chunk->block_length; c++)
{
new_chunk->data.push_back((uint8_t)parser.get_next_byte());
new_chunk->data.push_back((uint8_t)parser.get_next_byte(tape));
}
if(new_chunk->block_length && !(new_chunk->block_flag&0x40))
{
uint16_t calculated_data_crc = parser.get_crc();
uint16_t stored_data_crc = (uint16_t)parser.get_next_short();
uint16_t stored_data_crc = (uint16_t)parser.get_next_short(tape);
stored_data_crc = (uint16_t)((stored_data_crc >> 8) | (stored_data_crc << 8));
new_chunk->data_crc_matched = stored_data_crc == calculated_data_crc;
}
@ -233,13 +130,13 @@ std::unique_ptr<File> GetNextFile(std::deque<File::Chunk> &chunks)
std::list<File> StaticAnalyser::Acorn::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
Acorn1200BaudTapeParser parser(tape);
Storage::Tape::Acorn::Parser parser;
// populate chunk list
std::deque<File::Chunk> chunk_list;
while(!parser.is_at_end())
while(!tape->is_at_end())
{
std::unique_ptr<File::Chunk> chunk = GetNextChunk(parser);
std::unique_ptr<File::Chunk> chunk = GetNextChunk(tape, parser);
if(chunk)
{
chunk_list.push_back(*chunk);

View File

@ -9,7 +9,7 @@
#include "Disk.hpp"
#include "../../Storage/Disk/DiskController.hpp"
#include "../../Storage/Disk/Encodings/CommodoreGCR.hpp"
#include "Utilities.hpp"
#include "../../Storage/Data/Commodore.hpp"
#include <limits>
#include <vector>
@ -234,7 +234,7 @@ std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storag
{
new_file.raw_name.push_back(directory[header_pointer + 5 + c]);
}
new_file.name = petscii_from_bytes(&new_file.raw_name[0], 16, false);
new_file.name = Storage::Data::Commodore::petscii_from_bytes(&new_file.raw_name[0], 16, false);
size_t number_of_sectors = (size_t)directory[header_pointer + 0x1e] + ((size_t)directory[header_pointer + 0x1f] << 8);
new_file.data.reserve((number_of_sectors - 1) * 254 + 252);

View File

@ -8,387 +8,28 @@
#include "Tape.hpp"
#include "../TapeParser.hpp"
#include "Utilities.hpp"
#include "../../Storage/Tape/Parsers/Commodore.hpp"
using namespace StaticAnalyser::Commodore;
enum class WaveType {
Short, Medium, Long, Unrecognised
};
enum class SymbolType {
One, Zero, Word, EndOfBlock, LeadIn
};
struct Header {
enum {
RelocatableProgram,
NonRelocatableProgram,
DataSequenceHeader,
DataBlock,
EndOfTape,
Unknown
} type;
std::vector<uint8_t> data;
std::wstring name;
std::vector<uint8_t> raw_name;
uint16_t starting_address;
uint16_t ending_address;
bool parity_was_valid;
bool duplicate_matched;
};
struct Data {
std::vector<uint8_t> data;
bool parity_was_valid;
bool duplicate_matched;
};
class CommodoreROMTapeParser: public StaticAnalyer::TapeParser<WaveType, SymbolType> {
public:
CommodoreROMTapeParser(const std::shared_ptr<Storage::Tape::Tape> &tape) :
TapeParser(tape),
_wave_period(0.0f),
_previous_was_high(false),
_parity_byte(0) {}
/*!
Advances to the next block on the tape, treating it as a header, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Header> get_next_header()
{
return duplicate_match<Header>(
get_next_header_body(true),
get_next_header_body(false)
);
}
/*!
Advances to the next block on the tape, treating it as data, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Data> get_next_data()
{
return duplicate_match<Data>(
get_next_data_body(true),
get_next_data_body(false)
);
}
void spin()
{
while(!is_at_end())
{
SymbolType symbol = get_next_symbol();
switch(symbol)
{
case SymbolType::One: printf("1"); break;
case SymbolType::Zero: printf("0"); break;
case SymbolType::Word: printf(" "); break;
case SymbolType::EndOfBlock: printf("\n"); break;
case SymbolType::LeadIn: printf("-"); break;
}
}
}
private:
/*!
Template for the logic in selecting which of two copies of something to consider authoritative,
including setting the duplicate_matched flag.
*/
template<class ObjectType>
std::unique_ptr<ObjectType> duplicate_match(std::unique_ptr<ObjectType> first_copy, std::unique_ptr<ObjectType> second_copy)
{
// if only one copy was parsed successfully, return it
if(!first_copy) return second_copy;
if(!second_copy) return first_copy;
// if no copies were second_copy, return nullptr
if(!first_copy && !second_copy) return nullptr;
// otherwise plan to return either one with a correct check digit, doing a comparison with the other
std::unique_ptr<ObjectType> *copy_to_return = &first_copy;
if(!first_copy->parity_was_valid && second_copy->parity_was_valid) copy_to_return = &second_copy;
(*copy_to_return)->duplicate_matched = true;
if(first_copy->data.size() != second_copy->data.size())
(*copy_to_return)->duplicate_matched = false;
else
(*copy_to_return)->duplicate_matched = !(memcmp(&first_copy->data[0], &second_copy->data[0], first_copy->data.size()));
return std::move(*copy_to_return);
}
std::unique_ptr<Header> get_next_header_body(bool is_original)
{
std::unique_ptr<Header> header(new Header);
reset_error_flag();
// find and proceed beyond lead-in tone
proceed_to_symbol(SymbolType::LeadIn);
// look for landing zone
proceed_to_landing_zone(is_original);
reset_parity_byte();
// get header type
uint8_t header_type = get_next_byte();
switch(header_type)
{
default: header->type = Header::Unknown; break;
case 0x01: header->type = Header::RelocatableProgram; break;
case 0x02: header->type = Header::DataBlock; break;
case 0x03: header->type = Header::NonRelocatableProgram; break;
case 0x04: header->type = Header::DataSequenceHeader; break;
case 0x05: header->type = Header::EndOfTape; break;
}
// grab rest of data
header->data.reserve(191);
for(size_t c = 0; c < 191; c++)
{
header->data.push_back(get_next_byte());
}
uint8_t parity_byte = get_parity_byte();
header->parity_was_valid = get_next_byte() == parity_byte;
// parse if this is not pure data
if(header->type != Header::DataBlock)
{
header->starting_address = (uint16_t)(header->data[0] | (header->data[1] << 8));
header->ending_address = (uint16_t)(header->data[2] | (header->data[3] << 8));
for(size_t c = 0; c < 16; c++)
{
header->raw_name.push_back(header->data[4 + c]);
}
header->name = petscii_from_bytes(&header->raw_name[0], 16, false);
}
if(get_error_flag()) return nullptr;
return header;
}
std::unique_ptr<Data> get_next_data_body(bool is_original)
{
std::unique_ptr<Data> data(new Data);
reset_error_flag();
// find and proceed beyond lead-in tone to the next landing zone
proceed_to_symbol(SymbolType::LeadIn);
proceed_to_landing_zone(is_original);
reset_parity_byte();
// accumulate until the next non-word marker is hit
while(!is_at_end())
{
SymbolType start_symbol = get_next_symbol();
if(start_symbol != SymbolType::Word) break;
data->data.push_back(get_next_byte_contents());
}
// the above has reead the parity byte to the end of the data; if it matched the calculated parity it'll now be zero
data->parity_was_valid = !get_parity_byte();
data->duplicate_matched = false;
// remove the captured parity
data->data.erase(data->data.end()-1);
if(get_error_flag()) return nullptr;
return data;
}
/*!
Finds and completes the next landing zone.
*/
void proceed_to_landing_zone(bool is_original)
{
uint8_t landing_zone[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
while(!is_at_end())
{
memmove(landing_zone, &landing_zone[1], sizeof(uint8_t) * 8);
landing_zone[8] = get_next_byte();
bool is_landing_zone = true;
for(int c = 0; c < 9; c++)
{
if(landing_zone[c] != ((is_original ? 0x80 : 0x00) | 0x9) - c)
{
is_landing_zone = false;
break;
}
}
if(is_landing_zone) break;
}
}
/*!
Swallows symbols until it reaches the first instance of the required symbol, swallows that
and returns.
*/
void proceed_to_symbol(SymbolType required_symbol)
{
while(!is_at_end())
{
SymbolType symbol = get_next_symbol();
if(symbol == required_symbol) return;
}
}
/*!
Swallows the next byte; sets the error flag if it is not equal to @c value.
*/
void expect_byte(uint8_t value)
{
uint8_t next_byte = get_next_byte();
if(next_byte != value) set_error_flag();
}
uint8_t _parity_byte;
void reset_parity_byte() { _parity_byte = 0; }
uint8_t get_parity_byte() { return _parity_byte; }
void add_parity_byte(uint8_t byte) { _parity_byte ^= byte; }
/*!
Proceeds to the next word marker then returns the result of @c get_next_byte_contents.
*/
uint8_t get_next_byte()
{
proceed_to_symbol(SymbolType::Word);
return get_next_byte_contents();
}
/*!
Reads the next nine symbols and applies a binary test to each to differentiate between ::One and not-::One.
Returns a byte composed of the first eight of those as bits; sets the error flag if any symbol is not
::One and not ::Zero, or if the ninth bit is not equal to the odd parity of the other eight.
*/
uint8_t get_next_byte_contents()
{
int byte_plus_parity = 0;
int c = 9;
while(c--)
{
SymbolType next_symbol = get_next_symbol();
if((next_symbol != SymbolType::One) && (next_symbol != SymbolType::Zero)) set_error_flag();
byte_plus_parity = (byte_plus_parity >> 1) | (((next_symbol == SymbolType::One) ? 1 : 0) << 8);
}
int check = byte_plus_parity;
check ^= (check >> 4);
check ^= (check >> 2);
check ^= (check >> 1);
if((check&1) == (byte_plus_parity >> 8))
set_error_flag();
add_parity_byte((uint8_t)byte_plus_parity);
return (uint8_t)byte_plus_parity;
}
/*!
Returns the result of two consecutive @c get_next_byte calls, arranged in little-endian format.
*/
uint16_t get_next_short()
{
uint16_t value = get_next_byte();
value |= get_next_byte() << 8;
return value;
}
/*!
Per the contract with StaticAnalyser::TapeParser; sums time across pulses. If this pulse
indicates a high to low transition, inspects the time since the last transition, to produce
a long, medium, short or unrecognised wave period.
*/
void process_pulse(Storage::Tape::Tape::Pulse pulse)
{
// The Complete Commodore Inner Space Anthology, P 97, gives half-cycle lengths of:
// short: 182µs => 0.000364s cycle
// medium: 262µs => 0.000524s cycle
// long: 342µs => 0.000684s cycle
bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
if(!is_high && _previous_was_high)
{
if(_wave_period >= 0.000764) push_wave(WaveType::Unrecognised);
else if(_wave_period >= 0.000604) push_wave(WaveType::Long);
else if(_wave_period >= 0.000444) push_wave(WaveType::Medium);
else if(_wave_period >= 0.000284) push_wave(WaveType::Short);
else push_wave(WaveType::Unrecognised);
_wave_period = 0.0f;
}
_wave_period += pulse.length.get_float();
_previous_was_high = is_high;
}
bool _previous_was_high;
float _wave_period;
/*!
Per the contract with StaticAnalyser::TapeParser; produces any of a word marker, an end-of-block marker,
a zero, a one or a lead-in symbol based on the currently captured waves.
*/
void inspect_waves(const std::vector<WaveType> &waves)
{
if(waves.size() < 2) return;
if(waves[0] == WaveType::Long && waves[1] == WaveType::Medium)
{
push_symbol(SymbolType::Word, 2);
return;
}
if(waves[0] == WaveType::Long && waves[1] == WaveType::Short)
{
push_symbol(SymbolType::EndOfBlock, 2);
return;
}
if(waves[0] == WaveType::Short && waves[1] == WaveType::Medium)
{
push_symbol(SymbolType::Zero, 2);
return;
}
if(waves[0] == WaveType::Medium && waves[1] == WaveType::Short)
{
push_symbol(SymbolType::One, 2);
return;
}
if(waves[0] == WaveType::Short)
{
push_symbol(SymbolType::LeadIn, 1);
return;
}
// Otherwise, eject at least one wave as all options are exhausted.
remove_waves(1);
}
};
std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
CommodoreROMTapeParser parser(tape);
Storage::Tape::Commodore::Parser parser;
std::list<File> file_list;
std::unique_ptr<Header> header = parser.get_next_header();
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(tape);
while(!parser.is_at_end())
while(!tape->is_at_end())
{
if(!header)
{
header = parser.get_next_header();
header = parser.get_next_header(tape);
continue;
}
switch(header->type)
{
case Header::DataSequenceHeader:
case Storage::Tape::Commodore::Header::DataSequenceHeader:
{
File new_file;
new_file.name = header->name;
@ -398,11 +39,11 @@ std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storag
new_file.type = File::DataSequence;
new_file.data.swap(header->data);
while(!parser.is_at_end())
while(!tape->is_at_end())
{
header = parser.get_next_header();
header = parser.get_next_header(tape);
if(!header) continue;
if(header->type != Header::DataBlock) break;
if(header->type != Storage::Tape::Commodore::Header::DataBlock) break;
std::copy(header->data.begin(), header->data.end(), std::back_inserter(new_file.data));
}
@ -410,10 +51,10 @@ std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storag
}
break;
case Header::RelocatableProgram:
case Header::NonRelocatableProgram:
case Storage::Tape::Commodore::Header::RelocatableProgram:
case Storage::Tape::Commodore::Header::NonRelocatableProgram:
{
std::unique_ptr<Data> data = parser.get_next_data();
std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(tape);
if(data)
{
File new_file;
@ -422,17 +63,17 @@ std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storag
new_file.starting_address = header->starting_address;
new_file.ending_address = header->ending_address;
new_file.data.swap(data->data);
new_file.type = (header->type == Header::RelocatableProgram) ? File::RelocatableProgram : File::NonRelocatableProgram;
new_file.type = (header->type == Storage::Tape::Commodore::Header::RelocatableProgram) ? File::RelocatableProgram : File::NonRelocatableProgram;
file_list.push_back(new_file);
}
header = parser.get_next_header();
header = parser.get_next_header(tape);
}
break;
default:
header = parser.get_next_header();
header = parser.get_next_header(tape);
break;
}
}

View File

@ -8,6 +8,8 @@
#include "StaticAnalyser.hpp"
#include "Tape.hpp"
using namespace StaticAnalyser::Oric;
void StaticAnalyser::Oric::AddTargets(
@ -16,14 +18,20 @@ void StaticAnalyser::Oric::AddTargets(
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<StaticAnalyser::Target> &destination)
{
// TODO: any sort of sanity checking at all; at the minute just trust the file type
// approximation already performed.
Target target;
target.machine = Target::Oric;
target.probability = 1.0;
target.disks = disks;
target.tapes = tapes;
target.cartridges = cartridges;
target.loadingCommand = "CLOAD\"\"\n";
destination.push_back(target);
for(auto tape : tapes)
{
std::list<File> tape_files = GetFiles(tape);
if(tape_files.size())
{
target.tapes.push_back(tape);
target.loadingCommand = "CLOAD\"\"\n";
}
}
if(target.tapes.size() || target.disks.size() || target.cartridges.size())
destination.push_back(target);
}

View File

@ -0,0 +1,95 @@
//
// Tape.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Tape.hpp"
#include "../../Storage/Tape/Parsers/Oric.hpp"
using namespace StaticAnalyser::Oric;
std::list<File> StaticAnalyser::Oric::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
std::list<File> files;
Storage::Tape::Oric::Parser parser;
tape->reset();
while(!tape->is_at_end())
{
// sync to next lead-in, check that it's one of three 0x16s
bool is_fast = parser.sync_and_get_encoding_speed(tape);
int next_bytes[2];
next_bytes[0] = parser.get_next_byte(tape, is_fast);
next_bytes[1] = parser.get_next_byte(tape, is_fast);
if(next_bytes[0] != 0x16 || next_bytes[1] != 0x16) continue;
// get the first byte that isn't a 0x16, check it was a 0x24
int byte = 0x16;
while(!tape->is_at_end() && byte == 0x16)
{
byte = parser.get_next_byte(tape, is_fast);
}
if(byte != 0x24) continue;
// skip two empty bytes
parser.get_next_byte(tape, is_fast);
parser.get_next_byte(tape, is_fast);
// get data and launch types
File new_file;
switch(parser.get_next_byte(tape, is_fast))
{
case 0x00: new_file.data_type = File::ProgramType::BASIC; break;
case 0x80: new_file.data_type = File::ProgramType::MachineCode; break;
default: new_file.data_type = File::ProgramType::None; break;
}
switch(parser.get_next_byte(tape, is_fast))
{
case 0x80: new_file.launch_type = File::ProgramType::BASIC; break;
case 0xc7: new_file.launch_type = File::ProgramType::MachineCode; break;
default: new_file.launch_type = File::ProgramType::None; break;
}
// read end and start addresses
new_file.ending_address = (uint16_t)(parser.get_next_byte(tape, is_fast) << 8);
new_file.ending_address |= (uint16_t)parser.get_next_byte(tape, is_fast);
new_file.starting_address = (uint16_t)(parser.get_next_byte(tape, is_fast) << 8);
new_file.starting_address |= (uint16_t)parser.get_next_byte(tape, is_fast);
// skip an empty byte
parser.get_next_byte(tape, is_fast);
// read file name, up to 16 characters and null terminated
char file_name[17];
int name_pos = 0;
while(name_pos < 16)
{
file_name[name_pos] = (char)parser.get_next_byte(tape, is_fast);
if(!file_name[name_pos]) break;
name_pos++;
}
file_name[16] = '\0';
new_file.name = file_name;
// read body
size_t body_length = new_file.ending_address - new_file.starting_address + 1;
new_file.data.reserve(body_length);
for(size_t c = 0; c < body_length; c++)
{
new_file.data.push_back((uint8_t)parser.get_next_byte(tape, is_fast));
}
// only one validation check: was there enough tape?
if(!tape->is_at_end())
{
files.push_back(new_file);
}
}
tape->reset();
return files;
}

View File

@ -0,0 +1,38 @@
//
// Tape.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Oric_Tape_hpp
#define StaticAnalyser_Oric_Tape_hpp
#include "../../Storage/Tape/Tape.hpp"
#include <list>
#include <string>
#include <vector>
namespace StaticAnalyser {
namespace Oric {
struct File {
std::string name;
uint16_t starting_address;
uint16_t ending_address;
enum ProgramType {
BASIC,
MachineCode,
None
};
ProgramType data_type, launch_type;
std::vector<uint8_t> data;
};
std::list<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
}
}
#endif /* Tape_hpp */

View File

@ -1,14 +1,14 @@
//
// Utilities.cpp
// Commodore.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/09/2016.
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Utilities.hpp"
#include "Commodore.hpp"
std::wstring StaticAnalyser::Commodore::petscii_from_bytes(const uint8_t *string, int length, bool shifted)
std::wstring Storage::Data::Commodore::petscii_from_bytes(const uint8_t *string, int length, bool shifted)
{
std::wstring result;

View File

@ -1,22 +1,24 @@
//
// Utilities.hpp
// Commodore.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/09/2016.
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Commodore_Utilities_hpp
#define Analyser_Commodore_Utilities_hpp
#ifndef Storage_Data_Commodore_hpp
#define Storage_Data_Commodore_hpp
#include <string>
namespace StaticAnalyser {
namespace Storage {
namespace Data {
namespace Commodore {
std::wstring petscii_from_bytes(const uint8_t *string, int length, bool shifted);
}
}
}
#endif /* Utilities_hpp */
#endif /* Commodore_hpp */

View File

@ -93,11 +93,11 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
if(_phase_counter == 6) _data_start_address = (uint16_t)(next_byte << 8);
if(_phase_counter == 7) _data_start_address |= next_byte;
_phase_counter++;
if(_phase_counter >= 9 && !next_byte) // advance after the filename-ending NULL byte
{
_next_phase = Gap;
}
_phase_counter++;
break;
case Gap:
@ -151,7 +151,7 @@ Tape::Pulse OricTAP::virtual_get_next_pulse()
case Gap:
_bit_count = 13;
pulse.type = Pulse::Zero;
pulse.type = (_phase_counter&1) ? Pulse::Low : Pulse::High;
pulse.length.length = 100;
return pulse;

View File

@ -0,0 +1,100 @@
//
// Acorn.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Acorn.hpp"
using namespace Storage::Tape::Acorn;
Parser::Parser() :
::Storage::Tape::Parser<WaveType, SymbolType>(),
_crc(0x1021, 0x0000) {}
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
SymbolType symbol = get_next_symbol(tape);
return (symbol == SymbolType::One) ? 1 : 0;
}
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int value = 0;
int c = 8;
if(get_next_bit(tape))
{
set_error_flag();
return -1;
}
while(c--)
{
value = (value >> 1) | (get_next_bit(tape) << 7);
}
if(!get_next_bit(tape))
{
set_error_flag();
return -1;
}
_crc.add((uint8_t)value);
return value;
}
int Parser::get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int result = get_next_byte(tape);
result |= get_next_byte(tape) << 8;
return result;
}
int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int result = get_next_short(tape);
result |= get_next_short(tape) << 8;
return result;
}
void Parser::reset_crc() { _crc.reset(); }
uint16_t Parser::get_crc() { return _crc.get_value(); }
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
{
switch(pulse.type)
{
default: break;
case Storage::Tape::Tape::Pulse::High:
case Storage::Tape::Tape::Pulse::Low:
float pulse_length = pulse.length.get_float();
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) { push_wave(WaveType::Short); return; }
if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) { push_wave(WaveType::Long); return; }
break;
}
push_wave(WaveType::Unrecognised);
}
void Parser::inspect_waves(const std::vector<WaveType> &waves)
{
if(waves.size() < 2) return;
if(waves[0] == WaveType::Long && waves[1] == WaveType::Long)
{
push_symbol(SymbolType::Zero, 2);
return;
}
if(waves.size() < 4) return;
if( waves[0] == WaveType::Short &&
waves[1] == WaveType::Short &&
waves[2] == WaveType::Short &&
waves[3] == WaveType::Short)
{
push_symbol(SymbolType::One, 4);
return;
}
remove_waves(1);
}

View File

@ -0,0 +1,48 @@
//
// Acorn.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Storage_Tape_Parsers_Acorn_hpp
#define Storage_Tape_Parsers_Acorn_hpp
#include "TapeParser.hpp"
#include "../../../NumberTheory/CRC.hpp"
namespace Storage {
namespace Tape {
namespace Acorn {
enum class WaveType {
Short, Long, Unrecognised
};
enum class SymbolType {
One, Zero
};
class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
public:
Parser();
int get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape);
int get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape);
int get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape);
int get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape);
void reset_crc();
uint16_t get_crc();
private:
void process_pulse(Storage::Tape::Tape::Pulse pulse);
void inspect_waves(const std::vector<WaveType> &waves);
NumberTheory::CRC16 _crc;
};
}
}
}
#endif /* Acorn_hpp */

View File

@ -0,0 +1,313 @@
//
// Commodore.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Commodore.hpp"
#include "../../Data/Commodore.hpp"
using namespace Storage::Tape::Commodore;
Parser::Parser() :
Storage::Tape::Parser<WaveType, SymbolType>(),
_wave_period(0.0f),
_previous_was_high(false),
_parity_byte(0) {}
/*!
Advances to the next block on the tape, treating it as a header, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Header> Parser::get_next_header(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
return duplicate_match<Header>(
get_next_header_body(tape, true),
get_next_header_body(tape, false)
);
}
/*!
Advances to the next block on the tape, treating it as data, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Data> Parser::get_next_data(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
return duplicate_match<Data>(
get_next_data_body(tape, true),
get_next_data_body(tape, false)
);
}
/*!
Template for the logic in selecting which of two copies of something to consider authoritative,
including setting the duplicate_matched flag.
*/
template<class ObjectType>
std::unique_ptr<ObjectType> Parser::duplicate_match(std::unique_ptr<ObjectType> first_copy, std::unique_ptr<ObjectType> second_copy)
{
// if only one copy was parsed successfully, return it
if(!first_copy) return second_copy;
if(!second_copy) return first_copy;
// if no copies were second_copy, return nullptr
if(!first_copy && !second_copy) return nullptr;
// otherwise plan to return either one with a correct check digit, doing a comparison with the other
std::unique_ptr<ObjectType> *copy_to_return = &first_copy;
if(!first_copy->parity_was_valid && second_copy->parity_was_valid) copy_to_return = &second_copy;
(*copy_to_return)->duplicate_matched = true;
if(first_copy->data.size() != second_copy->data.size())
(*copy_to_return)->duplicate_matched = false;
else
(*copy_to_return)->duplicate_matched = !(memcmp(&first_copy->data[0], &second_copy->data[0], first_copy->data.size()));
return std::move(*copy_to_return);
}
std::unique_ptr<Header> Parser::get_next_header_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original)
{
std::unique_ptr<Header> header(new Header);
reset_error_flag();
// find and proceed beyond lead-in tone
proceed_to_symbol(tape, SymbolType::LeadIn);
// look for landing zone
proceed_to_landing_zone(tape, is_original);
reset_parity_byte();
// get header type
uint8_t header_type = get_next_byte(tape);
switch(header_type)
{
default: header->type = Header::Unknown; break;
case 0x01: header->type = Header::RelocatableProgram; break;
case 0x02: header->type = Header::DataBlock; break;
case 0x03: header->type = Header::NonRelocatableProgram; break;
case 0x04: header->type = Header::DataSequenceHeader; break;
case 0x05: header->type = Header::EndOfTape; break;
}
// grab rest of data
header->data.reserve(191);
for(size_t c = 0; c < 191; c++)
{
header->data.push_back(get_next_byte(tape));
}
uint8_t parity_byte = get_parity_byte();
header->parity_was_valid = get_next_byte(tape) == parity_byte;
// parse if this is not pure data
if(header->type != Header::DataBlock)
{
header->starting_address = (uint16_t)(header->data[0] | (header->data[1] << 8));
header->ending_address = (uint16_t)(header->data[2] | (header->data[3] << 8));
for(size_t c = 0; c < 16; c++)
{
header->raw_name.push_back(header->data[4 + c]);
}
header->name = Storage::Data::Commodore::petscii_from_bytes(&header->raw_name[0], 16, false);
}
if(get_error_flag()) return nullptr;
return header;
}
std::unique_ptr<Data> Parser::get_next_data_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original)
{
std::unique_ptr<Data> data(new Data);
reset_error_flag();
// find and proceed beyond lead-in tone to the next landing zone
proceed_to_symbol(tape, SymbolType::LeadIn);
proceed_to_landing_zone(tape, is_original);
reset_parity_byte();
// accumulate until the next non-word marker is hit
while(!tape->is_at_end())
{
SymbolType start_symbol = get_next_symbol(tape);
if(start_symbol != SymbolType::Word) break;
data->data.push_back(get_next_byte_contents(tape));
}
// the above has reead the parity byte to the end of the data; if it matched the calculated parity it'll now be zero
data->parity_was_valid = !get_parity_byte();
data->duplicate_matched = false;
// remove the captured parity
data->data.erase(data->data.end()-1);
if(get_error_flag()) return nullptr;
return data;
}
/*!
Finds and completes the next landing zone.
*/
void Parser::proceed_to_landing_zone(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original)
{
uint8_t landing_zone[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
while(!tape->is_at_end())
{
memmove(landing_zone, &landing_zone[1], sizeof(uint8_t) * 8);
landing_zone[8] = get_next_byte(tape);
bool is_landing_zone = true;
for(int c = 0; c < 9; c++)
{
if(landing_zone[c] != ((is_original ? 0x80 : 0x00) | 0x9) - c)
{
is_landing_zone = false;
break;
}
}
if(is_landing_zone) break;
}
}
/*!
Swallows symbols until it reaches the first instance of the required symbol, swallows that
and returns.
*/
void Parser::proceed_to_symbol(const std::shared_ptr<Storage::Tape::Tape> &tape, SymbolType required_symbol)
{
while(!tape->is_at_end())
{
SymbolType symbol = get_next_symbol(tape);
if(symbol == required_symbol) return;
}
}
/*!
Swallows the next byte; sets the error flag if it is not equal to @c value.
*/
void Parser::expect_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, uint8_t value)
{
uint8_t next_byte = get_next_byte(tape);
if(next_byte != value) set_error_flag();
}
void Parser::reset_parity_byte() { _parity_byte = 0; }
uint8_t Parser::get_parity_byte() { return _parity_byte; }
void Parser::add_parity_byte(uint8_t byte) { _parity_byte ^= byte; }
/*!
Proceeds to the next word marker then returns the result of @c get_next_byte_contents.
*/
uint8_t Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
proceed_to_symbol(tape, SymbolType::Word);
return get_next_byte_contents(tape);
}
/*!
Reads the next nine symbols and applies a binary test to each to differentiate between ::One and not-::One.
Returns a byte composed of the first eight of those as bits; sets the error flag if any symbol is not
::One and not ::Zero, or if the ninth bit is not equal to the odd parity of the other eight.
*/
uint8_t Parser::get_next_byte_contents(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int byte_plus_parity = 0;
int c = 9;
while(c--)
{
SymbolType next_symbol = get_next_symbol(tape);
if((next_symbol != SymbolType::One) && (next_symbol != SymbolType::Zero)) set_error_flag();
byte_plus_parity = (byte_plus_parity >> 1) | (((next_symbol == SymbolType::One) ? 1 : 0) << 8);
}
int check = byte_plus_parity;
check ^= (check >> 4);
check ^= (check >> 2);
check ^= (check >> 1);
if((check&1) == (byte_plus_parity >> 8))
set_error_flag();
add_parity_byte((uint8_t)byte_plus_parity);
return (uint8_t)byte_plus_parity;
}
/*!
Returns the result of two consecutive @c get_next_byte calls, arranged in little-endian format.
*/
uint16_t Parser::get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
uint16_t value = get_next_byte(tape);
value |= get_next_byte(tape) << 8;
return value;
}
/*!
Per the contract with StaticAnalyser::TapeParser; sums time across pulses. If this pulse
indicates a high to low transition, inspects the time since the last transition, to produce
a long, medium, short or unrecognised wave period.
*/
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
{
// The Complete Commodore Inner Space Anthology, P 97, gives half-cycle lengths of:
// short: 182µs => 0.000364s cycle
// medium: 262µs => 0.000524s cycle
// long: 342µs => 0.000684s cycle
bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
if(!is_high && _previous_was_high)
{
if(_wave_period >= 0.000764) push_wave(WaveType::Unrecognised);
else if(_wave_period >= 0.000604) push_wave(WaveType::Long);
else if(_wave_period >= 0.000444) push_wave(WaveType::Medium);
else if(_wave_period >= 0.000284) push_wave(WaveType::Short);
else push_wave(WaveType::Unrecognised);
_wave_period = 0.0f;
}
_wave_period += pulse.length.get_float();
_previous_was_high = is_high;
}
/*!
Per the contract with StaticAnalyser::TapeParser; produces any of a word marker, an end-of-block marker,
a zero, a one or a lead-in symbol based on the currently captured waves.
*/
void Parser::inspect_waves(const std::vector<WaveType> &waves)
{
if(waves.size() < 2) return;
if(waves[0] == WaveType::Long && waves[1] == WaveType::Medium)
{
push_symbol(SymbolType::Word, 2);
return;
}
if(waves[0] == WaveType::Long && waves[1] == WaveType::Short)
{
push_symbol(SymbolType::EndOfBlock, 2);
return;
}
if(waves[0] == WaveType::Short && waves[1] == WaveType::Medium)
{
push_symbol(SymbolType::Zero, 2);
return;
}
if(waves[0] == WaveType::Medium && waves[1] == WaveType::Short)
{
push_symbol(SymbolType::One, 2);
return;
}
if(waves[0] == WaveType::Short)
{
push_symbol(SymbolType::LeadIn, 1);
return;
}
// Otherwise, eject at least one wave as all options are exhausted.
remove_waves(1);
}

View File

@ -0,0 +1,139 @@
//
// Commodore.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Storage_Tape_Parsers_Commodore_hpp
#define Storage_Tape_Parsers_Commodore_hpp
#include "TapeParser.hpp"
//#include "Utilities.hpp"
#include <memory>
#include <string>
namespace Storage {
namespace Tape {
namespace Commodore {
enum class WaveType {
Short, Medium, Long, Unrecognised
};
enum class SymbolType {
One, Zero, Word, EndOfBlock, LeadIn
};
struct Header {
enum {
RelocatableProgram,
NonRelocatableProgram,
DataSequenceHeader,
DataBlock,
EndOfTape,
Unknown
} type;
std::vector<uint8_t> data;
std::wstring name;
std::vector<uint8_t> raw_name;
uint16_t starting_address;
uint16_t ending_address;
bool parity_was_valid;
bool duplicate_matched;
};
struct Data {
std::vector<uint8_t> data;
bool parity_was_valid;
bool duplicate_matched;
};
class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
public:
Parser();
/*!
Advances to the next block on the tape, treating it as a header, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Header> get_next_header(const std::shared_ptr<Storage::Tape::Tape> &tape);
/*!
Advances to the next block on the tape, treating it as data, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Data> get_next_data(const std::shared_ptr<Storage::Tape::Tape> &tape);
private:
/*!
Template for the logic in selecting which of two copies of something to consider authoritative,
including setting the duplicate_matched flag.
*/
template<class ObjectType>
std::unique_ptr<ObjectType> duplicate_match(std::unique_ptr<ObjectType> first_copy, std::unique_ptr<ObjectType> second_copy);
std::unique_ptr<Header> get_next_header_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original);
std::unique_ptr<Data> get_next_data_body(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original);
/*!
Finds and completes the next landing zone.
*/
void proceed_to_landing_zone(const std::shared_ptr<Storage::Tape::Tape> &tape, bool is_original);
/*!
Swallows symbols until it reaches the first instance of the required symbol, swallows that
and returns.
*/
void proceed_to_symbol(const std::shared_ptr<Storage::Tape::Tape> &tape, SymbolType required_symbol);
/*!
Swallows the next byte; sets the error flag if it is not equal to @c value.
*/
void expect_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, uint8_t value);
uint8_t _parity_byte;
void reset_parity_byte();
uint8_t get_parity_byte();
void add_parity_byte(uint8_t byte);
/*!
Proceeds to the next word marker then returns the result of @c get_next_byte_contents.
*/
uint8_t get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape);
/*!
Reads the next nine symbols and applies a binary test to each to differentiate between ::One and not-::One.
Returns a byte composed of the first eight of those as bits; sets the error flag if any symbol is not
::One and not ::Zero, or if the ninth bit is not equal to the odd parity of the other eight.
*/
uint8_t get_next_byte_contents(const std::shared_ptr<Storage::Tape::Tape> &tape);
/*!
Returns the result of two consecutive @c get_next_byte calls, arranged in little-endian format.
*/
uint16_t get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape);
/*!
Per the contract with StaticAnalyser::TapeParser; sums time across pulses. If this pulse
indicates a high to low transition, inspects the time since the last transition, to produce
a long, medium, short or unrecognised wave period.
*/
void process_pulse(Storage::Tape::Tape::Pulse pulse);
bool _previous_was_high;
float _wave_period;
/*!
Per the contract with StaticAnalyser::TapeParser; produces any of a word marker, an end-of-block marker,
a zero, a one or a lead-in symbol based on the currently captured waves.
*/
void inspect_waves(const std::vector<WaveType> &waves);
};
}
}
}
#endif /* Commodore_hpp */

View File

@ -0,0 +1,196 @@
//
// Oric.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Oric.hpp"
using namespace Storage::Tape::Oric;
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool use_fast_encoding)
{
_detection_mode = use_fast_encoding ? FastZero : SlowZero;
_cycle_length = 0.0f;
int result = 0;
int bit_count = 0;
while(bit_count < 11 && !tape->is_at_end())
{
SymbolType symbol = get_next_symbol(tape);
if(!bit_count && symbol != SymbolType::Zero) continue;
_detection_mode = use_fast_encoding ? FastData : SlowData;
result |= ((symbol == SymbolType::One) ? 1 : 0) << bit_count;
bit_count++;
}
// TODO: check parity?
return tape->is_at_end() ? -1 : ((result >> 1)&0xff);
}
bool Parser::sync_and_get_encoding_speed(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
_detection_mode = Sync;
while(!tape->is_at_end())
{
SymbolType symbol = get_next_symbol(tape);
switch(symbol)
{
case SymbolType::FoundSlow: return false;
case SymbolType::FoundFast: return true;
default: break;
}
}
return false;
}
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
{
const float length_threshold = 0.0003125f;
bool wave_is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
if(wave_is_high != _wave_was_high && _cycle_length > 0.0f)
{
if(_cycle_length > 2.0 * length_threshold)
push_wave(WaveType::Unrecognised);
else push_wave(_cycle_length < length_threshold ? WaveType::Short : WaveType::Long);
_cycle_length = 0.0f;
}
_wave_was_high = wave_is_high;
_cycle_length += pulse.length.get_float();
}
void Parser::inspect_waves(const std::vector<WaveType> &waves)
{
switch(_detection_mode)
{
case FastZero:
if(waves.size() < 2) return;
if(waves[0] == WaveType::Short && waves[1] == WaveType::Long)
{
push_symbol(SymbolType::Zero, 2);
return;
}
break;
case FastData:
if(waves.size() < 2) return;
if(waves[0] == WaveType::Short && waves[1] != WaveType::Unrecognised)
{
push_symbol((waves[1] == WaveType::Long) ? SymbolType::Zero : SymbolType::One, 2);
return;
}
break;
case SlowZero:
if(waves.size() < 8) return;
if(
waves[0] == WaveType::Long && waves[1] == WaveType::Long && waves[2] == WaveType::Long && waves[3] == WaveType::Long &&
waves[4] == WaveType::Long && waves[5] == WaveType::Long && waves[6] == WaveType::Long && waves[7] == WaveType::Long
)
{
push_symbol(SymbolType::Zero, 8);
return;
}
break;
case SlowData:
#define CHECK_RUN(length, type, symbol) \
if(waves.size() >= length)\
{\
size_t c;\
for(c = 0; c < length; c++) if(waves[c] != type) break;\
if(c == length)\
{\
push_symbol(symbol, 8);\
return;\
}\
}
CHECK_RUN(8, WaveType::Long, SymbolType::Zero);
CHECK_RUN(16, WaveType::Short, SymbolType::One);
#undef CHECK_RUN
if(waves.size() < 16) return; // TODO, maybe: if there are any inconsistencies in the first 8, don't return
break;
case Sync:
{
// Sync is 0x16, either encoded fast or slow; i.e. 0 0110 1000 1
// So, fast: [short, long]*2, [short, short]*2, [short, long], [short, short], [short, long]*3, [short, short] = 20
// [short, short] = 1; [short, long] = 0
// Slow: long*16, short*32, long*8, short*16, long*24, short*16 = 112
Pattern slow_sync[] =
{
{.type = WaveType::Long, 16},
{.type = WaveType::Short, 32},
{.type = WaveType::Long, 8},
{.type = WaveType::Short, 16},
{.type = WaveType::Long, 24},
{.type = WaveType::Short, 16},
{.type = WaveType::Unrecognised}
};
Pattern fast_sync[] =
{
{.type = WaveType::Short, 1},
{.type = WaveType::Long, 1},
{.type = WaveType::Short, 1},
{.type = WaveType::Long, 1},
{.type = WaveType::Short, 5},
{.type = WaveType::Long, 1},
{.type = WaveType::Short, 3},
{.type = WaveType::Long, 1},
{.type = WaveType::Short, 1},
{.type = WaveType::Long, 1},
{.type = WaveType::Short, 1},
{.type = WaveType::Long, 1},
{.type = WaveType::Short, 2},
{.type = WaveType::Unrecognised}
};
size_t slow_sync_matching_depth = pattern_matching_depth(waves, slow_sync);
size_t fast_sync_matching_depth = pattern_matching_depth(waves, fast_sync);
if(slow_sync_matching_depth == 112)
{
push_symbol(SymbolType::FoundSlow, 112);
return;
}
if(fast_sync_matching_depth == 20)
{
push_symbol(SymbolType::FoundFast, 20);
return;
}
if(slow_sync_matching_depth < waves.size() && fast_sync_matching_depth < waves.size())
{
int least_depth = (int)std::min(slow_sync_matching_depth, fast_sync_matching_depth);
remove_waves(least_depth ? least_depth : 1);
}
return;
}
break;
}
remove_waves(1);
}
size_t Parser::pattern_matching_depth(const std::vector<WaveType> &waves, Pattern *pattern)
{
size_t depth = 0;
int pattern_depth = 0;
while(depth < waves.size() && pattern->type != WaveType::Unrecognised)
{
if(waves[depth] != pattern->type) break;
depth++;
pattern_depth++;
if(pattern_depth == pattern->count)
{
pattern_depth = 0;
pattern++;
}
}
return depth;
}

View File

@ -0,0 +1,58 @@
//
// Oric.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Storage_Tape_Parsers_Oric_hpp
#define Storage_Tape_Parsers_Oric_hpp
#include "TapeParser.hpp"
namespace Storage {
namespace Tape {
namespace Oric {
enum class WaveType {
Short, Long, Unrecognised
};
enum class SymbolType {
One, Zero, FoundFast, FoundSlow
};
class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
public:
int get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape, bool use_fast_encoding);
bool sync_and_get_encoding_speed(const std::shared_ptr<Storage::Tape::Tape> &tape);
private:
void process_pulse(Storage::Tape::Tape::Pulse pulse);
void inspect_waves(const std::vector<WaveType> &waves);
enum DetectionMode {
FastData,
SlowData,
FastZero,
SlowZero,
Sync
} _detection_mode;
bool _wave_was_high;
float _cycle_length;
struct Pattern
{
WaveType type;
int count;
};
size_t pattern_matching_depth(const std::vector<WaveType> &waves, Pattern *pattern);
};
}
}
}
#endif /* Oric_hpp */

View File

@ -9,7 +9,13 @@
#ifndef TapeParser_hpp
#define TapeParser_hpp
namespace StaticAnalyer {
#include "../Tape.hpp"
#include <memory>
#include <vector>
namespace Storage {
namespace Tape {
/*!
A partly-abstract base class to help in the authorship of tape format parsers;
@ -18,17 +24,35 @@ namespace StaticAnalyer {
Very optional, not intended to box in the approaches taken for analysis.
*/
template <typename WaveType, typename SymbolType> class TapeParser {
template <typename WaveType, typename SymbolType> class Parser {
public:
/// Instantiates a new parser with the supplied @c tape.
TapeParser(const std::shared_ptr<Storage::Tape::Tape> &tape) : _tape(tape), _has_next_symbol(false), _error_flag(false) {}
Parser() : _has_next_symbol(false), _error_flag(false) {}
/// Resets the error flag.
void reset_error_flag() { _error_flag = false; }
/// @returns @c true if an error has occurred since the error flag was last reset; @c false otherwise.
bool get_error_flag() { return _error_flag; }
/// @returns @c true if the encapsulated tape has reached its end; @c false otherwise.
bool is_at_end() { return _tape->is_at_end(); }
/*!
Asks the parser to continue taking pulses from the tape until either the subclass next declares a symbol
or the tape runs out, returning the most-recently declared symbol.
*/
SymbolType get_next_symbol(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
while(!_has_next_symbol && !tape->is_at_end())
{
process_pulse(tape->get_next_pulse());
}
_has_next_symbol = false;
return _next_symbol;
}
/*!
Should be implemented by subclasses. Consumes @c pulse. Is likely either to call @c push_wave
or to take no action.
*/
virtual void process_pulse(Storage::Tape::Tape::Pulse pulse) = 0;
protected:
@ -67,20 +91,6 @@ template <typename WaveType, typename SymbolType> class TapeParser {
remove_waves(number_of_waves);
}
/*!
Asks the parser to continue taking pulses from the tape until either the subclass next declares a symbol
or the tape runs out, returning the most-recently declared symbol.
*/
SymbolType get_next_symbol()
{
while(!_has_next_symbol && !is_at_end())
{
process_pulse(_tape->get_next_pulse());
}
_has_next_symbol = false;
return _next_symbol;
}
void set_error_flag()
{
_error_flag = true;
@ -89,12 +99,6 @@ template <typename WaveType, typename SymbolType> class TapeParser {
private:
bool _error_flag;
/*!
Should be implemented by subclasses. Consumes @c pulse. Is likely either to call @c push_wave
or to take no action.
*/
virtual void process_pulse(Storage::Tape::Tape::Pulse pulse) = 0;
/*!
Should be implemented by subclasses. Inspects @c waves for a potential new symbol. If one is
found should call @c push_symbol. May wish alternatively to call @c remove_waves to have entries
@ -106,10 +110,9 @@ template <typename WaveType, typename SymbolType> class TapeParser {
std::vector<WaveType> _wave_queue;
SymbolType _next_symbol;
bool _has_next_symbol;
std::shared_ptr<Storage::Tape::Tape> _tape;
};
}
}
#endif /* TapeParser_hpp */