diff --git a/ClockReceiver/ClockReceiver.hpp b/ClockReceiver/ClockReceiver.hpp index f3f8c9b4a..51e43f1da 100644 --- a/ClockReceiver/ClockReceiver.hpp +++ b/ClockReceiver/ClockReceiver.hpp @@ -9,6 +9,43 @@ #ifndef ClockReceiver_hpp #define ClockReceiver_hpp +/* + Informal pattern for all classes that run from a clock cycle: + + Each will implement either or both of run_for(Cycles) and run_for(HalfCycles), as + is appropriate. + + Callers that are accumulating HalfCycles but want to talk to receivers that implement + only run_for(Cycles) can use HalfCycle.flush_cycles if they have appropriate storage, or + can wrap the receiver in HalfClockReceiver in order automatically to bind half-cycle + storage to it. + + Alignment rule: + + run_for(Cycles) may be called only after an even number of half cycles. E.g. the following + sequence will have undefined results: + + run_for(HalfCycles(1)) + run_for(Cycles(1)) + + An easy way to ensure this as a caller is to pick only one of run_for(Cycles) and + run_for(HalfCycles) to use. + + Reasoning: + + Users of this template may with to implement run_for(Cycles) and run_for(HalfCycles) + where there is a need to implement at half-cycle precision but a faster execution + path can be offered for full-cycle precision. Those users are permitted to assume + phase in run_for(Cycles) and should do so to be compatible with callers that use + only run_for(Cycles). + + Corollary: + + Starting from nothing, the first run_for(HalfCycles(1)) will do the **first** half + of a full cycle. The second will do the second half. Etc. + +*/ + /*! 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. @@ -69,7 +106,7 @@ template class WrappedInt { inline bool operator !=(const T &rhs) const { return length_ != rhs.length_; } inline bool operator !() const { return !length_; } - inline operator bool() const { return !!length_; } + // bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse inline int as_int() const { return length_; } @@ -116,35 +153,20 @@ class HalfCycles: public WrappedInt { inline HalfCycles(const Cycles &cycles) : WrappedInt(cycles.as_int() << 1) {} inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt(half_cycles.length_) {} + + /// @returns The number of whole cycles completely covered by this span of half cycles. + inline Cycles cycles() { + return Cycles(length_ >> 1); + } + + ///Flushes the whole cycles in @c this, subtracting that many from the total stored here. + inline Cycles flush_cycles() { + Cycles result(length_ >> 1); + length_ &= 1; + return result; + } }; -/* - Alignment rule: - - run_for(Cycles) may be called only at the start of a cycle. E.g. the following - sequence will have undefined results: - - run_for(HalfCycles(1)) - run_for(Cycles(1)) - - An easy way to ensure this as a caller is to pick only one of run_for(Cycles) and - run_for(HalfCycles) to use. - - Reasoning: - - Users of this template may with to implement run_for(Cycles) and run_for(HalfCycles) - where there is a need to implement at half-cycle precision but a faster execution - path can be offered for full-cycle precision. Those users are permitted to assume - phase in run_for(Cycles) and should do so to be compatible with callers that use - only run_for(Cycles). - - Corollary: - - Starting from nothing, the first run_for(HalfCycles(1)) will do the **first** half - of a full cycle. The second will do the second half. Etc. - -*/ - /*! If a component implements only run_for(Cycles), an owner can wrap it in HalfClockReceiver automatically to gain run_for(HalfCycles). @@ -155,13 +177,12 @@ template class HalfClockReceiver: public T { using T::run_for; inline void run_for(const HalfCycles &half_cycles) { - int cycles = half_cycles.as_int() + half_cycle_carry_; - half_cycle_carry_ = cycles & 1; - T::run_for(Cycles(cycles >> 1)); + half_cycles_ += half_cycles; + T::run_for(half_cycles_.flush_cycles()); } private: - int half_cycle_carry_ = 0; + HalfCycles half_cycles_; }; #endif /* ClockReceiver_hpp */ diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 77531b576..02a0ae773 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -351,7 +351,7 @@ void Machine::flush() { #pragma mark - Deferred scheduling inline void Machine::update_display() { - if(cycles_since_display_update_) { + if(cycles_since_display_update_ > 0) { video_output_->run_for(cycles_since_display_update_.flush()); } } @@ -363,7 +363,7 @@ inline void Machine::queue_next_display_interrupt() { } inline void Machine::update_audio() { - if(cycles_since_audio_update_) { + if(cycles_since_audio_update_ > 0) { speaker_->run_for(cycles_since_audio_update_.divide(Cycles(Speaker::clock_rate_divider))); } } diff --git a/Processors/Z80/Z80AllRAM.cpp b/Processors/Z80/Z80AllRAM.cpp index 57c14a48f..9bb0aa520 100644 --- a/Processors/Z80/Z80AllRAM.cpp +++ b/Processors/Z80/Z80AllRAM.cpp @@ -16,10 +16,10 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processorz80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_); } - return Cycles(0); + return HalfCycles(0); } void run_for(const Cycles &cycles) {