mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
It'll be helpful to be able to isolate the tape interface when I'm faking a fast load. So factored out.
This commit is contained in:
parent
0efe4b312c
commit
43a7d1b7ae
@ -25,8 +25,7 @@ Machine::Machine() :
|
||||
_displayOutputPosition(0),
|
||||
_audioOutputPosition(0),
|
||||
_audioOutputPositionError(0),
|
||||
_currentOutputLine(0),
|
||||
_tape({.is_running = false, .dataRegister = 0})
|
||||
_currentOutputLine(0)
|
||||
{
|
||||
memset(_keyStates, 0, sizeof(_keyStates));
|
||||
memset(_palette, 0xf, sizeof(_palette));
|
||||
@ -112,9 +111,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
case 0x4:
|
||||
if(isReadOperation(operation))
|
||||
{
|
||||
*value = (uint8_t)(_tape.dataRegister >> 2);
|
||||
_interruptStatus &= ~Interrupt::TransmitDataEmpty;
|
||||
evaluate_interrupts();
|
||||
*value = _tape.get_data_register();
|
||||
_tape.clear_interrupts(Interrupt::TransmitDataEmpty);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -188,8 +186,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
_speaker.set_is_enabled(new_speaker_is_enabled);
|
||||
}
|
||||
|
||||
// TODO: tape mode, tape motor, caps lock LED
|
||||
_tape.is_running = ((*value)&0x40) ? true : false;
|
||||
_tape.set_is_running(((*value)&0x40) ? true : false);
|
||||
// TODO: tape mode, caps lock LED
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -292,89 +290,14 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
break;
|
||||
}
|
||||
|
||||
if(_tape.is_running && _tape.media != nullptr)
|
||||
{
|
||||
_tape.time_into_pulse += (unsigned int)_tape.pulseStepper->step();
|
||||
if(_tape.time_into_pulse == _tape.currentPulse.length.length)
|
||||
{
|
||||
get_next_tape_pulse();
|
||||
|
||||
_tape.crossings[0] = _tape.crossings[1];
|
||||
_tape.crossings[1] = _tape.crossings[2];
|
||||
_tape.crossings[2] = _tape.crossings[3];
|
||||
|
||||
_tape.crossings[3] = Tape::Unrecognised;
|
||||
if(_tape.currentPulse.type != Storage::Tape::Pulse::Zero)
|
||||
{
|
||||
float pulse_length = (float)_tape.currentPulse.length.length / (float)_tape.currentPulse.length.clock_rate;
|
||||
if(pulse_length > 0.4 / 2400.0 && pulse_length < 0.6 / 2400.0) _tape.crossings[3] = Tape::Short;
|
||||
if(pulse_length > 0.4 / 1200.0 && pulse_length < 0.6 / 1200.0) _tape.crossings[3] = Tape::Long;
|
||||
}
|
||||
|
||||
if(_tape.crossings[0] == Tape::Long && _tape.crossings[1] == Tape::Long)
|
||||
{
|
||||
push_tape_bit(0);
|
||||
_tape.crossings[1] = Tape::Unrecognised;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_tape.crossings[0] == Tape::Short && _tape.crossings[1] == Tape::Short && _tape.crossings[2] == Tape::Short && _tape.crossings[3] == Tape::Short)
|
||||
{
|
||||
push_tape_bit(1);
|
||||
_tape.crossings[3] = Tape::Unrecognised;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_tape.run_for_cycle();
|
||||
|
||||
return cycles;
|
||||
}
|
||||
|
||||
void Machine::set_tape(std::shared_ptr<Storage::Tape> tape)
|
||||
{
|
||||
_tape.media = tape;
|
||||
get_next_tape_pulse();
|
||||
}
|
||||
|
||||
inline void Machine::get_next_tape_pulse()
|
||||
{
|
||||
_tape.time_into_pulse = 0;
|
||||
_tape.currentPulse = _tape.media->get_next_pulse();
|
||||
if(_tape.pulseStepper == nullptr || _tape.currentPulse.length.clock_rate != _tape.pulseStepper->get_output_rate())
|
||||
{
|
||||
_tape.pulseStepper = std::shared_ptr<SignalProcessing::Stepper>(new SignalProcessing::Stepper(_tape.currentPulse.length.clock_rate, 2000000));
|
||||
}
|
||||
}
|
||||
|
||||
inline void Machine::push_tape_bit(uint16_t bit)
|
||||
{
|
||||
_tape.dataRegister = (uint16_t)((_tape.dataRegister >> 1) | (bit << 10));
|
||||
|
||||
if(_tape.bits_since_start)
|
||||
{
|
||||
_tape.bits_since_start--;
|
||||
|
||||
if(_tape.bits_since_start == 7)
|
||||
{
|
||||
_interruptStatus &= ~Interrupt::TransmitDataEmpty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((_tape.dataRegister&0x3) == 0x1)
|
||||
{
|
||||
_interruptStatus |= Interrupt::TransmitDataEmpty;
|
||||
_tape.bits_since_start = 9;
|
||||
}
|
||||
|
||||
if(_tape.dataRegister == 0x3ff)
|
||||
_interruptStatus |= Interrupt::HighToneDetect;
|
||||
else
|
||||
_interruptStatus &= ~Interrupt::HighToneDetect;
|
||||
}
|
||||
// printf(".");
|
||||
|
||||
evaluate_interrupts();
|
||||
_tape.set_tape(tape);
|
||||
}
|
||||
|
||||
void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data)
|
||||
@ -395,6 +318,12 @@ inline void Machine::signal_interrupt(Electron::Interrupt interrupt)
|
||||
evaluate_interrupts();
|
||||
}
|
||||
|
||||
void Machine::tape_did_change_interrupt_status(Tape *tape)
|
||||
{
|
||||
_interruptStatus = (_interruptStatus & ~(Interrupt::TransmitDataEmpty | Interrupt::ReceiveDataFull | Interrupt::HighToneDetect)) | _tape.get_interrupt_status();
|
||||
evaluate_interrupts();
|
||||
}
|
||||
|
||||
inline void Machine::evaluate_interrupts()
|
||||
{
|
||||
if(_interruptStatus & _interruptControl)
|
||||
@ -664,3 +593,104 @@ Machine::Speaker::~Speaker()
|
||||
{
|
||||
// fclose(rawStream);
|
||||
}
|
||||
|
||||
/*
|
||||
Tape
|
||||
*/
|
||||
|
||||
Tape::Tape() : _is_running(false), _data_register(0), _delegate(nullptr) {}
|
||||
|
||||
void Tape::set_tape(std::shared_ptr<Storage::Tape> tape)
|
||||
{
|
||||
_tape = tape;
|
||||
get_next_tape_pulse();
|
||||
}
|
||||
|
||||
inline void Tape::get_next_tape_pulse()
|
||||
{
|
||||
_time_into_pulse = 0;
|
||||
_current_pulse = _tape->get_next_pulse();
|
||||
if(_pulse_stepper == nullptr || _current_pulse.length.clock_rate != _pulse_stepper->get_output_rate())
|
||||
{
|
||||
_pulse_stepper = std::shared_ptr<SignalProcessing::Stepper>(new SignalProcessing::Stepper(_current_pulse.length.clock_rate, 2000000));
|
||||
}
|
||||
}
|
||||
|
||||
inline void Tape::push_tape_bit(uint16_t bit)
|
||||
{
|
||||
_data_register = (uint16_t)((_data_register >> 1) | (bit << 10));
|
||||
|
||||
uint8_t old_interrupt_status = _interrupt_status;
|
||||
|
||||
if(_bits_since_start)
|
||||
{
|
||||
_bits_since_start--;
|
||||
|
||||
if(_bits_since_start == 7)
|
||||
{
|
||||
_interrupt_status &= ~Interrupt::TransmitDataEmpty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if((_data_register&0x3) == 0x1)
|
||||
{
|
||||
_interrupt_status |= Interrupt::TransmitDataEmpty;
|
||||
_bits_since_start = 9;
|
||||
}
|
||||
|
||||
if(_data_register == 0x3ff)
|
||||
_interrupt_status |= Interrupt::HighToneDetect;
|
||||
else
|
||||
_interrupt_status &= ~Interrupt::HighToneDetect;
|
||||
}
|
||||
|
||||
if(old_interrupt_status != _interrupt_status && _delegate) _delegate->tape_did_change_interrupt_status(this);
|
||||
}
|
||||
|
||||
inline void Tape::clear_interrupts(uint8_t interrupts)
|
||||
{
|
||||
if(_interrupt_status & interrupts)
|
||||
{
|
||||
_interrupt_status &= ~interrupts;
|
||||
if(_delegate) _delegate->tape_did_change_interrupt_status(this);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Tape::run_for_cycle()
|
||||
{
|
||||
if(_is_running && _tape != nullptr)
|
||||
{
|
||||
_time_into_pulse += (unsigned int)_pulse_stepper->step();
|
||||
if(_time_into_pulse == _current_pulse.length.length)
|
||||
{
|
||||
get_next_tape_pulse();
|
||||
|
||||
_crossings[0] = _crossings[1];
|
||||
_crossings[1] = _crossings[2];
|
||||
_crossings[2] = _crossings[3];
|
||||
|
||||
_crossings[3] = Tape::Unrecognised;
|
||||
if(_current_pulse.type != Storage::Tape::Pulse::Zero)
|
||||
{
|
||||
float pulse_length = (float)_current_pulse.length.length / (float)_current_pulse.length.clock_rate;
|
||||
if(pulse_length > 0.4 / 2400.0 && pulse_length < 0.6 / 2400.0) _crossings[3] = Tape::Short;
|
||||
if(pulse_length > 0.4 / 1200.0 && pulse_length < 0.6 / 1200.0) _crossings[3] = Tape::Long;
|
||||
}
|
||||
|
||||
if(_crossings[0] == Tape::Long && _crossings[1] == Tape::Long)
|
||||
{
|
||||
push_tape_bit(0);
|
||||
_crossings[1] = Tape::Unrecognised;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_crossings[0] == Tape::Short && _crossings[1] == Tape::Short && _crossings[2] == Tape::Short && _crossings[3] == Tape::Short)
|
||||
{
|
||||
push_tape_bit(1);
|
||||
_crossings[3] = Tape::Unrecognised;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,13 +57,57 @@ enum Key: uint16_t {
|
||||
KeyBreak = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
class Tape {
|
||||
public:
|
||||
Tape();
|
||||
|
||||
void set_tape(std::shared_ptr<Storage::Tape> tape);
|
||||
|
||||
inline uint8_t get_data_register() { return (uint8_t)(_data_register >> 2); }
|
||||
|
||||
inline uint8_t get_interrupt_status() { return _interrupt_status; }
|
||||
inline void clear_interrupts(uint8_t interrupts);
|
||||
|
||||
class Delegate {
|
||||
public:
|
||||
virtual void tape_did_change_interrupt_status(Tape *tape) = 0;
|
||||
};
|
||||
inline void set_delegate(Delegate *delegate) { _delegate = delegate; }
|
||||
|
||||
inline void run_for_cycle();
|
||||
|
||||
void set_is_running(bool is_running) { _is_running = is_running; }
|
||||
|
||||
private:
|
||||
inline void push_tape_bit(uint16_t bit);
|
||||
inline void get_next_tape_pulse();
|
||||
|
||||
std::shared_ptr<Storage::Tape> _tape;
|
||||
|
||||
Storage::Tape::Pulse _current_pulse;
|
||||
std::shared_ptr<SignalProcessing::Stepper> _pulse_stepper;
|
||||
uint32_t _time_into_pulse;
|
||||
|
||||
bool _is_running;
|
||||
|
||||
int _bits_since_start;
|
||||
uint16_t _data_register;
|
||||
|
||||
uint8_t _interrupt_status;
|
||||
Delegate *_delegate;
|
||||
|
||||
enum {
|
||||
Long, Short, Unrecognised
|
||||
} _crossings[4];
|
||||
};
|
||||
|
||||
/*!
|
||||
@abstract Represents an Acorn Electron.
|
||||
|
||||
@discussion An instance of Electron::Machine represents the current state of an
|
||||
Acorn Electron.
|
||||
*/
|
||||
class Machine: public CPU6502::Processor<Machine> {
|
||||
class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
|
||||
|
||||
public:
|
||||
|
||||
@ -81,6 +125,8 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
Outputs::Speaker *get_speaker() { return &_speaker; }
|
||||
const char *get_signal_decoder();
|
||||
|
||||
virtual void tape_did_change_interrupt_status(Tape *tape);
|
||||
|
||||
private:
|
||||
|
||||
inline void update_display();
|
||||
@ -88,9 +134,6 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
inline void signal_interrupt(Interrupt interrupt);
|
||||
inline void evaluate_interrupts();
|
||||
|
||||
inline void get_next_tape_pulse();
|
||||
inline void push_tape_bit(uint16_t bit);
|
||||
|
||||
// Things that directly constitute the memory map.
|
||||
uint8_t _roms[16][16384];
|
||||
uint8_t _os[16384], _ram[32768];
|
||||
@ -114,20 +157,7 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
uint8_t *_currentLine, *_writePointer;
|
||||
|
||||
// Tape.
|
||||
struct Tape {
|
||||
std::shared_ptr<Storage::Tape> media;
|
||||
Storage::Tape::Pulse currentPulse;
|
||||
std::shared_ptr<SignalProcessing::Stepper> pulseStepper;
|
||||
uint32_t time_into_pulse;
|
||||
bool is_running;
|
||||
int bits_since_start;
|
||||
|
||||
uint16_t dataRegister;
|
||||
|
||||
enum {
|
||||
Long, Short, Unrecognised
|
||||
} crossings[4];
|
||||
} _tape;
|
||||
Tape _tape;
|
||||
|
||||
// Outputs.
|
||||
Outputs::CRT *_crt;
|
||||
|
Loading…
Reference in New Issue
Block a user