mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-09 00:37:27 +00:00
Commutes Sleeper
to ClockingHint::Source
, making state more granular.
This commit is contained in:
parent
6220ccb5d3
commit
db8d8d8404
88
ClockReceiver/ClockingHintSource.hpp
Normal file
88
ClockReceiver/ClockingHintSource.hpp
Normal 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 */
|
@ -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 */
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user