From 886946cc8cf36690e9aa81f81a4d9ff6ef48bdc5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 9 Jul 2019 23:27:27 -0400 Subject: [PATCH] Rejigs time-until-event tracking. --- Inputs/QuadratureMouse/QuadratureMouse.hpp | 8 ++++ Machines/Apple/Macintosh/Macintosh.cpp | 33 +++++++------ Machines/Apple/Macintosh/Video.cpp | 20 ++++---- .../Clock Signal.xcodeproj/project.pbxproj | 4 ++ .../Clock SignalTests/MacintoshVideoTests.mm | 46 +++++++++++++++++++ 5 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 OSBindings/Mac/Clock SignalTests/MacintoshVideoTests.mm diff --git a/Inputs/QuadratureMouse/QuadratureMouse.hpp b/Inputs/QuadratureMouse/QuadratureMouse.hpp index 638f7f29a..d9e048d11 100644 --- a/Inputs/QuadratureMouse/QuadratureMouse.hpp +++ b/Inputs/QuadratureMouse/QuadratureMouse.hpp @@ -101,6 +101,14 @@ class QuadratureMouse: public Mouse { return button_flags_; } + /*! + @returns @c true if any mouse motion is waiting to be communicated; + @c false otherwise. + */ + bool has_steps() { + return axes_[0] || axes_[1]; + } + private: int number_of_buttons_ = 0; std::atomic button_flags_; diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index a593d7e25..7b3e08c04 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -147,16 +147,18 @@ template class ConcreteMachin // See: Guide to the Macintosh Hardware Family p149 (PDF p188). Some extra division // may occur here in order to provide VSYNC at a proper moment. // Possibly route vsync. - if(time_until_video_event_ >= cycle.length) { + if(time_since_video_update_ < time_until_video_event_) { via_clock_ += cycle.length; via_.run_for(via_clock_.divide(HalfCycles(10))); - time_until_video_event_ -= cycle.length; } else { - auto cycles_to_progress = cycle.length; - while(time_until_video_event_ < cycles_to_progress) { - cycles_to_progress -= time_until_video_event_; + auto via_time_base = time_since_video_update_ - cycle.length; + auto via_cycles_outstanding = cycle.length; + while(time_until_video_event_ < time_since_video_update_) { + const auto via_cycles = time_until_video_event_ - via_time_base; + via_time_base = HalfCycles(0); + via_cycles_outstanding -= via_cycles; - via_clock_ += time_until_video_event_; + via_clock_ += via_cycles; via_.run_for(via_clock_.divide(HalfCycles(10))); video_.run_for(time_until_video_event_); @@ -166,9 +168,8 @@ template class ConcreteMachin via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !video_.vsync()); } - via_clock_ += cycles_to_progress; + via_clock_ += via_cycles_outstanding; via_.run_for(via_clock_.divide(HalfCycles(10))); - time_until_video_event_ -= cycles_to_progress; } // The keyboard also has a clock, albeit a very slow one — 100,000 cycles/second. @@ -182,12 +183,14 @@ template class ConcreteMachin } // Feed mouse inputs within at most 1250 cycles of each other. - time_since_mouse_update_ += cycle.length; - const auto mouse_ticks = time_since_mouse_update_.divide(HalfCycles(2500)); - if(mouse_ticks > HalfCycles(0)) { - mouse_.prepare_step(); - scc_.set_dcd(0, mouse_.get_channel(1) & 1); - scc_.set_dcd(1, mouse_.get_channel(0) & 1); + if(mouse_.has_steps()) { + time_since_mouse_update_ += cycle.length; + const auto mouse_ticks = time_since_mouse_update_.divide(HalfCycles(2500)); + if(mouse_ticks > HalfCycles(0)) { + mouse_.prepare_step(); + scc_.set_dcd(0, mouse_.get_channel(1) & 1); + scc_.set_dcd(1, mouse_.get_channel(0) & 1); + } } // TODO: SCC should be clocked at a divide-by-two, if and when it actually has @@ -438,8 +441,8 @@ template class ConcreteMachin struct IWM { IWM(int clock_rate) : iwm(clock_rate) {} - Apple::IWM iwm; HalfCycles time_since_update; + Apple::IWM iwm; void flush() { iwm.run_for(time_since_update.flush_cycles()); diff --git a/Machines/Apple/Macintosh/Video.cpp b/Machines/Apple/Macintosh/Video.cpp index ea9fcf78e..77cdcc5b9 100644 --- a/Machines/Apple/Macintosh/Video.cpp +++ b/Machines/Apple/Macintosh/Video.cpp @@ -54,10 +54,10 @@ void Video::run_for(HalfCycles duration) { // since pixel output occurs at twice the processor clock. So divide by 16 to get // the number of fetches. while(duration > HalfCycles(0)) { - auto cycles_left_in_line = std::min(line_length - frame_position_%line_length, duration); - - const int line = (frame_position_ / line_length).as_int(); const auto pixel_start = frame_position_ % line_length; + const int line = (frame_position_ / line_length).as_int(); + + const auto cycles_left_in_line = std::min(line_length - pixel_start, duration); // Line timing, entirely invented as I can find exactly zero words of documentation: // @@ -119,16 +119,14 @@ void Video::run_for(HalfCycles duration) { if(first_word < sync_start && final_word >= sync_start) crt_.output_blank((sync_start - 32) * 16); if(first_word < sync_end && final_word >= sync_end) crt_.output_sync((sync_end - sync_start) * 16); if(final_word == 44) crt_.output_blank((44 - sync_end) * 16); - } else if(line >= 353 && line < 356) { - /* Output a sync line. */ - if(final_word == 44) { + } else if(final_word == 44) { + if(line >= 353 && line < 356) { + /* Output a sync line. */ crt_.output_sync(sync_start * 16); crt_.output_blank((sync_end - sync_start) * 16); crt_.output_sync((44 - sync_end) * 16); - } - } else { - /* Output a blank line. */ - if(final_word == 44) { + } else { + /* Output a blank line. */ crt_.output_blank(sync_start * 16); crt_.output_sync((sync_end - sync_start) * 16); crt_.output_blank((44 - sync_end) * 16); @@ -172,7 +170,7 @@ HalfCycles Video::get_next_sequence_point() { if(line >= 353 && line < 356) { // Currently in vsync, so get time until start of line 357, // when vsync will end. - return HalfCycles(357) * line_length - frame_position_; + return HalfCycles(356) * line_length - frame_position_; } else { // Not currently in vsync, so get time until start of line 353. const auto start_of_vsync = HalfCycles(353) * line_length; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 6c2d37e70..71fb17477 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -671,6 +671,7 @@ 4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; }; 4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; }; 4BE7C9181E3D397100A5496D /* TIA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE7C9161E3D397100A5496D /* TIA.cpp */; }; + 4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; }; 4BE9A6B11EDE293000CBCB47 /* zexdoc.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */; }; 4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; }; 4BEA52631DF339D7007E74F2 /* SoundGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52611DF339D7007E74F2 /* SoundGenerator.cpp */; }; @@ -1485,6 +1486,7 @@ 4BE7C9161E3D397100A5496D /* TIA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TIA.cpp; sourceTree = ""; }; 4BE7C9171E3D397100A5496D /* TIA.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TIA.hpp; sourceTree = ""; }; 4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRTC6845.hpp; path = 6845/CRTC6845.hpp; sourceTree = ""; }; + 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MacintoshVideoTests.mm; sourceTree = ""; }; 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */ = {isa = PBXFileReference; lastKnownFileType = file; name = zexdoc.com; path = Zexall/zexdoc.com; sourceTree = ""; }; 4BEA525D1DF33323007E74F2 /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = Electron/Tape.cpp; sourceTree = ""; }; 4BEA525F1DF333D8007E74F2 /* Tape.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = Electron/Tape.hpp; sourceTree = ""; }; @@ -2960,6 +2962,7 @@ 4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */, 4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */, 4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */, + 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */, 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */, 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */, 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */, @@ -4255,6 +4258,7 @@ 4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */, 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */, 4BEE1EC022B5E236000A26A6 /* MacGCRTests.mm in Sources */, + 4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */, 4B08A2781EE39306008B7065 /* TestMachine.mm in Sources */, 4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */, 4B322E011F5A2990004EB04C /* Z80AllRAM.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/MacintoshVideoTests.mm b/OSBindings/Mac/Clock SignalTests/MacintoshVideoTests.mm new file mode 100644 index 000000000..b955d9348 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/MacintoshVideoTests.mm @@ -0,0 +1,46 @@ +// +// MacintoshVideoTests.m +// Clock SignalTests +// +// Created by Thomas Harte on 09/07/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#import + +#include +#include "../../../Machines/Apple/Macintosh/Video.hpp" + +@interface MacintoshVideoTests : XCTestCase +@end + +@implementation MacintoshVideoTests { + Apple::Macintosh::DeferredAudio _dummy_audio; + Apple::Macintosh::DriveSpeedAccumulator _dummy_drive_speed_accumulator; + std::unique_ptr _video; + uint16_t _ram[64*1024]; +} + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. + _video.reset(new Apple::Macintosh::Video(_ram, _dummy_audio, _dummy_drive_speed_accumulator)); +} + +- (void)testPrediction { + int c = 5; + bool vsync = _video->vsync(); + while(c--) { + int remaining_time_in_state = _video->get_next_sequence_point().as_int(); + NSLog(@"Vsync %@ expected for %@ half-cycles", vsync ? @"on" : @"off", @(remaining_time_in_state)); + while(remaining_time_in_state--) { + XCTAssertEqual(vsync, _video->vsync()); + _video->run_for(HalfCycles(1)); + + if(remaining_time_in_state) + XCTAssertEqual(remaining_time_in_state, _video->get_next_sequence_point().as_int()); + } + vsync ^= true; + } +} + +@end