1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-19 14:25:35 +00:00

Merge pull request #633 from TomHarte/Deferrer

Starts to formalise just-in-time handling
This commit is contained in:
Thomas Harte 2019-07-30 11:35:13 -04:00 committed by GitHub
commit bba34b28b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 223 additions and 115 deletions

View File

@ -149,10 +149,12 @@ template <class T> class WrappedInt {
Flushes the value in @c this. The current value is returned, and the internal value Flushes the value in @c this. The current value is returned, and the internal value
is reset to zero. is reset to zero.
*/ */
forceinline T flush() { template <typename Result> Result flush() {
T result(length_); // Jiggery pokery here; switching to function overloading avoids
length_ = 0; // the namespace-level requirement for template specialisation.
return result; Result r;
static_cast<T *>(this)->fill(r);
return r;
} }
// operator int() is deliberately not provided, to avoid accidental subtitution of // operator int() is deliberately not provided, to avoid accidental subtitution of
@ -168,6 +170,13 @@ class Cycles: public WrappedInt<Cycles> {
forceinline constexpr Cycles(int l) noexcept : WrappedInt<Cycles>(l) {} forceinline constexpr Cycles(int l) noexcept : WrappedInt<Cycles>(l) {}
forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {} forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {}
forceinline constexpr Cycles(const Cycles &cycles) noexcept : WrappedInt<Cycles>(cycles.length_) {} forceinline constexpr Cycles(const Cycles &cycles) noexcept : WrappedInt<Cycles>(cycles.length_) {}
private:
friend WrappedInt;
void fill(Cycles &result) {
result.length_ = length_;
length_ = 0;
}
}; };
/// Describes an integer number of half cycles: single clock signal transitions. /// Describes an integer number of half cycles: single clock signal transitions.
@ -176,7 +185,7 @@ class HalfCycles: public WrappedInt<HalfCycles> {
forceinline constexpr HalfCycles(int l) noexcept : WrappedInt<HalfCycles>(l) {} forceinline constexpr HalfCycles(int l) noexcept : WrappedInt<HalfCycles>(l) {}
forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {} forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {}
forceinline constexpr HalfCycles(const Cycles cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_int() * 2) {} forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
forceinline constexpr HalfCycles(const HalfCycles &half_cycles) noexcept : WrappedInt<HalfCycles>(half_cycles.length_) {} forceinline constexpr HalfCycles(const HalfCycles &half_cycles) noexcept : WrappedInt<HalfCycles>(half_cycles.length_) {}
/// @returns The number of whole cycles completely covered by this span of half cycles. /// @returns The number of whole cycles completely covered by this span of half cycles.
@ -184,20 +193,6 @@ class HalfCycles: public WrappedInt<HalfCycles> {
return Cycles(length_ >> 1); return Cycles(length_ >> 1);
} }
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
forceinline Cycles flush_cycles() {
Cycles result(length_ >> 1);
length_ &= 1;
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;
return result;
}
/*! /*!
Severs from @c this the effect of dividing by @c divisor; @c this will end up with 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. the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
@ -208,8 +203,23 @@ class HalfCycles: public WrappedInt<HalfCycles> {
length_ %= half_divisor.length_; length_ %= half_divisor.length_;
return result; return result;
} }
private:
friend WrappedInt;
void fill(Cycles &result) {
result = Cycles(length_ >> 1);
length_ &= 1;
}
void fill(HalfCycles &result) {
result.length_ = length_;
length_ = 0;
}
}; };
// 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 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).
@ -220,7 +230,7 @@ template <class T> class HalfClockReceiver: public T {
forceinline void run_for(const HalfCycles half_cycles) { forceinline void run_for(const HalfCycles half_cycles) {
half_cycles_ += half_cycles; half_cycles_ += half_cycles;
T::run_for(half_cycles_.flush_cycles()); T::run_for(half_cycles_.flush<Cycles>());
} }
private: private:

View File

@ -1,26 +1,26 @@
// //
// ClockDeferrer.hpp // DeferredQueue.hpp
// Clock Signal // Clock Signal
// //
// Created by Thomas Harte on 23/08/2018. // Created by Thomas Harte on 23/08/2018.
// Copyright © 2018 Thomas Harte. All rights reserved. // Copyright © 2018 Thomas Harte. All rights reserved.
// //
#ifndef ClockDeferrer_h #ifndef DeferredQueue_h
#define ClockDeferrer_h #define DeferredQueue_h
#include <functional> #include <functional>
#include <vector> #include <vector>
/*! /*!
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 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. that occur between those actions, triggering each action when it is reached.
*/ */
template <typename TimeUnit> class ClockDeferrer { template <typename TimeUnit> class DeferredQueue {
public: public:
/// Constructs a ClockDeferrer that will call target(period) in between deferred actions. /// Constructs a DeferredQueue that will call target(period) in between deferred actions.
ClockDeferrer(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {} DeferredQueue(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
/*! /*!
Schedules @c action to occur in @c delay units of time. Schedules @c action to occur in @c delay units of time.
@ -79,4 +79,4 @@ template <typename TimeUnit> class ClockDeferrer {
std::vector<DeferredAction> pending_actions_; std::vector<DeferredAction> pending_actions_;
}; };
#endif /* ClockDeferrer_h */ #endif /* DeferredQueue_h */

View File

@ -0,0 +1,110 @@
//
// 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 T, class LocalTimeScale, class TargetTimeScale = LocalTimeScale> class JustInTimeActor {
public:
/// Constructs a new JustInTimeActor using the same construction arguments as the included object.
template<typename... Args> JustInTimeActor(Args&&... args) : object_(std::forward<Args>(args)...) {}
/// Adds time to the actor.
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->() {
flush();
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<TargetTimeScale>());
is_flushed_ = true;
}
private:
T object_;
LocalTimeScale time_since_update_;
bool is_flushed_ = true;
};
/*!
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 T, class LocalTimeScale, class TargetTimeScale = LocalTimeScale> class AsyncJustInTimeActor {
public:
/// Constructs a new AsyncJustInTimeActor using the same construction arguments as the included object.
template<typename... Args> AsyncJustInTimeActor(TargetTimeScale threshold, Args&&... args) :
object_(std::forward<Args>(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_;
}
/// 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_) {
task_queue_.flush();
object_.run_for(time_since_update_.template flush<TargetTimeScale>());
is_flushed_ = true;
}
}
private:
T object_;
LocalTimeScale time_since_update_;
TargetTimeScale threshold_;
bool is_flushed_ = true;
Concurrency::AsyncTaskQueue task_queue_;
};
#endif /* JustInTime_h */

View File

@ -38,7 +38,7 @@ template <typename T> void MOS6522<T>::set_register(int address, uint8_t value)
// Store locally and communicate outwards. // Store locally and communicate outwards.
registers_.output[1] = value; registers_.output[1] = value;
bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]); bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]);
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge)); registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
@ -48,7 +48,7 @@ template <typename T> void MOS6522<T>::set_register(int address, uint8_t value)
case 0x1: // Write Port A. case 0x1: // Write Port A.
registers_.output[0] = value; registers_.output[0] = value;
bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
bus_handler_.set_port_output(Port::A, value, registers_.data_direction[0]); bus_handler_.set_port_output(Port::A, value, registers_.data_direction[0]);
if(handshake_modes_[1] != HandshakeMode::None) { if(handshake_modes_[1] != HandshakeMode::None) {
@ -205,7 +205,7 @@ template <typename T> uint8_t MOS6522<T>::get_register(int address) {
} }
template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t output_mask, uint8_t output) { template <typename T> uint8_t MOS6522<T>::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<HalfCycles>());
const uint8_t input = bus_handler_.get_port_input(port); const uint8_t input = bus_handler_.get_port_input(port);
return (input & ~output_mask) | (output & output_mask); return (input & ~output_mask) | (output & output_mask);
} }
@ -220,7 +220,7 @@ template <typename T> void MOS6522<T>::reevaluate_interrupts() {
if(new_interrupt_status != last_posted_interrupt_status_) { if(new_interrupt_status != last_posted_interrupt_status_) {
last_posted_interrupt_status_ = new_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<HalfCycles>());
bus_handler_.set_interrupt_status(new_interrupt_status); bus_handler_.set_interrupt_status(new_interrupt_status);
} }
} }
@ -338,7 +338,7 @@ template <typename T> void MOS6522<T>::do_phase1() {
// Determine whether to toggle PB7. // Determine whether to toggle PB7.
if(registers_.auxiliary_control&0x80) { if(registers_.auxiliary_control&0x80) {
registers_.output[1] ^= 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<HalfCycles>());
bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]); bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]);
} }
} }
@ -369,7 +369,7 @@ template <typename T> void MOS6522<T>::run_for(const HalfCycles half_cycles) {
} }
template <typename T> void MOS6522<T>::flush() { template <typename T> void MOS6522<T>::flush() {
bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
bus_handler_.flush(); bus_handler_.flush();
} }
@ -422,7 +422,7 @@ template <typename T> void MOS6522<T>::set_control_line_output(Port port, Line l
control_outputs_[port].lines[line] = value; control_outputs_[port].lines[line] = value;
if(value != LineState::Input) { if(value != LineState::Input) {
bus_handler_.run_for(time_since_bus_handler_call_.flush()); bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
bus_handler_.set_control_line_output(port, line, value != LineState::Off); bus_handler_.set_control_line_output(port, line, value != LineState::Off);
} }
} }

