diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index 8e0811e66..71506b8a4 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -128,13 +128,14 @@ template class MOS6522: public MOS6522Storage { void access(int address); - uint8_t get_port_input(Port port, uint8_t output_mask, uint8_t output); + uint8_t get_port_input(Port port, uint8_t output_mask, uint8_t output, uint8_t timer_mask); inline void reevaluate_interrupts(); /// Sets the current intended output value for the port and line; /// if this affects the visible output, it will be passed to the handler. void set_control_line_output(Port port, Line line, LineState value); void evaluate_cb2_output(); + void evaluate_port_b_output(); }; } diff --git a/Components/6522/Implementation/6522Implementation.hpp b/Components/6522/Implementation/6522Implementation.hpp index 54c8f2bbd..c7c5f6ef4 100644 --- a/Components/6522/Implementation/6522Implementation.hpp +++ b/Components/6522/Implementation/6522Implementation.hpp @@ -8,6 +8,10 @@ #include "../../../Outputs/Log.hpp" +// As-yet unimplemented (incomplete list): +// +// PB6 count-down mode for timer 2. + namespace MOS { namespace MOS6522 { @@ -34,18 +38,18 @@ template void MOS6522::write(int address, uint8_t value) { address &= 0xf; access(address); switch(address) { - case 0x0: // Write Port B. + case 0x0: // Write Port B. ('ORB') // Store locally and communicate outwards. registers_.output[1] = value; bus_handler_.run_for(time_since_bus_handler_call_.flush()); - bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]); + evaluate_port_b_output(); registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge)); reevaluate_interrupts(); break; case 0xf: - case 0x1: // Write Port A. + case 0x1: // Write Port A. ('ORA') registers_.output[0] = value; bus_handler_.run_for(time_since_bus_handler_call_.flush()); @@ -59,28 +63,44 @@ template void MOS6522::write(int address, uint8_t value) { reevaluate_interrupts(); break; - case 0x2: // Port B direction. + case 0x2: // Port B direction ('DDRB'). registers_.data_direction[1] = value; break; - case 0x3: // Port A direction. + case 0x3: // Port A direction ('DDRA'). registers_.data_direction[0] = value; break; // Timer 1 - case 0x6: case 0x4: registers_.timer_latch[0] = (registers_.timer_latch[0]&0xff00) | value; break; - case 0x5: case 0x7: + case 0x6: case 0x4: // ('T1L-L' and 'T1C-L') + registers_.timer_latch[0] = (registers_.timer_latch[0]&0xff00) | value; + break; + case 0x7: // Timer 1 latch, high ('T1L-H'). registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | uint16_t(value << 8); - registers_.interrupt_flags &= ~InterruptFlag::Timer1; - if(address == 0x05) { - registers_.next_timer[0] = registers_.timer_latch[0]; - timer_is_running_[0] = true; + break; + case 0x5: // Timer 1 counter, high ('T1C-H'). + // Fill latch. + registers_.timer_latch[0] = (registers_.timer_latch[0]&0x00ff) | uint16_t(value << 8); + + // Restart timer. + registers_.next_timer[0] = registers_.timer_latch[0]; + timer_is_running_[0] = true; + + // If PB7 output mode is active, set it low. + if(registers_.auxiliary_control & 0x80) { + registers_.timer_port_b_output &= 0x7f; + evaluate_port_b_output(); } + + // Clear existing interrupt flag. + registers_.interrupt_flags &= ~InterruptFlag::Timer1; reevaluate_interrupts(); break; // Timer 2 - case 0x8: registers_.timer_latch[1] = value; break; - case 0x9: + case 0x8: // ('T2C-L') + registers_.timer_latch[1] = value; + break; + case 0x9: // ('T2C-H') registers_.interrupt_flags &= ~InterruptFlag::Timer2; registers_.next_timer[1] = registers_.timer_latch[1] | uint16_t(value << 8); timer_is_running_[1] = true; @@ -88,7 +108,7 @@ template void MOS6522::write(int address, uint8_t value) { break; // Shift - case 0xa: + case 0xa: // ('SR') registers_.shift = value; shift_bits_remaining_ = 8; registers_.interrupt_flags &= ~InterruptFlag::ShiftRegister; @@ -96,11 +116,18 @@ template void MOS6522::write(int address, uint8_t value) { break; // Control - case 0xb: + case 0xb: // Auxiliary control ('ACR'). registers_.auxiliary_control = value; evaluate_cb2_output(); + + // This is a bit of a guess: reset the timer-based PB7 output to its default high level + // any timer that timer-linked PB7 output is disabled. + if(!(registers_.auxiliary_control & 0x80)) { + registers_.timer_port_b_output |= 0x80; + } + evaluate_port_b_output(); break; - case 0xc: { + case 0xc: { // Peripheral control ('PCR'). // const auto old_peripheral_control = registers_.peripheral_control; registers_.peripheral_control = value; @@ -141,11 +168,11 @@ template void MOS6522::write(int address, uint8_t value) { } break; // Interrupt control - case 0xd: + case 0xd: // Interrupt flag regiser ('IFR'). registers_.interrupt_flags &= ~value; reevaluate_interrupts(); break; - case 0xe: + case 0xe: // Interrupt enable register ('IER'). if(value&0x80) registers_.interrupt_enable |= value; else @@ -159,54 +186,55 @@ template uint8_t MOS6522::read(int address) { address &= 0xf; access(address); switch(address) { - case 0x0: + case 0x0: // Read Port B ('IRB'). registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge); reevaluate_interrupts(); - return get_port_input(Port::B, registers_.data_direction[1], registers_.output[1]); + return get_port_input(Port::B, registers_.data_direction[1], registers_.output[1], registers_.auxiliary_control & 0x80); case 0xf: - case 0x1: + case 0x1: // Read Port A ('IRA'). registers_.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge); reevaluate_interrupts(); - return get_port_input(Port::A, registers_.data_direction[0], registers_.output[0]); + return get_port_input(Port::A, registers_.data_direction[0], registers_.output[0], 0); - case 0x2: return registers_.data_direction[1]; - case 0x3: return registers_.data_direction[0]; + case 0x2: return registers_.data_direction[1]; // Port B direction ('DDRB'). + case 0x3: return registers_.data_direction[0]; // Port A direction ('DDRA'). // Timer 1 - case 0x4: + case 0x4: // Timer 1 low-order latches ('T1L-L'). registers_.interrupt_flags &= ~InterruptFlag::Timer1; reevaluate_interrupts(); return registers_.timer[0] & 0x00ff; - case 0x5: return registers_.timer[0] >> 8; - case 0x6: return registers_.timer_latch[0] & 0x00ff; - case 0x7: return registers_.timer_latch[0] >> 8; + case 0x5: return registers_.timer[0] >> 8; // Timer 1 high-order counter ('T1C-H') + case 0x6: return registers_.timer_latch[0] & 0x00ff; // Timer 1 low-order latches ('T1L-L'). + case 0x7: return registers_.timer_latch[0] >> 8; // Timer 1 high-order latches ('T1L-H'). // Timer 2 - case 0x8: + case 0x8: // Timer 2 low-order counter ('T2C-L'). registers_.interrupt_flags &= ~InterruptFlag::Timer2; reevaluate_interrupts(); return registers_.timer[1] & 0x00ff; - case 0x9: return registers_.timer[1] >> 8; + case 0x9: return registers_.timer[1] >> 8; // Timer 2 high-order counter ('T2C-H'). - case 0xa: + case 0xa: // Shift register ('SR'). shift_bits_remaining_ = 8; registers_.interrupt_flags &= ~InterruptFlag::ShiftRegister; reevaluate_interrupts(); return registers_.shift; - case 0xb: return registers_.auxiliary_control; - case 0xc: return registers_.peripheral_control; + case 0xb: return registers_.auxiliary_control; // Auxiliary control ('ACR'). + case 0xc: return registers_.peripheral_control; // Peripheral control ('PCR'). - case 0xd: return registers_.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00); - case 0xe: return registers_.interrupt_enable | 0x80; + case 0xd: return registers_.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00); // Interrupt flag register ('IFR'). + case 0xe: return registers_.interrupt_enable | 0x80; // Interrupt enable register ('IER'). } return 0xff; } -template uint8_t MOS6522::get_port_input(Port port, uint8_t output_mask, uint8_t output) { +template uint8_t MOS6522::get_port_input(Port port, uint8_t output_mask, uint8_t output, uint8_t timer_mask) { bus_handler_.run_for(time_since_bus_handler_call_.flush()); const uint8_t input = bus_handler_.get_port_input(port); + output = (output & ~timer_mask) | (registers_.timer_port_b_output & timer_mask); return (input & ~output_mask) | (output & output_mask); } @@ -276,10 +304,13 @@ template void MOS6522::do_phase2() { registers_.timer_needs_reload = false; registers_.timer[0] = registers_.timer_latch[0]; } else { - registers_.timer[0] --; + -- registers_.timer[0]; } - registers_.timer[1] --; + // Count down timer 2 if it is in timed interrupt mode (i.e. auxiliary control bit 5 is clear). + // TODO: implement count down on PB6 if this bit isn't set. + registers_.timer[1] -= 1 ^ ((registers_.auxiliary_control >> 5)&1); + if(registers_.next_timer[0] >= 0) { registers_.timer[0] = uint16_t(registers_.next_timer[0]); registers_.next_timer[0] = -1; @@ -337,13 +368,22 @@ template void MOS6522::do_phase1() { // Determine whether to toggle PB7. if(registers_.auxiliary_control&0x80) { - registers_.output[1] ^= 0x80; + registers_.timer_port_b_output ^= 0x80; bus_handler_.run_for(time_since_bus_handler_call_.flush()); - bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]); + evaluate_port_b_output(); } } } +template void MOS6522::evaluate_port_b_output() { + // Apply current timer-linked PB7 output if any atop the stated output. + const uint8_t timer_control_bit = registers_.auxiliary_control & 0x80; + bus_handler_.set_port_output( + Port::B, + (registers_.output[1] & (0xff ^ timer_control_bit)) | timer_control_bit, + registers_.data_direction[1] | timer_control_bit); +} + /*! Runs for a specified number of half cycles. */ template void MOS6522::run_for(const HalfCycles half_cycles) { auto number_of_half_cycles = half_cycles.as_integral(); diff --git a/Components/6522/Implementation/6522Storage.hpp b/Components/6522/Implementation/6522Storage.hpp index da9e30981..ceff38a8f 100644 --- a/Components/6522/Implementation/6522Storage.hpp +++ b/Components/6522/Implementation/6522Storage.hpp @@ -34,7 +34,9 @@ class MOS6522Storage { uint8_t peripheral_control = 0; uint8_t interrupt_flags = 0; uint8_t interrupt_enable = 0; + bool timer_needs_reload = false; + uint8_t timer_port_b_output = 0xff; } registers_; // Control state. diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index c4c11eb2c..b86a62c7d 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -860,6 +860,7 @@ 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; }; 4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; }; 4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; }; + 4BF8D4C82516E27A00BBE21B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BB8617024E22F4900A00E03 /* Accelerate.framework */; }; 4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; }; 4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; }; 4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; }; @@ -1830,6 +1831,7 @@ buildActionMask = 2147483647; files = ( 4B9F11CA2272433900701480 /* libz.tbd in Frameworks */, + 4BF8D4C82516E27A00BBE21B /* Accelerate.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/OSBindings/Mac/Clock SignalTests/6522Tests.swift b/OSBindings/Mac/Clock SignalTests/6522Tests.swift index d56ce5903..7126a09c5 100644 --- a/OSBindings/Mac/Clock SignalTests/6522Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6522Tests.swift @@ -101,6 +101,116 @@ class MOS6522Tests: XCTestCase { } + // MARK: PB7 timer 1 tests + // These follow the same logic and check for the same results as the VICE VIC-20 via_pb7 tests. + + // Perfoms: + // + // (1) establish initial ACR and port B output value, and grab port B input value. + // (2) start timer 1, grab port B input value. + // (3) set final ACR, grab port B input value. + // (4) allow timer 1 to expire, grab port B input value. + private func runTest(startACR: UInt8, endACR: UInt8, portBOutput: UInt8) -> [UInt8] { + var result: [UInt8] = [] + + // Clear all register values. + for n: UInt in 0...15 { + m6522.setValue(0, forRegister: n) + } + m6522.run(forHalfCycles: 2) + + // Set data direction and current port B value. + m6522.setValue(0xff, forRegister: 2) + m6522.run(forHalfCycles: 2) + m6522.setValue(portBOutput, forRegister: 0) + m6522.run(forHalfCycles: 2) + + // Set initial ACR and grab the current port B value. + m6522.setValue(startACR, forRegister: 0xb) + m6522.run(forHalfCycles: 2) + result.append(m6522.value(forRegister: 0)) + m6522.run(forHalfCycles: 2) + + // Start timer 1 and grab the value. + m6522.setValue(1, forRegister: 0x5) + m6522.run(forHalfCycles: 2) + result.append(m6522.value(forRegister: 0)) + m6522.run(forHalfCycles: 2) + + // Set the final ACR value and grab value. + m6522.setValue(endACR, forRegister: 0xb) + m6522.run(forHalfCycles: 2) + result.append(m6522.value(forRegister: 0)) + m6522.run(forHalfCycles: 2) + + // Make sure timer 1 has expired. + m6522.run(forHalfCycles: 512) + + // Grab the final value. + result.append(m6522.value(forRegister: 0)) + + return result + } + + func testTimer1PB7() { + // Original top row. [original Vic-20 screen output in comments on the right] + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x00, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@ (i.e. 0, 0, 0, 0) + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x40, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@ (i.e. 0, 0, 0, 0) + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x80, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@ (i.e. 0, 0, 1, 0) + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0xc0, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@ (i.e. 0, 0, 1, 0) + + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x00, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc (i.e. 1, 1, 1, 1) + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x40, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc (i.e. 1, 1, 1, 1) + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0x80, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca (i.e. 1, 1, 1, 0) + XCTAssertEqual(runTest(startACR: 0x00, endACR: 0xc0, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca (i.e. 1, 1, 1, 0) + + // Second row. [same output as first row] + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x00, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@ + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x40, portBOutput: 0x00), [0x00, 0x00, 0x00, 0x00]) // @@@@ + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x80, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@ + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0xc0, portBOutput: 0x00), [0x00, 0x00, 0x80, 0x00]) // @@b@ + + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x00, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x40, portBOutput: 0xff), [0xff, 0xff, 0xff, 0xff]) // cccc + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0x80, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca + XCTAssertEqual(runTest(startACR: 0x40, endACR: 0xc0, portBOutput: 0xff), [0xff, 0xff, 0xff, 0x7f]) // ccca + + // Third row. + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x00, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@ (i.e. 1, 0, 0, 0) + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x40, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@ (i.e. 1, 0, 0, 0) + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x80, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b (i.e. 1, 0, 0, 1) + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0xc0, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b (i.e. 1, 0, 0, 1) + + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x00, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc (i.e. 1, 0, 1, 1) + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x40, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc (i.e. 1, 0, 1, 1) + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0x80, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac (i.e. 1, 0, 0, 1) + XCTAssertEqual(runTest(startACR: 0x80, endACR: 0xc0, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac (i.e. 1, 0, 0, 1) + + // Final row. [same output as third row] + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x00, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@ + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x40, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x00]) // b@@@ + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x80, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0xc0, portBOutput: 0x00), [0x80, 0x00, 0x00, 0x80]) // b@@b + + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x00, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x40, portBOutput: 0xff), [0xff, 0x7f, 0xff, 0xff]) // cacc + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0x80, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac + XCTAssertEqual(runTest(startACR: 0xc0, endACR: 0xc0, portBOutput: 0xff), [0xff, 0x7f, 0x7f, 0xff]) // caac + + // Conclusions: + // + // after inital ACR and port B value: [original data if not in PB7 output mode, otherwise 1] + // after starting timer 1: [original data if not in PB7 output mode, otherwise 0] + // after final ACR value: [original data if not in PB7 output mode, 1 if has transitioned to PB7 mode, 0 if was already in PB7 mode] + // after timer 1 expiry: [original data if not in PB7 mode, 1 if timer has expired while in PB7 mode] + // + // i.e. + // (1) there is separate storage for the programmer-set PB7 and the timer output; + // (2) the timer output is reset upon a timer write only if PB7 output is enabled; + // (3) expiry toggles the output. + } + + // MARK: Data direction tests func testDataDirection() { // set low four bits of register B as output, the top four as input diff --git a/OSBindings/Mac/Clock SignalTests/PCMSegmentEventSourceTests.mm b/OSBindings/Mac/Clock SignalTests/PCMSegmentEventSourceTests.mm index 6eca9d231..38d9ac14d 100644 --- a/OSBindings/Mac/Clock SignalTests/PCMSegmentEventSourceTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PCMSegmentEventSourceTests.mm @@ -70,12 +70,9 @@ - (void)testSeekToSecondBit { Storage::Disk::PCMSegmentEventSource segmentSource = self.segmentSource; - Storage::Time target_time(1, 10); - Storage::Time found_time = segmentSource.seek_to(target_time); - found_time.simplify(); - - XCTAssertTrue(found_time.length == 1 && found_time.clock_rate == 20, @"A request to seek to 1/10th should have seeked to 1/20th"); + const float found_time = segmentSource.seek_to(1.0f / 10.0f); + XCTAssertTrue(fabsf(found_time - 1.0f / 20.0f) < 0.01f, @"A request to seek to 1/10th should have seeked to 1/20th"); Storage::Disk::Track::Event next_event = segmentSource.get_next_event(); next_event.length.simplify(); @@ -85,12 +82,9 @@ - (void)testSeekBeyondFinalBit { Storage::Disk::PCMSegmentEventSource segmentSource = self.segmentSource; - Storage::Time target_time(24, 10); + const float found_time = segmentSource.seek_to(2.4f); - Storage::Time found_time = segmentSource.seek_to(target_time); - found_time.simplify(); - - XCTAssertTrue(found_time.length == 47 && found_time.clock_rate == 20, @"A request to seek to 24/10ths should have seeked to 47/20ths"); + XCTAssertTrue(fabsf(found_time - 47.0f / 20.0f) < 0.01f, @"A request to seek to 24/10ths should have seeked to 47/20ths"); Storage::Disk::Track::Event next_event = segmentSource.get_next_event(); next_event.length.simplify(); diff --git a/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm b/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm index fd3c5bfee..d20e87477 100644 --- a/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm @@ -73,16 +73,14 @@ } Storage::Disk::PCMTrack track(segments); - Storage::Time late_time(967445, 2045454); + const float late_time = 967445.0f / 2045454.0f; const auto offset = track.seek_to(late_time); XCTAssert(offset <= late_time, "Found location should be at or before sought time"); const auto difference = late_time - offset; - const double difference_duration = difference.get(); - XCTAssert(difference_duration >= 0.0 && difference_duration < 0.005, "Next event should occur soon"); + XCTAssert(difference >= 0.0 && difference < 0.005, "Next event should occur soon"); - const double offset_duration = offset.get(); - XCTAssert(offset_duration >= 0.0 && offset_duration < 0.5, "Next event should occur soon"); + XCTAssert(offset >= 0.0 && offset < 0.5, "Next event should occur soon"); auto next_event = track.get_next_event(); double next_event_duration = next_event.length.get();