From a6354ebb01c7a040f40eebcb2dd7f35d6c3bf669 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Dec 2016 21:37:05 -0500 Subject: [PATCH] Reimplemented `PCMTrack` to use `PCMSegmentEventSource`, eliminating code duplication. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 +- Storage/Disk/PCMTrack.cpp | 159 +++++++++--------- Storage/Disk/PCMTrack.hpp | 19 +-- 3 files changed, 82 insertions(+), 100 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 0294a1e02..4f0b62798 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ 4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0BE4261D3481E700D5256B /* DigitalPhaseLockedLoop.cpp */; }; 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; }; 4B121F951E05E66800BFDA12 /* PCMPatchedTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F941E05E66800BFDA12 /* PCMPatchedTrackTests.mm */; }; - 4B121F981E060CF000BFDA12 /* PCMSegment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F961E060CF000BFDA12 /* PCMSegment.cpp */; }; 4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */; }; 4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; }; 4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; }; @@ -385,6 +384,7 @@ 4BCF1FAB1DADD41B0039D2E7 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA91DADD41B0039D2E7 /* StaticAnalyser.cpp */; }; 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */; }; 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; }; + 4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F961E060CF000BFDA12 /* PCMSegment.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; 4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; }; 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; }; @@ -2348,6 +2348,7 @@ 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */, 4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */, 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, + 4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */, 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, @@ -2454,7 +2455,6 @@ 4B3BA0D11D318B44005DD7A7 /* TestMachine.mm in Sources */, 4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */, 4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */, - 4B121F981E060CF000BFDA12 /* PCMSegment.cpp in Sources */, 4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */, 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */, 4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */, diff --git a/Storage/Disk/PCMTrack.cpp b/Storage/Disk/PCMTrack.cpp index 7f44b001f..e61098a49 100644 --- a/Storage/Disk/PCMTrack.cpp +++ b/Storage/Disk/PCMTrack.cpp @@ -11,112 +11,103 @@ using namespace Storage::Disk; -PCMTrack::PCMTrack(std::vector segments) +PCMTrack::PCMTrack() : segment_pointer_(0) +{} + +PCMTrack::PCMTrack(const std::vector &segments) : PCMTrack() { - segments_ = std::move(segments); - fix_length(); + // sum total length of all segments + Time total_length; + for(auto segment : segments) + { + total_length += segment.length_of_a_bit * segment.number_of_bits; + } + + // each segment is then some proportion of the total; for them all to sum to 1 they'll + // need to be adjusted to be + for(auto segment : segments) + { + Time original_length_of_segment = segment.length_of_a_bit * segment.number_of_bits; + Time proportion_of_whole = original_length_of_segment / total_length; + PCMSegment length_adjusted_segment = segment; + length_adjusted_segment.length_of_a_bit = proportion_of_whole / segment.number_of_bits; + length_adjusted_segment.length_of_a_bit.simplify(); + segment_event_sources_.emplace_back(length_adjusted_segment); + } } -PCMTrack::PCMTrack(PCMSegment segment) +PCMTrack::PCMTrack(const PCMSegment &segment) : PCMTrack() { - segment.length_of_a_bit.length = 1; - segment.length_of_a_bit.clock_rate = 1; - segments_.push_back(std::move(segment)); - fix_length(); + // a single segment necessarily fills the track + PCMSegment length_adjusted_segment = segment; + length_adjusted_segment.length_of_a_bit.length = 1; + length_adjusted_segment.length_of_a_bit.clock_rate = segment.number_of_bits; + segment_event_sources_.emplace_back(length_adjusted_segment); } Track::Event PCMTrack::get_next_event() { - // find the next 1 in the input stream, keeping count of length as we go, and assuming it's going - // to be a flux transition - next_event_.type = Track::Event::FluxTransition; - next_event_.length.length = 0; - while(segment_pointer_ < segments_.size()) - { - unsigned int clock_multiplier = track_clock_rate_ / segments_[segment_pointer_].length_of_a_bit.clock_rate; - unsigned int bit_length = clock_multiplier * segments_[segment_pointer_].length_of_a_bit.length; + // ask the current segment for a new event + Track::Event event = segment_event_sources_[segment_pointer_].get_next_event(); - const uint8_t *segment_data = &segments_[segment_pointer_].data[0]; - while(bit_pointer_ < segments_[segment_pointer_].number_of_bits) + // if it was a flux transition, that's code for end-of-segment, so dig deeper + if(event.type == Track::Event::IndexHole) + { + // multiple segments may be crossed, so start summing lengths in case the net + // effect is an index hole + Time total_length = event.length; + + // continue until somewhere no returning an index hole + while(event.type == Track::Event::IndexHole) { - // for timing simplicity, bits are modelled as happening at the end of their window - // TODO: should I account for the converse bit ordering? Or can I assume MSB first? - int bit = segment_data[bit_pointer_ >> 3] & (0x80 >> (bit_pointer_&7)); - bit_pointer_++; - next_event_.length.length += bit_length; + // advance to the [start of] the next segment + segment_pointer_ = (segment_pointer_ + 1) % segment_event_sources_.size(); + segment_event_sources_[segment_pointer_].reset(); - if(bit) return next_event_; + // if this is all the way back to the start, that's a genuine index hole, + // so set the summed length and return + if(!segment_pointer_) + { + return event; + } + + // otherwise get the next event (if it's not another index hole, the loop will end momentarily), + // summing in any prior accumulated time + event = segment_event_sources_[segment_pointer_].get_next_event(); + total_length += event.length; + event.length = total_length; } - bit_pointer_ = 0; - segment_pointer_++; } - // check whether we actually reached the index hole - if(segment_pointer_ == segments_.size()) - { - segment_pointer_ = 0; - next_event_.type = Track::Event::IndexHole; - } - - return next_event_; + return event; } Storage::Time PCMTrack::seek_to(const Time &time_since_index_hole) { + // initial condition: no time yet accumulated, the whole thing requested yet to navigate + Storage::Time accumulated_time; + Storage::Time time_left_to_seek = time_since_index_hole; + + // search from the first segment 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 target_time = time_since_index_hole; - time_so_far.clock_rate = NumberTheory::least_common_multiple(next_event_.length.clock_rate, target_time.clock_rate); - target_time.length *= time_so_far.clock_rate / target_time.clock_rate; - target_time.clock_rate = time_so_far.clock_rate; - - while(segment_pointer_ < segments_.size()) + do { - // 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 = target_time.length - time_so_far.length; - if(time_in_this_segment >= time_remaining) + // if this segment extends beyond the amount of time left to seek, trust it to complete + // the seek + Storage::Time segment_time = segment_event_sources_[segment_pointer_].get_length(); + if(segment_time > time_left_to_seek) { - // 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; + return accumulated_time + segment_event_sources_[segment_pointer_].seek_to(time_left_to_seek); } - // otherwise, accumulate time and keep moving - time_so_far.length += time_in_this_segment; - segment_pointer_++; + // otherwise swallow this segment, updating the time left to seek and time so far accumulated + time_left_to_seek -= segment_time; + accumulated_time += segment_time; + segment_pointer_ = (segment_pointer_ + 1) % segment_event_sources_.size(); } - return target_time; -} - -void PCMTrack::fix_length() -{ - // find the least common multiple of all segment clock rates - track_clock_rate_ = segments_[0].length_of_a_bit.clock_rate; - for(size_t c = 1; c < segments_.size(); c++) - { - track_clock_rate_ = NumberTheory::least_common_multiple(track_clock_rate_, segments_[c].length_of_a_bit.clock_rate); - } - - // thereby determine the total length, storing it to next_event as the track-total divisor - next_event_.length.clock_rate = 0; - for(size_t c = 0; c < segments_.size(); c++) - { - unsigned int multiplier = track_clock_rate_ / segments_[c].length_of_a_bit.clock_rate; - next_event_.length.clock_rate += segments_[c].length_of_a_bit.length * segments_[c].number_of_bits * multiplier; - } - - segment_pointer_ = bit_pointer_ = 0; + while(segment_pointer_); + + // if all segments have now been swallowed, the closest we can get is the very end of + // the list of segments + return accumulated_time; } diff --git a/Storage/Disk/PCMTrack.hpp b/Storage/Disk/PCMTrack.hpp index c3ae19e62..255bed80f 100644 --- a/Storage/Disk/PCMTrack.hpp +++ b/Storage/Disk/PCMTrack.hpp @@ -28,13 +28,13 @@ class PCMTrack: public Track { /*! Creates a @c PCMTrack consisting of multiple segments of data, permitting multiple clock rates. */ - PCMTrack(std::vector segments); + PCMTrack(const std::vector &segments); /*! Creates a @c PCMTrack consisting of a single continuous run of data, implying a constant clock rate. The segment's @c length_of_a_bit will be ignored and therefore need not be filled in. */ - PCMTrack(PCMSegment segment); + PCMTrack(const PCMSegment &segment); // as per @c Track Event get_next_event(); @@ -42,21 +42,12 @@ class PCMTrack: public Track { private: // storage for the segments that describe this track - std::vector segments_; - - // a helper to determine the overall track clock rate and it's length - void fix_length(); - - // the event perpetually returned; impliedly contains the length of the entire track - // as its clock rate, per the need for everything on a Track to sum to a length of 1 - PCMTrack::Event next_event_; - - // contains the master clock rate - unsigned int track_clock_rate_; + std::vector segment_event_sources_; // a pointer to the first bit to consider as the next event size_t segment_pointer_; - size_t bit_pointer_; + + PCMTrack(); }; }