View File

@ -77,7 +77,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
Cycles cycles_since_video_update_; Cycles cycles_since_video_update_;
void update_video() { void update_video() {
video_.run_for(cycles_since_video_update_.flush()); video_.run_for(cycles_since_video_update_.flush<Cycles>());
} }
static const int audio_divider = 8; static const int audio_divider = 8;
void update_audio() { void update_audio() {

View File

@ -11,7 +11,7 @@
#include "../../../Outputs/CRT/CRT.hpp" #include "../../../Outputs/CRT/CRT.hpp"
#include "../../../ClockReceiver/ClockReceiver.hpp" #include "../../../ClockReceiver/ClockReceiver.hpp"
#include "../../../ClockReceiver/ClockDeferrer.hpp" #include "../../../ClockReceiver/DeferredQueue.hpp"
#include <array> #include <array>
#include <vector> #include <vector>
@ -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; 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. // Maintain a DeferredQueue for delayed mode switches.
ClockDeferrer<Cycles> deferrer_; DeferredQueue<Cycles> deferrer_;
}; };
template <class BusHandler, bool is_iie> class Video: public VideoBase { template <class BusHandler, bool is_iie> class Video: public VideoBase {

View File

@ -24,7 +24,7 @@ struct DeferredAudio {
DeferredAudio() : audio(queue), speaker(audio) {} DeferredAudio() : audio(queue), speaker(audio) {}
void flush() { void flush() {
speaker.run_for(queue, time_since_update.flush_cycles()); speaker.run_for(queue, time_since_update.flush<Cycles>());
} }
}; };

View File

@ -24,6 +24,8 @@
#include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp" #include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp"
#include "../../../Outputs/Log.hpp" #include "../../../Outputs/Log.hpp"
#include "../../../ClockReceiver/JustInTime.hpp"
//#define LOG_TRACE //#define LOG_TRACE
#include "../../../Components/6522/6522.hpp" #include "../../../Components/6522/6522.hpp"
@ -456,7 +458,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
private: private:
void update_video() { void update_video() {
video_.run_for(time_since_video_update_.flush()); video_.run_for(time_since_video_update_.flush<HalfCycles>());
time_until_video_event_ = video_.get_next_sequence_point(); time_until_video_event_ = video_.get_next_sequence_point();
} }
@ -471,7 +473,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
Apple::IWM iwm; Apple::IWM iwm;
void flush() { void flush() {
iwm.run_for(time_since_update.flush_cycles()); iwm.run_for(time_since_update.flush<Cycles>());
} }
}; };
@ -610,7 +612,6 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
HalfCycles keyboard_clock_; HalfCycles keyboard_clock_;
HalfCycles time_since_video_update_; HalfCycles time_since_video_update_;
HalfCycles time_until_video_event_; HalfCycles time_until_video_event_;
HalfCycles time_since_iwm_update_;
HalfCycles time_since_mouse_update_; HalfCycles time_since_mouse_update_;
bool ROM_is_overlay_ = true; bool ROM_is_overlay_ = true;

