1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

Abstracted the concept of an Acorn shifter away from being a PLLParser. The Acorn tape parser now skips using that class and uses the shifter. The actual Electron also uses the shifter. So the two are completely aligned. Net result: the Electron should successfully load exactly when static analysis was successful.

This commit is contained in:
Thomas Harte 2017-07-16 19:24:01 -04:00
parent 8ddd686049
commit 1d3ae31755
4 changed files with 80 additions and 41 deletions

View File

@ -17,7 +17,9 @@ Tape::Tape() :
delegate_(nullptr), delegate_(nullptr),
output_({.bits_remaining_until_empty = 0, .cycles_into_pulse = 0}), output_({.bits_remaining_until_empty = 0, .cycles_into_pulse = 0}),
last_posted_interrupt_status_(0), last_posted_interrupt_status_(0),
interrupt_status_(0) {} interrupt_status_(0) {
shifter_.set_delegate(this);
}
void Tape::push_tape_bit(uint16_t bit) { void Tape::push_tape_bit(uint16_t bit) {
data_register_ = (uint16_t)((data_register_ >> 1) | (bit << 10)); data_register_ = (uint16_t)((data_register_ >> 1) | (bit << 10));
@ -71,30 +73,11 @@ uint8_t Tape::get_data_register() {
} }
void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse) { void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse) {
crossings_[0] = crossings_[1]; shifter_.process_pulse(pulse);
crossings_[1] = crossings_[2]; }
crossings_[2] = crossings_[3];
crossings_[3] = Tape::Unrecognised; void Tape::acorn_shifter_output_bit(int value) {
if(pulse.type != Storage::Tape::Tape::Pulse::Zero) { push_tape_bit((uint16_t)value);
float pulse_length = (float)pulse.length.length / (float)pulse.length.clock_rate;
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 1200.0) {
crossings_[3] = pulse_length > 1.0 / 3000.0 ? Tape::Long : Tape::Short;
}
// if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) crossings_[3] = Tape::Short;
// if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) crossings_[3] = Tape::Long;
}
if(crossings_[0] == Tape::Long && crossings_[1] == Tape::Long) {
push_tape_bit(0);
crossings_[0] = crossings_[1] = Tape::Recognised;
} else {
if(crossings_[0] == Tape::Short && crossings_[1] == Tape::Short && crossings_[2] == Tape::Short && crossings_[3] == Tape::Short) {
push_tape_bit(1);
crossings_[0] = crossings_[1] =
crossings_[2] = crossings_[3] = Tape::Recognised;
}
}
} }
void Tape::run_for_cycles(unsigned int number_of_cycles) { void Tape::run_for_cycles(unsigned int number_of_cycles) {

View File

@ -10,13 +10,16 @@
#define Electron_Tape_h #define Electron_Tape_h
#include "../../Storage/Tape/Tape.hpp" #include "../../Storage/Tape/Tape.hpp"
#include "../../Storage/Tape/Parsers/Acorn.hpp"
#include "Interrupts.hpp" #include "Interrupts.hpp"
#include <cstdint> #include <cstdint>
namespace Electron { namespace Electron {
class Tape: public Storage::Tape::TapePlayer { class Tape:
public Storage::Tape::TapePlayer,
public Storage::Tape::Acorn::Shifter::Delegate {
public: public:
Tape(); Tape();
@ -39,6 +42,8 @@ class Tape: public Storage::Tape::TapePlayer {
inline void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; } inline void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; }
void set_is_in_input_mode(bool is_in_input_mode); void set_is_in_input_mode(bool is_in_input_mode);
void acorn_shifter_output_bit(int value);
private: private:
void process_input_pulse(Storage::Tape::Tape::Pulse pulse); void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
inline void push_tape_bit(uint16_t bit); inline void push_tape_bit(uint16_t bit);
@ -62,9 +67,7 @@ class Tape: public Storage::Tape::TapePlayer {
uint8_t interrupt_status_, last_posted_interrupt_status_; uint8_t interrupt_status_, last_posted_interrupt_status_;
Delegate *delegate_; Delegate *delegate_;
enum { ::Storage::Tape::Acorn::Shifter shifter_;
Long, Short, Unrecognised, Recognised
} crossings_[4];
}; };
} }

View File

@ -14,9 +14,9 @@ namespace {
const int PLLClockRate = 1920000; const int PLLClockRate = 1920000;
} }
Parser::Parser() : Parser::Parser() : crc_(0x1021, 0x0000) {
::Storage::Tape::PLLParser<SymbolType>(PLLClockRate, PLLClockRate / 4800), shifter_.set_delegate(this);
crc_(0x1021, 0x0000) {} }
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape) { int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape) {
SymbolType symbol = get_next_symbol(tape); SymbolType symbol = get_next_symbol(tape);
@ -56,14 +56,38 @@ int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape) {
void Parser::reset_crc() { crc_.reset(); } void Parser::reset_crc() { crc_.reset(); }
uint16_t Parser::get_crc() { return crc_.get_value(); } uint16_t Parser::get_crc() { return crc_.get_value(); }
bool Parser::did_update_shifter(int new_value, int length) { void Parser::acorn_shifter_output_bit(int value) {
if(length < 4) return false; push_symbol(value ? SymbolType::One : SymbolType::Zero);
}
switch(new_value & 0xf) { void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) {
case 0x5: printf("0"); push_symbol(SymbolType::Zero); return true; shifter_.process_pulse(pulse);
case 0xf: printf("1"); push_symbol(SymbolType::One); return true; }
default:
printf("?");
return false; Shifter::Shifter() :
pll_(PLLClockRate / 4800, 15),
was_high_(false),
input_pattern_(0),
input_bit_counter_(0) {
pll_.set_delegate(this);
}
void Shifter::process_pulse(const Storage::Tape::Tape::Pulse &pulse) {
pll_.run_for_cycles((int)((float)PLLClockRate * 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;
}
void Shifter::digital_phase_locked_loop_output_bit(int value) {
input_pattern_ = ((input_pattern_ << 1) | (unsigned int)value) & 0xf;
switch(input_pattern_) {
case 0x5: delegate_->acorn_shifter_output_bit(0); input_pattern_ = 0; break;
case 0xf: delegate_->acorn_shifter_output_bit(1); input_pattern_ = 0; break;
default: break;;
} }
} }

View File

@ -17,11 +17,37 @@ namespace Storage {
namespace Tape { namespace Tape {
namespace Acorn { namespace Acorn {
class Shifter: public Storage::DigitalPhaseLockedLoop::Delegate {
public:
Shifter();
void process_pulse(const Storage::Tape::Tape::Pulse &pulse);
class Delegate {
public:
virtual void acorn_shifter_output_bit(int value) = 0;
};
void set_delegate(Delegate *delegate) {
delegate_ = delegate;
}
void digital_phase_locked_loop_output_bit(int value);
private:
Storage::DigitalPhaseLockedLoop pll_;
bool was_high_;
unsigned int input_pattern_;
unsigned int input_bit_counter_;
Delegate *delegate_;
};
enum class SymbolType { enum class SymbolType {
One, Zero One, Zero
}; };
class Parser: public Storage::Tape::PLLParser<SymbolType> { class Parser: public Storage::Tape::Parser<SymbolType>, public Shifter::Delegate {
public: public:
Parser(); Parser();
@ -32,10 +58,13 @@ class Parser: public Storage::Tape::PLLParser<SymbolType> {
void reset_crc(); void reset_crc();
uint16_t get_crc(); uint16_t get_crc();
bool did_update_shifter(int new_value, int length); void acorn_shifter_output_bit(int value);
void process_pulse(const Storage::Tape::Tape::Pulse &pulse);
private: private:
bool did_update_shifter(int new_value, int length);
NumberTheory::CRC16 crc_; NumberTheory::CRC16 crc_;
Shifter shifter_;
}; };
} }