mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-07 23:25:00 +00:00
Reimplemented PCMTrack
to use PCMSegmentEventSource
, eliminating code duplication.
This commit is contained in:
@@ -11,7 +11,6 @@
|
|||||||
4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0BE4261D3481E700D5256B /* DigitalPhaseLockedLoop.cpp */; };
|
4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0BE4261D3481E700D5256B /* DigitalPhaseLockedLoop.cpp */; };
|
||||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
|
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
|
||||||
4B121F951E05E66800BFDA12 /* PCMPatchedTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F941E05E66800BFDA12 /* PCMPatchedTrackTests.mm */; };
|
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 */; };
|
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */; };
|
||||||
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
|
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
|
||||||
4B14145D1B5887A600E04248 /* 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 */; };
|
4BCF1FAB1DADD41B0039D2E7 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA91DADD41B0039D2E7 /* StaticAnalyser.cpp */; };
|
||||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */; };
|
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */; };
|
||||||
4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.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 */; };
|
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; };
|
||||||
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; };
|
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; };
|
||||||
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; };
|
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; };
|
||||||
@@ -2348,6 +2348,7 @@
|
|||||||
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
||||||
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
|
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
|
||||||
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
||||||
|
4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */,
|
||||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
||||||
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
||||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
||||||
@@ -2454,7 +2455,6 @@
|
|||||||
4B3BA0D11D318B44005DD7A7 /* TestMachine.mm in Sources */,
|
4B3BA0D11D318B44005DD7A7 /* TestMachine.mm in Sources */,
|
||||||
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */,
|
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */,
|
||||||
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
|
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
|
||||||
4B121F981E060CF000BFDA12 /* PCMSegment.cpp in Sources */,
|
|
||||||
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */,
|
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */,
|
||||||
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */,
|
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */,
|
||||||
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
|
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
|
||||||
|
@@ -11,112 +11,103 @@
|
|||||||
|
|
||||||
using namespace Storage::Disk;
|
using namespace Storage::Disk;
|
||||||
|
|
||||||
PCMTrack::PCMTrack(std::vector<PCMSegment> segments)
|
PCMTrack::PCMTrack() : segment_pointer_(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PCMTrack::PCMTrack(const std::vector<PCMSegment> &segments) : PCMTrack()
|
||||||
{
|
{
|
||||||
segments_ = std::move(segments);
|
// sum total length of all segments
|
||||||
fix_length();
|
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;
|
// a single segment necessarily fills the track
|
||||||
segment.length_of_a_bit.clock_rate = 1;
|
PCMSegment length_adjusted_segment = segment;
|
||||||
segments_.push_back(std::move(segment));
|
length_adjusted_segment.length_of_a_bit.length = 1;
|
||||||
fix_length();
|
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()
|
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
|
// ask the current segment for a new event
|
||||||
// to be a flux transition
|
Track::Event event = segment_event_sources_[segment_pointer_].get_next_event();
|
||||||
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;
|
|
||||||
|
|
||||||
const uint8_t *segment_data = &segments_[segment_pointer_].data[0];
|
// if it was a flux transition, that's code for end-of-segment, so dig deeper
|
||||||
while(bit_pointer_ < segments_[segment_pointer_].number_of_bits)
|
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
|
// advance to the [start of] the next segment
|
||||||
// TODO: should I account for the converse bit ordering? Or can I assume MSB first?
|
segment_pointer_ = (segment_pointer_ + 1) % segment_event_sources_.size();
|
||||||
int bit = segment_data[bit_pointer_ >> 3] & (0x80 >> (bit_pointer_&7));
|
segment_event_sources_[segment_pointer_].reset();
|
||||||
bit_pointer_++;
|
|
||||||
next_event_.length.length += bit_length;
|
|
||||||
|
|
||||||
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
|
return event;
|
||||||
if(segment_pointer_ == segments_.size())
|
|
||||||
{
|
|
||||||
segment_pointer_ = 0;
|
|
||||||
next_event_.type = Track::Event::IndexHole;
|
|
||||||
}
|
|
||||||
|
|
||||||
return next_event_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Storage::Time PCMTrack::seek_to(const Time &time_since_index_hole)
|
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;
|
segment_pointer_ = 0;
|
||||||
|
do
|
||||||
// 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())
|
|
||||||
{
|
{
|
||||||
// determine how long this segment is in terms of the master clock
|
// if this segment extends beyond the amount of time left to seek, trust it to complete
|
||||||
unsigned int clock_multiplier = time_so_far.clock_rate / next_event_.length.clock_rate;
|
// the seek
|
||||||
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;
|
Storage::Time segment_time = segment_event_sources_[segment_pointer_].get_length();
|
||||||
unsigned int time_in_this_segment = bit_length * segments_[segment_pointer_].number_of_bits;
|
if(segment_time > time_left_to_seek)
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
// get the amount of time actually to move into this segment
|
return accumulated_time + segment_event_sources_[segment_pointer_].seek_to(time_left_to_seek);
|
||||||
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
|
// otherwise swallow this segment, updating the time left to seek and time so far accumulated
|
||||||
time_so_far.length += time_in_this_segment;
|
time_left_to_seek -= segment_time;
|
||||||
segment_pointer_++;
|
accumulated_time += segment_time;
|
||||||
|
segment_pointer_ = (segment_pointer_ + 1) % segment_event_sources_.size();
|
||||||
}
|
}
|
||||||
return target_time;
|
while(segment_pointer_);
|
||||||
}
|
|
||||||
|
// if all segments have now been swallowed, the closest we can get is the very end of
|
||||||
void PCMTrack::fix_length()
|
// the list of segments
|
||||||
{
|
return accumulated_time;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
@@ -28,13 +28,13 @@ class PCMTrack: public Track {
|
|||||||
/*!
|
/*!
|
||||||
Creates a @c PCMTrack consisting of multiple segments of data, permitting multiple clock rates.
|
Creates a @c PCMTrack consisting of multiple segments of data, permitting multiple clock rates.
|
||||||
*/
|
*/
|
||||||
PCMTrack(std::vector<PCMSegment> segments);
|
PCMTrack(const std::vector<PCMSegment> &segments);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Creates a @c PCMTrack consisting of a single continuous run of data, implying a constant clock rate.
|
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.
|
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
|
// as per @c Track
|
||||||
Event get_next_event();
|
Event get_next_event();
|
||||||
@@ -42,21 +42,12 @@ class PCMTrack: public Track {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// storage for the segments that describe this track
|
// storage for the segments that describe this track
|
||||||
std::vector<PCMSegment> segments_;
|
std::vector<PCMSegmentEventSource> segment_event_sources_;
|
||||||
|
|
||||||
// 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_;
|
|
||||||
|
|
||||||
// a pointer to the first bit to consider as the next event
|
// a pointer to the first bit to consider as the next event
|
||||||
size_t segment_pointer_;
|
size_t segment_pointer_;
|
||||||
size_t bit_pointer_;
|
|
||||||
|
PCMTrack();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user