mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-11 14:37:37 +00:00
Split the normal tape parser class into two in order to add a new option: a PLL-driven tape parser. Decided to see what happens if I attempt to use that to parse CSW Acorn data.
This commit is contained in:
parent
6ca712b498
commit
253f9603ed
@ -10,8 +10,12 @@
|
||||
|
||||
using namespace Storage::Tape::Acorn;
|
||||
|
||||
namespace {
|
||||
const int PLLClockRate = 1920000;
|
||||
}
|
||||
|
||||
Parser::Parser() :
|
||||
::Storage::Tape::Parser<WaveType, SymbolType>(),
|
||||
::Storage::Tape::PLLParser<SymbolType>(PLLClockRate, PLLClockRate / 4800, 100),
|
||||
crc_(0x1021, 0x0000) {}
|
||||
|
||||
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
@ -52,38 +56,14 @@ int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
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 / 1200.0) {
|
||||
push_wave(pulse_length > 1.0 / 3000.0 ? WaveType::Long : WaveType::Short); return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
bool Parser::did_update_shifter(int new_value, int length) {
|
||||
if(length < 4) return false;
|
||||
|
||||
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);
|
||||
switch(new_value & 0xf) {
|
||||
case 0x5: printf("0"); push_symbol(SymbolType::Zero); return true;
|
||||
case 0xf: printf("1"); push_symbol(SymbolType::One); return true;
|
||||
default:
|
||||
printf("?");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -11,20 +11,17 @@
|
||||
|
||||
#include "TapeParser.hpp"
|
||||
#include "../../../NumberTheory/CRC.hpp"
|
||||
#include "../../Disk/DigitalPhaseLockedLoop.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> {
|
||||
class Parser: public Storage::Tape::PLLParser<SymbolType> {
|
||||
public:
|
||||
Parser();
|
||||
|
||||
@ -35,9 +32,9 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
|
||||
void reset_crc();
|
||||
uint16_t get_crc();
|
||||
|
||||
bool did_update_shifter(int new_value, int length);
|
||||
|
||||
private:
|
||||
void process_pulse(Storage::Tape::Tape::Pulse pulse);
|
||||
void inspect_waves(const std::vector<WaveType> &waves);
|
||||
NumberTheory::CRC16 crc_;
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
using namespace Storage::Tape::Commodore;
|
||||
|
||||
Parser::Parser() :
|
||||
Storage::Tape::Parser<WaveType, SymbolType>(),
|
||||
Storage::Tape::PulseClassificationParser<WaveType, SymbolType>(),
|
||||
wave_period_(0.0f),
|
||||
previous_was_high_(false),
|
||||
parity_byte_(0) {}
|
||||
@ -262,7 +262,7 @@ uint16_t Parser::get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape
|
||||
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)
|
||||
void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse)
|
||||
{
|
||||
// The Complete Commodore Inner Space Anthology, P 97, gives half-cycle lengths of:
|
||||
// short: 182µs => 0.000364s cycle
|
||||
|
@ -56,7 +56,7 @@ struct Data {
|
||||
bool duplicate_matched;
|
||||
};
|
||||
|
||||
class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
|
||||
class Parser: public Storage::Tape::PulseClassificationParser<WaveType, SymbolType> {
|
||||
public:
|
||||
Parser();
|
||||
|
||||
@ -126,7 +126,7 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
|
||||
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);
|
||||
void process_pulse(const Storage::Tape::Tape::Pulse &pulse);
|
||||
bool previous_was_high_;
|
||||
float wave_period_;
|
||||
|
||||
|
@ -45,7 +45,7 @@ bool Parser::sync_and_get_encoding_speed(const std::shared_ptr<Storage::Tape::Ta
|
||||
return false;
|
||||
}
|
||||
|
||||
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
|
||||
void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse)
|
||||
{
|
||||
const float maximum_short_length = 0.000512f;
|
||||
const float maximum_medium_length = 0.000728f;
|
||||
|
@ -26,13 +26,13 @@ enum class SymbolType {
|
||||
One, Zero, FoundFast, FoundSlow
|
||||
};
|
||||
|
||||
class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
|
||||
class Parser: public Storage::Tape::PulseClassificationParser<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 process_pulse(const Storage::Tape::Tape::Pulse &pulse);
|
||||
void inspect_waves(const std::vector<WaveType> &waves);
|
||||
|
||||
enum DetectionMode {
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define TapeParser_hpp
|
||||
|
||||
#include "../Tape.hpp"
|
||||
#include "../../Disk/DigitalPhaseLockedLoop.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -18,18 +19,9 @@
|
||||
namespace Storage {
|
||||
namespace Tape {
|
||||
|
||||
/*!
|
||||
A partly-abstract base class to help in the authorship of tape format parsers;
|
||||
provides hooks for pulse classification from pulses to waves and for symbol identification from
|
||||
waves.
|
||||
|
||||
Very optional, not intended to box in the approaches taken for analysis.
|
||||
*/
|
||||
template <typename WaveType, typename SymbolType> class Parser {
|
||||
template <typename SymbolType> class Parser {
|
||||
public:
|
||||
/// Instantiates a new parser with the supplied @c tape.
|
||||
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.
|
||||
@ -59,12 +51,18 @@ template <typename WaveType, typename SymbolType> class Parser {
|
||||
next_symbol_ = symbol;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Should be implemented by subclasses. Consumes @c pulse. Is likely either to call @c push_wave
|
||||
or to take no action.
|
||||
@returns `true` if there is no data left on the tape and the WaveType queue has been exhausted; `false` otherwise.
|
||||
*/
|
||||
virtual void process_pulse(Storage::Tape::Tape::Pulse pulse) = 0;
|
||||
bool is_at_end(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
return tape->is_at_end() && !has_next_symbol_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/*!
|
||||
Should be implemented by subclasses. Consumes @c pulse.
|
||||
*/
|
||||
virtual void process_pulse(const Storage::Tape::Tape::Pulse &pulse) = 0;
|
||||
|
||||
/*!
|
||||
An optional implementation for subclasses; called to announce that the tape has ended: that
|
||||
@ -72,7 +70,50 @@ template <typename WaveType, typename SymbolType> class Parser {
|
||||
*/
|
||||
virtual void mark_end() {}
|
||||
|
||||
/*!
|
||||
Sets @c symbol as the newly-recognised symbol.
|
||||
*/
|
||||
void push_symbol(SymbolType symbol) {
|
||||
has_next_symbol_ = true;
|
||||
next_symbol_ = symbol;
|
||||
}
|
||||
|
||||
void set_error_flag() {
|
||||
error_flag_ = true;
|
||||
}
|
||||
|
||||
bool error_flag_;
|
||||
SymbolType next_symbol_;
|
||||
bool has_next_symbol_;
|
||||
};
|
||||
|
||||
/*!
|
||||
A partly-abstract base class to help in the authorship of tape format parsers;
|
||||
provides hooks for receipt of pulses, which are intended to be classified into waves,
|
||||
and for symbol identification from waves.
|
||||
|
||||
Very optional, not intended to box in the approaches taken for analysis. See also
|
||||
the PLLParser.
|
||||
*/
|
||||
template <typename WaveType, typename SymbolType> class PulseClassificationParser: public Parser<SymbolType> {
|
||||
public:
|
||||
virtual void process_pulse(const Storage::Tape::Tape::Pulse &pulse) = 0;
|
||||
|
||||
/*
|
||||
process_pulse should either call @c push_wave or to take no action.
|
||||
*/
|
||||
|
||||
protected:
|
||||
/*!
|
||||
Sets @c symbol as the newly-recognised symbol and removes @c nunber_of_waves waves from the front of the list.
|
||||
|
||||
Expected to be called by subclasses from @c process_pulse when it recognises that the first @c number_of_waves
|
||||
waves together represent @c symbol.
|
||||
*/
|
||||
void push_symbol(SymbolType symbol, int number_of_waves) {
|
||||
Parser<SymbolType>::push_symbol(symbol);
|
||||
remove_waves(number_of_waves);
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds @c wave to the back of the list of recognised waves and calls @c inspect_waves to check for a new symbol.
|
||||
@ -94,32 +135,7 @@ template <typename WaveType, typename SymbolType> class Parser {
|
||||
wave_queue_.erase(wave_queue_.begin(), wave_queue_.begin()+number_of_waves);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets @c symbol as the newly-recognised symbol and removes @c nunber_of_waves waves from the front of the list.
|
||||
|
||||
Expected to be called by subclasses from @c process_pulse when it recognises that the first @c number_of_waves
|
||||
waves together represent @c symbol.
|
||||
*/
|
||||
void push_symbol(SymbolType symbol, int number_of_waves) {
|
||||
has_next_symbol_ = true;
|
||||
next_symbol_ = symbol;
|
||||
remove_waves(number_of_waves);
|
||||
}
|
||||
|
||||
void set_error_flag() {
|
||||
error_flag_ = true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns `true` if there is no data left on the tape and the WaveType queue has been exhausted; `false` otherwise.
|
||||
*/
|
||||
bool is_at_end(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
return tape->is_at_end() && wave_queue_.empty() && !has_next_symbol_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool error_flag_;
|
||||
|
||||
/*!
|
||||
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
|
||||
@ -129,8 +145,68 @@ template <typename WaveType, typename SymbolType> class Parser {
|
||||
virtual void inspect_waves(const std::vector<WaveType> &waves) = 0;
|
||||
|
||||
std::vector<WaveType> wave_queue_;
|
||||
SymbolType next_symbol_;
|
||||
bool has_next_symbol_;
|
||||
};
|
||||
|
||||
/**
|
||||
A partly-abstract base class to help in the authorship of tape format parsers;
|
||||
provides a phase-locked loop, an overridable hook for deriving when to push
|
||||
bits to the PLL, a shift register populated by the PLL and a shout-out whenever
|
||||
the shift register changes. Subclasses are hence intended to push symbols.
|
||||
*/
|
||||
template <typename SymbolType> class PLLParser:
|
||||
public Storage::DigitalPhaseLockedLoop::Delegate,
|
||||
public Parser<SymbolType> {
|
||||
public:
|
||||
/// Instantiates a new parser with the supplied @c tape.
|
||||
PLLParser(int clock_rate, int clocks_per_bit, int tolerance) :
|
||||
clock_rate_(clock_rate),
|
||||
pll_(clocks_per_bit, tolerance, 3),
|
||||
input_bit_counter_(0),
|
||||
input_pattern_(0),
|
||||
was_high_(false) {
|
||||
pll_.set_delegate(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/*!
|
||||
The default implementation marks transitions between high and not high with
|
||||
PLL-pushed bits.
|
||||
*/
|
||||
void process_pulse(const Storage::Tape::Tape::Pulse &pulse) {
|
||||
pll_.run_for_cycles((int)((float)clock_rate_ * pulse.length.get_float()));
|
||||
|
||||
bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
|
||||
if(is_high != was_high_) {
|
||||
pll_.add_pulse();
|
||||
}
|
||||
was_high_ = is_high;
|
||||
}
|
||||
|
||||
/*!
|
||||
Communicates to a subclass that a shift register shifting from left to right and
|
||||
clocked by the PLL now has value @c value and its running length total is now at @c length bits.
|
||||
|
||||
If this function returns true then the running length total is zeroed.
|
||||
|
||||
So expected usage is: if there are at least enough bits available to make a meaningful
|
||||
value, inspect the shifter. If the low bits of the shifter make a meaningful value,
|
||||
call push_symbol and return true. Otherwise return false.
|
||||
*/
|
||||
virtual bool did_update_shifter(int new_value, int length) = 0;
|
||||
|
||||
void digital_phase_locked_loop_output_bit(int value) {
|
||||
input_pattern_ = (input_pattern_ << 1) | value;
|
||||
input_bit_counter_++;
|
||||
if(did_update_shifter(input_pattern_, input_bit_counter_)) input_bit_counter_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Storage::DigitalPhaseLockedLoop pll_;
|
||||
int clock_rate_;
|
||||
int input_pattern_;
|
||||
int input_bit_counter_;
|
||||
bool was_high_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using namespace Storage::Tape::ZX8081;
|
||||
|
||||
Parser::Parser() : pulse_was_high_(false), pulse_time_(0) {}
|
||||
|
||||
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse) {
|
||||
void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) {
|
||||
pulse_time_ += pulse.length;
|
||||
bool pulse_is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
|
||||
|
||||
|
@ -29,7 +29,7 @@ enum class SymbolType {
|
||||
One, Zero, FileGap, Unrecognised
|
||||
};
|
||||
|
||||
class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
|
||||
class Parser: public Storage::Tape::PulseClassificationParser<WaveType, SymbolType> {
|
||||
public:
|
||||
Parser();
|
||||
|
||||
@ -50,7 +50,7 @@ class Parser: public Storage::Tape::Parser<WaveType, SymbolType> {
|
||||
Time pulse_time_;
|
||||
void post_pulse();
|
||||
|
||||
void process_pulse(Storage::Tape::Tape::Pulse pulse);
|
||||
void process_pulse(const Storage::Tape::Tape::Pulse &pulse);
|
||||
void mark_end();
|
||||
|
||||
void inspect_waves(const std::vector<WaveType> &waves);
|
||||
|
Loading…
x
Reference in New Issue
Block a user