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),
output_({.bits_remaining_until_empty = 0, .cycles_into_pulse = 0}),
last_posted_interrupt_status_(0),
interrupt_status_(0) {}
interrupt_status_(0) {
shifter_.set_delegate(this);
}
void Tape::push_tape_bit(uint16_t bit) {
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) {
crossings_[0] = crossings_[1];
crossings_[1] = crossings_[2];
crossings_[2] = crossings_[3];
shifter_.process_pulse(pulse);
}
crossings_[3] = Tape::Unrecognised;
if(pulse.type != Storage::Tape::Tape::Pulse::Zero) {
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::acorn_shifter_output_bit(int value) {
push_tape_bit((uint16_t)value);
}
void Tape::run_for_cycles(unsigned int number_of_cycles) {

View File

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

View File

@ -14,9 +14,9 @@ namespace {
const int PLLClockRate = 1920000;
}
Parser::Parser() :
::Storage::Tape::PLLParser<SymbolType>(PLLClockRate, PLLClockRate / 4800),
crc_(0x1021, 0x0000) {}
Parser::Parser() : crc_(0x1021, 0x0000) {
shifter_.set_delegate(this);
}
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &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(); }
uint16_t Parser::get_crc() { return crc_.get_value(); }
bool Parser::did_update_shifter(int new_value, int length) {
if(length < 4) return false;
void Parser::acorn_shifter_output_bit(int value) {
push_symbol(value ? SymbolType::One : SymbolType::Zero);
}
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;
void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) {
shifter_.process_pulse(pulse);
}
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 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 {
One, Zero
};
class Parser: public Storage::Tape::PLLParser<SymbolType> {
class Parser: public Storage::Tape::Parser<SymbolType>, public Shifter::Delegate {
public:
Parser();
@ -32,10 +58,13 @@ class Parser: public Storage::Tape::PLLParser<SymbolType> {
void reset_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:
bool did_update_shifter(int new_value, int length);
NumberTheory::CRC16 crc_;
Shifter shifter_;
};
}