View File

@ -55,13 +55,13 @@ class Bus {
// video backlog accumulation counter // video backlog accumulation counter
Cycles cycles_since_video_update_; Cycles cycles_since_video_update_;
inline void update_video() { inline void update_video() {
tia_.run_for(cycles_since_video_update_.flush()); tia_.run_for(cycles_since_video_update_.flush<Cycles>());
} }
// RIOT backlog accumulation counter // RIOT backlog accumulation counter
Cycles cycles_since_6532_update_; Cycles cycles_since_6532_update_;
inline void update_6532() { inline void update_6532() {
mos6532_.run_for(cycles_since_6532_update_.flush()); mos6532_.run_for(cycles_since_6532_update_.flush<Cycles>());
} }
}; };

View File

@ -19,6 +19,7 @@
#include "../../Configurable/StandardOptions.hpp" #include "../../Configurable/StandardOptions.hpp"
#include "../../ClockReceiver/ForceInline.hpp" #include "../../ClockReceiver/ForceInline.hpp"
#include "../../ClockReceiver/JustInTime.hpp"
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp" #include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
@ -169,7 +170,7 @@ class ConcreteMachine:
} }
// ColecoVisions have composite output only. // ColecoVisions have composite output only.
vdp_.set_display_type(Outputs::Display::DisplayType::CompositeColour); vdp_->set_display_type(Outputs::Display::DisplayType::CompositeColour);
} }
~ConcreteMachine() { ~ConcreteMachine() {
@ -181,11 +182,11 @@ class ConcreteMachine:
} }
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { 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 { 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 { Outputs::Speaker::Speaker *get_speaker() override {
@ -210,7 +211,7 @@ class ConcreteMachine:
} }
const HalfCycles length = cycle.length + penalty; const HalfCycles length = cycle.length + penalty;
time_since_vdp_update_ += length; vdp_ += length;
time_since_sn76489_update_ += length; time_since_sn76489_update_ += length;
// Act only if necessary. // Act only if necessary.
@ -255,10 +256,9 @@ class ConcreteMachine:
case CPU::Z80::PartialMachineCycle::Input: case CPU::Z80::PartialMachineCycle::Input:
switch((address >> 5) & 7) { switch((address >> 5) & 7) {
case 5: case 5:
update_video(); *cycle.value = vdp_->get_register(address);
*cycle.value = vdp_.get_register(address); z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line());
z80_.set_non_maskable_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_->get_time_until_interrupt();
time_until_interrupt_ = vdp_.get_time_until_interrupt();
break; break;
case 7: { case 7: {
@ -299,10 +299,9 @@ class ConcreteMachine:
break; break;
case 5: case 5:
update_video(); vdp_->set_register(address, *cycle.value);
vdp_.set_register(address, *cycle.value); z80_.set_non_maskable_interrupt_line(vdp_->get_interrupt_line());
z80_.set_non_maskable_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_->get_time_until_interrupt();
time_until_interrupt_ = vdp_.get_time_until_interrupt();
break; break;
case 7: case 7:
@ -354,7 +353,7 @@ class ConcreteMachine:
} }
void flush() { void flush() {
update_video(); vdp_.flush();
update_audio(); update_audio();
audio_queue_.perform(); audio_queue_.perform();
} }
@ -396,12 +395,9 @@ class ConcreteMachine:
inline void update_audio() { inline void update_audio() {
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider))); 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<ConcreteMachine, false, false> z80_; CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
TI::TMS::TMS9918 vdp_; JustInTimeActor<TI::TMS::TMS9918, HalfCycles> vdp_;
Concurrency::DeferringAsyncTaskQueue audio_queue_; Concurrency::DeferringAsyncTaskQueue audio_queue_;
TI::SN76489 sn76489_; TI::SN76489 sn76489_;
@ -424,7 +420,6 @@ class ConcreteMachine:
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_; std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
bool joysticks_in_keypad_mode_ = false; bool joysticks_in_keypad_mode_ = false;
HalfCycles time_since_vdp_update_;
HalfCycles time_since_sn76489_update_; HalfCycles time_since_sn76489_update_;
HalfCycles time_until_interrupt_; HalfCycles time_until_interrupt_;

View File

@ -702,7 +702,7 @@ class ConcreteMachine:
private: private:
void update_video() { void update_video() {
mos6560_.run_for(cycles_since_mos6560_update_.flush()); mos6560_.run_for(cycles_since_mos6560_update_.flush<Cycles>());
} }
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_; CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;

View File

@ -510,7 +510,7 @@ class ConcreteMachine:
// MARK: - Work deferral updates. // MARK: - Work deferral updates.
inline void update_display() { inline void update_display() {
if(cycles_since_display_update_ > 0) { if(cycles_since_display_update_ > 0) {
video_output_.run_for(cycles_since_display_update_.flush()); video_output_.run_for(cycles_since_display_update_.flush<Cycles>());
} }
} }

View File

@ -45,6 +45,7 @@
#include "../../Configurable/StandardOptions.hpp" #include "../../Configurable/StandardOptions.hpp"
#include "../../ClockReceiver/ForceInline.hpp" #include "../../ClockReceiver/ForceInline.hpp"
#include "../../ClockReceiver/JustInTime.hpp"
#include "../../Analyser/Static/MSX/Target.hpp" #include "../../Analyser/Static/MSX/Target.hpp"
@ -187,7 +188,7 @@ class ConcreteMachine:
switch(target.region) { switch(target.region) {
case Target::Region::Japan: case Target::Region::Japan:
required_roms.emplace_back(machine_name, "a Japanese MSX BIOS", "msx-japanese.rom", 32*1024, 0xee229390); 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; is_ntsc = true;
character_generator = 0; character_generator = 0;
@ -195,7 +196,7 @@ class ConcreteMachine:
break; break;
case Target::Region::USA: case Target::Region::USA:
required_roms.emplace_back(machine_name, "an American MSX BIOS", "msx-american.rom", 32*1024, 0); 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; is_ntsc = true;
character_generator = 1; character_generator = 1;
@ -203,7 +204,7 @@ class ConcreteMachine:
break; break;
case Target::Region::Europe: case Target::Region::Europe:
required_roms.emplace_back(machine_name, "a European MSX BIOS", "msx-european.rom", 32*1024, 0); 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; is_ntsc = false;
character_generator = 1; character_generator = 1;
@ -278,11 +279,11 @@ class ConcreteMachine:
} }
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { 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 { 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 { Outputs::Speaker::Speaker *get_speaker() override {
@ -411,7 +412,7 @@ class ConcreteMachine:
// but otherwise runs without pause. // but otherwise runs without pause.
const HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0); const HalfCycles addition((cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode) ? 2 : 0);
const HalfCycles total_length = addition + cycle.length; const HalfCycles total_length = addition + cycle.length;
time_since_vdp_update_ += total_length; vdp_ += total_length;
time_since_ay_update_ += total_length; time_since_ay_update_ += total_length;
memory_slots_[0].cycles_since_update += total_length; memory_slots_[0].cycles_since_update += total_length;
memory_slots_[1].cycles_since_update += total_length; memory_slots_[1].cycles_since_update += total_length;
@ -489,7 +490,7 @@ class ConcreteMachine:
*cycle.value = read_pointers_[address >> 13][address & 8191]; *cycle.value = read_pointers_[address >> 13][address & 8191];
} else { } else {
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; 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<HalfCycles>());
*cycle.value = memory_slots_[slot_hit].handler->read(address); *cycle.value = memory_slots_[slot_hit].handler->read(address);
} }
break; break;
@ -500,7 +501,7 @@ class ConcreteMachine:
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3; int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
if(memory_slots_[slot_hit].handler) { if(memory_slots_[slot_hit].handler) {
update_audio(); 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<HalfCycles>());
memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]); memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]);
} }
} break; } break;
@ -508,10 +509,9 @@ class ConcreteMachine:
case CPU::Z80::PartialMachineCycle::Input: case CPU::Z80::PartialMachineCycle::Input:
switch(address & 0xff) { switch(address & 0xff) {
case 0x98: case 0x99: case 0x98: case 0x99:
vdp_.run_for(time_since_vdp_update_.flush()); *cycle.value = vdp_->get_register(address);
*cycle.value = vdp_.get_register(address); z80_.set_interrupt_line(vdp_->get_interrupt_line());
z80_.set_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_->get_time_until_interrupt();
time_until_interrupt_ = vdp_.get_time_until_interrupt();
break; break;
case 0xa2: case 0xa2:
@ -536,10 +536,9 @@ class ConcreteMachine:
const int port = address & 0xff; const int port = address & 0xff;
switch(port) { switch(port) {
case 0x98: case 0x99: case 0x98: case 0x99:
vdp_.run_for(time_since_vdp_update_.flush()); vdp_->set_register(address, *cycle.value);
vdp_.set_register(address, *cycle.value); z80_.set_interrupt_line(vdp_->get_interrupt_line());
z80_.set_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_->get_time_until_interrupt();
time_until_interrupt_ = vdp_.get_time_until_interrupt();
break; break;
case 0xa0: case 0xa1: case 0xa0: case 0xa1:
@ -612,7 +611,7 @@ class ConcreteMachine:
} }
void flush() { void flush() {
vdp_.run_for(time_since_vdp_update_.flush()); vdp_.flush();
update_audio(); update_audio();
audio_queue_.perform(); audio_queue_.perform();
} }
@ -753,7 +752,7 @@ class ConcreteMachine:
}; };
CPU::Z80::Processor<ConcreteMachine, false, false> z80_; CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
TI::TMS::TMS9918 vdp_; JustInTimeActor<TI::TMS::TMS9918, HalfCycles> vdp_;
Intel::i8255::i8255<i8255PortHandler> i8255_; Intel::i8255::i8255<i8255PortHandler> i8255_;
Concurrency::DeferringAsyncTaskQueue audio_queue_; Concurrency::DeferringAsyncTaskQueue audio_queue_;
@ -797,7 +796,6 @@ class ConcreteMachine:
uint8_t scratch_[8192]; uint8_t scratch_[8192];
uint8_t unpopulated_[8192]; uint8_t unpopulated_[8192];
HalfCycles time_since_vdp_update_;
HalfCycles time_since_ay_update_; HalfCycles time_since_ay_update_;
HalfCycles time_until_interrupt_; HalfCycles time_until_interrupt_;

