mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +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:
parent
8ddd686049
commit
1d3ae31755
@ -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) {
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;;
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user