1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +00:00

Revoked the operator bool() on WrappedInt as providing an indirect means for implicit but incorrect assignment to unwrapped ints. Got explicit about run_for intention and simplified HalfClockReceiver slightly by building a lossy and a flushing conversion to Cycles into HalfCycles. Adapted the all-RAM Z80 properly to return HalfCycles.

This commit is contained in:
Thomas Harte 2017-07-27 21:38:50 -04:00
parent b9f4f7a530
commit 9ef232157b
3 changed files with 58 additions and 37 deletions

View File

@ -9,6 +9,43 @@
#ifndef ClockReceiver_hpp #ifndef ClockReceiver_hpp
#define 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 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. Boolean operators, but forcing callers and receivers to be explicit as to usage.
@ -69,7 +106,7 @@ template <class T> class WrappedInt {
inline bool operator !=(const T &rhs) const { return length_ != rhs.length_; } inline bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
inline bool operator !() const { return !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_; } inline int as_int() const { return length_; }
@ -116,35 +153,20 @@ class HalfCycles: public WrappedInt<HalfCycles> {
inline HalfCycles(const Cycles &cycles) : WrappedInt<HalfCycles>(cycles.as_int() << 1) {} inline HalfCycles(const Cycles &cycles) : WrappedInt<HalfCycles>(cycles.as_int() << 1) {}
inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {} inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(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 If a component implements only run_for(Cycles), an owner can wrap it in HalfClockReceiver
automatically to gain run_for(HalfCycles). automatically to gain run_for(HalfCycles).
@ -155,13 +177,12 @@ template <class T> class HalfClockReceiver: public T {
using T::run_for; using T::run_for;
inline void run_for(const HalfCycles &half_cycles) { inline void run_for(const HalfCycles &half_cycles) {
int cycles = half_cycles.as_int() + half_cycle_carry_; half_cycles_ += half_cycles;
half_cycle_carry_ = cycles & 1; T::run_for(half_cycles_.flush_cycles());
T::run_for(Cycles(cycles >> 1));
} }
private: private:
int half_cycle_carry_ = 0; HalfCycles half_cycles_;
}; };
#endif /* ClockReceiver_hpp */ #endif /* ClockReceiver_hpp */

View File

@ -351,7 +351,7 @@ void Machine::flush() {
#pragma mark - Deferred scheduling #pragma mark - Deferred scheduling
inline void Machine::update_display() { 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()); 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() { 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))); speaker_->run_for(cycles_since_audio_update_.divide(Cycles(Speaker::clock_rate_divider)));
} }
} }

View File

@ -16,10 +16,10 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
public: public:
ConcreteAllRAMProcessor() : AllRAMProcessor() {} ConcreteAllRAMProcessor() : AllRAMProcessor() {}
inline Cycles perform_machine_cycle(const PartialMachineCycle &cycle) { inline HalfCycles perform_machine_cycle(const PartialMachineCycle &cycle) {
timestamp_ += cycle.length; timestamp_ += cycle.length;
if(!cycle.is_terminal()) { if(!cycle.is_terminal()) {
return Cycles(0); return HalfCycles(0);
} }
uint16_t address = cycle.address ? *cycle.address : 0x0000; uint16_t address = cycle.address ? *cycle.address : 0x0000;
@ -60,7 +60,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_); delegate_->z80_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) { void run_for(const Cycles &cycles) {