1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-02 20:30:00 +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:
Thomas Harte 2017-07-15 19:07:35 -04:00
parent 6ca712b498
commit 253f9603ed
9 changed files with 145 additions and 92 deletions

View File

@ -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;
}
}

View File

@ -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_;
};

View File

@ -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

View File

@ -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_;

View File

@ -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;

View File

@ -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 {

View File

@ -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_;
};
}

View File

@ -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;

View File

@ -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);