1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +00:00

Factored out the Tape and switched it to postfix underscores.

This commit is contained in:
Thomas Harte 2016-12-03 12:18:08 -05:00
parent 3f7f2c6117
commit d1d93829cf
6 changed files with 246 additions and 189 deletions

View File

@ -973,130 +973,3 @@ void Speaker::set_is_enabled(bool is_enabled)
}); });
} }
/*
Tape
*/
Tape::Tape() :
TapePlayer(2000000),
_is_running(false),
_data_register(0),
_delegate(nullptr),
_output({.bits_remaining_until_empty = 0, .cycles_into_pulse = 0}),
_last_posted_interrupt_status(0),
_interrupt_status(0)
{}
inline void Tape::push_tape_bit(uint16_t bit)
{
_data_register = (uint16_t)((_data_register >> 1) | (bit << 10));
if(_input.minimum_bits_until_full) _input.minimum_bits_until_full--;
if(_input.minimum_bits_until_full == 8) _interrupt_status &= ~Interrupt::ReceiveDataFull;
if(!_input.minimum_bits_until_full)
{
if((_data_register&0x3) == 0x1)
{
_interrupt_status |= Interrupt::ReceiveDataFull;
if(_is_in_input_mode) _input.minimum_bits_until_full = 9;
}
}
if(_output.bits_remaining_until_empty) _output.bits_remaining_until_empty--;
if(!_output.bits_remaining_until_empty) _interrupt_status |= Interrupt::TransmitDataEmpty;
if(_data_register == 0x3ff) _interrupt_status |= Interrupt::HighToneDetect;
else _interrupt_status &= ~Interrupt::HighToneDetect;
evaluate_interrupts();
}
inline void Tape::evaluate_interrupts()
{
if(_last_posted_interrupt_status != _interrupt_status)
{
_last_posted_interrupt_status = _interrupt_status;
if(_delegate) _delegate->tape_did_change_interrupt_status(this);
}
}
inline void Tape::clear_interrupts(uint8_t interrupts)
{
_interrupt_status &= ~interrupts;
evaluate_interrupts();
}
inline void Tape::set_is_in_input_mode(bool is_in_input_mode)
{
_is_in_input_mode = is_in_input_mode;
}
inline void Tape::set_counter(uint8_t value)
{
_output.cycles_into_pulse = 0;
_output.bits_remaining_until_empty = 0;
}
inline void Tape::set_data_register(uint8_t value)
{
_data_register = (uint16_t)((value << 2) | 1);
_output.bits_remaining_until_empty = 9;
}
inline uint8_t Tape::get_data_register()
{
return (uint8_t)(_data_register >> 2);
}
inline void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse)
{
_crossings[0] = _crossings[1];
_crossings[1] = _crossings[2];
_crossings[2] = _crossings[3];
_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 / 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;
}
}
}
inline void Tape::run_for_cycles(unsigned int number_of_cycles)
{
if(_is_enabled)
{
if(_is_in_input_mode)
{
if(_is_running)
{
TapePlayer::run_for_cycles((int)number_of_cycles);
}
}
else
{
_output.cycles_into_pulse += number_of_cycles;
while(_output.cycles_into_pulse > 1664) // 1664 = the closest you can get to 1200 baud if you're looking for something
{ // that divides the 125,000Hz clock that the sound divider runs off.
_output.cycles_into_pulse -= 1664;
push_tape_bit(1);
}
}
}
}

View File

