1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Factored out the stuff of running a timed event loop from the TapePlayer.

This commit is contained in:
Thomas Harte 2016-07-29 07:15:46 -04:00
parent e55db0cfe8
commit 0e581c7607
5 changed files with 138 additions and 57 deletions

View File

@ -315,6 +315,7 @@
4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; };
4BB299F91B587D8400A49093 /* tyan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298ED1B587D8400A49093 /* tyan */; };
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 */; };
4BB73EA71B587A5100552FC2 /* Atari2600Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EA51B587A5100552FC2 /* Atari2600Document.xib */; };
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>"; };
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>"; };
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; };
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>"; };
@ -967,9 +970,11 @@
4B69FB391C4D908A00B5F0AA /* Storage */ = {
isa = PBXGroup;
children = (
4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */,
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */,
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */,
4BAB62AA1D3272D200DF5BA0 /* Disk */,
4B69FB3A1C4D908A00B5F0AA /* Tape */,
4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */,
);
name = Storage;
path = ../../Storage;
@ -1914,6 +1919,7 @@
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */,
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */,
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */,
4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */,
4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */,
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,

View File

@ -17,14 +17,13 @@ void Tape::seek(Time seek_time)
}
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)
{
_tape = tape;
_input.pulse_stepper.reset();
reset_timer();
get_next_pulse();
}
@ -35,65 +34,34 @@ bool TapePlayer::has_tape()
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
if(_tape)
_input.current_pulse = _tape->get_next_pulse();
_current_pulse = _tape->get_next_pulse();
else
{
_input.current_pulse.length.length = 1;
_input.current_pulse.length.clock_rate = 1;
_input.current_pulse.type = Storage::Tape::Pulse::Zero;
_current_pulse.length.length = 1;
_current_pulse.length.clock_rate = 1;
_current_pulse.type = Storage::Tape::Pulse::Zero;
}
// if there was any time left over, map into the new time base
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));
}
set_next_event_time_interval(_current_pulse.length);
}
void TapePlayer::run_for_cycles(unsigned int number_of_cycles)
{
if(has_tape())
{
_input.time_into_pulse += (unsigned int)_input.pulse_stepper->step(number_of_cycles);
while(_input.time_into_pulse >= _input.current_pulse.length.length)
{
run_for_input_pulse();
}
::TimedEventLoop::run_for_cycles(number_of_cycles);
}
}
void TapePlayer::run_for_input_pulse()
{
process_input_pulse(_input.current_pulse);
get_next_pulse();
_input.time_into_pulse = 0;
jump_to_next_event();
}
void TapePlayer::process_next_event()
{
process_input_pulse(_current_pulse);
get_next_pulse();
}

View File

@ -10,8 +10,7 @@
#define Tape_hpp
#include <memory>
#include "../../SignalProcessing/Stepper.hpp"
#include "../Storage.hpp"
#include "../TimedEventLoop.hpp"
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
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:
TapePlayer(unsigned int input_clock_rate);
@ -59,18 +58,14 @@ class TapePlayer {
void run_for_input_pulse();
protected:
virtual void process_next_event();
virtual void process_input_pulse(Tape::Pulse pulse) = 0;
private:
inline void get_next_pulse();
unsigned int _input_clock_rate;
std::shared_ptr<Storage::Tape> _tape;
struct {
Tape::Pulse current_pulse;
std::unique_ptr<SignalProcessing::Stepper> pulse_stepper;
uint32_t time_into_pulse;
} _input;
Tape::Pulse _current_pulse;
};
}

View 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));
}
}

View 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 */