1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Commutes Sleeper to ClockingHint::Source, making state more granular.

This commit is contained in:
Thomas Harte 2018-05-27 23:17:06 -04:00
parent 6220ccb5d3
commit db8d8d8404
21 changed files with 190 additions and 158 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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,

View File

@ -83,8 +83,10 @@ i8272::i8272(BusHandler &bus_handler, Cycles clock_rate) :
posit_event(static_cast<int>(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<int>(mask); return; case __LINE__:
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = static_cast<int>(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<int>(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

View File

@ -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<int>(Event8272::CommandByte);
int resume_point_ = 0;
bool is_access_command_ = false;

View File

@ -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) {

View File

@ -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<Storage::Disk::Disk> &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;

View File

@ -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

View File

@ -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);
}

View File

@ -14,7 +14,7 @@
#include "../../Components/DiskII/DiskII.hpp"
#include "../../Storage/Disk/Disk.hpp"
#include "../../ClockReceiver/Sleeper.hpp"
#include "../../ClockReceiver/ClockingHintSource.hpp"
#include <cstdint>
#include <memory>
@ -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<Storage::Disk::Disk> &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<uint8_t> boot_;
Apple::DiskII diskii_;
bool diskii_is_sleeping_ = false;
ClockingHint::Preference diskii_clocking_preference_ = ClockingHint::Preference::RealTime;
};
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
size_t selected_drive_;
bool irq_enable_ = false;

View File

@ -201,7 +201,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> 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 <Analyser::Static::Oric::Target::DiskInterface disk_interface> 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 <Analyser::Static::Oric::Target::DiskInterface disk_interface> 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:

View File

@ -1037,7 +1037,7 @@
4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = "<group>"; };
4BB0A6592044FD3000FB3688 /* SN76489.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SN76489.cpp; sourceTree = "<group>"; };
4BB0A65A2044FD3000FB3688 /* SN76489.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SN76489.hpp; sourceTree = "<group>"; };
4BB146C61F49D7D700253439 /* Sleeper.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sleeper.hpp; sourceTree = "<group>"; };
4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClockingHintSource.hpp; sourceTree = "<group>"; };
4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = "<group>"; };
4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.in.json; path = FUSE/tests.in.json; sourceTree = "<group>"; };
4BB297E51B587D8300A49093 /* start */ = {isa = PBXFileReference; lastKnownFileType = file; path = " start"; sourceTree = "<group>"; };
@ -3124,7 +3124,7 @@
children = (
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */,
4BB06B211F316A3F00600C7A /* ForceInline.hpp */,
4BB146C61F49D7D700253439 /* Sleeper.hpp */,
4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */,
4B449C942063389900A095C8 /* TimeTypes.hpp */,
);
name = ClockReceiver;

View File

@ -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> 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();
}
}
}

View File

@ -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<Drive> 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;
};
}

View File

@ -46,15 +46,15 @@ void Drive::set_disk(const std::shared_ptr<Disk> &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() {

View File

@ -15,14 +15,14 @@
#include "../TimedEventLoop.hpp"
#include "../../Activity/Observer.hpp"
#include "../../ClockReceiver/Sleeper.hpp"
#include "../../ClockReceiver/ClockingHintSource.hpp"
#include <memory>
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.

View File

@ -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<Storage::Tape::Tape> tape) {
tape_ = tape;
reset_timer();
get_next_pulse();
update_sleep_observer();
update_clocking_observer();
}
std::shared_ptr<Storage::Tape::Tape> 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();
}
}

View File

@ -12,7 +12,7 @@
#include <memory>
#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;
};