diff --git a/ClockReceiver/ClockingHintSource.hpp b/ClockReceiver/ClockingHintSource.hpp new file mode 100644 index 000000000..c0d7b24a2 --- /dev/null +++ b/ClockReceiver/ClockingHintSource.hpp @@ -0,0 +1,88 @@ +// +// ClockingHintSource.h +// Clock Signal +// +// Created by Thomas Harte on 20/08/2017. +// Copyright 2017 Thomas Harte. All rights reserved. +// + +#ifndef ClockingHintSource_hpp +#define ClockingHintSource_hpp + +namespace ClockingHint { + +enum class Preference { + /// The component doesn't currently require a clock signal. + None, + /// The component can be clocked only immediate prior to (explicit) accesses. + JustInTime, + /// The component require real-time clocking. + RealTime +}; + +class Source; + +struct Observer { + /// Called to inform an observer that the component @c component has changed its clocking requirements. + virtual void set_component_prefers_clocking(Source *component, Preference clocking) = 0; +}; + +/*! + An clocking hint source is any component that can provide hints as to the type of + clocking required for accurate emulation. A disk controller is an archetypal example. + + Types of clocking are: + + - none: + a component that acts and reacts to direct contact but does not have a state that autonomously evolves. + E.g. a ROM, RAM, or some kinds of disk controller when not in the process of performing a command. + + - just-in-time: + a component that has an evolving state but can receive clock updates only immediately before a + direct contact. This is possibly the most common kind of component. + + - real-time: + a component that needs to be clocked in 'real time' (i.e. in terms of the emulated machine). For example + so that it can announce an interrupt at the proper moment, because it is monitoring some aspect of + the machine rather than waiting to be called upon, or because there's some other non-obvious relationship + at play. + + A clocking hint source can signal changes in preferred clocking to an observer. + + This is intended to allow for performance improvements to machines with components that can be messaged selectively. + The observer callout is virtual so the intended use case is that a machine holds a component that might go through + periods of different clocking requirements. + + Transitions should be sufficiently infrequent that a virtual call to announce them costs little enough that + the saved or deferred ::run_fors add up to a substantial amount. + + The hint provided is just that: a hint. Owners may perform ::run_for at a greater frequency. +*/ +class Source { + public: + /// Registers @c observer as the new clocking observer. + void set_clocking_hint_observer(Observer *observer) { + observer_ = observer; + update_clocking_observer(); + } + + /// @returns the current preferred clocking strategy. + virtual Preference preferred_clocking() = 0; + + private: + Observer *observer_ = nullptr; + + protected: + /*! + Provided for subclasses; call this whenever the clocking preference might have changed. + This will notify the observer if there is one. + */ + void update_clocking_observer() { + if(!observer_) return; + observer_->set_component_prefers_clocking(this, preferred_clocking()); + } +}; + +} + +#endif /* ClockingHintSource_h */ diff --git a/ClockReceiver/Sleeper.hpp b/ClockReceiver/Sleeper.hpp deleted file mode 100644 index 7026b97ce..000000000 --- a/ClockReceiver/Sleeper.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// Sleeper.h -// Clock Signal -// -// Created by Thomas Harte on 20/08/2017. -// Copyright 2017 Thomas Harte. All rights reserved. -// - -#ifndef Sleeper_hpp -#define Sleeper_hpp - -/*! - A sleeper is any component that sometimes requires a clock but at other times is 'asleep', i.e. is not doing - any clock-derived work, so needn't receive a clock. A disk controller is an archetypal example. - - A sleeper will signal sleeps and wakes to an observer. - - This is intended to allow for performance improvements to machines with components that can sleep. The observer - callout is virtual so the intended use case is that a machine holds a component that might sleep. Its transitions - into and out of sleep are sufficiently infrequent that a virtual call to announce them costs sufficiently little that - the saved ::run_fors add up to a substantial amount. - - By convention, sleeper components must be willing to accept ::run_for even after announcing sleep. It's a hint, - not a command. -*/ -class Sleeper { - public: - Sleeper() : sleep_observer_(nullptr) {} - - class SleepObserver { - public: - /// Called to inform an observer that the component @c component has either gone to sleep or become awake. - virtual void set_component_is_sleeping(Sleeper *component, bool is_sleeping) = 0; - }; - - /// Registers @c observer as the new sleep observer; - void set_sleep_observer(SleepObserver *observer) { - sleep_observer_ = observer; - } - - /// @returns @c true if the component is currently sleeping; @c false otherwise. - virtual bool is_sleeping() = 0; - - protected: - /// Provided for subclasses; send sleep announcements to the sleep_observer_. - SleepObserver *sleep_observer_; - - /*! - Provided for subclasses; call this whenever is_sleeping might have changed, and the observer will be notified, - if one exists. - - @c is_sleeping will be called only if there is an observer. - */ - void update_sleep_observer() { - if(!sleep_observer_) return; - sleep_observer_->set_component_is_sleeping(this, is_sleeping()); - } -}; - -#endif /* Sleeper_h */ diff --git a/Components/1770/1770.hpp b/Components/1770/1770.hpp index ae26830fc..cee4a7f23 100644 --- a/Components/1770/1770.hpp +++ b/Components/1770/1770.hpp @@ -43,7 +43,6 @@ class WD1770: public Storage::Disk::MFMController { /// Runs the controller for @c number_of_cycles cycles. void run_for(const Cycles cycles); - using Storage::Disk::Controller::run_for; enum Flag: uint8_t { NotReady = 0x80, diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 183a0c008..8cbc434c8 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -83,8 +83,10 @@ i8272::i8272(BusHandler &bus_handler, Cycles clock_rate) : posit_event(static_cast(Event8272::CommandByte)); } -bool i8272::is_sleeping() { - return is_sleeping_ && Storage::Disk::MFMController::is_sleeping(); +ClockingHint::Preference i8272::preferred_clocking() { + const auto mfm_controller_preferred_clocking = Storage::Disk::MFMController::preferred_clocking(); + if(mfm_controller_preferred_clocking != ClockingHint::Preference::None) return mfm_controller_preferred_clocking; + return is_sleeping_ ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime; } void i8272::run_for(Cycles cycles) { @@ -159,7 +161,7 @@ void i8272::run_for(Cycles cycles) { } is_sleeping_ = !delay_time_ && !drives_seeking_ && !head_timers_running_; - if(is_sleeping_) update_sleep_observer(); + if(is_sleeping_) update_clocking_observer(); } void i8272::set_register(int address, uint8_t value) { @@ -198,7 +200,7 @@ uint8_t i8272::get_register(int address) { #define MS_TO_CYCLES(x) x * 8000 #define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = static_cast(mask); return; case __LINE__: -#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = static_cast(Event8272::Timer); delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; update_sleep_observer(); case __LINE__: if(delay_time_) return; +#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = static_cast(Event8272::Timer); delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; update_clocking_observer(); case __LINE__: if(delay_time_) return; #define PASTE(x, y) x##y #define CONCAT(x, y) PASTE(x, y) @@ -257,7 +259,7 @@ uint8_t i8272::get_register(int address) { if(drives_[active_drive_].head_unload_delay[active_head_] == 0) { \ head_timers_running_++; \ is_sleeping_ = false; \ - update_sleep_observer(); \ + update_clocking_observer(); \ } \ drives_[active_drive_].head_unload_delay[active_head_] = MS_TO_CYCLES(head_unload_time_);\ } @@ -720,7 +722,7 @@ void i8272::posit_event(int event_type) { if(drives_[drive].phase != Drive::Seeking) { drives_seeking_++; is_sleeping_ = false; - update_sleep_observer(); + update_clocking_observer(); } // Set currently seeking, with a step to occur right now (yes, it sounds like jamming these diff --git a/Components/8272/i8272.hpp b/Components/8272/i8272.hpp index a07e0ae27..566da8ca5 100644 --- a/Components/8272/i8272.hpp +++ b/Components/8272/i8272.hpp @@ -39,7 +39,7 @@ class i8272: public Storage::Disk::MFMController { void set_dma_acknowledge(bool dack); void set_terminal_count(bool tc); - bool is_sleeping(); + ClockingHint::Preference preferred_clocking() override; protected: virtual void select_drive(int number) = 0; @@ -67,7 +67,7 @@ class i8272: public Storage::Disk::MFMController { ResultEmpty = (1 << 5), NoLongerReady = (1 << 6) }; - void posit_event(int type); + void posit_event(int type) override; int interesting_event_mask_ = static_cast(Event8272::CommandByte); int resume_point_ = 0; bool is_access_command_ = false; diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index a19556b21..e8d6539ce 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -23,8 +23,8 @@ DiskII::DiskII() : inputs_(input_command), drives_{{2045454, 300, 1}, {2045454, 300, 1}} { - drives_[0].set_sleep_observer(this); - drives_[1].set_sleep_observer(this); + drives_[0].set_clocking_hint_observer(this); + drives_[1].set_clocking_hint_observer(this); drives_[active_drive_].set_event_delegate(this); } @@ -73,7 +73,7 @@ void DiskII::select_drive(int drive) { } void DiskII::run_for(const Cycles cycles) { - if(is_sleeping()) return; + if(preferred_clocking() == ClockingHint::Preference::None) return; if(!controller_can_sleep_) { int integer_cycles = cycles.as_int(); @@ -137,7 +137,7 @@ void DiskII::set_controller_can_sleep() { (shift_register_ == (is_write_protected() ? 0xff : 0x00)) ); if(controller_could_sleep != controller_can_sleep_) - update_sleep_observer(); + update_clocking_observer(); } bool DiskII::is_write_protected() { @@ -199,14 +199,14 @@ void DiskII::process_event(const Storage::Disk::Track::Event &event) { } } -void DiskII::set_component_is_sleeping(Sleeper *component, bool is_sleeping) { - drive_is_sleeping_[0] = drives_[0].is_sleeping(); - drive_is_sleeping_[1] = drives_[1].is_sleeping(); - update_sleep_observer(); +void DiskII::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) { + drive_is_sleeping_[0] = drives_[0].preferred_clocking() == ClockingHint::Preference::None; + drive_is_sleeping_[1] = drives_[1].preferred_clocking() == ClockingHint::Preference::None; + update_clocking_observer(); } -bool DiskII::is_sleeping() { - return controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1]; +ClockingHint::Preference DiskII::preferred_clocking() { + return (controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1]) ? ClockingHint::Preference::None : ClockingHint::Preference::RealTime; } void DiskII::set_data_input(uint8_t input) { diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp index ee81e0892..96904142c 100644 --- a/Components/DiskII/DiskII.hpp +++ b/Components/DiskII/DiskII.hpp @@ -10,7 +10,7 @@ #define DiskII_hpp #include "../../ClockReceiver/ClockReceiver.hpp" -#include "../../ClockReceiver/Sleeper.hpp" +#include "../../ClockReceiver/ClockingHintSource.hpp" #include "../../Storage/Disk/Disk.hpp" #include "../../Storage/Disk/Drive.hpp" @@ -28,8 +28,8 @@ namespace Apple { */ class DiskII: public Storage::Disk::Drive::EventDelegate, - public Sleeper::SleepObserver, - public Sleeper { + public ClockingHint::Source, + public ClockingHint::Observer { public: DiskII(); @@ -76,7 +76,7 @@ class DiskII: void set_disk(const std::shared_ptr &disk, int drive); // As per Sleeper. - bool is_sleeping() override; + ClockingHint::Preference preferred_clocking() override; // The Disk II functions as a potential target for @c Activity::Sources. void set_activity_observer(Activity::Observer *observer); @@ -95,7 +95,7 @@ class DiskII: uint8_t trigger_address(int address, uint8_t value); void process_event(const Storage::Disk::Track::Event &event) override; - void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override; + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) override; uint8_t state_ = 0; uint8_t inputs_ = 0; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 10dca0dac..e5557533a 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -694,7 +694,7 @@ class ConcreteMachine: public KeyboardMachine::Machine, public Utility::TypeRecipient, public CPU::Z80::BusHandler, - public Sleeper::SleepObserver, + public ClockingHint::Observer, public Machine, public Activity::Source { public: @@ -714,11 +714,8 @@ class ConcreteMachine: Memory::Fuzz(ram_, sizeof(ram_)); // register this class as the sleep observer for the FDC and tape - fdc_.set_sleep_observer(this); - fdc_is_sleeping_ = fdc_.is_sleeping(); - - tape_player_.set_sleep_observer(this); - tape_player_is_sleeping_ = tape_player_.is_sleeping(); + fdc_.set_clocking_hint_observer(this); + tape_player_.set_clocking_hint_observer(this); ay_.ay().set_port_handler(&key_state_); } @@ -967,9 +964,9 @@ class ConcreteMachine: return true; } - void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override final { - fdc_is_sleeping_ = fdc_.is_sleeping(); - tape_player_is_sleeping_ = tape_player_.is_sleeping(); + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override final { + fdc_is_sleeping_ = fdc_.preferred_clocking() == ClockingHint::Preference::None; + tape_player_is_sleeping_ = tape_player_.preferred_clocking() == ClockingHint::Preference::None; } // MARK: - Keyboard diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp index 159c062ed..3421a1907 100644 --- a/Machines/AppleII/DiskIICard.cpp +++ b/Machines/AppleII/DiskIICard.cpp @@ -20,7 +20,7 @@ DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sec boot_ = std::move(*roms[0]); diskii_.set_state_machine(*roms[1]); set_select_constraints(None); - diskii_.set_sleep_observer(this); + diskii_.set_clocking_hint_observer(this); } void DiskIICard::perform_bus_operation(Select select, bool is_read, uint16_t address, uint8_t *value) { @@ -41,7 +41,7 @@ void DiskIICard::perform_bus_operation(Select select, bool is_read, uint16_t add } void DiskIICard::run_for(Cycles cycles, int stretches) { - if(diskii_is_sleeping_) return; + if(diskii_clocking_preference_ == ClockingHint::Preference::None) return; diskii_.run_for(Cycles(cycles.as_int() * 2)); } @@ -53,7 +53,7 @@ void DiskIICard::set_activity_observer(Activity::Observer *observer) { diskii_.set_activity_observer(observer); } -void DiskIICard::set_component_is_sleeping(Sleeper *component, bool is_sleeping) { - diskii_is_sleeping_ = is_sleeping; - set_select_constraints(is_sleeping ? (IO | Device) : 0); +void DiskIICard::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) { + diskii_clocking_preference_ = preference; + set_select_constraints((preference != ClockingHint::Preference::RealTime) ? (IO | Device) : 0); } diff --git a/Machines/AppleII/DiskIICard.hpp b/Machines/AppleII/DiskIICard.hpp index 9d5eaa8ec..2072241a1 100644 --- a/Machines/AppleII/DiskIICard.hpp +++ b/Machines/AppleII/DiskIICard.hpp @@ -14,7 +14,7 @@ #include "../../Components/DiskII/DiskII.hpp" #include "../../Storage/Disk/Disk.hpp" -#include "../../ClockReceiver/Sleeper.hpp" +#include "../../ClockReceiver/ClockingHintSource.hpp" #include #include @@ -22,7 +22,7 @@ namespace AppleII { -class DiskIICard: public Card, public Sleeper::SleepObserver { +class DiskIICard: public Card, public ClockingHint::Observer { public: DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector); @@ -34,10 +34,10 @@ class DiskIICard: public Card, public Sleeper::SleepObserver { void set_disk(const std::shared_ptr &disk, int drive); private: - void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override; + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override; std::vector boot_; Apple::DiskII diskii_; - bool diskii_is_sleeping_ = false; + ClockingHint::Preference diskii_clocking_preference_ = ClockingHint::Preference::RealTime; }; } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 6c00d7d28..cb7248524 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -304,7 +304,7 @@ class ConcreteMachine: public Utility::TypeRecipient, public Storage::Tape::BinaryTapePlayer::Delegate, public Machine, - public Sleeper::SleepObserver, + public ClockingHint::Observer, public Activity::Source { public: ConcreteMachine() : @@ -331,7 +331,7 @@ class ConcreteMachine: user_port_via_port_handler_->set_interrupt_delegate(this); keyboard_via_port_handler_->set_interrupt_delegate(this); tape_->set_delegate(this); - tape_->set_sleep_observer(this); + tape_->set_clocking_hint_observer(this); // install a joystick joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_)); @@ -749,8 +749,8 @@ class ConcreteMachine: return selection_set; } - void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override { - tape_is_sleeping_ = is_sleeping; + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override { + tape_is_sleeping_ = clocking == ClockingHint::Preference::None; set_use_fast_tape(); } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 0281f9ba1..6b36bd1b8 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -88,7 +88,7 @@ class ConcreteMachine: public KeyboardMachine::Machine, public Configurable::Device, public MemoryMap, - public Sleeper::SleepObserver, + public ClockingHint::Observer, public Activity::Source { public: ConcreteMachine(): @@ -108,7 +108,7 @@ class ConcreteMachine: ay_.set_port_handler(&ay_port_handler_); speaker_.set_input_rate(3579545.0f / 2.0f); - tape_player_.set_sleep_observer(this); + tape_player_.set_clocking_hint_observer(this); // Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC. mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f}); @@ -555,8 +555,8 @@ class ConcreteMachine: } // MARK: - Sleeper - void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override { - tape_player_is_sleeping_ = tape_player_.is_sleeping(); + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override { + tape_player_is_sleeping_ = tape_player_.preferred_clocking() == ClockingHint::Preference::None; set_use_fast_tape(); } diff --git a/Machines/Oric/Microdisc.hpp b/Machines/Oric/Microdisc.hpp index 3aef6aefe..fe3d0f0ef 100644 --- a/Machines/Oric/Microdisc.hpp +++ b/Machines/Oric/Microdisc.hpp @@ -28,7 +28,6 @@ class Microdisc: public WD::WD1770 { bool get_interrupt_request_line(); void run_for(const Cycles cycles); - using WD::WD1770::run_for; enum PagingFlags { /// Indicates that the BASIC ROM should be disabled; if this is set then either @@ -52,8 +51,9 @@ class Microdisc: public WD::WD1770 { private: void set_control_register(uint8_t control, uint8_t changes); - void set_head_load_request(bool head_load); + void set_head_load_request(bool head_load) override; bool get_drive_is_ready(); + std::array, 4> drives_; size_t selected_drive_; bool irq_enable_ = false; diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 30b7c1c39..5dda32e31 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -201,7 +201,7 @@ template class Co public Utility::TypeRecipient, public Storage::Tape::BinaryTapePlayer::Delegate, public Microdisc::Delegate, - public Sleeper::SleepObserver, + public ClockingHint::Observer, public Activity::Source, public Machine { @@ -219,7 +219,7 @@ template class Co Memory::Fuzz(ram_, sizeof(ram_)); if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) { - diskii_.set_sleep_observer(this); + diskii_.set_clocking_hint_observer(this); } } @@ -571,8 +571,8 @@ template class Co } } - void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override final { - diskii_is_sleeping_ = diskii_.is_sleeping(); + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference is_sleeping) override final { + diskii_is_sleeping_ = diskii_.preferred_clocking() == ClockingHint::Preference::None; } private: diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 8ffd5537e..2355efa97 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1037,7 +1037,7 @@ 4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = ""; }; 4BB0A6592044FD3000FB3688 /* SN76489.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SN76489.cpp; sourceTree = ""; }; 4BB0A65A2044FD3000FB3688 /* SN76489.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SN76489.hpp; sourceTree = ""; }; - 4BB146C61F49D7D700253439 /* Sleeper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sleeper.hpp; sourceTree = ""; }; + 4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockingHintSource.hpp; sourceTree = ""; }; 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = ""; }; 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.in.json; path = FUSE/tests.in.json; sourceTree = ""; }; 4BB297E51B587D8300A49093 /* start */ = {isa = PBXFileReference; lastKnownFileType = file; path = " start"; sourceTree = ""; }; @@ -3124,7 +3124,7 @@ children = ( 4BF6606A1F281573002CB053 /* ClockReceiver.hpp */, 4BB06B211F316A3F00600C7A /* ForceInline.hpp */, - 4BB146C61F49D7D700253439 /* Sleeper.hpp */, + 4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */, 4B449C942063389900A095C8 /* TimeTypes.hpp */, ); name = ClockReceiver; diff --git a/Storage/Disk/Controller/DiskController.cpp b/Storage/Disk/Controller/DiskController.cpp index f4c403664..60996efde 100644 --- a/Storage/Disk/Controller/DiskController.cpp +++ b/Storage/Disk/Controller/DiskController.cpp @@ -22,12 +22,12 @@ Controller::Controller(Cycles clock_rate) : set_drive(empty_drive_); } -void Controller::set_component_is_sleeping(Sleeper *component, bool is_sleeping) { - update_sleep_observer(); +void Controller::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) { + update_clocking_observer(); } -bool Controller::is_sleeping() { - return !drive_ || drive_->is_sleeping(); +ClockingHint::Preference Controller::preferred_clocking() { + return (!drive_ || (drive_->preferred_clocking() == ClockingHint::Preference::None)) ? ClockingHint::Preference::None : ClockingHint::Preference::RealTime; } void Controller::run_for(const Cycles cycles) { @@ -77,23 +77,23 @@ void Controller::digital_phase_locked_loop_output_bit(int value) { void Controller::set_drive(std::shared_ptr drive) { if(drive_ != drive) { - bool was_sleeping = is_sleeping(); + ClockingHint::Preference former_prefernece = preferred_clocking(); // invalidate_track(); if(drive_) { drive_->set_event_delegate(nullptr); - drive_->set_sleep_observer(nullptr); + drive_->set_clocking_hint_observer(nullptr); } drive_ = drive; if(drive_) { drive_->set_event_delegate(this); - drive_->set_sleep_observer(this); + drive_->set_clocking_hint_observer(this); } else { drive_ = empty_drive_; } - if(is_sleeping() != was_sleeping) { - update_sleep_observer(); + if(preferred_clocking() != former_prefernece) { + update_clocking_observer(); } } } diff --git a/Storage/Disk/Controller/DiskController.hpp b/Storage/Disk/Controller/DiskController.hpp index 3382db073..b900bc80f 100644 --- a/Storage/Disk/Controller/DiskController.hpp +++ b/Storage/Disk/Controller/DiskController.hpp @@ -15,7 +15,7 @@ #include "../Track/PCMPatchedTrack.hpp" #include "../../../ClockReceiver/ClockReceiver.hpp" -#include "../../../ClockReceiver/Sleeper.hpp" +#include "../../../ClockReceiver/ClockingHintSource.hpp" namespace Storage { namespace Disk { @@ -29,7 +29,11 @@ namespace Disk { TODO: communication of head size and permissible stepping extents, appropriate simulation of gain. */ -class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDelegate, public Sleeper, public Sleeper::SleepObserver { +class Controller: + public DigitalPhaseLockedLoop::Delegate, + public Drive::EventDelegate, + public ClockingHint::Source, + public ClockingHint::Observer { protected: /*! Constructs a @c Controller that will be run at @c clock_rate. @@ -65,7 +69,7 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDe Should be implemented by subclasses if they implement writing; communicates that all bits supplied to write_bit have now been written. */ - virtual void process_write_completed(); + virtual void process_write_completed() override; /*! Puts the controller and the drive returned by get_drive() into write mode, supplying to @@ -97,9 +101,9 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDe Drive &get_drive(); /*! - As per Sleeper. + As per ClockingHint::Source. */ - bool is_sleeping(); + ClockingHint::Preference preferred_clocking() override; private: Time bit_length_; @@ -113,14 +117,15 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDe std::shared_ptr empty_drive_; - void set_component_is_sleeping(Sleeper *component, bool is_sleeping); + // ClockingHint::Observer. + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override; // for Drive::EventDelegate - void process_event(const Track::Event &event); - void advance(const Cycles cycles); + void process_event(const Track::Event &event) override; + void advance(const Cycles cycles) override ; // to satisfy DigitalPhaseLockedLoop::Delegate - void digital_phase_locked_loop_output_bit(int value); + void digital_phase_locked_loop_output_bit(int value) override; }; } diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index d2c61f98b..535f19624 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -46,15 +46,15 @@ void Drive::set_disk(const std::shared_ptr &disk) { has_disk_ = !!disk_; invalidate_track(); - update_sleep_observer(); + update_clocking_observer(); } bool Drive::has_disk() { return has_disk_; } -bool Drive::is_sleeping() { - return !motor_is_on_ || !has_disk_; +ClockingHint::Preference Drive::preferred_clocking() { + return (!motor_is_on_ || !has_disk_) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime; } bool Drive::get_is_track_zero() { @@ -119,7 +119,7 @@ void Drive::set_motor_on(bool motor_is_on) { ready_index_count_ = 0; if(disk_) disk_->flush_tracks(); } - update_sleep_observer(); + update_clocking_observer(); } bool Drive::get_motor_on() { diff --git a/Storage/Disk/Drive.hpp b/Storage/Disk/Drive.hpp index a5601ea9a..8606534f8 100644 --- a/Storage/Disk/Drive.hpp +++ b/Storage/Disk/Drive.hpp @@ -15,14 +15,14 @@ #include "../TimedEventLoop.hpp" #include "../../Activity/Observer.hpp" -#include "../../ClockReceiver/Sleeper.hpp" +#include "../../ClockReceiver/ClockingHintSource.hpp" #include namespace Storage { namespace Disk { -class Drive: public Sleeper, public TimedEventLoop { +class Drive: public ClockingHint::Source, public TimedEventLoop { public: Drive(unsigned int input_clock_rate, int revolutions_per_minute, int number_of_heads); ~Drive(); @@ -121,7 +121,7 @@ class Drive: public Sleeper, public TimedEventLoop { void set_event_delegate(EventDelegate *); // As per Sleeper. - bool is_sleeping(); + ClockingHint::Preference preferred_clocking() override; /// Adds an activity observer; it'll be notified of disk activity. /// The caller can specify whether to add an LED based on disk motor. @@ -171,9 +171,9 @@ class Drive: public Sleeper, public TimedEventLoop { Time cycles_per_bit_; // TimedEventLoop call-ins and state. - void process_next_event(); + void process_next_event() override; void get_next_event(const Time &duration_already_passed); - void advance(const Cycles cycles); + void advance(const Cycles cycles) override; Track::Event current_event_; // Helper for track changes. diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index 1b66af089..5b83c05b1 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -65,15 +65,15 @@ void Tape::set_offset(uint64_t offset) { // MARK: - Player -bool TapePlayer::is_sleeping() { - return !tape_ || tape_->is_at_end(); +ClockingHint::Preference TapePlayer::preferred_clocking() { + return (!tape_ || tape_->is_at_end()) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime; } void TapePlayer::set_tape(std::shared_ptr tape) { tape_ = tape; reset_timer(); get_next_pulse(); - update_sleep_observer(); + update_clocking_observer(); } std::shared_ptr TapePlayer::get_tape() { @@ -88,7 +88,7 @@ void TapePlayer::get_next_pulse() { // get the new pulse if(tape_) { current_pulse_ = tape_->get_next_pulse(); - if(tape_->is_at_end()) update_sleep_observer(); + if(tape_->is_at_end()) update_clocking_observer(); } else { current_pulse_.length.length = 1; current_pulse_.length.clock_rate = 1; @@ -119,14 +119,15 @@ BinaryTapePlayer::BinaryTapePlayer(unsigned int input_clock_rate) : TapePlayer(input_clock_rate) {} -bool BinaryTapePlayer::is_sleeping() { - return !motor_is_running_ || TapePlayer::is_sleeping(); +ClockingHint::Preference BinaryTapePlayer::preferred_clocking() { + if(!motor_is_running_) return ClockingHint::Preference::None; + return TapePlayer::preferred_clocking(); } void BinaryTapePlayer::set_motor_control(bool enabled) { if(motor_is_running_ != enabled) { motor_is_running_ = enabled; - update_sleep_observer(); + update_clocking_observer(); } } diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index b28e815c4..329da3e3b 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -12,7 +12,7 @@ #include #include "../../ClockReceiver/ClockReceiver.hpp" -#include "../../ClockReceiver/Sleeper.hpp" +#include "../../ClockReceiver/ClockingHintSource.hpp" #include "../TimedEventLoop.hpp" @@ -95,7 +95,7 @@ class Tape { Will call @c process_input_pulse instantaneously upon reaching *the end* of a pulse. Therefore a subclass can decode pulses into data within process_input_pulse, using the supplied pulse's @c length and @c type. */ -class TapePlayer: public TimedEventLoop, public Sleeper { +class TapePlayer: public TimedEventLoop, public ClockingHint::Source { public: TapePlayer(unsigned int input_clock_rate); @@ -107,10 +107,10 @@ class TapePlayer: public TimedEventLoop, public Sleeper { void run_for_input_pulse(); - bool is_sleeping(); + ClockingHint::Preference preferred_clocking() override; protected: - virtual void process_next_event(); + virtual void process_next_event() override; virtual void process_input_pulse(const Tape::Pulse &pulse) = 0; private: @@ -145,11 +145,11 @@ class BinaryTapePlayer: public TapePlayer { }; void set_delegate(Delegate *delegate); - bool is_sleeping(); + ClockingHint::Preference preferred_clocking() override; protected: Delegate *delegate_ = nullptr; - virtual void process_input_pulse(const Storage::Tape::Tape::Pulse &pulse); + void process_input_pulse(const Storage::Tape::Tape::Pulse &pulse) override; bool input_level_ = false; bool motor_is_running_ = false; };