From 5149f290d0a908d70e8ead5a5e79e62db87087cf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 28 Jul 2019 21:49:54 -0400 Subject: [PATCH 01/12] Starts trying to formalise just-in-time execution. Which, at least, simplifies Cycle/HalfCycle to Cycle run_for usage via template. --- ClockReceiver/ClockReceiver.hpp | 10 ++-- .../{ClockDeferrer.hpp => DeferredQueue.hpp} | 16 +++--- ClockReceiver/JustInTime.hpp | 55 +++++++++++++++++++ Machines/Apple/AppleII/AppleII.cpp | 2 +- Machines/Apple/AppleII/Video.hpp | 6 +- Machines/Apple/Macintosh/DeferredAudio.hpp | 2 +- Machines/Apple/Macintosh/Macintosh.cpp | 5 +- Machines/Atari2600/Bus.hpp | 4 +- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/Electron/Electron.cpp | 2 +- Machines/Oric/Oric.cpp | 6 +- .../Clock Signal.xcodeproj/project.pbxproj | 6 +- 12 files changed, 87 insertions(+), 29 deletions(-) rename ClockReceiver/{ClockDeferrer.hpp => DeferredQueue.hpp} (85%) create mode 100644 ClockReceiver/JustInTime.hpp diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index 482025d52..ba4a67629 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -149,8 +149,8 @@ template class WrappedInt { Flushes the value in @c this. The current value is returned, and the internal value is reset to zero. */ - forceinline T flush() { - T result(length_); + template forceinline Target flush() { + const Target result(length_); length_ = 0; return result; } @@ -185,8 +185,8 @@ class HalfCycles: public WrappedInt { } /// Flushes the whole cycles in @c this, subtracting that many from the total stored here. - forceinline Cycles flush_cycles() { - Cycles result(length_ >> 1); + template forceinline Cycles flush() { + const Cycles result(length_ >> 1); length_ &= 1; return result; } @@ -220,7 +220,7 @@ template class HalfClockReceiver: public T { forceinline void run_for(const HalfCycles half_cycles) { half_cycles_ += half_cycles; - T::run_for(half_cycles_.flush_cycles()); + T::run_for(half_cycles_.flush()); } private: diff --git a/ClockReceiver/ClockDeferrer.hpp b/ClockReceiver/DeferredQueue.hpp similarity index 85% rename from ClockReceiver/ClockDeferrer.hpp rename to ClockReceiver/DeferredQueue.hpp index cc41cbdd0..d2680f322 100644 --- a/ClockReceiver/ClockDeferrer.hpp +++ b/ClockReceiver/DeferredQueue.hpp @@ -1,26 +1,26 @@ // -// ClockDeferrer.hpp +// DeferredQueue.hpp // Clock Signal // // Created by Thomas Harte on 23/08/2018. // Copyright © 2018 Thomas Harte. All rights reserved. // -#ifndef ClockDeferrer_h -#define ClockDeferrer_h +#ifndef DeferredQueue_h +#define DeferredQueue_h #include #include /*! - A ClockDeferrer maintains a list of ordered actions and the times at which + A DeferredQueue maintains a list of ordered actions and the times at which they should happen, and divides a total execution period up into the portions that occur between those actions, triggering each action when it is reached. */ -template class ClockDeferrer { +template class DeferredQueue { public: - /// Constructs a ClockDeferrer that will call target(period) in between deferred actions. - ClockDeferrer(std::function &&target) : target_(std::move(target)) {} + /// Constructs a DeferredQueue that will call target(period) in between deferred actions. + DeferredQueue(std::function &&target) : target_(std::move(target)) {} /*! Schedules @c action to occur in @c delay units of time. @@ -79,4 +79,4 @@ template class ClockDeferrer { std::vector pending_actions_; }; -#endif /* ClockDeferrer_h */ +#endif /* DeferredQueue_h */ diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp new file mode 100644 index 000000000..e9a3dd72b --- /dev/null +++ b/ClockReceiver/JustInTime.hpp @@ -0,0 +1,55 @@ +// +// JustInTime.hpp +// Clock Signal +// +// Created by Thomas Harte on 28/07/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#ifndef JustInTime_h +#define JustInTime_h + +//#include "../Concurrency/AsyncTaskQueue.hpp" + +/*! + A JustInTimeActor holds (i) an embedded object with a run_for method; and (ii) an amount + of time since run_for was last called. + + Time can be added using the += operator. The -> operator can be used to access the + embedded object. All time accumulated will be pushed to object before the pointer is returned. + + Machines that accumulate HalfCycle time but supply to a Cycle-counted device may supply a + separate @c TargetTimeScale at template declaration. +*/ +template class JustInTimeActor { + public: + /// Constructs a new JustInTimeActor using the same construction arguments as the included object. + template JustInTimeActor(Args&&... args) : object_(std::forward(args)...) {} + + /// Adds time to the actor. + inline void operator += (const TimeScale &rhs) { + time_since_update_ += rhs; + } + + /// Flushes all accumulated time and returns a pointer to the included object. + inline T *operator->() { + object_.run_for(time_since_update_.template flush()); + return &object_; + } + + private: + T object_; + LocalTimeScale time_since_update_; + +}; + +/*! + A JustInTimeAsyncActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue. + Any time the amount of accumulated time crosses a threshold provided at construction time, + the object will be updated on the AsyncTaskQueue. +*/ +template class JustInTimeAsyncActor { + +}; + +#endif /* JustInTime_h */ diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 8a857c6ef..e39dec9da 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -77,7 +77,7 @@ template class ConcreteMachine: Cycles cycles_since_video_update_; void update_video() { - video_.run_for(cycles_since_video_update_.flush()); + video_.run_for(cycles_since_video_update_.flush()); } static const int audio_divider = 8; void update_audio() { diff --git a/Machines/Apple/AppleII/Video.hpp b/Machines/Apple/AppleII/Video.hpp index 7ae6b179b..e74b06c25 100644 --- a/Machines/Apple/AppleII/Video.hpp +++ b/Machines/Apple/AppleII/Video.hpp @@ -11,7 +11,7 @@ #include "../../../Outputs/CRT/CRT.hpp" #include "../../../ClockReceiver/ClockReceiver.hpp" -#include "../../../ClockReceiver/ClockDeferrer.hpp" +#include "../../../ClockReceiver/DeferredQueue.hpp" #include #include @@ -251,8 +251,8 @@ class VideoBase { */ void output_fat_low_resolution(uint8_t *target, const uint8_t *source, size_t length, int column, int row) const; - // Maintain a ClockDeferrer for delayed mode switches. - ClockDeferrer deferrer_; + // Maintain a DeferredQueue for delayed mode switches. + DeferredQueue deferrer_; }; template class Video: public VideoBase { diff --git a/Machines/Apple/Macintosh/DeferredAudio.hpp b/Machines/Apple/Macintosh/DeferredAudio.hpp index 72ebc6b10..6fda448fa 100644 --- a/Machines/Apple/Macintosh/DeferredAudio.hpp +++ b/Machines/Apple/Macintosh/DeferredAudio.hpp @@ -24,7 +24,7 @@ struct DeferredAudio { DeferredAudio() : audio(queue), speaker(audio) {} void flush() { - speaker.run_for(queue, time_since_update.flush_cycles()); + speaker.run_for(queue, time_since_update.flush()); } }; diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 802288ad9..44c84a3e9 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -24,6 +24,8 @@ #include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp" #include "../../../Outputs/Log.hpp" +#include "../../../ClockReceiver/JustInTime.hpp" + //#define LOG_TRACE #include "../../../Components/6522/6522.hpp" @@ -471,7 +473,7 @@ template class ConcreteMachin Apple::IWM iwm; void flush() { - iwm.run_for(time_since_update.flush_cycles()); + iwm.run_for(time_since_update.flush()); } }; @@ -610,7 +612,6 @@ template class ConcreteMachin HalfCycles keyboard_clock_; HalfCycles time_since_video_update_; HalfCycles time_until_video_event_; - HalfCycles time_since_iwm_update_; HalfCycles time_since_mouse_update_; bool ROM_is_overlay_ = true; diff --git a/Machines/Atari2600/Bus.hpp b/Machines/Atari2600/Bus.hpp index ba58c81c5..efcb05091 100644 --- a/Machines/Atari2600/Bus.hpp +++ b/Machines/Atari2600/Bus.hpp @@ -55,13 +55,13 @@ class Bus { // video backlog accumulation counter Cycles cycles_since_video_update_; inline void update_video() { - tia_.run_for(cycles_since_video_update_.flush()); + tia_.run_for(cycles_since_video_update_.flush()); } // RIOT backlog accumulation counter Cycles cycles_since_6532_update_; inline void update_6532() { - mos6532_.run_for(cycles_since_6532_update_.flush()); + mos6532_.run_for(cycles_since_6532_update_.flush()); } }; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 2ac58a2e5..4c65dc304 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -702,7 +702,7 @@ class ConcreteMachine: private: void update_video() { - mos6560_.run_for(cycles_since_mos6560_update_.flush()); + mos6560_.run_for(cycles_since_mos6560_update_.flush()); } CPU::MOS6502::Processor m6502_; diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index f0a31815e..e87f2641f 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -510,7 +510,7 @@ class ConcreteMachine: // MARK: - Work deferral updates. inline void update_display() { if(cycles_since_display_update_ > 0) { - video_output_.run_for(cycles_since_display_update_.flush()); + video_output_.run_for(cycles_since_display_update_.flush()); } } diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index b90ce9fd8..158acc11e 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -182,7 +182,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { private: void update_ay() { - speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush_cycles()); + speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush()); } bool ay_bdir_ = false; bool ay_bc1_ = false; @@ -585,7 +585,7 @@ template class Co uint8_t ram_[65536]; Cycles cycles_since_video_update_; inline void update_video() { - video_output_.run_for(cycles_since_video_update_.flush()); + video_output_.run_for(cycles_since_video_update_.flush()); } // ROM bookkeeping @@ -617,7 +617,7 @@ template class Co Apple::DiskII diskii_; Cycles cycles_since_diskii_update_; void flush_diskii() { - diskii_.run_for(cycles_since_diskii_update_.flush()); + diskii_.run_for(cycles_since_diskii_update_.flush()); } std::vector pravetz_rom_; std::size_t pravetz_rom_base_pointer_ = 0; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index d43ce8c20..c5ea334a6 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -983,6 +983,7 @@ 4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = ""; }; 4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = ""; }; 4B7F1896215486A100388727 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = ""; }; + 4B80214322EE7C3E00068002 /* JustInTime.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JustInTime.hpp; sourceTree = ""; }; 4B80ACFE1F85CAC900176895 /* BestEffortUpdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BestEffortUpdater.cpp; path = ../../Concurrency/BestEffortUpdater.cpp; sourceTree = ""; }; 4B80ACFF1F85CACA00176895 /* BestEffortUpdater.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BestEffortUpdater.hpp; path = ../../Concurrency/BestEffortUpdater.hpp; sourceTree = ""; }; 4B8334811F5D9FF70097E338 /* PartialMachineCycle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PartialMachineCycle.cpp; sourceTree = ""; }; @@ -1051,7 +1052,7 @@ 4B894516201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = ""; }; 4B894517201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = ""; }; 4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = ""; }; - 4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockDeferrer.hpp; sourceTree = ""; }; + 4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DeferredQueue.hpp; sourceTree = ""; }; 4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = ""; }; 4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = ""; }; 4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LowpassSpeaker.hpp; sourceTree = ""; }; @@ -3383,10 +3384,11 @@ 4BF660691F281573002CB053 /* ClockReceiver */ = { isa = PBXGroup; children = ( - 4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */, 4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */, 4BF6606A1F281573002CB053 /* ClockReceiver.hpp */, + 4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */, 4BB06B211F316A3F00600C7A /* ForceInline.hpp */, + 4B80214322EE7C3E00068002 /* JustInTime.hpp */, 4B449C942063389900A095C8 /* TimeTypes.hpp */, ); name = ClockReceiver; From a43ada82b24e13ca934ffae0a572cda89195e761 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 15:38:41 -0400 Subject: [PATCH 02/12] Experiments with a JustInTimeActor in the Master System. --- ClockReceiver/ClockReceiver.hpp | 23 +++++----- ClockReceiver/JustInTime.hpp | 13 ++++-- .../Implementation/6522Implementation.hpp | 14 +++--- Machines/Apple/Macintosh/Macintosh.cpp | 2 +- Machines/ColecoVision/ColecoVision.cpp | 2 +- Machines/MSX/MSX.cpp | 10 ++--- Machines/MasterSystem/MasterSystem.cpp | 44 ++++++++----------- 7 files changed, 55 insertions(+), 53 deletions(-) diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index ba4a67629..a37d03b7e 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -150,7 +150,7 @@ template class WrappedInt { is reset to zero. */ template forceinline Target flush() { - const Target result(length_); + const Target result = T(length_); length_ = 0; return result; } @@ -176,7 +176,7 @@ class HalfCycles: public WrappedInt { forceinline constexpr HalfCycles(int l) noexcept : WrappedInt(l) {} forceinline constexpr HalfCycles() noexcept : WrappedInt() {} - forceinline constexpr HalfCycles(const Cycles cycles) noexcept : WrappedInt(cycles.as_int() * 2) {} + forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt(cycles.as_int() * 2) {} forceinline constexpr HalfCycles(const HalfCycles &half_cycles) noexcept : WrappedInt(half_cycles.length_) {} /// @returns The number of whole cycles completely covered by this span of half cycles. @@ -184,17 +184,20 @@ class HalfCycles: public WrappedInt { return Cycles(length_ >> 1); } - /// Flushes the whole cycles in @c this, subtracting that many from the total stored here. - template forceinline Cycles flush() { - const Cycles result(length_ >> 1); - length_ &= 1; + /*! + Flushes the value in @c this. The current value is returned, and the internal value + is reset to zero. + */ + template forceinline Target flush() { + const Target result(length_); + length_ = 0; return result; } - /// Flushes the half cycles in @c this, returning the number stored and setting this total to zero. - forceinline HalfCycles flush() { - HalfCycles result(length_); - length_ = 0; + /// Flushes the whole cycles in @c this, subtracting that many from the total stored here. + template <> forceinline Cycles flush() { + const Cycles result(length_ >> 1); + length_ &= 1; return result; } diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp index e9a3dd72b..21d0cdf27 100644 --- a/ClockReceiver/JustInTime.hpp +++ b/ClockReceiver/JustInTime.hpp @@ -27,20 +27,27 @@ template template JustInTimeActor(Args&&... args) : object_(std::forward(args)...) {} /// Adds time to the actor. - inline void operator += (const TimeScale &rhs) { + inline void operator += (const LocalTimeScale &rhs) { time_since_update_ += rhs; + is_flushed_ = false; } /// Flushes all accumulated time and returns a pointer to the included object. inline T *operator->() { - object_.run_for(time_since_update_.template flush()); + flush(); return &object_; } + /// Flushes all accumulated time. + inline void flush() { + if(!is_flushed_) object_.run_for(time_since_update_.template flush()); + is_flushed_ = true; + } + private: T object_; LocalTimeScale time_since_update_; - + bool is_flushed_ = true; }; /*! diff --git a/Components/6522/Implementation/6522Implementation.hpp b/Components/6522/Implementation/6522Implementation.hpp index 9b6f5118e..c941e2106 100644 --- a/Components/6522/Implementation/6522Implementation.hpp +++ b/Components/6522/Implementation/6522Implementation.hpp @@ -38,7 +38,7 @@ template void MOS6522::set_register(int address, uint8_t value) // Store locally and communicate outwards. registers_.output[1] = value; - bus_handler_.run_for(time_since_bus_handler_call_.flush()); + bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]); registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge)); @@ -48,7 +48,7 @@ template void MOS6522::set_register(int address, uint8_t value) case 0x1: // Write Port A. registers_.output[0] = value; - bus_handler_.run_for(time_since_bus_handler_call_.flush()); + bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.set_port_output(Port::A, value, registers_.data_direction[0]); if(handshake_modes_[1] != HandshakeMode::None) { @@ -205,7 +205,7 @@ template uint8_t MOS6522::get_register(int address) { } template uint8_t MOS6522::get_port_input(Port port, uint8_t output_mask, uint8_t output) { - bus_handler_.run_for(time_since_bus_handler_call_.flush()); + bus_handler_.run_for(time_since_bus_handler_call_.flush()); const uint8_t input = bus_handler_.get_port_input(port); return (input & ~output_mask) | (output & output_mask); } @@ -220,7 +220,7 @@ template void MOS6522::reevaluate_interrupts() { if(new_interrupt_status != last_posted_interrupt_status_) { last_posted_interrupt_status_ = new_interrupt_status; - bus_handler_.run_for(time_since_bus_handler_call_.flush()); + bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.set_interrupt_status(new_interrupt_status); } } @@ -338,7 +338,7 @@ template void MOS6522::do_phase1() { // Determine whether to toggle PB7. if(registers_.auxiliary_control&0x80) { registers_.output[1] ^= 0x80; - bus_handler_.run_for(time_since_bus_handler_call_.flush()); + bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]); } } @@ -369,7 +369,7 @@ template void MOS6522::run_for(const HalfCycles half_cycles) { } template void MOS6522::flush() { - bus_handler_.run_for(time_since_bus_handler_call_.flush()); + bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.flush(); } @@ -422,7 +422,7 @@ template void MOS6522::set_control_line_output(Port port, Line l control_outputs_[port].lines[line] = value; if(value != LineState::Input) { - bus_handler_.run_for(time_since_bus_handler_call_.flush()); + bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.set_control_line_output(port, line, value != LineState::Off); } } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 44c84a3e9..190db046e 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -458,7 +458,7 @@ template class ConcreteMachin private: void update_video() { - video_.run_for(time_since_video_update_.flush()); + video_.run_for(time_since_video_update_.flush()); time_until_video_event_ = video_.get_next_sequence_point(); } diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index d699a4016..3def8f292 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -397,7 +397,7 @@ class ConcreteMachine: speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider))); } inline void update_video() { - vdp_.run_for(time_since_vdp_update_.flush()); + vdp_.run_for(time_since_vdp_update_.flush()); } CPU::Z80::Processor z80_; diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index a4b563d01..89c8ee825 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -489,7 +489,7 @@ class ConcreteMachine: *cycle.value = read_pointers_[address >> 13][address & 8191]; } else { int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; - memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); + memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); *cycle.value = memory_slots_[slot_hit].handler->read(address); } break; @@ -500,7 +500,7 @@ class ConcreteMachine: int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; if(memory_slots_[slot_hit].handler) { update_audio(); - memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); + memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush()); memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]); } } break; @@ -508,7 +508,7 @@ class ConcreteMachine: case CPU::Z80::PartialMachineCycle::Input: switch(address & 0xff) { case 0x98: case 0x99: - vdp_.run_for(time_since_vdp_update_.flush()); + vdp_.run_for(time_since_vdp_update_.flush()); *cycle.value = vdp_.get_register(address); z80_.set_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_.get_time_until_interrupt(); @@ -536,7 +536,7 @@ class ConcreteMachine: const int port = address & 0xff; switch(port) { case 0x98: case 0x99: - vdp_.run_for(time_since_vdp_update_.flush()); + vdp_.run_for(time_since_vdp_update_.flush()); vdp_.set_register(address, *cycle.value); z80_.set_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_.get_time_until_interrupt(); @@ -612,7 +612,7 @@ class ConcreteMachine: } void flush() { - vdp_.run_for(time_since_vdp_update_.flush()); + vdp_.run_for(time_since_vdp_update_.flush()); update_audio(); audio_queue_.perform(); } diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 0f5f89eda..63468faa2 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -18,6 +18,7 @@ #include "../KeyboardMachine.hpp" #include "../../ClockReceiver/ForceInline.hpp" +#include "../../ClockReceiver/JustInTime.hpp" #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "../../Outputs/Log.hpp" @@ -168,16 +169,16 @@ class ConcreteMachine: } void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { - vdp_.set_tv_standard( + vdp_->set_tv_standard( (region_ == Target::Region::Europe) ? TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC); - time_until_debounce_ = vdp_.get_time_until_line(-1); + time_until_debounce_ = vdp_->get_time_until_line(-1); - vdp_.set_scan_target(scan_target); + vdp_->set_scan_target(scan_target); } void set_display_type(Outputs::Display::DisplayType display_type) override { - vdp_.set_display_type(display_type); + vdp_->set_display_type(display_type); } Outputs::Speaker::Speaker *get_speaker() override { @@ -189,7 +190,7 @@ class ConcreteMachine: } forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { - time_since_vdp_update_ += cycle.length; + vdp_ += cycle.length; time_since_sn76489_update_ += cycle.length; if(cycle.is_terminal()) { @@ -233,17 +234,15 @@ class ConcreteMachine: *cycle.value = 0xff; break; case 0x40: - update_video(); - *cycle.value = vdp_.get_current_line(); + *cycle.value = vdp_->get_current_line(); break; case 0x41: - *cycle.value = vdp_.get_latched_horizontal_counter(); + *cycle.value = vdp_->get_latched_horizontal_counter(); break; case 0x80: case 0x81: - update_video(); - *cycle.value = vdp_.get_register(address); - z80_.set_interrupt_line(vdp_.get_interrupt_line()); - time_until_interrupt_ = vdp_.get_time_until_interrupt(); + *cycle.value = vdp_->get_register(address); + z80_.set_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); break; case 0xc0: { Joystick *const joypad1 = static_cast(joysticks_[0].get()); @@ -284,8 +283,7 @@ class ConcreteMachine: // Latch if either TH has newly gone to 1. if((new_ths^previous_ths)&new_ths) { - update_video(); - vdp_.latch_horizontal_counter(); + vdp_->latch_horizontal_counter(); } } break; case 0x40: case 0x41: @@ -293,10 +291,9 @@ class ConcreteMachine: sn76489_.set_register(*cycle.value); break; case 0x80: case 0x81: - update_video(); - vdp_.set_register(address, *cycle.value); - z80_.set_interrupt_line(vdp_.get_interrupt_line()); - time_until_interrupt_ = vdp_.get_time_until_interrupt(); + vdp_->set_register(address, *cycle.value); + z80_.set_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); break; case 0xc0: LOG("TODO: [output] I/O port A/N; " << int(*cycle.value)); @@ -331,15 +328,14 @@ class ConcreteMachine: time_until_debounce_ -= cycle.length; if(time_until_debounce_ <= HalfCycles(0)) { z80_.set_non_maskable_interrupt_line(pause_is_pressed_); - update_video(); - time_until_debounce_ = vdp_.get_time_until_line(-1); + time_until_debounce_ = vdp_->get_time_until_line(-1); } return HalfCycles(0); } void flush() { - update_video(); + vdp_.flush(); update_audio(); audio_queue_.perform(); } @@ -412,16 +408,13 @@ class ConcreteMachine: inline void update_audio() { speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider))); } - inline void update_video() { - vdp_.run_for(time_since_vdp_update_.flush()); - } using Target = Analyser::Static::Sega::Target; Target::Model model_; Target::Region region_; Target::PagingScheme paging_scheme_; CPU::Z80::Processor z80_; - TI::TMS::TMS9918 vdp_; + JustInTimeActor vdp_; Concurrency::DeferringAsyncTaskQueue audio_queue_; TI::SN76489 sn76489_; @@ -431,7 +424,6 @@ class ConcreteMachine: Inputs::Keyboard keyboard_; bool reset_is_pressed_ = false, pause_is_pressed_ = false; - HalfCycles time_since_vdp_update_; HalfCycles time_since_sn76489_update_; HalfCycles time_until_interrupt_; HalfCycles time_until_debounce_; From 2f2478d2d3af5ae44834ce1051178770c716f0c2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 16:38:57 -0400 Subject: [PATCH 03/12] Implements AsyncJustInTimeActor, experimentally. --- ClockReceiver/JustInTime.hpp | 44 +++++++++++++++++-- .../xcschemes/Clock Signal.xcscheme | 2 +- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp index 21d0cdf27..e5f9a5ef6 100644 --- a/ClockReceiver/JustInTime.hpp +++ b/ClockReceiver/JustInTime.hpp @@ -9,7 +9,7 @@ #ifndef JustInTime_h #define JustInTime_h -//#include "../Concurrency/AsyncTaskQueue.hpp" +#include "../Concurrency/AsyncTaskQueue.hpp" /*! A JustInTimeActor holds (i) an embedded object with a run_for method; and (ii) an amount @@ -51,12 +51,50 @@ template }; /*! - A JustInTimeAsyncActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue. + A AsyncJustInTimeActor acts like a JustInTimeActor but additionally contains an AsyncTaskQueue. Any time the amount of accumulated time crosses a threshold provided at construction time, the object will be updated on the AsyncTaskQueue. */ -template class JustInTimeAsyncActor { +template class AsyncJustInTimeActor { + public: + /// Constructs a new AsyncJustInTimeActor using the same construction arguments as the included object. + template AsyncJustInTimeActor(TargetTimeScale threshold, Args&&... args) : + object_(std::forward(args)...), + threshold_(threshold) {} + /// Adds time to the actor. + inline void operator += (const LocalTimeScale &rhs) { + time_since_update_ += rhs; + if(time_since_update_ >= threshold_) { + time_since_update_ -= threshold_; + task_queue_.enqueue([this] () { + object_.run_for(threshold_); + }); + } + is_flushed_ = false; + } + + /// Flushes all accumulated time and returns a pointer to the included object. + inline T *operator->() { + flush(); + return &object_; + } + + /// Flushes all accumulated time. + inline void flush() { + if(!is_flushed_) { + task_queue_.flush(); + object_.run_for(time_since_update_.template flush()); + is_flushed_ = true; + } + } + + private: + T object_; + LocalTimeScale time_since_update_; + TargetTimeScale threshold_; + bool is_flushed_ = true; + Concurrency::AsyncTaskQueue task_queue_; }; #endif /* JustInTime_h */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 0cd11b1be..3ae34802d 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -69,7 +69,7 @@ Date: Mon, 29 Jul 2019 17:17:04 -0400 Subject: [PATCH 04/12] Adds a route to not bumping time. --- ClockReceiver/JustInTime.hpp | 10 ++++++++++ Machines/MasterSystem/MasterSystem.cpp | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp index e5f9a5ef6..96df1e102 100644 --- a/ClockReceiver/JustInTime.hpp +++ b/ClockReceiver/JustInTime.hpp @@ -38,6 +38,11 @@ template return &object_; } + /// Returns a pointer to the included object without flushing time. + inline T *last_valid() { + return *object_; + } + /// Flushes all accumulated time. inline void flush() { if(!is_flushed_) object_.run_for(time_since_update_.template flush()); @@ -80,6 +85,11 @@ template return &object_; } + /// Returns a pointer to the included object without flushing time. + inline T *last_valid() { + return *object_; + } + /// Flushes all accumulated time. inline void flush() { if(!is_flushed_) { diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 63468faa2..7aff87195 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -237,7 +237,7 @@ class ConcreteMachine: *cycle.value = vdp_->get_current_line(); break; case 0x41: - *cycle.value = vdp_->get_latched_horizontal_counter(); + *cycle.value = vdp_.last_valid()->get_latched_horizontal_counter(); break; case 0x80: case 0x81: *cycle.value = vdp_->get_register(address); From 9859f99513d07b4558c880d60e8b6baa45de5b0d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 17:17:04 -0400 Subject: [PATCH 05/12] Adds a route to not bumping time. --- ClockReceiver/JustInTime.hpp | 10 ++++++++++ Machines/MasterSystem/MasterSystem.cpp | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp index e5f9a5ef6..eff555061 100644 --- a/ClockReceiver/JustInTime.hpp +++ b/ClockReceiver/JustInTime.hpp @@ -38,6 +38,11 @@ template return &object_; } + /// Returns a pointer to the included object without flushing time. + inline T *last_valid() { + return &object_; + } + /// Flushes all accumulated time. inline void flush() { if(!is_flushed_) object_.run_for(time_since_update_.template flush()); @@ -80,6 +85,11 @@ template return &object_; } + /// Returns a pointer to the included object without flushing time. + inline T *last_valid() { + return &object_; + } + /// Flushes all accumulated time. inline void flush() { if(!is_flushed_) { diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 63468faa2..7aff87195 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -237,7 +237,7 @@ class ConcreteMachine: *cycle.value = vdp_->get_current_line(); break; case 0x41: - *cycle.value = vdp_->get_latched_horizontal_counter(); + *cycle.value = vdp_.last_valid()->get_latched_horizontal_counter(); break; case 0x80: case 0x81: *cycle.value = vdp_->get_register(address); From 7dcad516bd6123fe8b2bbcb47f14a3902890c2d5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 17:21:34 -0400 Subject: [PATCH 06/12] Undoes incorrect project change. --- .../xcshareddata/xcschemes/Clock Signal.xcscheme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 3ae34802d..0cd11b1be 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -69,7 +69,7 @@ Date: Mon, 29 Jul 2019 21:22:31 -0400 Subject: [PATCH 07/12] Rolls out JustInTime acting to the MSX and ColecoVision. --- Machines/ColecoVision/ColecoVision.cpp | 31 +++++++++++-------------- Machines/MSX/MSX.cpp | 32 ++++++++++++-------------- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 3def8f292..ce8b85666 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -19,6 +19,7 @@ #include "../../Configurable/StandardOptions.hpp" #include "../../ClockReceiver/ForceInline.hpp" +#include "../../ClockReceiver/JustInTime.hpp" #include "../../Outputs/Speaker/Implementation/CompoundSource.hpp" #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" @@ -169,7 +170,7 @@ class ConcreteMachine: } // ColecoVisions have composite output only. - vdp_.set_display_type(Outputs::Display::DisplayType::CompositeColour); + vdp_->set_display_type(Outputs::Display::DisplayType::CompositeColour); } ~ConcreteMachine() { @@ -181,11 +182,11 @@ class ConcreteMachine: } void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { - vdp_.set_scan_target(scan_target); + vdp_->set_scan_target(scan_target); } void set_display_type(Outputs::Display::DisplayType display_type) override { - vdp_.set_display_type(display_type); + vdp_->set_display_type(display_type); } Outputs::Speaker::Speaker *get_speaker() override { @@ -210,7 +211,7 @@ class ConcreteMachine: } const HalfCycles length = cycle.length + penalty; - time_since_vdp_update_ += length; + vdp_ += length; time_since_sn76489_update_ += length; // Act only if necessary. @@ -255,10 +256,9 @@ class ConcreteMachine: case CPU::Z80::PartialMachineCycle::Input: switch((address >> 5) & 7) { case 5: - update_video(); - *cycle.value = vdp_.get_register(address); - z80_.set_non_maskable_interrupt_line(vdp_.get_interrupt_line()); - time_until_interrupt_ = vdp_.get_time_until_interrupt(); + *cycle.value = vdp_->get_register(address); + z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); break; case 7: { @@ -299,10 +299,9 @@ class ConcreteMachine: break; case 5: - update_video(); - vdp_.set_register(address, *cycle.value); - z80_.set_non_maskable_interrupt_line(vdp_.get_interrupt_line()); - time_until_interrupt_ = vdp_.get_time_until_interrupt(); + vdp_->set_register(address, *cycle.value); + z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); break; case 7: @@ -354,7 +353,7 @@ class ConcreteMachine: } void flush() { - update_video(); + vdp_.flush(); update_audio(); audio_queue_.perform(); } @@ -396,12 +395,9 @@ class ConcreteMachine: inline void update_audio() { speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider))); } - inline void update_video() { - vdp_.run_for(time_since_vdp_update_.flush()); - } CPU::Z80::Processor z80_; - TI::TMS::TMS9918 vdp_; + JustInTimeActor vdp_; Concurrency::DeferringAsyncTaskQueue audio_queue_; TI::SN76489 sn76489_; @@ -424,7 +420,6 @@ class ConcreteMachine: std::vector> joysticks_; bool joysticks_in_keypad_mode_ = false; - HalfCycles time_since_vdp_update_; HalfCycles time_since_sn76489_update_; HalfCycles time_until_interrupt_; diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 89c8ee825..df139190e 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -45,6 +45,7 @@ #include "../../Configurable/StandardOptions.hpp" #include "../../ClockReceiver/ForceInline.hpp" +#include "../../ClockReceiver/JustInTime.hpp" #include "../../Analyser/Static/MSX/Target.hpp" @@ -187,7 +188,7 @@ class ConcreteMachine: switch(target.region) { case Target::Region::Japan: required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390); - vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC); + vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); is_ntsc = true; character_generator = 0; @@ -195,7 +196,7 @@ class ConcreteMachine: break; case Target::Region::USA: required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0); - vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC); + vdp_->set_tv_standard(TI::TMS::TVStandard::NTSC); is_ntsc = true; character_generator = 1; @@ -203,7 +204,7 @@ class ConcreteMachine: break; case Target::Region::Europe: required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0); - vdp_.set_tv_standard(TI::TMS::TVStandard::PAL); + vdp_->set_tv_standard(TI::TMS::TVStandard::PAL); is_ntsc = false; character_generator = 1; @@ -278,11 +279,11 @@ class ConcreteMachine: } void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { - vdp_.set_scan_target(scan_target); + vdp_->set_scan_target(scan_target); } void set_display_type(Outputs::Display::DisplayType display_type) override { - vdp_.set_display_type(display_type); + vdp_->set_display_type(display_type); } Outputs::Speaker::Speaker *get_speaker() override { @@ -411,7 +412,7 @@ class ConcreteMachine: // but otherwise runs without pause. const HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0); const HalfCycles total_length = addition + cycle.length; - time_since_vdp_update_ += total_length; + vdp_ += total_length; time_since_ay_update_ += total_length; memory_slots_[0].cycles_since_update += total_length; memory_slots_[1].cycles_since_update += total_length; @@ -508,10 +509,9 @@ class ConcreteMachine: case CPU::Z80::PartialMachineCycle::Input: switch(address & 0xff) { case 0x98: case 0x99: - vdp_.run_for(time_since_vdp_update_.flush()); - *cycle.value = vdp_.get_register(address); - z80_.set_interrupt_line(vdp_.get_interrupt_line()); - time_until_interrupt_ = vdp_.get_time_until_interrupt(); + *cycle.value = vdp_->get_register(address); + z80_.set_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); break; case 0xa2: @@ -536,10 +536,9 @@ class ConcreteMachine: const int port = address & 0xff; switch(port) { case 0x98: case 0x99: - vdp_.run_for(time_since_vdp_update_.flush()); - vdp_.set_register(address, *cycle.value); - z80_.set_interrupt_line(vdp_.get_interrupt_line()); - time_until_interrupt_ = vdp_.get_time_until_interrupt(); + vdp_->set_register(address, *cycle.value); + z80_.set_interrupt_line(vdp_->get_interrupt_line()); + time_until_interrupt_ = vdp_->get_time_until_interrupt(); break; case 0xa0: case 0xa1: @@ -612,7 +611,7 @@ class ConcreteMachine: } void flush() { - vdp_.run_for(time_since_vdp_update_.flush()); + vdp_.flush(); update_audio(); audio_queue_.perform(); } @@ -753,7 +752,7 @@ class ConcreteMachine: }; CPU::Z80::Processor z80_; - TI::TMS::TMS9918 vdp_; + JustInTimeActor vdp_; Intel::i8255::i8255 i8255_; Concurrency::DeferringAsyncTaskQueue audio_queue_; @@ -797,7 +796,6 @@ class ConcreteMachine: uint8_t scratch_[8192]; uint8_t unpopulated_[8192]; - HalfCycles time_since_vdp_update_; HalfCycles time_since_ay_update_; HalfCycles time_until_interrupt_; From 2f90ed1f32bed715f776b214d7c6cc888134098a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 21:23:37 -0400 Subject: [PATCH 08/12] Attempts to reformulate into valid C++. --- ClockReceiver/ClockReceiver.hpp | 44 ++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index a37d03b7e..1a77a8e72 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -48,6 +48,16 @@ */ +namespace { + +template Target flush(int &value) { + const Target result = Source(value); + value = 0; + return result; +} + +} + /*! Provides a class that wraps a plain int, providing most of the basic arithmetic and Boolean operators, but forcing callers and receivers to be explicit as to usage. @@ -150,9 +160,7 @@ template class WrappedInt { is reset to zero. */ template forceinline Target flush() { - const Target result = T(length_); - length_ = 0; - return result; + return ::flush(length_); } // operator int() is deliberately not provided, to avoid accidental subtitution of @@ -184,23 +192,6 @@ class HalfCycles: public WrappedInt { return Cycles(length_ >> 1); } - /*! - Flushes the value in @c this. The current value is returned, and the internal value - is reset to zero. - */ - template forceinline Target flush() { - const Target result(length_); - length_ = 0; - return result; - } - - /// Flushes the whole cycles in @c this, subtracting that many from the total stored here. - template <> forceinline Cycles flush() { - const Cycles result(length_ >> 1); - length_ &= 1; - return result; - } - /*! Severs from @c this the effect of dividing by @c divisor; @c this will end up with the value of @c this modulo @c divisor and @c divided by @c divisor is returned. @@ -213,6 +204,19 @@ class HalfCycles: public WrappedInt { } }; +namespace { + +template <> Cycles flush(int &value) { + const Cycles result(value >> 1); + value &= 1; + return result; +} + +} + +// Create a specialisation of WrappedInt::flush for converting HalfCycles to Cycles +// without losing the fractional part. + /*! If a component implements only run_for(Cycles), an owner can wrap it in HalfClockReceiver automatically to gain run_for(HalfCycles). From bc25c526838e8bf8a0ac3cb116b4691745dfcb17 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 22:49:02 -0400 Subject: [PATCH 09/12] Although duplicative, resolves function redefinition. --- ClockReceiver/ClockReceiver.hpp | 54 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index 1a77a8e72..4ca56dd70 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -48,16 +48,6 @@ */ -namespace { - -template Target flush(int &value) { - const Target result = Source(value); - value = 0; - return result; -} - -} - /*! Provides a class that wraps a plain int, providing most of the basic arithmetic and Boolean operators, but forcing callers and receivers to be explicit as to usage. @@ -155,14 +145,6 @@ template class WrappedInt { return result; } - /*! - Flushes the value in @c this. The current value is returned, and the internal value - is reset to zero. - */ - template forceinline Target flush() { - return ::flush(length_); - } - // operator int() is deliberately not provided, to avoid accidental subtitution of // classes that use this template. @@ -176,6 +158,16 @@ class Cycles: public WrappedInt { forceinline constexpr Cycles(int l) noexcept : WrappedInt(l) {} forceinline constexpr Cycles() noexcept : WrappedInt() {} forceinline constexpr Cycles(const Cycles &cycles) noexcept : WrappedInt(cycles.length_) {} + + /*! + Flushes the value in @c this. The current value is returned, and the internal value + is reset to zero. + */ + template T flush() { + const T result(*this); + length_ = 0; + return result; + } }; /// Describes an integer number of half cycles: single clock signal transitions. @@ -192,6 +184,22 @@ class HalfCycles: public WrappedInt { return Cycles(length_ >> 1); } + /*! + Flushes the value in @c this. The current value is returned, and the internal value + is reset to zero. + */ + template T flush() { + const T result(*this); + length_ = 0; + return result; + } + + template <> Cycles flush() { + const Cycles result(length_ >> 1); + length_ &= 1; + return result; + } + /*! Severs from @c this the effect of dividing by @c divisor; @c this will end up with the value of @c this modulo @c divisor and @c divided by @c divisor is returned. @@ -204,16 +212,6 @@ class HalfCycles: public WrappedInt { } }; -namespace { - -template <> Cycles flush(int &value) { - const Cycles result(value >> 1); - value &= 1; - return result; -} - -} - // Create a specialisation of WrappedInt::flush for converting HalfCycles to Cycles // without losing the fractional part. From 12441bddabfe20ee42e2c7a652ce02e4b0ec3641 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 23:04:02 -0400 Subject: [PATCH 10/12] Tries operator overloading as a workaround. --- ClockReceiver/ClockReceiver.hpp | 51 ++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index 4ca56dd70..c929aa186 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -145,6 +145,18 @@ template class WrappedInt { return result; } + /*! + Flushes the value in @c this. The current value is returned, and the internal value + is reset to zero. + */ + forceinline template Result flush() { + // Jiggery pokery here; switching to function overloading avoids + // the namespace-level requirement for template specialisation. + Result r; + static_cast(this)->fill_flush(r); + return r; + } + // operator int() is deliberately not provided, to avoid accidental subtitution of // classes that use this template. @@ -159,14 +171,11 @@ class Cycles: public WrappedInt { forceinline constexpr Cycles() noexcept : WrappedInt() {} forceinline constexpr Cycles(const Cycles &cycles) noexcept : WrappedInt(cycles.length_) {} - /*! - Flushes the value in @c this. The current value is returned, and the internal value - is reset to zero. - */ - template T flush() { - const T result(*this); + private: + friend WrappedInt; + void fill_flush(Cycles &result) { + result.length_ = length_; length_ = 0; - return result; } }; @@ -184,22 +193,6 @@ class HalfCycles: public WrappedInt { return Cycles(length_ >> 1); } - /*! - Flushes the value in @c this. The current value is returned, and the internal value - is reset to zero. - */ - template T flush() { - const T result(*this); - length_ = 0; - return result; - } - - template <> Cycles flush() { - const Cycles result(length_ >> 1); - length_ &= 1; - return result; - } - /*! Severs from @c this the effect of dividing by @c divisor; @c this will end up with the value of @c this modulo @c divisor and @c divided by @c divisor is returned. @@ -210,6 +203,18 @@ class HalfCycles: public WrappedInt { length_ %= half_divisor.length_; return result; } + + private: + friend WrappedInt; + void fill_flush(Cycles &result) { + result = Cycles(length_ >> 1); + length_ &= 1; + } + + void fill_flush(HalfCycles &result) { + result.length_ = length_; + length_ = 0; + } }; // Create a specialisation of WrappedInt::flush for converting HalfCycles to Cycles From 0521de668aecbb5a0e9b1444b2568ed9b57ad267 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Jul 2019 23:07:02 -0400 Subject: [PATCH 11/12] Omits redundant qualifier. This code can't be anything but `inline`. --- ClockReceiver/ClockReceiver.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index c929aa186..e19299fb3 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -149,7 +149,7 @@ template class WrappedInt { Flushes the value in @c this. The current value is returned, and the internal value is reset to zero. */ - forceinline template Result flush() { + template Result flush() { // Jiggery pokery here; switching to function overloading avoids // the namespace-level requirement for template specialisation. Result r; From d8a41575c81515d04cf2b5e6eab8aa825caf76ad Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 30 Jul 2019 10:54:56 -0400 Subject: [PATCH 12/12] Picks a better function name. --- ClockReceiver/ClockReceiver.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index e19299fb3..6fe49cf1d 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -153,7 +153,7 @@ template class WrappedInt { // Jiggery pokery here; switching to function overloading avoids // the namespace-level requirement for template specialisation. Result r; - static_cast(this)->fill_flush(r); + static_cast(this)->fill(r); return r; } @@ -173,7 +173,7 @@ class Cycles: public WrappedInt { private: friend WrappedInt; - void fill_flush(Cycles &result) { + void fill(Cycles &result) { result.length_ = length_; length_ = 0; } @@ -206,12 +206,12 @@ class HalfCycles: public WrappedInt { private: friend WrappedInt; - void fill_flush(Cycles &result) { + void fill(Cycles &result) { result = Cycles(length_ >> 1); length_ &= 1; } - void fill_flush(HalfCycles &result) { + void fill(HalfCycles &result) { result.length_ = length_; length_ = 0; }