1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +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:
Thomas Harte 2016-01-23 00:14:44 -05:00
parent 0efe4b312c
commit 43a7d1b7ae
2 changed files with 163 additions and 103 deletions

View File

@ -25,8 +25,7 @@ Machine::Machine() :
_displayOutputPosition(0), _displayOutputPosition(0),
_audioOutputPosition(0), _audioOutputPosition(0),
_audioOutputPositionError(0), _audioOutputPositionError(0),
_currentOutputLine(0), _currentOutputLine(0)
_tape({.is_running = false, .dataRegister = 0})
{ {
memset(_keyStates, 0, sizeof(_keyStates)); memset(_keyStates, 0, sizeof(_keyStates));
memset(_palette, 0xf, sizeof(_palette)); memset(_palette, 0xf, sizeof(_palette));
@ -112,9 +111,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
case 0x4: case 0x4:
if(isReadOperation(operation)) if(isReadOperation(operation))
{ {
*value = (uint8_t)(_tape.dataRegister >> 2); *value = _tape.get_data_register();
_interruptStatus &= ~Interrupt::TransmitDataEmpty; _tape.clear_interrupts(Interrupt::TransmitDataEmpty);
evaluate_interrupts();
} }
else else
{ {
@ -188,8 +186,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
_speaker.set_is_enabled(new_speaker_is_enabled); _speaker.set_is_enabled(new_speaker_is_enabled);
} }
// TODO: tape mode, tape motor, caps lock LED _tape.set_is_running(((*value)&0x40) ? true : false);
_tape.is_running = ((*value)&0x40) ? true : false; // TODO: tape mode, caps lock LED
} }
break; break;
default: default:
@ -292,89 +290,14 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
break; break;
} }
if(_tape.is_running && _tape.media != nullptr) _tape.run_for_cycle();
{
_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;
}
}
}
}
return cycles; return cycles;
} }
void Machine::set_tape(std::shared_ptr<Storage::Tape> tape) void Machine::set_tape(std::shared_ptr<Storage::Tape> tape)
{ {
_tape.media = tape; _tape.set_tape(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();
} }
void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) 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(); 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() inline void Machine::evaluate_interrupts()
{ {
if(_interruptStatus & _interruptControl) if(_interruptStatus & _interruptControl)
@ -664,3 +593,104 @@ Machine::Speaker::~Speaker()
{ {
// fclose(rawStream); // 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;
}
}
}
}
}

View File

@ -57,13 +57,57 @@ enum Key: uint16_t {
KeyBreak = 0xffff 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. @abstract Represents an Acorn Electron.
@discussion An instance of Electron::Machine represents the current state of an @discussion An instance of Electron::Machine represents the current state of an
Acorn Electron. Acorn Electron.
*/ */
class Machine: public CPU6502::Processor<Machine> { class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
public: public:
@ -81,6 +125,8 @@ class Machine: public CPU6502::Processor<Machine> {
Outputs::Speaker *get_speaker() { return &_speaker; } Outputs::Speaker *get_speaker() { return &_speaker; }
const char *get_signal_decoder(); const char *get_signal_decoder();
virtual void tape_did_change_interrupt_status(Tape *tape);
private: private:
inline void update_display(); inline void update_display();
@ -88,9 +134,6 @@ class Machine: public CPU6502::Processor<Machine> {
inline void signal_interrupt(Interrupt interrupt); inline void signal_interrupt(Interrupt interrupt);
inline void evaluate_interrupts(); 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. // Things that directly constitute the memory map.
uint8_t _roms[16][16384]; uint8_t _roms[16][16384];
uint8_t _os[16384], _ram[32768]; uint8_t _os[16384], _ram[32768];
@ -114,20 +157,7 @@ class Machine: public CPU6502::Processor<Machine> {
uint8_t *_currentLine, *_writePointer; uint8_t *_currentLine, *_writePointer;
// Tape. // Tape.
struct Tape { Tape _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;
// Outputs. // Outputs.
Outputs::CRT *_crt; Outputs::CRT *_crt;