mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-16 18:30:32 +00:00
Implemented a very basic PCMTrack
test, nevertheless revealing an oversight in PCMSegmentEventSource
related to improperly counting to the index hole if the final bit is set. Took that as a message that I should comment and document the event source.
This commit is contained in:
parent
a6354ebb01
commit
e081f224b6
@ -385,6 +385,7 @@
|
||||
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 */; };
|
||||
4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; };
|
||||
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 */; };
|
||||
@ -901,6 +902,7 @@
|
||||
4BD14B101D74627C0088EAD6 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Acorn/StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||
4BD468F51D8DF41D0084958B /* 1770.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 1770.cpp; path = 1770/1770.cpp; sourceTree = "<group>"; };
|
||||
4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = "<group>"; };
|
||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = "<group>"; };
|
||||
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
|
||||
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = "<group>"; };
|
||||
4BD69F921D98760000243FE1 /* AcornADF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AcornADF.cpp; sourceTree = "<group>"; };
|
||||
@ -1687,8 +1689,9 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */,
|
||||
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
|
||||
4B121F941E05E66800BFDA12 /* PCMPatchedTrackTests.mm */,
|
||||
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
|
||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
|
||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||
@ -2463,6 +2466,7 @@
|
||||
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */,
|
||||
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */,
|
||||
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */,
|
||||
4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */,
|
||||
4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */,
|
||||
4B121F951E05E66800BFDA12 /* PCMPatchedTrackTests.mm in Sources */,
|
||||
);
|
||||
|
8
OSBindings/Mac/Clock SignalTests/PCMTrackTests.h
Normal file
8
OSBindings/Mac/Clock SignalTests/PCMTrackTests.h
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// PCMTrackTests.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/12/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
54
OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm
Normal file
54
OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// PCMTrackTests.m
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/12/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include "PCMTrack.hpp"
|
||||
|
||||
@interface PCMTrackTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation PCMTrackTests
|
||||
|
||||
- (Storage::Disk::PCMTrack)multiSpeedTrack
|
||||
{
|
||||
Storage::Disk::PCMSegment quickSegment, slowSegment;
|
||||
|
||||
quickSegment.data = {0xff};
|
||||
quickSegment.number_of_bits = 8;
|
||||
quickSegment.length_of_a_bit.length = 1;
|
||||
quickSegment.length_of_a_bit.clock_rate = 100;
|
||||
|
||||
slowSegment.data = {0xff};
|
||||
slowSegment.number_of_bits = 8;
|
||||
slowSegment.length_of_a_bit.length = 1;
|
||||
slowSegment.length_of_a_bit.clock_rate = 3;
|
||||
|
||||
return Storage::Disk::PCMTrack({quickSegment, slowSegment});
|
||||
}
|
||||
|
||||
- (void)testMultispeedTrack
|
||||
{
|
||||
Storage::Disk::PCMTrack track = self.multiSpeedTrack;
|
||||
std::vector<Storage::Disk::Track::Event> events;
|
||||
Storage::Time total_length;
|
||||
do {
|
||||
events.push_back(track.get_next_event());
|
||||
total_length += events.back().length;
|
||||
} while(events.back().type != Storage::Disk::Track::Event::IndexHole);
|
||||
|
||||
XCTAssert(events.size() == 17, "Should have received 17 events; got %lu", events.size());
|
||||
|
||||
total_length.simplify();
|
||||
XCTAssert(total_length.length == 1 && total_length.clock_rate == 1, "Events should have summed to a total time of 1; instead got %u/%u", total_length.length, total_length.clock_rate);
|
||||
|
||||
Storage::Time transition_length = events[0].length + events.back().length;
|
||||
XCTAssert(events[8].length == transition_length, "Time taken in transition between speed zones should be half of a bit length in the first part plus half of a bit length in the second");
|
||||
}
|
||||
|
||||
@end
|
@ -13,38 +13,64 @@ using namespace Storage::Disk;
|
||||
PCMSegmentEventSource::PCMSegmentEventSource(const PCMSegment &segment) :
|
||||
segment_(segment)
|
||||
{
|
||||
// add an extra bit of storage at the bottom if one is going to be needed;
|
||||
// events returned are going to be in integral multiples of the length of a bit
|
||||
// other than the very first and very last which will include a half bit length
|
||||
if(segment_.length_of_a_bit.length&1)
|
||||
{
|
||||
segment_.length_of_a_bit.length <<= 1;
|
||||
segment_.length_of_a_bit.clock_rate <<= 1;
|
||||
}
|
||||
|
||||
// load up the clock rate once only
|
||||
next_event_.length.clock_rate = segment_.length_of_a_bit.clock_rate;
|
||||
|
||||
// set initial conditions
|
||||
reset();
|
||||
}
|
||||
|
||||
void PCMSegmentEventSource::reset()
|
||||
{
|
||||
// start with the first bit to be considered the zeroth, and assume that it'll be
|
||||
// flux transitions for the foreseeable
|
||||
bit_pointer_ = 0;
|
||||
next_event_.type = Track::Event::FluxTransition;
|
||||
}
|
||||
|
||||
Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event()
|
||||
{
|
||||
// track the initial bit pointer for potentially considering whether this was an
|
||||
// initial index hole or a subsequent one later on
|
||||
size_t initial_bit_pointer = bit_pointer_;
|
||||
|
||||
// if starting from the beginning, pull half a bit backward, as if the initial bit
|
||||
// is set, it should be in the centre of its window
|
||||
next_event_.length.length = bit_pointer_ ? 0 : -(segment_.length_of_a_bit.length >> 1);
|
||||
|
||||
// search for the next bit that is set, if any
|
||||
const uint8_t *segment_data = segment_.data.data();
|
||||
while(bit_pointer_ < segment_.number_of_bits)
|
||||
{
|
||||
int bit = segment_data[bit_pointer_ >> 3] & (0x80 >> (bit_pointer_&7));
|
||||
bit_pointer_++;
|
||||
bit_pointer_++; // so this always points one beyond the most recent bit returned
|
||||
next_event_.length.length += segment_.length_of_a_bit.length;
|
||||
|
||||
// if this bit is set, return the event
|
||||
if(bit) return next_event_;
|
||||
}
|
||||
|
||||
if(initial_bit_pointer < segment_.number_of_bits) next_event_.length.length += (segment_.length_of_a_bit.length >> 1);
|
||||
// if the end is reached without a bit being set, it'll be index holes from now on
|
||||
next_event_.type = Track::Event::IndexHole;
|
||||
|
||||
// test whether this is the very first time that bits have been exhausted. If so then
|
||||
// allow an extra half bit's length to run from the position of the potential final transition
|
||||
// event to the end of the segment. Otherwise don't allow any extra time, as it's already
|
||||
// been consumed
|
||||
if(initial_bit_pointer <= segment_.number_of_bits)
|
||||
{
|
||||
next_event_.length.length += (segment_.length_of_a_bit.length >> 1);
|
||||
bit_pointer_++;
|
||||
}
|
||||
return next_event_;
|
||||
}
|
||||
|
||||
@ -60,7 +86,7 @@ Storage::Time PCMSegmentEventSource::seek_to(const Time &time_from_start)
|
||||
if(time_from_start >= length)
|
||||
{
|
||||
next_event_.type = Track::Event::IndexHole;
|
||||
bit_pointer_ = segment_.number_of_bits;
|
||||
bit_pointer_ = segment_.number_of_bits+1;
|
||||
return length;
|
||||
}
|
||||
|
||||
|
@ -29,14 +29,39 @@ struct PCMSegment {
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
/*!
|
||||
Provides a stream of events by inspecting a PCMSegment.
|
||||
*/
|
||||
class PCMSegmentEventSource {
|
||||
public:
|
||||
/*!
|
||||
Constructs a @c PCMSegmentEventSource that will derive events from @c segment.
|
||||
The event source is initially @c reset.
|
||||
*/
|
||||
PCMSegmentEventSource(const PCMSegment &segment);
|
||||
|
||||
/*!
|
||||
@returns the next event that will occur in this event stream.
|
||||
*/
|
||||
Track::Event get_next_event();
|
||||
|
||||
/*!
|
||||
Resets the event source to the beginning of its event stream, exactly as if
|
||||
it has just been constructed.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/*!
|
||||
Seeks as close to @c time_from_start as the event source can manage while not
|
||||
exceeding it.
|
||||
|
||||
@returns the time the source is now at.
|
||||
*/
|
||||
Time seek_to(const Time &time_from_start);
|
||||
|
||||
/*!
|
||||
@returns the total length of the stream of data that the source will provide.
|
||||
*/
|
||||
Time get_length();
|
||||
|
||||
private:
|
||||
|
@ -22,6 +22,7 @@ PCMTrack::PCMTrack(const std::vector<PCMSegment> &segments) : PCMTrack()
|
||||
{
|
||||
total_length += segment.length_of_a_bit * segment.number_of_bits;
|
||||
}
|
||||
total_length.simplify();
|
||||
|
||||
// each segment is then some proportion of the total; for them all to sum to 1 they'll
|
||||
// need to be adjusted to be
|
||||
@ -29,6 +30,7 @@ PCMTrack::PCMTrack(const std::vector<PCMSegment> &segments) : PCMTrack()
|
||||
{
|
||||
Time original_length_of_segment = segment.length_of_a_bit * segment.number_of_bits;
|
||||
Time proportion_of_whole = original_length_of_segment / total_length;
|
||||
proportion_of_whole.simplify();
|
||||
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();
|
||||
|
@ -67,6 +67,11 @@ struct Time {
|
||||
return other.clock_rate * length >= clock_rate * other.length;
|
||||
}
|
||||
|
||||
inline bool operator == (const Time &other) const
|
||||
{
|
||||
return other.clock_rate * length == clock_rate * other.length;
|
||||
}
|
||||
|
||||
inline Time operator + (const Time &other) const
|
||||
{
|
||||
Time result;
|
||||
|
Loading…
x
Reference in New Issue
Block a user