mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 07:30:21 +00:00
Factored out the stuff of running a timed event loop from the TapePlayer.
This commit is contained in:
parent
e55db0cfe8
commit
0e581c7607
@ -315,6 +315,7 @@
|
|||||||
4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; };
|
4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; };
|
||||||
4BB299F91B587D8400A49093 /* tyan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298ED1B587D8400A49093 /* tyan */; };
|
4BB299F91B587D8400A49093 /* tyan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298ED1B587D8400A49093 /* tyan */; };
|
||||||
4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C51D4B558F00248BDF /* Factors.cpp */; };
|
4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C51D4B558F00248BDF /* Factors.cpp */; };
|
||||||
|
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; };
|
||||||
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; };
|
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; };
|
||||||
4BB73EA71B587A5100552FC2 /* Atari2600Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EA51B587A5100552FC2 /* Atari2600Document.xib */; };
|
4BB73EA71B587A5100552FC2 /* Atari2600Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EA51B587A5100552FC2 /* Atari2600Document.xib */; };
|
||||||
4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EA81B587A5100552FC2 /* Assets.xcassets */; };
|
4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EA81B587A5100552FC2 /* Assets.xcassets */; };
|
||||||
@ -703,6 +704,8 @@
|
|||||||
4BB298ED1B587D8400A49093 /* tyan */ = {isa = PBXFileReference; lastKnownFileType = file; path = tyan; sourceTree = "<group>"; };
|
4BB298ED1B587D8400A49093 /* tyan */ = {isa = PBXFileReference; lastKnownFileType = file; path = tyan; sourceTree = "<group>"; };
|
||||||
4BB697C51D4B558F00248BDF /* Factors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Factors.cpp; path = ../../NumberTheory/Factors.cpp; sourceTree = "<group>"; };
|
4BB697C51D4B558F00248BDF /* Factors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Factors.cpp; path = ../../NumberTheory/Factors.cpp; sourceTree = "<group>"; };
|
||||||
4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.hpp; sourceTree = "<group>"; };
|
4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.hpp; sourceTree = "<group>"; };
|
||||||
|
4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = "<group>"; };
|
||||||
|
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = "<group>"; };
|
||||||
4BB73E9E1B587A5100552FC2 /* Clock Signal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Clock Signal.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
4BB73E9E1B587A5100552FC2 /* Clock Signal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Clock Signal.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4BB73EA11B587A5100552FC2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
4BB73EA11B587A5100552FC2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
4BB73EA61B587A5100552FC2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Atari2600Document.xib; sourceTree = "<group>"; };
|
4BB73EA61B587A5100552FC2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Atari2600Document.xib; sourceTree = "<group>"; };
|
||||||
@ -967,9 +970,11 @@
|
|||||||
4B69FB391C4D908A00B5F0AA /* Storage */ = {
|
4B69FB391C4D908A00B5F0AA /* Storage */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */,
|
||||||
|
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */,
|
||||||
|
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */,
|
||||||
4BAB62AA1D3272D200DF5BA0 /* Disk */,
|
4BAB62AA1D3272D200DF5BA0 /* Disk */,
|
||||||
4B69FB3A1C4D908A00B5F0AA /* Tape */,
|
4B69FB3A1C4D908A00B5F0AA /* Tape */,
|
||||||
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */,
|
|
||||||
);
|
);
|
||||||
name = Storage;
|
name = Storage;
|
||||||
path = ../../Storage;
|
path = ../../Storage;
|
||||||
@ -1914,6 +1919,7 @@
|
|||||||
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */,
|
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */,
|
||||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
|
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
|
||||||
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */,
|
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */,
|
||||||
|
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */,
|
||||||
4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */,
|
4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */,
|
||||||
4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */,
|
4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */,
|
||||||
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
|
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
|
||||||
|
@ -17,14 +17,13 @@ void Tape::seek(Time seek_time)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TapePlayer::TapePlayer(unsigned int input_clock_rate) :
|
TapePlayer::TapePlayer(unsigned int input_clock_rate) :
|
||||||
_input_clock_rate(input_clock_rate)
|
TimedEventLoop(input_clock_rate)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void TapePlayer::set_tape(std::shared_ptr<Storage::Tape> tape)
|
void TapePlayer::set_tape(std::shared_ptr<Storage::Tape> tape)
|
||||||
{
|
{
|
||||||
_tape = tape;
|
_tape = tape;
|
||||||
_input.pulse_stepper.reset();
|
reset_timer();
|
||||||
|
|
||||||
get_next_pulse();
|
get_next_pulse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,65 +34,34 @@ bool TapePlayer::has_tape()
|
|||||||
|
|
||||||
void TapePlayer::get_next_pulse()
|
void TapePlayer::get_next_pulse()
|
||||||
{
|
{
|
||||||
unsigned int previous_clock_rate = 0;
|
|
||||||
|
|
||||||
// figure out how much time has been run since the last bit ended
|
|
||||||
if(_input.pulse_stepper == nullptr)
|
|
||||||
_input.time_into_pulse = 0;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_input.time_into_pulse -= _input.current_pulse.length.length;
|
|
||||||
previous_clock_rate = _input.current_pulse.length.clock_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the new pulse
|
// get the new pulse
|
||||||
if(_tape)
|
if(_tape)
|
||||||
_input.current_pulse = _tape->get_next_pulse();
|
_current_pulse = _tape->get_next_pulse();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_input.current_pulse.length.length = 1;
|
_current_pulse.length.length = 1;
|
||||||
_input.current_pulse.length.clock_rate = 1;
|
_current_pulse.length.clock_rate = 1;
|
||||||
_input.current_pulse.type = Storage::Tape::Pulse::Zero;
|
_current_pulse.type = Storage::Tape::Pulse::Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there was any time left over, map into the new time base
|
set_next_event_time_interval(_current_pulse.length);
|
||||||
if(_input.pulse_stepper && _input.time_into_pulse)
|
|
||||||
{
|
|
||||||
// simplify the quotient
|
|
||||||
unsigned int common_divisor = NumberTheory::greatest_common_divisor(_input.time_into_pulse, previous_clock_rate);
|
|
||||||
_input.time_into_pulse /= common_divisor;
|
|
||||||
previous_clock_rate /= common_divisor;
|
|
||||||
|
|
||||||
// build a quotient that is the sum of the time overrun plus the incoming time and adjust the time overrun
|
|
||||||
// to be in terms of the new quotient
|
|
||||||
unsigned int denominator = NumberTheory::least_common_multiple(previous_clock_rate, _input.current_pulse.length.clock_rate);
|
|
||||||
_input.current_pulse.length.length *= denominator / _input.current_pulse.length.clock_rate;
|
|
||||||
_input.current_pulse.length.clock_rate = denominator;
|
|
||||||
_input.time_into_pulse *= denominator / previous_clock_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust stepper if required
|
|
||||||
if(_input.pulse_stepper == nullptr || _input.current_pulse.length.clock_rate != _input.pulse_stepper->get_output_rate())
|
|
||||||
{
|
|
||||||
_input.pulse_stepper.reset(new SignalProcessing::Stepper(_input.current_pulse.length.clock_rate, _input_clock_rate));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TapePlayer::run_for_cycles(unsigned int number_of_cycles)
|
void TapePlayer::run_for_cycles(unsigned int number_of_cycles)
|
||||||
{
|
{
|
||||||
if(has_tape())
|
if(has_tape())
|
||||||
{
|
{
|
||||||
_input.time_into_pulse += (unsigned int)_input.pulse_stepper->step(number_of_cycles);
|
::TimedEventLoop::run_for_cycles(number_of_cycles);
|
||||||
while(_input.time_into_pulse >= _input.current_pulse.length.length)
|
|
||||||
{
|
|
||||||
run_for_input_pulse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TapePlayer::run_for_input_pulse()
|
void TapePlayer::run_for_input_pulse()
|
||||||
{
|
{
|
||||||
process_input_pulse(_input.current_pulse);
|
jump_to_next_event();
|
||||||
get_next_pulse();
|
}
|
||||||
_input.time_into_pulse = 0;
|
|
||||||
|
void TapePlayer::process_next_event()
|
||||||
|
{
|
||||||
|
process_input_pulse(_current_pulse);
|
||||||
|
get_next_pulse();
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
#define Tape_hpp
|
#define Tape_hpp
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "../../SignalProcessing/Stepper.hpp"
|
#include "../TimedEventLoop.hpp"
|
||||||
#include "../Storage.hpp"
|
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ class Tape {
|
|||||||
Will call @c process_input_pulse instantaneously upon reaching *the end* of a pulse. Therefore a subclass
|
Will call @c process_input_pulse instantaneously upon reaching *the end* of a pulse. Therefore a subclass
|
||||||
can decode pulses into data within process_input_pulse, using the supplied pulse's @c length and @c type.
|
can decode pulses into data within process_input_pulse, using the supplied pulse's @c length and @c type.
|
||||||
*/
|
*/
|
||||||
class TapePlayer {
|
class TapePlayer: public TimedEventLoop {
|
||||||
public:
|
public:
|
||||||
TapePlayer(unsigned int input_clock_rate);
|
TapePlayer(unsigned int input_clock_rate);
|
||||||
|
|
||||||
@ -59,18 +58,14 @@ class TapePlayer {
|
|||||||
void run_for_input_pulse();
|
void run_for_input_pulse();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void process_next_event();
|
||||||
virtual void process_input_pulse(Tape::Pulse pulse) = 0;
|
virtual void process_input_pulse(Tape::Pulse pulse) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline void get_next_pulse();
|
inline void get_next_pulse();
|
||||||
|
|
||||||
unsigned int _input_clock_rate;
|
|
||||||
std::shared_ptr<Storage::Tape> _tape;
|
std::shared_ptr<Storage::Tape> _tape;
|
||||||
struct {
|
Tape::Pulse _current_pulse;
|
||||||
Tape::Pulse current_pulse;
|
|
||||||
std::unique_ptr<SignalProcessing::Stepper> pulse_stepper;
|
|
||||||
uint32_t time_into_pulse;
|
|
||||||
} _input;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
72
Storage/TimedEventLoop.cpp
Normal file
72
Storage/TimedEventLoop.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// TimedEventLoop.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 29/07/2016.
|
||||||
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TimedEventLoop.hpp"
|
||||||
|
#include "../NumberTheory/Factors.hpp"
|
||||||
|
|
||||||
|
using namespace Storage;
|
||||||
|
|
||||||
|
TimedEventLoop::TimedEventLoop(unsigned int input_clock_rate) :
|
||||||
|
_input_clock_rate(input_clock_rate) {}
|
||||||
|
|
||||||
|
void TimedEventLoop::run_for_cycles(unsigned int number_of_cycles)
|
||||||
|
{
|
||||||
|
_time_into_interval += (unsigned int)_stepper->step(number_of_cycles);
|
||||||
|
while(_time_into_interval >= _event_interval.length)
|
||||||
|
{
|
||||||
|
process_next_event();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimedEventLoop::reset_timer()
|
||||||
|
{
|
||||||
|
_time_into_interval = 0;
|
||||||
|
_stepper.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimedEventLoop::jump_to_next_event()
|
||||||
|
{
|
||||||
|
reset_timer();
|
||||||
|
process_next_event();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimedEventLoop::set_next_event_time_interval(Time interval)
|
||||||
|
{
|
||||||
|
// figure out how much time has been run since the last bit ended
|
||||||
|
if(_stepper)
|
||||||
|
{
|
||||||
|
_time_into_interval -= _event_interval.length;
|
||||||
|
if(_time_into_interval)
|
||||||
|
{
|
||||||
|
// simplify the quotient
|
||||||
|
unsigned int common_divisor = NumberTheory::greatest_common_divisor(_time_into_interval, _event_interval.clock_rate);
|
||||||
|
_time_into_interval /= common_divisor;
|
||||||
|
_event_interval.clock_rate /= common_divisor;
|
||||||
|
|
||||||
|
// build a quotient that is the sum of the time overrun plus the incoming time and adjust the time overrun
|
||||||
|
// to be in terms of the new quotient
|
||||||
|
unsigned int denominator = NumberTheory::least_common_multiple(_event_interval.clock_rate, interval.clock_rate);
|
||||||
|
interval.length *= denominator / interval.clock_rate;
|
||||||
|
interval.clock_rate = denominator;
|
||||||
|
_time_into_interval *= denominator / _event_interval.clock_rate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_time_into_interval = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store new interval
|
||||||
|
_event_interval = interval;
|
||||||
|
|
||||||
|
// adjust stepper if required
|
||||||
|
if(!_stepper || _event_interval.clock_rate != _stepper->get_output_rate())
|
||||||
|
{
|
||||||
|
_stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate));
|
||||||
|
}
|
||||||
|
}
|
40
Storage/TimedEventLoop.hpp
Normal file
40
Storage/TimedEventLoop.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// TimedEventLoop.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 29/07/2016.
|
||||||
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TimedEventLoop_hpp
|
||||||
|
#define TimedEventLoop_hpp
|
||||||
|
|
||||||
|
#include "Storage.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "../SignalProcessing/Stepper.hpp"
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
|
||||||
|
class TimedEventLoop {
|
||||||
|
public:
|
||||||
|
TimedEventLoop(unsigned int input_clock_rate);
|
||||||
|
void run_for_cycles(unsigned int number_of_cycles);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void reset_timer();
|
||||||
|
void set_next_event_time_interval(Time interval);
|
||||||
|
void jump_to_next_event();
|
||||||
|
|
||||||
|
virtual void process_next_event() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int _input_clock_rate;
|
||||||
|
Time _event_interval;
|
||||||
|
std::unique_ptr<SignalProcessing::Stepper> _stepper;
|
||||||
|
uint32_t _time_into_interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* TimedEventLoop_hpp */
|
Loading…
x
Reference in New Issue
Block a user