diff --git a/NumberTheory/Factors.cpp b/NumberTheory/Factors.cpp index 4c44cfbfc..d383efc79 100644 --- a/NumberTheory/Factors.cpp +++ b/NumberTheory/Factors.cpp @@ -29,6 +29,8 @@ unsigned int NumberTheory::greatest_common_divisor(unsigned int a, unsigned int unsigned int NumberTheory::least_common_multiple(unsigned int a, unsigned int b) { + if(a == b) return a; + unsigned int gcd = greatest_common_divisor(a, b); - return (a / gcd) * (b / gcd); + return (a / gcd) * (b / gcd) * gcd; } diff --git a/Storage/Disk/Disk.hpp b/Storage/Disk/Disk.hpp index 7aaf98f79..08e3ee607 100644 --- a/Storage/Disk/Disk.hpp +++ b/Storage/Disk/Disk.hpp @@ -37,9 +37,16 @@ class Track { }; /*! - Returns the next event that will be detected during rotation of this disk. + @returns the next event that will be detected during rotation of this disk. */ virtual Event get_next_event() = 0; + + /*! + Jumps to the event latest offset that is less than or equal to the input time. + + @returns the time jumped to. + */ + virtual Time seek_to(Time time_since_index_hole) = 0; }; /*! diff --git a/Storage/Disk/DiskDrive.cpp b/Storage/Disk/DiskDrive.cpp index 90a11a977..12c2d5c55 100644 --- a/Storage/Disk/DiskDrive.cpp +++ b/Storage/Disk/DiskDrive.cpp @@ -17,7 +17,11 @@ DiskDrive::DiskDrive(unsigned int clock_rate, unsigned int clock_rate_multiplier _head_position(0), TimedEventLoop(clock_rate * clock_rate_multiplier) -{} +{ + _rotational_multiplier.length = 60; + _rotational_multiplier.clock_rate = revolutions_per_minute; + _rotational_multiplier.simplify(); +} void DiskDrive::set_expected_bit_length(Time bit_length) { @@ -33,7 +37,7 @@ void DiskDrive::set_expected_bit_length(Time bit_length) void DiskDrive::set_disk(std::shared_ptr disk) { _disk = disk; - set_track(); + set_track(Time()); } bool DiskDrive::has_disk() @@ -49,14 +53,32 @@ bool DiskDrive::get_is_track_zero() void DiskDrive::step(int direction) { _head_position = std::max(_head_position + direction, 0); - set_track(); + Time extra_time = get_time_into_next_event() / _rotational_multiplier; + extra_time.simplify(); + _time_into_track += extra_time; + set_track(_time_into_track); } -void DiskDrive::set_track() +void DiskDrive::set_track(Time initial_offset) { _track = _disk->get_track_at_position((unsigned int)_head_position); + // TODO: probably a better implementation of the empty track? + Time offset; + if(_track && _time_into_track.length > 0) + { + Time time_found = _track->seek_to(_time_into_track).simplify(); + offset = (_time_into_track - time_found).simplify(); + _time_into_track = time_found; + } + else + { + offset = _time_into_track; + _time_into_track.set_zero(); + } + reset_timer(); get_next_event(); + reset_timer_to_offset(offset * _rotational_multiplier); } void DiskDrive::run_for_cycles(int number_of_cycles) @@ -88,11 +110,7 @@ void DiskDrive::get_next_event() // divide interval, which is in terms of a rotation of the disk, by rotation speed, and // convert it into revolutions per second - Time event_interval = _current_event.length; - event_interval.length *= 60; - event_interval.clock_rate *= _revolutions_per_minute; - event_interval.simplify(); - set_next_event_time_interval(event_interval); + set_next_event_time_interval(_current_event.length * _rotational_multiplier); } void DiskDrive::process_next_event() @@ -101,9 +119,11 @@ void DiskDrive::process_next_event() { case Track::Event::FluxTransition: _pll->add_pulse(); + _time_into_track += _current_event.length; break; case Track::Event::IndexHole: _cycles_since_index_hole = 0; + _time_into_track.set_zero(); process_index_hole(); break; } diff --git a/Storage/Disk/DiskDrive.hpp b/Storage/Disk/DiskDrive.hpp index c207a2729..ee7674d0b 100644 --- a/Storage/Disk/DiskDrive.hpp +++ b/Storage/Disk/DiskDrive.hpp @@ -93,16 +93,18 @@ class DiskDrive: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop unsigned int _clock_rate; unsigned int _clock_rate_multiplier; unsigned int _revolutions_per_minute; + Time _rotational_multiplier; std::shared_ptr _pll; std::shared_ptr _disk; std::shared_ptr _track; int _head_position; unsigned int _cycles_since_index_hole; - void set_track(); + void set_track(Time initial_offset); inline void get_next_event(); Track::Event _current_event; + Time _time_into_track; }; } diff --git a/Storage/Disk/PCMTrack.cpp b/Storage/Disk/PCMTrack.cpp index f909c33a0..2a8d53f56 100644 --- a/Storage/Disk/PCMTrack.cpp +++ b/Storage/Disk/PCMTrack.cpp @@ -61,6 +61,45 @@ PCMTrack::Event PCMTrack::get_next_event() return _next_event; } +Time PCMTrack::seek_to(Time time_since_index_hole) +{ + _segment_pointer = 0; + + // pick a common clock rate for counting time on this track and multiply up the time being sought appropriately + Time time_so_far; + time_so_far.clock_rate = NumberTheory::least_common_multiple(_next_event.length.clock_rate, time_since_index_hole.clock_rate); + time_since_index_hole.length *= time_so_far.clock_rate / time_since_index_hole.clock_rate; + time_since_index_hole.clock_rate = time_so_far.clock_rate; + + while(_segment_pointer < _segments.size()) + { + // determine how long this segment is in terms of the master clock + unsigned int clock_multiplier = time_so_far.clock_rate / _next_event.length.clock_rate; + unsigned int bit_length = ((clock_multiplier / _track_clock_rate) / _segments[_segment_pointer].length_of_a_bit.clock_rate) * _segments[_segment_pointer].length_of_a_bit.length; + unsigned int time_in_this_segment = bit_length * _segments[_segment_pointer].number_of_bits; + + // if this segment goes on longer than the time being sought, end here + unsigned int time_remaining = time_since_index_hole.length - time_so_far.length; + if(time_in_this_segment >= time_remaining) + { + // get the amount of time actually to move into this segment + unsigned int time_found = time_remaining - (time_remaining % bit_length); + + // resolve that into the stateful bit count + _bit_pointer = 1 + (time_remaining / bit_length); + + // update and return the time sought to + time_so_far.length += time_found; + return time_so_far; + } + + // otherwise, accumulate time and keep moving + time_so_far.length += time_in_this_segment; + _segment_pointer++; + } + return time_since_index_hole; +} + void PCMTrack::fix_length() { // find the least common multiple of all segment clock rates diff --git a/Storage/Disk/PCMTrack.hpp b/Storage/Disk/PCMTrack.hpp index f16c0ca7d..99e426281 100644 --- a/Storage/Disk/PCMTrack.hpp +++ b/Storage/Disk/PCMTrack.hpp @@ -47,6 +47,7 @@ class PCMTrack: public Track { // as per @c Track Event get_next_event(); + Time seek_to(Time time_since_index_hole); private: // storage for the segments that describe this track diff --git a/Storage/Storage.hpp b/Storage/Storage.hpp index 5797fba34..c5aa45f8d 100644 --- a/Storage/Storage.hpp +++ b/Storage/Storage.hpp @@ -19,27 +19,109 @@ namespace Storage { */ struct Time { unsigned int length, clock_rate; + Time() : length(0), clock_rate(1) {} /*! Reduces this @c Time to its simplest form — eliminates all common factors from @c length and @c clock_rate. */ - inline void simplify() + inline Time &simplify() { unsigned int common_divisor = NumberTheory::greatest_common_divisor(length, clock_rate); length /= common_divisor; clock_rate /= common_divisor; + return *this; } /*! - Returns the floating point conversion of this @c Time. This will often be less precise. + @returns the floating point conversion of this @c Time. This will often be less precise. */ inline float get_float() { return (float)length / (float)clock_rate; } + + inline bool operator < (Time other) + { + return other.clock_rate * length < clock_rate * other.length; + } + + inline Time operator + (Time other) + { + Time result; + result.clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate); + result.length = length * (result.clock_rate / clock_rate) + other.length * (result.clock_rate / other.clock_rate); + return result; + } + + inline Time &operator += (Time other) + { + unsigned int combined_clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate); + length = length * (combined_clock_rate / clock_rate) + other.length * (combined_clock_rate / other.clock_rate); + clock_rate = combined_clock_rate; + return *this; + } + + inline Time operator - (Time other) + { + Time result; + result.clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate); + result.length = length * (result.clock_rate / clock_rate) - other.length * (result.clock_rate / other.clock_rate); + return result; + } + + inline Time operator -= (Time other) + { + unsigned int combined_clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate); + length = length * (combined_clock_rate / clock_rate) - other.length * (combined_clock_rate / other.clock_rate); + clock_rate = combined_clock_rate; + return *this; + } + + inline Time operator * (Time other) + { + Time result; + result.clock_rate = clock_rate * other.clock_rate; + result.length = length * other.length; + return result; + } + + inline Time &operator *= (Time other) + { + length *= other.length; + clock_rate *= other.clock_rate; + return *this; + } + + inline Time operator / (Time other) + { + Time result; + result.clock_rate = clock_rate * other.length; + result.length = length * other.clock_rate; + return result; + } + + inline Time &operator /= (Time other) + { + length *= other.clock_rate; + clock_rate *= other.length; + return *this; + } + + inline void set_zero() + { + length = 0; + clock_rate = 1; + } + + inline void set_one() + { + length = 1; + clock_rate = 1; + } }; + } #endif /* Storage_h */ diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index b0851b7f8..4a35527bd 100644 --- a/Storage/TimedEventLoop.cpp +++ b/Storage/TimedEventLoop.cpp @@ -29,6 +29,18 @@ void TimedEventLoop::reset_timer() _stepper.reset(); } +void TimedEventLoop::reset_timer_to_offset(Time offset) +{ + unsigned int common_clock_rate = NumberTheory::least_common_multiple(offset.clock_rate, _event_interval.clock_rate); + _time_into_interval = offset.length * (common_clock_rate / offset.clock_rate); + _event_interval.length *= common_clock_rate / _event_interval.clock_rate; + _event_interval.clock_rate = common_clock_rate; + if(common_clock_rate != _stepper->get_output_rate()) + { + _stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate)); + } +} + void TimedEventLoop::jump_to_next_event() { reset_timer(); @@ -70,3 +82,10 @@ void TimedEventLoop::set_next_event_time_interval(Time interval) _stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate)); } } + +Time TimedEventLoop::get_time_into_next_event() +{ + Time result = _event_interval; + result.length = _time_into_interval; + return result; +} diff --git a/Storage/TimedEventLoop.hpp b/Storage/TimedEventLoop.hpp index 33beaa35c..431464853 100644 --- a/Storage/TimedEventLoop.hpp +++ b/Storage/TimedEventLoop.hpp @@ -66,6 +66,11 @@ namespace Storage { */ void reset_timer(); + /*! + Sets the amount of time into the current event to @c offset. + */ + void reset_timer_to_offset(Time offset); + /*! Causes an immediate call to @c process_next_event and a call to @c reset_timer with the net effect of processing the current event immediately and fast forwarding exactly to the @@ -73,6 +78,12 @@ namespace Storage { */ void jump_to_next_event(); + /*! + @returns the amount of time that has passed since the last call to @c set_next_time_interval, + which will always be less than or equal to the time that was supplied to @c set_next_time_interval. + */ + Time get_time_into_next_event(); + private: unsigned int _input_clock_rate; Time _event_interval;