@ -16,6 +16,8 @@
#include "../CRTMachine.hpp" #include "../CRTMachine.hpp"
#include "../Typer.hpp" #include "../Typer.hpp"
#include "Plus3.hpp" #include "Plus3.hpp"
#include "Tape.hpp"
#include "Interrupts.hpp"
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
@ -35,15 +37,6 @@ enum ROMSlot: uint8_t {
ROMSlotOS, ROMSlotDFS, ROMSlotADFS ROMSlotOS, ROMSlotDFS, ROMSlotADFS
}; };
enum Interrupt: uint8_t {
PowerOnReset = 0x02,
DisplayEnd = 0x04,
RealTimeClock = 0x08,
ReceiveDataFull = 0x10,
TransmitDataEmpty = 0x20,
HighToneDetect = 0x40
};
enum Key: uint16_t { enum Key: uint16_t {
KeySpace = 0x0000 | 0x08, KeyCopy = 0x0000 | 0x02, KeyRight = 0x0000 | 0x01, KeySpace = 0x0000 | 0x08, KeyCopy = 0x0000 | 0x02, KeyRight = 0x0000 | 0x01,
KeyDelete = 0x0010 | 0x08, KeyReturn = 0x0010 | 0x04, KeyDown = 0x0010 | 0x02, KeyLeft = 0x0010 | 0x01, KeyDelete = 0x0010 | 0x08, KeyReturn = 0x0010 | 0x04, KeyDown = 0x0010 | 0x02, KeyLeft = 0x0010 | 0x01,
@ -65,57 +58,6 @@ enum Key: uint16_t {
TerminateSequence = 0xffff, NotMapped = 0xfffe, TerminateSequence = 0xffff, NotMapped = 0xfffe,
}; };
class Tape: public Storage::Tape::TapePlayer {
public:
Tape();
inline void run_for_cycles(unsigned int number_of_cycles);
inline uint8_t get_data_register();
inline void set_data_register(uint8_t value);
inline void set_counter(uint8_t value);
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 set_is_running(bool is_running) { _is_running = is_running; }
inline void set_is_enabled(bool is_enabled) { _is_enabled = is_enabled; }
inline void set_is_in_input_mode(bool is_in_input_mode);
private:
void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
inline void push_tape_bit(uint16_t bit);
inline void get_next_tape_pulse();
struct {
int minimum_bits_until_full;
} _input;
struct {
unsigned int cycles_into_pulse;
unsigned int bits_remaining_until_empty;
} _output;
bool _is_running;
bool _is_enabled;
bool _is_in_input_mode;
inline void evaluate_interrupts();
uint16_t _data_register;
uint8_t _interrupt_status, _last_posted_interrupt_status;
Delegate *_delegate;
enum {
Long, Short, Unrecognised, Recognised
} _crossings[4];
};
class Speaker: public ::Outputs::Filter<Speaker> { class Speaker: public ::Outputs::Filter<Speaker> {
public: public:
void set_divider(uint8_t divider); void set_divider(uint8_t divider);

View File

@ -0,0 +1,27 @@
//
// Interrupts.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Interrupts_h
#define Interrupts_h
#include <cstdint>
namespace Electron {
enum Interrupt: uint8_t {
PowerOnReset = 0x02,
DisplayEnd = 0x04,
RealTimeClock = 0x08,
ReceiveDataFull = 0x10,
TransmitDataEmpty = 0x20,
HighToneDetect = 0x40
};
}
#endif /* Interrupts_h */

135
Machines/Electron/Tape.cpp Normal file
View File

@ -0,0 +1,135 @@
//
// Tape.cpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Tape.hpp"
using namespace Electron;
Tape::Tape() :
TapePlayer(2000000),
is_running_(false),
data_register_(0),
delegate_(nullptr),
output_({.bits_remaining_until_empty = 0, .cycles_into_pulse = 0}),
last_posted_interrupt_status_(0),
interrupt_status_(0)
{}
void Tape::push_tape_bit(uint16_t bit)
{
data_register_ = (uint16_t)((data_register_ >> 1) | (bit << 10));
if(input_.minimum_bits_until_full) input_.minimum_bits_until_full--;
if(input_.minimum_bits_until_full == 8) interrupt_status_ &= ~Interrupt::ReceiveDataFull;
if(!input_.minimum_bits_until_full)
{
if((data_register_&0x3) == 0x1)
{
interrupt_status_ |= Interrupt::ReceiveDataFull;
if(is_in_input_mode_) input_.minimum_bits_until_full = 9;
}
}
if(output_.bits_remaining_until_empty) output_.bits_remaining_until_empty--;
if(!output_.bits_remaining_until_empty) interrupt_status_ |= Interrupt::TransmitDataEmpty;
if(data_register_ == 0x3ff) interrupt_status_ |= Interrupt::HighToneDetect;
else interrupt_status_ &= ~Interrupt::HighToneDetect;
evaluate_interrupts();
}
void Tape::evaluate_interrupts()
{
if(last_posted_interrupt_status_ != interrupt_status_)
{
last_posted_interrupt_status_ = interrupt_status_;
if(delegate_) delegate_->tape_did_change_interrupt_status(this);
}
}
void Tape::clear_interrupts(uint8_t interrupts)
{
interrupt_status_ &= ~interrupts;
evaluate_interrupts();
}
void Tape::set_is_in_input_mode(bool is_in_input_mode)
{
is_in_input_mode_ = is_in_input_mode;
}
void Tape::set_counter(uint8_t value)
{
output_.cycles_into_pulse = 0;
output_.bits_remaining_until_empty = 0;
}
void Tape::set_data_register(uint8_t value)
{
data_register_ = (uint16_t)((value << 2) | 1);
output_.bits_remaining_until_empty = 9;
}
uint8_t Tape::get_data_register()
{
return (uint8_t)(data_register_ >> 2);
}
void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse)
{
crossings_[0] = crossings_[1];
crossings_[1] = crossings_[2];
crossings_[2] = crossings_[3];
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 / 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)
{
if(is_enabled_)
{
if(is_in_input_mode_)
{
if(is_running_)
{
TapePlayer::run_for_cycles((int)number_of_cycles);
}
}
else
{
output_.cycles_into_pulse += number_of_cycles;
while(output_.cycles_into_pulse > 1664) // 1664 = the closest you can get to 1200 baud if you're looking for something
{ // that divides the 125,000Hz clock that the sound divider runs off.
output_.cycles_into_pulse -= 1664;
push_tape_bit(1);
}
}
}
}

View File

@ -0,0 +1,72 @@
//
// Tape.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/12/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Electron_Tape_h
#define Electron_Tape_h
#include "../../Storage/Tape/Tape.hpp"
#include "Interrupts.hpp"
#include <cstdint>
namespace Electron {
class Tape: public Storage::Tape::TapePlayer {
public:
Tape();
void run_for_cycles(unsigned int number_of_cycles);
uint8_t get_data_register();
void set_data_register(uint8_t value);
void set_counter(uint8_t value);
inline uint8_t get_interrupt_status() { return interrupt_status_; }
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 set_is_running(bool is_running) { is_running_ = is_running; }
inline void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; }
void set_is_in_input_mode(bool is_in_input_mode);
private:
void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
inline void push_tape_bit(uint16_t bit);
inline void get_next_tape_pulse();
struct {
int minimum_bits_until_full;
} input_;
struct {
unsigned int cycles_into_pulse;
unsigned int bits_remaining_until_empty;
} output_;
bool is_running_;
bool is_enabled_;
bool is_in_input_mode_;
inline void evaluate_interrupts();
uint16_t data_register_;
uint8_t interrupt_status_, last_posted_interrupt_status_;
Delegate *delegate_;
enum {
Long, Short, Unrecognised, Recognised
} crossings_[4];
};
}
#endif /* Electron_Tape_h */

View File

@ -383,6 +383,7 @@
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; };
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; }; 4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; };
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; }; 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; };
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; };
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; };
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; };
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; };
@ -892,6 +893,9 @@
4BD69F931D98760000243FE1 /* AcornADF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AcornADF.hpp; sourceTree = "<group>"; }; 4BD69F931D98760000243FE1 /* AcornADF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AcornADF.hpp; sourceTree = "<group>"; };
4BE77A2C1D84ADFB00BC3827 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = File.cpp; path = ../../StaticAnalyser/Commodore/File.cpp; sourceTree = "<group>"; }; 4BE77A2C1D84ADFB00BC3827 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = File.cpp; path = ../../StaticAnalyser/Commodore/File.cpp; sourceTree = "<group>"; };
4BE77A2D1D84ADFB00BC3827 /* File.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = File.hpp; path = ../../StaticAnalyser/Commodore/File.hpp; sourceTree = "<group>"; }; 4BE77A2D1D84ADFB00BC3827 /* File.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = File.hpp; path = ../../StaticAnalyser/Commodore/File.hpp; sourceTree = "<group>"; };
4BEA525D1DF33323007E74F2 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = Electron/Tape.cpp; sourceTree = "<group>"; };
4BEA525F1DF333D8007E74F2 /* Tape.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = Electron/Tape.hpp; sourceTree = "<group>"; };
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Interrupts.hpp; path = Electron/Interrupts.hpp; sourceTree = "<group>"; };
4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; };
4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; };
@ -1052,10 +1056,13 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B2E2D9B1C3A070400138695 /* Electron.cpp */, 4B2E2D9B1C3A070400138695 /* Electron.cpp */,
4B2E2D9C1C3A070400138695 /* Electron.hpp */,
4B30512E1D98ACC600B4FED8 /* Plus3.cpp */, 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */,
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */, 4BEA525D1DF33323007E74F2 /* Tape.cpp */,
4BC8A62B1DCE60E000DAC693 /* Typer.cpp */, 4BC8A62B1DCE60E000DAC693 /* Typer.cpp */,
4B2E2D9C1C3A070400138695 /* Electron.hpp */,
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */,
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */,
4BEA525F1DF333D8007E74F2 /* Tape.hpp */,
); );
name = Electron; name = Electron;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2314,6 +2321,7 @@
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */, 4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
4BC8A62D1DCE60E000DAC693 /* Typer.cpp in Sources */, 4BC8A62D1DCE60E000DAC693 /* Typer.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */, 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,