1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +00:00

Made an attempt to eliminate creeping tape processing accuracy misses, which implied factoring out the GCM and LCD functions, which I then felt didn't really amount to signal processing.

This commit is contained in:
Thomas Harte 2016-07-29 05:19:01 -04:00
parent 5c1614ce7b
commit e55db0cfe8
8 changed files with 197 additions and 34 deletions

34
NumberTheory/Factors.cpp Normal file
View File

@ -0,0 +1,34 @@
//
// Factors.cpp
// Clock Signal
//
// Created by Thomas Harte on 29/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Factors.hpp"
unsigned int NumberTheory::greatest_common_divisor(unsigned int a, unsigned int b)
{
if(a < b)
{
unsigned int swap = b;
b = a;
a = swap;
}
while(1) {
if(!a) return b;
if(!b) return a;
unsigned int remainder = a%b;
a = b;
b = remainder;
}
}
unsigned int NumberTheory::least_common_multiple(unsigned int a, unsigned int b)
{
unsigned int gcd = greatest_common_divisor(a, b);
return (a*b) / gcd;
}

17
NumberTheory/Factors.hpp Normal file
View File

@ -0,0 +1,17 @@
//
// Factors.hpp
// Clock Signal
//
// Created by Thomas Harte on 29/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Factors_hpp
#define Factors_hpp
namespace NumberTheory {
unsigned int greatest_common_divisor(unsigned int a, unsigned int b);
unsigned int least_common_multiple(unsigned int a, unsigned int b);
}
#endif /* Factors_hpp */

View File