View File

@ -18,6 +18,7 @@
#include "../KeyboardMachine.hpp" #include "../KeyboardMachine.hpp"
#include "../../ClockReceiver/ForceInline.hpp" #include "../../ClockReceiver/ForceInline.hpp"
#include "../../ClockReceiver/JustInTime.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../Outputs/Log.hpp" #include "../../Outputs/Log.hpp"
@ -168,16 +169,16 @@ class ConcreteMachine:
} }
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
vdp_.set_tv_standard( vdp_->set_tv_standard(
(region_ == Target::Region::Europe) ? (region_ == Target::Region::Europe) ?
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC); 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 { 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 { Outputs::Speaker::Speaker *get_speaker() override {
@ -189,7 +190,7 @@ class ConcreteMachine:
} }
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { 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; time_since_sn76489_update_ += cycle.length;
if(cycle.is_terminal()) { if(cycle.is_terminal()) {
@ -233,17 +234,15 @@ class ConcreteMachine:
*cycle.value = 0xff; *cycle.value = 0xff;
break; break;
case 0x40: case 0x40:
update_video(); *cycle.value = vdp_->get_current_line();
*cycle.value = vdp_.get_current_line();
break; break;
case 0x41: case 0x41:
*cycle.value = vdp_.get_latched_horizontal_counter(); *cycle.value = vdp_.last_valid()->get_latched_horizontal_counter();
break; break;
case 0x80: case 0x81: case 0x80: case 0x81:
update_video(); *cycle.value = vdp_->get_register(address);
*cycle.value = vdp_.get_register(address); z80_.set_interrupt_line(vdp_->get_interrupt_line());
z80_.set_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_->get_time_until_interrupt();
time_until_interrupt_ = vdp_.get_time_until_interrupt();
break; break;
case 0xc0: { case 0xc0: {
Joystick *const joypad1 = static_cast<Joystick *>(joysticks_[0].get()); Joystick *const joypad1 = static_cast<Joystick *>(joysticks_[0].get());
@ -284,8 +283,7 @@ class ConcreteMachine:
// Latch if either TH has newly gone to 1. // Latch if either TH has newly gone to 1.
if((new_ths^previous_ths)&new_ths) { if((new_ths^previous_ths)&new_ths) {
update_video(); vdp_->latch_horizontal_counter();
vdp_.latch_horizontal_counter();
} }
} break; } break;
case 0x40: case 0x41: case 0x40: case 0x41:
@ -293,10 +291,9 @@ class ConcreteMachine:
sn76489_.set_register(*cycle.value); sn76489_.set_register(*cycle.value);
break; break;
case 0x80: case 0x81: case 0x80: case 0x81:
update_video(); vdp_->set_register(address, *cycle.value);
vdp_.set_register(address, *cycle.value); z80_.set_interrupt_line(vdp_->get_interrupt_line());
z80_.set_interrupt_line(vdp_.get_interrupt_line()); time_until_interrupt_ = vdp_->get_time_until_interrupt();
time_until_interrupt_ = vdp_.get_time_until_interrupt();
break; break;
case 0xc0: case 0xc0:
LOG("TODO: [output] I/O port A/N; " << int(*cycle.value)); LOG("TODO: [output] I/O port A/N; " << int(*cycle.value));
@ -331,15 +328,14 @@ class ConcreteMachine:
time_until_debounce_ -= cycle.length; time_until_debounce_ -= cycle.length;
if(time_until_debounce_ <= HalfCycles(0)) { if(time_until_debounce_ <= HalfCycles(0)) {
z80_.set_non_maskable_interrupt_line(pause_is_pressed_); 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); return HalfCycles(0);
} }
void flush() { void flush() {
update_video(); vdp_.flush();
update_audio(); update_audio();
audio_queue_.perform(); audio_queue_.perform();
} }
@ -412,16 +408,13 @@ class ConcreteMachine:
inline void update_audio() { inline void update_audio() {
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider))); 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; using Target = Analyser::Static::Sega::Target;
Target::Model model_; Target::Model model_;
Target::Region region_; Target::Region region_;
Target::PagingScheme paging_scheme_; Target::PagingScheme paging_scheme_;
CPU::Z80::Processor<ConcreteMachine, false, false> z80_; CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
TI::TMS::TMS9918 vdp_; JustInTimeActor<TI::TMS::TMS9918, HalfCycles> vdp_;
Concurrency::DeferringAsyncTaskQueue audio_queue_; Concurrency::DeferringAsyncTaskQueue audio_queue_;
TI::SN76489 sn76489_; TI::SN76489 sn76489_;
@ -431,7 +424,6 @@ class ConcreteMachine:
Inputs::Keyboard keyboard_; Inputs::Keyboard keyboard_;
bool reset_is_pressed_ = false, pause_is_pressed_ = false; bool reset_is_pressed_ = false, pause_is_pressed_ = false;
HalfCycles time_since_vdp_update_;
HalfCycles time_since_sn76489_update_; HalfCycles time_since_sn76489_update_;
HalfCycles time_until_interrupt_; HalfCycles time_until_interrupt_;
HalfCycles time_until_debounce_; HalfCycles time_until_debounce_;

View File

@ -182,7 +182,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
private: private:
void update_ay() { void update_ay() {
speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush_cycles()); speaker_.run_for(audio_queue_, cycles_since_ay_update_.flush<Cycles>());
} }
bool ay_bdir_ = false; bool ay_bdir_ = false;
bool ay_bc1_ = false; bool ay_bc1_ = false;
@ -585,7 +585,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
uint8_t ram_[65536]; uint8_t ram_[65536];
Cycles cycles_since_video_update_; Cycles cycles_since_video_update_;
inline void update_video() { inline void update_video() {
video_output_.run_for(cycles_since_video_update_.flush()); video_output_.run_for(cycles_since_video_update_.flush<Cycles>());
} }
// ROM bookkeeping // ROM bookkeeping
@ -617,7 +617,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
Apple::DiskII diskii_; Apple::DiskII diskii_;
Cycles cycles_since_diskii_update_; Cycles cycles_since_diskii_update_;
void flush_diskii() { void flush_diskii() {
diskii_.run_for(cycles_since_diskii_update_.flush()); diskii_.run_for(cycles_since_diskii_update_.flush<Cycles>());
} }
std::vector<uint8_t> pravetz_rom_; std::vector<uint8_t> pravetz_rom_;
std::size_t pravetz_rom_base_pointer_ = 0; std::size_t pravetz_rom_base_pointer_ = 0;

View File

@ -983,6 +983,7 @@
4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = "<group>"; }; 4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = "<group>"; };
4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; }; 4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
4B7F1896215486A100388727 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; }; 4B7F1896215486A100388727 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
4B80214322EE7C3E00068002 /* JustInTime.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JustInTime.hpp; sourceTree = "<group>"; };
4B80ACFE1F85CAC900176895 /* BestEffortUpdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BestEffortUpdater.cpp; path = ../../Concurrency/BestEffortUpdater.cpp; sourceTree = "<group>"; }; 4B80ACFE1F85CAC900176895 /* BestEffortUpdater.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BestEffortUpdater.cpp; path = ../../Concurrency/BestEffortUpdater.cpp; sourceTree = "<group>"; };
4B80ACFF1F85CACA00176895 /* BestEffortUpdater.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BestEffortUpdater.hpp; path = ../../Concurrency/BestEffortUpdater.hpp; sourceTree = "<group>"; }; 4B80ACFF1F85CACA00176895 /* BestEffortUpdater.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BestEffortUpdater.hpp; path = ../../Concurrency/BestEffortUpdater.hpp; sourceTree = "<group>"; };
4B8334811F5D9FF70097E338 /* PartialMachineCycle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PartialMachineCycle.cpp; sourceTree = "<group>"; }; 4B8334811F5D9FF70097E338 /* PartialMachineCycle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PartialMachineCycle.cpp; sourceTree = "<group>"; };
@ -1051,7 +1052,7 @@
4B894516201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; }; 4B894516201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
4B894517201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; }; 4B894517201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = "<group>"; }; 4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = "<group>"; };
4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockDeferrer.hpp; sourceTree = "<group>"; }; 4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DeferredQueue.hpp; sourceTree = "<group>"; };
4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = "<group>"; }; 4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = "<group>"; };
4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = "<group>"; }; 4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = "<group>"; };
4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LowpassSpeaker.hpp; sourceTree = "<group>"; }; 4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LowpassSpeaker.hpp; sourceTree = "<group>"; };
@ -3383,10 +3384,11 @@
4BF660691F281573002CB053 /* ClockReceiver */ = { 4BF660691F281573002CB053 /* ClockReceiver */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B8A7E85212F988200F2BBC6 /* ClockDeferrer.hpp */,
4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */, 4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */,
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */, 4BF6606A1F281573002CB053 /* ClockReceiver.hpp */,
4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */,
4BB06B211F316A3F00600C7A /* ForceInline.hpp */, 4BB06B211F316A3F00600C7A /* ForceInline.hpp */,
4B80214322EE7C3E00068002 /* JustInTime.hpp */,
4B449C942063389900A095C8 /* TimeTypes.hpp */, 4B449C942063389900A095C8 /* TimeTypes.hpp */,
); );
name = ClockReceiver; name = ClockReceiver;