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:
parent
8ddd686049
commit
1d3ae31755
@ -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) {
|
||||||
|
@ -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];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user