@ -314,6 +314,7 @@
4BB299F71B587D8400A49093 /* txan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EB1B587D8400A49093 /* txan */; };
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 */; };
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 */; };
@ -700,6 +701,8 @@
4BB298EB1B587D8400A49093 /* txan */ = {isa = PBXFileReference; lastKnownFileType = file; path = txan; sourceTree = "<group>"; };
4BB298EC1B587D8400A49093 /* txsn */ = {isa = PBXFileReference; lastKnownFileType = file; path = txsn; 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>"; };
4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.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>"; };
@ -1291,6 +1294,15 @@
path = "Wolfgang Lorenz 6502 test suite";
sourceTree = "<group>";
};
4BB697C81D4B559300248BDF /* NumberTheory */ = {
isa = PBXGroup;
children = (
4BB697C51D4B558F00248BDF /* Factors.cpp */,
4BB697C61D4B558F00248BDF /* Factors.hpp */,
);
name = NumberTheory;
sourceTree = "<group>";
};
4BB73E951B587A5100552FC2 = {
isa = PBXGroup;
children = (
@ -1300,6 +1312,7 @@
4BB73EC01B587A5100552FC2 /* Clock SignalUITests */,
4BC9DF4A1D04691600F44158 /* Components */,
4BB73EDC1B587CA500552FC2 /* Machines */,
4BB697C81D4B559300248BDF /* NumberTheory */,
4B366DFD1B5C165F0026627B /* Outputs */,
4BB73EDD1B587CA500552FC2 /* Processors */,
4BB73E9F1B587A5100552FC2 /* Products */,
@ -1876,6 +1889,7 @@
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */,
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */,
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */,
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */,

View File

@ -13,11 +13,25 @@
namespace SignalProcessing {
/*!
Allows a repeating action running at an input rate to determine how many times it should
trigger an action that runs at an unrelated output rate; therefore it allows something with one
clock to sample something with another.
Uses a Bresenham-like error term internally for full-integral storage with no drift.
*/
class Stepper
{
public:
/*!
Establishes a stepper with a one-to-one conversion rate.
*/
Stepper() : Stepper(1,1) {}
/*!
Establishes a stepper that will receive steps at the @c input_rate and dictate the number
of steps that should be taken at the @c output_rate.
*/
Stepper(uint64_t output_rate, uint64_t input_rate) :
accumulated_error_(0),
input_rate_(input_rate),
@ -26,6 +40,11 @@ class Stepper
adjustment_up_((int64_t)(output_rate % input_rate) << 1),
adjustment_down_((int64_t)input_rate << 1) {}
/*!
Advances one step at the input rate.
@returns the number of output steps.
*/
inline uint64_t step()
{
uint64_t update = whole_step_;
@ -38,11 +57,34 @@ class Stepper
return update;
}
/*!
Advances by @c number_of_steps steps at the input rate.
@returns the number of output steps.
*/
inline uint64_t step(uint64_t number_of_steps)
{
uint64_t update = whole_step_ * number_of_steps;
accumulated_error_ += adjustment_up_ * (int64_t)number_of_steps;
if(accumulated_error_ > 0)
{
update += 1 + (uint64_t)(accumulated_error_ / adjustment_down_);
accumulated_error_ = (accumulated_error_ % adjustment_down_) - adjustment_down_;
}
return update;
}
/*!
@returns the output rate.
*/
inline uint64_t get_output_rate()
{
return output_rate_;
}
/*!
@returns the input rate.
*/
inline uint64_t get_input_rate()
{
return input_rate_;

View File

@ -12,13 +12,48 @@ using namespace Storage;
DiskDrive::DiskDrive(unsigned int clock_rate, unsigned int revolutions_per_minute) :
_clock_rate(clock_rate),
_revolutions_per_minute(revolutions_per_minute) {}
_revolutions_per_minute(revolutions_per_minute),
_head_position(0) {}
void DiskDrive::set_expected_bit_length(Time bit_length)
{
_bit_length = bit_length;
}
void DiskDrive::set_disk(std::shared_ptr<Disk> disk)
{
_disk = disk;
// _track.reset();
}
bool DiskDrive::has_disk()
{
return (bool)_disk;
}
bool DiskDrive::get_is_track_zero()
{
return _head_position == 0;
}
void DiskDrive::step(int direction)
{
_head_position = std::max(_head_position + direction, 0);
_track = _disk->get_track_at_position((unsigned int)_head_position);
}
void DiskDrive::digital_phase_locked_loop_output_bit(int value)
{
process_input_bit(value, _cycles_since_index_hole);
}
void DiskDrive::run_for_cycles(unsigned int number_of_cycles)
{
if(has_disk())
{
while(number_of_cycles--)
{
}
}
}

View File

@ -11,6 +11,7 @@
#include "Disk.hpp"
#include "DigitalPhaseLockedLoop.hpp"
#include "../../SignalProcessing/Stepper.hpp"
namespace Storage {
@ -40,6 +41,20 @@ class DiskDrive: public DigitalPhaseLockedLoop::Delegate {
Time _bit_length;
unsigned int _clock_rate;
unsigned int _revolutions_per_minute;
std::shared_ptr<DigitalPhaseLockedLoop> _pll;
std::shared_ptr<Disk> _disk;
std::shared_ptr<Track> _track;
int _head_position;
unsigned int _cycles_since_index_hole;
void install_track();
struct {
Track::Event current_event;
std::unique_ptr<SignalProcessing::Stepper> pulse_stepper;
uint32_t time_into_pulse;
} _input;
};
}

View File

@ -7,34 +7,10 @@
//
#include "PCMTrack.hpp"
#include "../../NumberTheory/Factors.hpp"
using namespace Storage;
unsigned int greatest_common_divisor(unsigned int a, unsigned int b)
{
if(a < b)
{
unsigned int swap = b;
b = a;
a = swap;
}
while(1) {
if(!a) return b;
if(!b) return a;
unsigned int remainder = a%b;
a = b;
b = remainder;
}
}
unsigned int least_common_multiple(unsigned int a, unsigned int b)
{
unsigned int gcd = greatest_common_divisor(a, b);
return (a*b) / gcd;
}
PCMTrack::PCMTrack(std::vector<PCMSegment> segments)
{
_segments = std::move(segments);
@ -87,7 +63,7 @@ void PCMTrack::fix_length()
_track_clock_rate = _segments[0].length_of_a_bit.clock_rate;
for(size_t c = 1; c < _segments.size(); c++)
{
_track_clock_rate = least_common_multiple(_track_clock_rate, _segments[c].length_of_a_bit.clock_rate);
_track_clock_rate = NumberTheory::least_common_multiple(_track_clock_rate, _segments[c].length_of_a_bit.clock_rate);
}
// therby determine the total length, storing it to next_event as the track-total divisor

View File

@ -7,6 +7,7 @@
//
#include "Tape.hpp"
#include "../../NumberTheory/Factors.hpp"
using namespace Storage;
@ -22,6 +23,8 @@ TapePlayer::TapePlayer(unsigned int input_clock_rate) :
void TapePlayer::set_tape(std::shared_ptr<Storage::Tape> tape)
{
_tape = tape;
_input.pulse_stepper.reset();
get_next_pulse();
}
@ -32,7 +35,18 @@ bool TapePlayer::has_tape()
void TapePlayer::get_next_pulse()
{
_input.time_into_pulse = 0;
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();
else
@ -41,6 +55,24 @@ void TapePlayer::get_next_pulse()
_input.current_pulse.length.clock_rate = 1;
_input.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));
@ -51,13 +83,10 @@ void TapePlayer::run_for_cycles(unsigned int number_of_cycles)
{
if(has_tape())
{
while(number_of_cycles--)
_input.time_into_pulse += (unsigned int)_input.pulse_stepper->step(number_of_cycles);
while(_input.time_into_pulse >= _input.current_pulse.length.length)
{
_input.time_into_pulse += (unsigned int)_input.pulse_stepper->step();
while(_input.time_into_pulse >= _input.current_pulse.length.length)
{
run_for_input_pulse();
}
run_for_input_pulse();
}
}
}
@ -66,4 +95,5 @@ void TapePlayer::run_for_input_pulse()
{
process_input_pulse(_input.current_pulse);
get_next_pulse();
_input.time_into_pulse = 0;
}