mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-26 09:29:45 +00:00
Merge pull request #456 from TomHarte/TristateSleeper
Commutes `Sleeper` to `ClockingHint::Source`
This commit is contained in:
commit
a26ab7086d
@ -52,79 +52,79 @@
|
|||||||
*/
|
*/
|
||||||
template <class T> class WrappedInt {
|
template <class T> class WrappedInt {
|
||||||
public:
|
public:
|
||||||
inline WrappedInt(int l) : length_(l) {}
|
constexpr WrappedInt(int l) : length_(l) {}
|
||||||
inline WrappedInt() : length_(0) {}
|
constexpr WrappedInt() : length_(0) {}
|
||||||
|
|
||||||
inline T &operator =(const T &rhs) {
|
T &operator =(const T &rhs) {
|
||||||
length_ = rhs.length_;
|
length_ = rhs.length_;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator +=(const T &rhs) {
|
T &operator +=(const T &rhs) {
|
||||||
length_ += rhs.length_;
|
length_ += rhs.length_;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator -=(const T &rhs) {
|
T &operator -=(const T &rhs) {
|
||||||
length_ -= rhs.length_;
|
length_ -= rhs.length_;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator ++() {
|
T &operator ++() {
|
||||||
++ length_;
|
++ length_;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator ++(int) {
|
T &operator ++(int) {
|
||||||
length_ ++;
|
length_ ++;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator --() {
|
T &operator --() {
|
||||||
-- length_;
|
-- length_;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator --(int) {
|
T &operator --(int) {
|
||||||
length_ --;
|
length_ --;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator %=(const T &rhs) {
|
T &operator %=(const T &rhs) {
|
||||||
length_ %= rhs.length_;
|
length_ %= rhs.length_;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T &operator &=(const T &rhs) {
|
T &operator &=(const T &rhs) {
|
||||||
length_ &= rhs.length_;
|
length_ &= rhs.length_;
|
||||||
return *static_cast<T *>(this);
|
return *static_cast<T *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
|
constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
|
||||||
inline T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
|
constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
|
||||||
|
|
||||||
inline T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
|
constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
|
||||||
inline T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
|
constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
|
||||||
|
|
||||||
inline T operator -() const { return T(- length_); }
|
constexpr T operator -() const { return T(- length_); }
|
||||||
|
|
||||||
inline bool operator <(const T &rhs) const { return length_ < rhs.length_; }
|
constexpr bool operator <(const T &rhs) const { return length_ < rhs.length_; }
|
||||||
inline bool operator >(const T &rhs) const { return length_ > rhs.length_; }
|
constexpr bool operator >(const T &rhs) const { return length_ > rhs.length_; }
|
||||||
inline bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
|
constexpr bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
|
||||||
inline bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
|
constexpr bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
|
||||||
inline bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
|
constexpr bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
|
||||||
inline bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
|
constexpr bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
|
||||||
|
|
||||||
inline bool operator !() const { return !length_; }
|
constexpr bool operator !() const { return !length_; }
|
||||||
// bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse
|
// bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse
|
||||||
|
|
||||||
inline int as_int() const { return length_; }
|
constexpr int as_int() const { return length_; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||||
*/
|
*/
|
||||||
inline T divide(const T &divisor) {
|
T divide(const T &divisor) {
|
||||||
T result(length_ / divisor.length_);
|
T result(length_ / divisor.length_);
|
||||||
length_ %= divisor.length_;
|
length_ %= divisor.length_;
|
||||||
return result;
|
return result;
|
||||||
@ -134,7 +134,7 @@ template <class T> class WrappedInt {
|
|||||||
Flushes the value in @c this. The current value is returned, and the internal value
|
Flushes the value in @c this. The current value is returned, and the internal value
|
||||||
is reset to zero.
|
is reset to zero.
|
||||||
*/
|
*/
|
||||||
inline T flush() {
|
T flush() {
|
||||||
T result(length_);
|
T result(length_);
|
||||||
length_ = 0;
|
length_ = 0;
|
||||||
return result;
|
return result;
|
||||||
@ -150,34 +150,34 @@ template <class T> class WrappedInt {
|
|||||||
/// Describes an integer number of whole cycles: pairs of clock signal transitions.
|
/// Describes an integer number of whole cycles: pairs of clock signal transitions.
|
||||||
class Cycles: public WrappedInt<Cycles> {
|
class Cycles: public WrappedInt<Cycles> {
|
||||||
public:
|
public:
|
||||||
inline Cycles(int l) : WrappedInt<Cycles>(l) {}
|
constexpr Cycles(int l) : WrappedInt<Cycles>(l) {}
|
||||||
inline Cycles() : WrappedInt<Cycles>() {}
|
constexpr Cycles() : WrappedInt<Cycles>() {}
|
||||||
inline Cycles(const Cycles &cycles) : WrappedInt<Cycles>(cycles.length_) {}
|
constexpr Cycles(const Cycles &cycles) : WrappedInt<Cycles>(cycles.length_) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Describes an integer number of half cycles: single clock signal transitions.
|
/// Describes an integer number of half cycles: single clock signal transitions.
|
||||||
class HalfCycles: public WrappedInt<HalfCycles> {
|
class HalfCycles: public WrappedInt<HalfCycles> {
|
||||||
public:
|
public:
|
||||||
inline HalfCycles(int l) : WrappedInt<HalfCycles>(l) {}
|
constexpr HalfCycles(int l) : WrappedInt<HalfCycles>(l) {}
|
||||||
inline HalfCycles() : WrappedInt<HalfCycles>() {}
|
constexpr HalfCycles() : WrappedInt<HalfCycles>() {}
|
||||||
|
|
||||||
inline HalfCycles(const Cycles cycles) : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
|
constexpr HalfCycles(const Cycles cycles) : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
|
||||||
inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {}
|
constexpr HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {}
|
||||||
|
|
||||||
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
||||||
inline Cycles cycles() {
|
constexpr Cycles cycles() {
|
||||||
return Cycles(length_ >> 1);
|
return Cycles(length_ >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
|
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
|
||||||
inline Cycles flush_cycles() {
|
Cycles flush_cycles() {
|
||||||
Cycles result(length_ >> 1);
|
Cycles result(length_ >> 1);
|
||||||
length_ &= 1;
|
length_ &= 1;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flushes the half cycles in @c this, returning the number stored and setting this total to zero.
|
/// Flushes the half cycles in @c this, returning the number stored and setting this total to zero.
|
||||||
inline HalfCycles flush() {
|
HalfCycles flush() {
|
||||||
HalfCycles result(length_);
|
HalfCycles result(length_);
|
||||||
length_ = 0;
|
length_ = 0;
|
||||||
return result;
|
return result;
|
||||||
@ -187,7 +187,7 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
|||||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||||
*/
|
*/
|
||||||
inline Cycles divide_cycles(const Cycles &divisor) {
|
Cycles divide_cycles(const Cycles &divisor) {
|
||||||
HalfCycles half_divisor = HalfCycles(divisor);
|
HalfCycles half_divisor = HalfCycles(divisor);
|
||||||
Cycles result(length_ / half_divisor.length_);
|
Cycles result(length_ / half_divisor.length_);
|
||||||
length_ %= half_divisor.length_;
|
length_ %= half_divisor.length_;
|
||||||
@ -203,7 +203,6 @@ template <class T> class HalfClockReceiver: public T {
|
|||||||
public:
|
public:
|
||||||
using T::T;
|
using T::T;
|
||||||
|
|
||||||
using T::run_for;
|
|
||||||
inline void run_for(const HalfCycles half_cycles) {
|
inline void run_for(const HalfCycles half_cycles) {
|
||||||
half_cycles_ += half_cycles;
|
half_cycles_ += half_cycles;
|
||||||
T::run_for(half_cycles_.flush_cycles());
|
T::run_for(half_cycles_.flush_cycles());
|
||||||
|
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.
|
/// Runs the controller for @c number_of_cycles cycles.
|
||||||
void run_for(const Cycles cycles);
|
void run_for(const Cycles cycles);
|
||||||
using Storage::Disk::Controller::run_for;
|
|
||||||
|
|
||||||
enum Flag: uint8_t {
|
enum Flag: uint8_t {
|
||||||
NotReady = 0x80,
|
NotReady = 0x80,
|
||||||
|
@ -83,8 +83,10 @@ i8272::i8272(BusHandler &bus_handler, Cycles clock_rate) :
|
|||||||
posit_event(static_cast<int>(Event8272::CommandByte));
|
posit_event(static_cast<int>(Event8272::CommandByte));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool i8272::is_sleeping() {
|
ClockingHint::Preference i8272::preferred_clocking() {
|
||||||
return is_sleeping_ && Storage::Disk::MFMController::is_sleeping();
|
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) {
|
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_;
|
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) {
|
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 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_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 PASTE(x, y) x##y
|
||||||
#define CONCAT(x, y) PASTE(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) { \
|
if(drives_[active_drive_].head_unload_delay[active_head_] == 0) { \
|
||||||
head_timers_running_++; \
|
head_timers_running_++; \
|
||||||
is_sleeping_ = false; \
|
is_sleeping_ = false; \
|
||||||
update_sleep_observer(); \
|
update_clocking_observer(); \
|
||||||
} \
|
} \
|
||||||
drives_[active_drive_].head_unload_delay[active_head_] = MS_TO_CYCLES(head_unload_time_);\
|
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) {
|
if(drives_[drive].phase != Drive::Seeking) {
|
||||||
drives_seeking_++;
|
drives_seeking_++;
|
||||||
is_sleeping_ = false;
|
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
|
// 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_dma_acknowledge(bool dack);
|
||||||
void set_terminal_count(bool tc);
|
void set_terminal_count(bool tc);
|
||||||
|
|
||||||
bool is_sleeping();
|
ClockingHint::Preference preferred_clocking() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void select_drive(int number) = 0;
|
virtual void select_drive(int number) = 0;
|
||||||
@ -67,7 +67,7 @@ class i8272: public Storage::Disk::MFMController {
|
|||||||
ResultEmpty = (1 << 5),
|
ResultEmpty = (1 << 5),
|
||||||
NoLongerReady = (1 << 6)
|
NoLongerReady = (1 << 6)
|
||||||
};
|
};
|
||||||
void posit_event(int type);
|
void posit_event(int type) override;
|
||||||
int interesting_event_mask_ = static_cast<int>(Event8272::CommandByte);
|
int interesting_event_mask_ = static_cast<int>(Event8272::CommandByte);
|
||||||
int resume_point_ = 0;
|
int resume_point_ = 0;
|
||||||
bool is_access_command_ = false;
|
bool is_access_command_ = false;
|
||||||
|
@ -19,12 +19,13 @@ namespace {
|
|||||||
const uint8_t input_flux = 0x1;
|
const uint8_t input_flux = 0x1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DiskII::DiskII() :
|
DiskII::DiskII(int clock_rate) :
|
||||||
|
clock_rate_(clock_rate),
|
||||||
inputs_(input_command),
|
inputs_(input_command),
|
||||||
drives_{{2045454, 300, 1}, {2045454, 300, 1}}
|
drives_{{static_cast<unsigned int>(clock_rate), 300, 1}, {static_cast<unsigned int>(clock_rate), 300, 1}}
|
||||||
{
|
{
|
||||||
drives_[0].set_sleep_observer(this);
|
drives_[0].set_clocking_hint_observer(this);
|
||||||
drives_[1].set_sleep_observer(this);
|
drives_[1].set_clocking_hint_observer(this);
|
||||||
drives_[active_drive_].set_event_delegate(this);
|
drives_[active_drive_].set_event_delegate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,71 +74,75 @@ void DiskII::select_drive(int drive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DiskII::run_for(const Cycles cycles) {
|
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();
|
||||||
int integer_cycles = cycles.as_int();
|
while(integer_cycles--) {
|
||||||
while(integer_cycles--) {
|
const int address = (state_ & 0xf0) | inputs_ | ((shift_register_&0x80) >> 6);
|
||||||
const int address = (state_ & 0xf0) | inputs_ | ((shift_register_&0x80) >> 6);
|
inputs_ |= input_flux;
|
||||||
inputs_ |= input_flux;
|
state_ = state_machine_[static_cast<std::size_t>(address)];
|
||||||
state_ = state_machine_[static_cast<std::size_t>(address)];
|
switch(state_ & 0xf) {
|
||||||
switch(state_ & 0xf) {
|
default: shift_register_ = 0; break; // clear
|
||||||
default: shift_register_ = 0; break; // clear
|
case 0x8: break; // nop
|
||||||
case 0x8: break; // nop
|
|
||||||
|
|
||||||
case 0x9: shift_register_ = static_cast<uint8_t>(shift_register_ << 1); break; // shift left, bringing in a zero
|
case 0x9: shift_register_ = static_cast<uint8_t>(shift_register_ << 1); break; // shift left, bringing in a zero
|
||||||
case 0xd: shift_register_ = static_cast<uint8_t>((shift_register_ << 1) | 1); break; // shift left, bringing in a one
|
case 0xd: shift_register_ = static_cast<uint8_t>((shift_register_ << 1) | 1); break; // shift left, bringing in a one
|
||||||
|
|
||||||
case 0xa: // shift right, bringing in write protected status
|
case 0xa: // shift right, bringing in write protected status
|
||||||
shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00);
|
shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00);
|
||||||
|
|
||||||
// If the controller is in the sense write protect loop but the register will never change,
|
// If the controller is in the sense write protect loop but the register will never change,
|
||||||
// short circuit further work and return now.
|
// short circuit further work and return now.
|
||||||
if(shift_register_ == is_write_protected() ? 0xff : 0x00) {
|
if(shift_register_ == (is_write_protected() ? 0xff : 0x00)) {
|
||||||
if(!drive_is_sleeping_[0]) drives_[0].run_for(Cycles(integer_cycles));
|
if(!drive_is_sleeping_[0]) drives_[0].run_for(Cycles(integer_cycles));
|
||||||
if(!drive_is_sleeping_[1]) drives_[1].run_for(Cycles(integer_cycles));
|
if(!drive_is_sleeping_[1]) drives_[1].run_for(Cycles(integer_cycles));
|
||||||
set_controller_can_sleep();
|
decide_clocking_preference();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xb: shift_register_ = data_input_; break; // load data register from data bus
|
case 0xb: shift_register_ = data_input_; break; // load data register from data bus
|
||||||
}
|
|
||||||
|
|
||||||
// Currently writing?
|
|
||||||
if(inputs_&input_mode) {
|
|
||||||
// state_ & 0x80 should be the current level sent to the disk;
|
|
||||||
// therefore transitions in that bit should become flux transitions
|
|
||||||
drives_[active_drive_].write_bit(!!((state_ ^ address) & 0x80));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: surely there's a less heavyweight solution than this?
|
|
||||||
if(!drive_is_sleeping_[0]) drives_[0].run_for(Cycles(1));
|
|
||||||
if(!drive_is_sleeping_[1]) drives_[1].run_for(Cycles(1));
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if(!drive_is_sleeping_[0]) drives_[0].run_for(cycles);
|
// Currently writing?
|
||||||
if(!drive_is_sleeping_[1]) drives_[1].run_for(cycles);
|
if(inputs_&input_mode) {
|
||||||
|
// state_ & 0x80 should be the current level sent to the disk;
|
||||||
|
// therefore transitions in that bit should become flux transitions
|
||||||
|
drives_[active_drive_].write_bit(!!((state_ ^ address) & 0x80));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: surely there's a less heavyweight solution than inline updates?
|
||||||
|
if(!drive_is_sleeping_[0]) drives_[0].run_for(Cycles(1));
|
||||||
|
if(!drive_is_sleeping_[1]) drives_[1].run_for(Cycles(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
set_controller_can_sleep();
|
decide_clocking_preference();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskII::set_controller_can_sleep() {
|
void DiskII::decide_clocking_preference() {
|
||||||
// Permit the controller to sleep if it's in sense write protect mode, and the shift register
|
ClockingHint::Preference prior_preference = clocking_preference_;
|
||||||
// has already filled with the result of shifting eight times.
|
|
||||||
bool controller_could_sleep = controller_can_sleep_;
|
// If in read mode, clocking is either:
|
||||||
controller_can_sleep_ =
|
//
|
||||||
(
|
// just-in-time, if drives are running or the shift register has any 1s in it or a flux event hasn't yet passed; or
|
||||||
(inputs_ == input_flux) &&
|
// none, given that drives are not running, the shift register has already emptied and there's no flux about to fire.
|
||||||
!motor_is_enabled_ &&
|
if(!(inputs_ & ~input_flux)) {
|
||||||
!shift_register_
|
clocking_preference_ = (!motor_is_enabled_ && !shift_register_ && !(inputs_&input_flux)) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
|
||||||
) ||
|
}
|
||||||
(
|
|
||||||
(inputs_ == (input_command | input_flux)) &&
|
// If in writing mode, clocking is real time.
|
||||||
(shift_register_ == (is_write_protected() ? 0xff : 0x00))
|
if(inputs_ & input_mode) {
|
||||||
);
|
clocking_preference_ = ClockingHint::Preference::RealTime;
|
||||||
if(controller_could_sleep != controller_can_sleep_)
|
}
|
||||||
update_sleep_observer();
|
|
||||||
|
// If in sense-write-protect mode, clocking is just-in-time if the shift register hasn't yet filled with the value that
|
||||||
|
// corresponds to the current write protect status. Otherwise it is none.
|
||||||
|
if((inputs_ & ~input_flux) == input_command) {
|
||||||
|
clocking_preference_ = (shift_register_ == (is_write_protected() ? 0xff : 0x00)) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Announce a change if there was one.
|
||||||
|
if(prior_preference != clocking_preference_)
|
||||||
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiskII::is_write_protected() {
|
bool DiskII::is_write_protected() {
|
||||||
@ -195,18 +200,18 @@ void DiskII::set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int driv
|
|||||||
void DiskII::process_event(const Storage::Disk::Track::Event &event) {
|
void DiskII::process_event(const Storage::Disk::Track::Event &event) {
|
||||||
if(event.type == Storage::Disk::Track::Event::FluxTransition) {
|
if(event.type == Storage::Disk::Track::Event::FluxTransition) {
|
||||||
inputs_ &= ~input_flux;
|
inputs_ &= ~input_flux;
|
||||||
set_controller_can_sleep();
|
decide_clocking_preference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskII::set_component_is_sleeping(Sleeper *component, bool is_sleeping) {
|
void DiskII::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) {
|
||||||
drive_is_sleeping_[0] = drives_[0].is_sleeping();
|
drive_is_sleeping_[0] = drives_[0].preferred_clocking() == ClockingHint::Preference::None;
|
||||||
drive_is_sleeping_[1] = drives_[1].is_sleeping();
|
drive_is_sleeping_[1] = drives_[1].preferred_clocking() == ClockingHint::Preference::None;
|
||||||
update_sleep_observer();
|
decide_clocking_preference();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiskII::is_sleeping() {
|
ClockingHint::Preference DiskII::preferred_clocking() {
|
||||||
return controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1];
|
return clocking_preference_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskII::set_data_input(uint8_t input) {
|
void DiskII::set_data_input(uint8_t input) {
|
||||||
@ -243,11 +248,11 @@ int DiskII::read_address(int address) {
|
|||||||
break;
|
break;
|
||||||
case 0xf:
|
case 0xf:
|
||||||
if(!(inputs_ & input_mode))
|
if(!(inputs_ & input_mode))
|
||||||
drives_[active_drive_].begin_writing(Storage::Time(1, 2045454), false);
|
drives_[active_drive_].begin_writing(Storage::Time(1, clock_rate_), false);
|
||||||
inputs_ |= input_mode;
|
inputs_ |= input_mode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
set_controller_can_sleep();
|
decide_clocking_preference();
|
||||||
return (address & 1) ? 0xff : shift_register_;
|
return (address & 1) ? 0xff : shift_register_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#define DiskII_hpp
|
#define DiskII_hpp
|
||||||
|
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
#include "../../ClockReceiver/Sleeper.hpp"
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||||
|
|
||||||
#include "../../Storage/Disk/Disk.hpp"
|
#include "../../Storage/Disk/Disk.hpp"
|
||||||
#include "../../Storage/Disk/Drive.hpp"
|
#include "../../Storage/Disk/Drive.hpp"
|
||||||
@ -28,10 +28,10 @@ namespace Apple {
|
|||||||
*/
|
*/
|
||||||
class DiskII:
|
class DiskII:
|
||||||
public Storage::Disk::Drive::EventDelegate,
|
public Storage::Disk::Drive::EventDelegate,
|
||||||
public Sleeper::SleepObserver,
|
public ClockingHint::Source,
|
||||||
public Sleeper {
|
public ClockingHint::Observer {
|
||||||
public:
|
public:
|
||||||
DiskII();
|
DiskII(int clock_rate);
|
||||||
|
|
||||||
/// Sets the current external value of the data bus.
|
/// Sets the current external value of the data bus.
|
||||||
void set_data_input(uint8_t input);
|
void set_data_input(uint8_t input);
|
||||||
@ -76,7 +76,7 @@ class DiskII:
|
|||||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||||
|
|
||||||
// As per Sleeper.
|
// As per Sleeper.
|
||||||
bool is_sleeping() override;
|
ClockingHint::Preference preferred_clocking() override;
|
||||||
|
|
||||||
// The Disk II functions as a potential target for @c Activity::Sources.
|
// The Disk II functions as a potential target for @c Activity::Sources.
|
||||||
void set_activity_observer(Activity::Observer *observer);
|
void set_activity_observer(Activity::Observer *observer);
|
||||||
@ -95,7 +95,9 @@ class DiskII:
|
|||||||
|
|
||||||
uint8_t trigger_address(int address, uint8_t value);
|
uint8_t trigger_address(int address, uint8_t value);
|
||||||
void process_event(const Storage::Disk::Track::Event &event) override;
|
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;
|
||||||
|
|
||||||
|
const int clock_rate_ = 0;
|
||||||
|
|
||||||
uint8_t state_ = 0;
|
uint8_t state_ = 0;
|
||||||
uint8_t inputs_ = 0;
|
uint8_t inputs_ = 0;
|
||||||
@ -108,11 +110,11 @@ class DiskII:
|
|||||||
std::array<uint8_t, 256> state_machine_;
|
std::array<uint8_t, 256> state_machine_;
|
||||||
Storage::Disk::Drive drives_[2];
|
Storage::Disk::Drive drives_[2];
|
||||||
bool drive_is_sleeping_[2];
|
bool drive_is_sleeping_[2];
|
||||||
bool controller_can_sleep_ = false;
|
|
||||||
int active_drive_ = 0;
|
int active_drive_ = 0;
|
||||||
bool motor_is_enabled_ = false;
|
bool motor_is_enabled_ = false;
|
||||||
|
|
||||||
void set_controller_can_sleep();
|
void decide_clocking_preference();
|
||||||
|
ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::RealTime;
|
||||||
|
|
||||||
uint8_t data_input_ = 0;
|
uint8_t data_input_ = 0;
|
||||||
};
|
};
|
||||||
|
@ -694,7 +694,7 @@ class ConcreteMachine:
|
|||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::Machine,
|
||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient,
|
||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
public Sleeper::SleepObserver,
|
public ClockingHint::Observer,
|
||||||
public Machine,
|
public Machine,
|
||||||
public Activity::Source {
|
public Activity::Source {
|
||||||
public:
|
public:
|
||||||
@ -714,11 +714,8 @@ class ConcreteMachine:
|
|||||||
Memory::Fuzz(ram_, sizeof(ram_));
|
Memory::Fuzz(ram_, sizeof(ram_));
|
||||||
|
|
||||||
// register this class as the sleep observer for the FDC and tape
|
// register this class as the sleep observer for the FDC and tape
|
||||||
fdc_.set_sleep_observer(this);
|
fdc_.set_clocking_hint_observer(this);
|
||||||
fdc_is_sleeping_ = fdc_.is_sleeping();
|
tape_player_.set_clocking_hint_observer(this);
|
||||||
|
|
||||||
tape_player_.set_sleep_observer(this);
|
|
||||||
tape_player_is_sleeping_ = tape_player_.is_sleeping();
|
|
||||||
|
|
||||||
ay_.ay().set_port_handler(&key_state_);
|
ay_.ay().set_port_handler(&key_state_);
|
||||||
}
|
}
|
||||||
@ -749,7 +746,7 @@ class ConcreteMachine:
|
|||||||
ay_.run_for(cycle.length);
|
ay_.run_for(cycle.length);
|
||||||
|
|
||||||
// Clock the FDC, if connected, using a lazy scale by two
|
// Clock the FDC, if connected, using a lazy scale by two
|
||||||
if(has_fdc_ && !fdc_is_sleeping_) fdc_.run_for(Cycles(cycle.length.as_int()));
|
time_since_fdc_update_ += cycle.length;
|
||||||
|
|
||||||
// Update typing activity
|
// Update typing activity
|
||||||
if(typer_) typer_->run_for(cycle.length);
|
if(typer_) typer_->run_for(cycle.length);
|
||||||
@ -796,11 +793,13 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// Check for an FDC access
|
// Check for an FDC access
|
||||||
if(has_fdc_ && (address & 0x580) == 0x100) {
|
if(has_fdc_ && (address & 0x580) == 0x100) {
|
||||||
|
flush_fdc();
|
||||||
fdc_.set_register(address & 1, *cycle.value);
|
fdc_.set_register(address & 1, *cycle.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a disk motor access
|
// Check for a disk motor access
|
||||||
if(has_fdc_ && !(address & 0x580)) {
|
if(has_fdc_ && !(address & 0x580)) {
|
||||||
|
flush_fdc();
|
||||||
fdc_.set_motor_on(!!(*cycle.value));
|
fdc_.set_motor_on(!!(*cycle.value));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -815,6 +814,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// Check for an FDC access
|
// Check for an FDC access
|
||||||
if(has_fdc_ && (address & 0x580) == 0x100) {
|
if(has_fdc_ && (address & 0x580) == 0x100) {
|
||||||
|
flush_fdc();
|
||||||
*cycle.value &= fdc_.get_register(address & 1);
|
*cycle.value &= fdc_.get_register(address & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -858,6 +858,7 @@ class ConcreteMachine:
|
|||||||
// Just flush the AY.
|
// Just flush the AY.
|
||||||
ay_.update();
|
ay_.update();
|
||||||
ay_.flush();
|
ay_.flush();
|
||||||
|
flush_fdc();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A CRTMachine function; indicates that outputs should be created now.
|
/// A CRTMachine function; indicates that outputs should be created now.
|
||||||
@ -967,9 +968,10 @@ class ConcreteMachine:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override final {
|
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override final {
|
||||||
fdc_is_sleeping_ = fdc_.is_sleeping();
|
fdc_is_sleeping_ = fdc_.preferred_clocking() == ClockingHint::Preference::None;
|
||||||
tape_player_is_sleeping_ = tape_player_.is_sleeping();
|
tape_player_is_sleeping_ = tape_player_.preferred_clocking() == ClockingHint::Preference::None;
|
||||||
|
printf("FDC: %s, tape %s\n", fdc_is_sleeping_ ? "sleeping" : "regular", tape_player_is_sleeping_ ? "sleeping" : "regular");
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Keyboard
|
// MARK: - Keyboard
|
||||||
@ -1063,6 +1065,14 @@ class ConcreteMachine:
|
|||||||
Intel::i8255::i8255<i8255PortHandler> i8255_;
|
Intel::i8255::i8255<i8255PortHandler> i8255_;
|
||||||
|
|
||||||
FDC fdc_;
|
FDC fdc_;
|
||||||
|
HalfCycles time_since_fdc_update_;
|
||||||
|
void flush_fdc() {
|
||||||
|
// Clock the FDC, if connected, using a lazy scale by two
|
||||||
|
if(has_fdc_ && !fdc_is_sleeping_) {
|
||||||
|
fdc_.run_for(Cycles(time_since_fdc_update_.as_int()));
|
||||||
|
}
|
||||||
|
time_since_fdc_update_ = HalfCycles(0);
|
||||||
|
}
|
||||||
|
|
||||||
InterruptTimer interrupt_timer_;
|
InterruptTimer interrupt_timer_;
|
||||||
Storage::Tape::BinaryTapePlayer tape_player_;
|
Storage::Tape::BinaryTapePlayer tape_player_;
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include "DiskIICard.hpp"
|
#include "DiskIICard.hpp"
|
||||||
#include "Video.hpp"
|
#include "Video.hpp"
|
||||||
|
|
||||||
|
#include "../../ClockReceiver/ForceInline.hpp"
|
||||||
|
|
||||||
#include "../../Analyser/Static/AppleII/Target.hpp"
|
#include "../../Analyser/Static/AppleII/Target.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -203,7 +205,7 @@ class ConcreteMachine:
|
|||||||
return &speaker_;
|
return &speaker_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
|
forceinline Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
|
||||||
++ cycles_since_video_update_;
|
++ cycles_since_video_update_;
|
||||||
++ cycles_since_card_update_;
|
++ cycles_since_card_update_;
|
||||||
cycles_since_audio_update_ += Cycles(7);
|
cycles_since_audio_update_ += Cycles(7);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
using namespace AppleII;
|
using namespace AppleII;
|
||||||
|
|
||||||
DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) {
|
DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) : diskii_(2045454) {
|
||||||
auto roms = rom_fetcher(
|
auto roms = rom_fetcher(
|
||||||
"DiskII",
|
"DiskII",
|
||||||
{
|
{
|
||||||
@ -20,7 +20,7 @@ DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sec
|
|||||||
boot_ = std::move(*roms[0]);
|
boot_ = std::move(*roms[0]);
|
||||||
diskii_.set_state_machine(*roms[1]);
|
diskii_.set_state_machine(*roms[1]);
|
||||||
set_select_constraints(None);
|
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) {
|
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) {
|
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));
|
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);
|
diskii_.set_activity_observer(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiskIICard::set_component_is_sleeping(Sleeper *component, bool is_sleeping) {
|
void DiskIICard::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) {
|
||||||
diskii_is_sleeping_ = is_sleeping;
|
diskii_clocking_preference_ = preference;
|
||||||
set_select_constraints(is_sleeping ? (IO | Device) : 0);
|
set_select_constraints((preference != ClockingHint::Preference::RealTime) ? (IO | Device) : 0);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#include "../../Components/DiskII/DiskII.hpp"
|
#include "../../Components/DiskII/DiskII.hpp"
|
||||||
#include "../../Storage/Disk/Disk.hpp"
|
#include "../../Storage/Disk/Disk.hpp"
|
||||||
#include "../../ClockReceiver/Sleeper.hpp"
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
namespace AppleII {
|
namespace AppleII {
|
||||||
|
|
||||||
class DiskIICard: public Card, public Sleeper::SleepObserver {
|
class DiskIICard: public Card, public ClockingHint::Observer {
|
||||||
public:
|
public:
|
||||||
DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector);
|
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);
|
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||||
|
|
||||||
private:
|
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_;
|
std::vector<uint8_t> boot_;
|
||||||
Apple::DiskII diskii_;
|
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 Utility::TypeRecipient,
|
||||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||||
public Machine,
|
public Machine,
|
||||||
public Sleeper::SleepObserver,
|
public ClockingHint::Observer,
|
||||||
public Activity::Source {
|
public Activity::Source {
|
||||||
public:
|
public:
|
||||||
ConcreteMachine() :
|
ConcreteMachine() :
|
||||||
@ -331,7 +331,7 @@ class ConcreteMachine:
|
|||||||
user_port_via_port_handler_->set_interrupt_delegate(this);
|
user_port_via_port_handler_->set_interrupt_delegate(this);
|
||||||
keyboard_via_port_handler_->set_interrupt_delegate(this);
|
keyboard_via_port_handler_->set_interrupt_delegate(this);
|
||||||
tape_->set_delegate(this);
|
tape_->set_delegate(this);
|
||||||
tape_->set_sleep_observer(this);
|
tape_->set_clocking_hint_observer(this);
|
||||||
|
|
||||||
// install a joystick
|
// install a joystick
|
||||||
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
|
joysticks_.emplace_back(new Joystick(*user_port_via_port_handler_, *keyboard_via_port_handler_));
|
||||||
@ -749,8 +749,8 @@ class ConcreteMachine:
|
|||||||
return selection_set;
|
return selection_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override {
|
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override {
|
||||||
tape_is_sleeping_ = is_sleeping;
|
tape_is_sleeping_ = clocking == ClockingHint::Preference::None;
|
||||||
set_use_fast_tape();
|
set_use_fast_tape();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ class ConcreteMachine:
|
|||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::Machine,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public MemoryMap,
|
public MemoryMap,
|
||||||
public Sleeper::SleepObserver,
|
public ClockingHint::Observer,
|
||||||
public Activity::Source {
|
public Activity::Source {
|
||||||
public:
|
public:
|
||||||
ConcreteMachine():
|
ConcreteMachine():
|
||||||
@ -108,7 +108,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
ay_.set_port_handler(&ay_port_handler_);
|
ay_.set_port_handler(&ay_port_handler_);
|
||||||
speaker_.set_input_rate(3579545.0f / 2.0f);
|
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.
|
// 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});
|
mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f});
|
||||||
@ -555,8 +555,8 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Sleeper
|
// MARK: - Sleeper
|
||||||
void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override {
|
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override {
|
||||||
tape_player_is_sleeping_ = tape_player_.is_sleeping();
|
tape_player_is_sleeping_ = tape_player_.preferred_clocking() == ClockingHint::Preference::None;
|
||||||
set_use_fast_tape();
|
set_use_fast_tape();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ class Microdisc: public WD::WD1770 {
|
|||||||
bool get_interrupt_request_line();
|
bool get_interrupt_request_line();
|
||||||
|
|
||||||
void run_for(const Cycles cycles);
|
void run_for(const Cycles cycles);
|
||||||
using WD::WD1770::run_for;
|
|
||||||
|
|
||||||
enum PagingFlags {
|
enum PagingFlags {
|
||||||
/// Indicates that the BASIC ROM should be disabled; if this is set then either
|
/// Indicates that the BASIC ROM should be disabled; if this is set then either
|
||||||
@ -52,8 +51,9 @@ class Microdisc: public WD::WD1770 {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void set_control_register(uint8_t control, uint8_t changes);
|
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();
|
bool get_drive_is_ready();
|
||||||
|
|
||||||
std::array<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
|
std::array<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
|
||||||
size_t selected_drive_;
|
size_t selected_drive_;
|
||||||
bool irq_enable_ = false;
|
bool irq_enable_ = false;
|
||||||
|
@ -201,7 +201,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient,
|
||||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||||
public Microdisc::Delegate,
|
public Microdisc::Delegate,
|
||||||
public Sleeper::SleepObserver,
|
public ClockingHint::Observer,
|
||||||
public Activity::Source,
|
public Activity::Source,
|
||||||
public Machine {
|
public Machine {
|
||||||
|
|
||||||
@ -212,14 +212,15 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
ay8910_(audio_queue_),
|
ay8910_(audio_queue_),
|
||||||
speaker_(ay8910_),
|
speaker_(ay8910_),
|
||||||
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),
|
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),
|
||||||
via_(via_port_handler_) {
|
via_(via_port_handler_),
|
||||||
|
diskii_(2000000) {
|
||||||
set_clock_rate(1000000);
|
set_clock_rate(1000000);
|
||||||
via_port_handler_.set_interrupt_delegate(this);
|
via_port_handler_.set_interrupt_delegate(this);
|
||||||
tape_player_.set_delegate(this);
|
tape_player_.set_delegate(this);
|
||||||
Memory::Fuzz(ram_, sizeof(ram_));
|
Memory::Fuzz(ram_, sizeof(ram_));
|
||||||
|
|
||||||
if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) {
|
if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) {
|
||||||
diskii_.set_sleep_observer(this);
|
diskii_.set_clocking_hint_observer(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,6 +411,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
flush_diskii();
|
||||||
const int disk_value = diskii_.read_address(address);
|
const int disk_value = diskii_.read_address(address);
|
||||||
if(isReadOperation(operation) && disk_value != diskii_.DidNotLoad) *value = static_cast<uint8_t>(disk_value);
|
if(isReadOperation(operation) && disk_value != diskii_.DidNotLoad) *value = static_cast<uint8_t>(disk_value);
|
||||||
}
|
}
|
||||||
@ -444,9 +446,11 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
microdisc_.run_for(Cycles(8));
|
microdisc_.run_for(Cycles(8));
|
||||||
break;
|
break;
|
||||||
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
|
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
|
||||||
if(!diskii_is_sleeping_) {
|
if(diskii_clocking_preference_ == ClockingHint::Preference::RealTime) {
|
||||||
diskii_.set_data_input(*value);
|
diskii_.set_data_input(*value);
|
||||||
diskii_.run_for(Cycles(2));
|
diskii_.run_for(Cycles(2));
|
||||||
|
} else {
|
||||||
|
cycles_since_diskii_update_ += Cycles(2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -457,6 +461,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
forceinline void flush() {
|
forceinline void flush() {
|
||||||
update_video();
|
update_video();
|
||||||
via_port_handler_.flush();
|
via_port_handler_.flush();
|
||||||
|
flush_diskii();
|
||||||
}
|
}
|
||||||
|
|
||||||
// to satisfy CRTMachine::Machine
|
// to satisfy CRTMachine::Machine
|
||||||
@ -571,8 +576,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override final {
|
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) override final {
|
||||||
diskii_is_sleeping_ = diskii_.is_sleeping();
|
diskii_clocking_preference_ = diskii_.preferred_clocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -617,9 +622,13 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
|
|
||||||
// the Pravetz/Disk II, if in use
|
// the Pravetz/Disk II, if in use
|
||||||
Apple::DiskII diskii_;
|
Apple::DiskII diskii_;
|
||||||
|
Cycles cycles_since_diskii_update_;
|
||||||
|
void flush_diskii() {
|
||||||
|
diskii_.run_for(cycles_since_diskii_update_.flush());
|
||||||
|
}
|
||||||
std::vector<uint8_t> pravetz_rom_;
|
std::vector<uint8_t> pravetz_rom_;
|
||||||
std::size_t pravetz_rom_base_pointer_ = 0;
|
std::size_t pravetz_rom_base_pointer_ = 0;
|
||||||
bool diskii_is_sleeping_ = false;
|
ClockingHint::Preference diskii_clocking_preference_ = ClockingHint::Preference::RealTime;
|
||||||
|
|
||||||
// Overlay RAM
|
// Overlay RAM
|
||||||
uint16_t ram_top_ = basic_visible_ram_top_;
|
uint16_t ram_top_ = basic_visible_ram_top_;
|
||||||
|
@ -1037,7 +1037,7 @@
|
|||||||
4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
4BB297E51B587D8300A49093 /* start */ = {isa = PBXFileReference; lastKnownFileType = file; path = " start"; sourceTree = "<group>"; };
|
||||||
@ -3124,7 +3124,7 @@
|
|||||||
children = (
|
children = (
|
||||||
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */,
|
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */,
|
||||||
4BB06B211F316A3F00600C7A /* ForceInline.hpp */,
|
4BB06B211F316A3F00600C7A /* ForceInline.hpp */,
|
||||||
4BB146C61F49D7D700253439 /* Sleeper.hpp */,
|
4BB146C61F49D7D700253439 /* ClockingHintSource.hpp */,
|
||||||
4B449C942063389900A095C8 /* TimeTypes.hpp */,
|
4B449C942063389900A095C8 /* TimeTypes.hpp */,
|
||||||
);
|
);
|
||||||
name = ClockReceiver;
|
name = ClockReceiver;
|
||||||
|
@ -22,12 +22,12 @@ Controller::Controller(Cycles clock_rate) :
|
|||||||
set_drive(empty_drive_);
|
set_drive(empty_drive_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::set_component_is_sleeping(Sleeper *component, bool is_sleeping) {
|
void Controller::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) {
|
||||||
update_sleep_observer();
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::is_sleeping() {
|
ClockingHint::Preference Controller::preferred_clocking() {
|
||||||
return !drive_ || drive_->is_sleeping();
|
return (!drive_ || (drive_->preferred_clocking() == ClockingHint::Preference::None)) ? ClockingHint::Preference::None : ClockingHint::Preference::RealTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::run_for(const Cycles cycles) {
|
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) {
|
void Controller::set_drive(std::shared_ptr<Drive> drive) {
|
||||||
if(drive_ != drive) {
|
if(drive_ != drive) {
|
||||||
bool was_sleeping = is_sleeping();
|
ClockingHint::Preference former_prefernece = preferred_clocking();
|
||||||
// invalidate_track();
|
// invalidate_track();
|
||||||
|
|
||||||
if(drive_) {
|
if(drive_) {
|
||||||
drive_->set_event_delegate(nullptr);
|
drive_->set_event_delegate(nullptr);
|
||||||
drive_->set_sleep_observer(nullptr);
|
drive_->set_clocking_hint_observer(nullptr);
|
||||||
}
|
}
|
||||||
drive_ = drive;
|
drive_ = drive;
|
||||||
if(drive_) {
|
if(drive_) {
|
||||||
drive_->set_event_delegate(this);
|
drive_->set_event_delegate(this);
|
||||||
drive_->set_sleep_observer(this);
|
drive_->set_clocking_hint_observer(this);
|
||||||
} else {
|
} else {
|
||||||
drive_ = empty_drive_;
|
drive_ = empty_drive_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(is_sleeping() != was_sleeping) {
|
if(preferred_clocking() != former_prefernece) {
|
||||||
update_sleep_observer();
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include "../Track/PCMPatchedTrack.hpp"
|
#include "../Track/PCMPatchedTrack.hpp"
|
||||||
|
|
||||||
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
||||||
#include "../../../ClockReceiver/Sleeper.hpp"
|
#include "../../../ClockReceiver/ClockingHintSource.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
namespace Disk {
|
namespace Disk {
|
||||||
@ -29,7 +29,11 @@ namespace Disk {
|
|||||||
|
|
||||||
TODO: communication of head size and permissible stepping extents, appropriate simulation of gain.
|
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:
|
protected:
|
||||||
/*!
|
/*!
|
||||||
Constructs a @c Controller that will be run at @c clock_rate.
|
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
|
Should be implemented by subclasses if they implement writing; communicates that
|
||||||
all bits supplied to write_bit have now been written.
|
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
|
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();
|
Drive &get_drive();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
As per Sleeper.
|
As per ClockingHint::Source.
|
||||||
*/
|
*/
|
||||||
bool is_sleeping();
|
ClockingHint::Preference preferred_clocking() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Time bit_length_;
|
Time bit_length_;
|
||||||
@ -113,14 +117,15 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDe
|
|||||||
|
|
||||||
std::shared_ptr<Drive> empty_drive_;
|
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
|
// for Drive::EventDelegate
|
||||||
void process_event(const Track::Event &event);
|
void process_event(const Track::Event &event) override;
|
||||||
void advance(const Cycles cycles);
|
void advance(const Cycles cycles) override ;
|
||||||
|
|
||||||
// to satisfy DigitalPhaseLockedLoop::Delegate
|
// 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_;
|
has_disk_ = !!disk_;
|
||||||
|
|
||||||
invalidate_track();
|
invalidate_track();
|
||||||
update_sleep_observer();
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Drive::has_disk() {
|
bool Drive::has_disk() {
|
||||||
return has_disk_;
|
return has_disk_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Drive::is_sleeping() {
|
ClockingHint::Preference Drive::preferred_clocking() {
|
||||||
return !motor_is_on_ || !has_disk_;
|
return (!motor_is_on_ || !has_disk_) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Drive::get_is_track_zero() {
|
bool Drive::get_is_track_zero() {
|
||||||
@ -119,7 +119,7 @@ void Drive::set_motor_on(bool motor_is_on) {
|
|||||||
ready_index_count_ = 0;
|
ready_index_count_ = 0;
|
||||||
if(disk_) disk_->flush_tracks();
|
if(disk_) disk_->flush_tracks();
|
||||||
}
|
}
|
||||||
update_sleep_observer();
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Drive::get_motor_on() {
|
bool Drive::get_motor_on() {
|
||||||
|
@ -15,14 +15,14 @@
|
|||||||
|
|
||||||
#include "../TimedEventLoop.hpp"
|
#include "../TimedEventLoop.hpp"
|
||||||
#include "../../Activity/Observer.hpp"
|
#include "../../Activity/Observer.hpp"
|
||||||
#include "../../ClockReceiver/Sleeper.hpp"
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
namespace Disk {
|
namespace Disk {
|
||||||
|
|
||||||
class Drive: public Sleeper, public TimedEventLoop {
|
class Drive: public ClockingHint::Source, public TimedEventLoop {
|
||||||
public:
|
public:
|
||||||
Drive(unsigned int input_clock_rate, int revolutions_per_minute, int number_of_heads);
|
Drive(unsigned int input_clock_rate, int revolutions_per_minute, int number_of_heads);
|
||||||
~Drive();
|
~Drive();
|
||||||
@ -121,7 +121,7 @@ class Drive: public Sleeper, public TimedEventLoop {
|
|||||||
void set_event_delegate(EventDelegate *);
|
void set_event_delegate(EventDelegate *);
|
||||||
|
|
||||||
// As per Sleeper.
|
// As per Sleeper.
|
||||||
bool is_sleeping();
|
ClockingHint::Preference preferred_clocking() override;
|
||||||
|
|
||||||
/// Adds an activity observer; it'll be notified of disk activity.
|
/// Adds an activity observer; it'll be notified of disk activity.
|
||||||
/// The caller can specify whether to add an LED based on disk motor.
|
/// 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_;
|
Time cycles_per_bit_;
|
||||||
|
|
||||||
// TimedEventLoop call-ins and state.
|
// TimedEventLoop call-ins and state.
|
||||||
void process_next_event();
|
void process_next_event() override;
|
||||||
void get_next_event(const Time &duration_already_passed);
|
void get_next_event(const Time &duration_already_passed);
|
||||||
void advance(const Cycles cycles);
|
void advance(const Cycles cycles) override;
|
||||||
Track::Event current_event_;
|
Track::Event current_event_;
|
||||||
|
|
||||||
// Helper for track changes.
|
// Helper for track changes.
|
||||||
|
@ -65,15 +65,15 @@ void Tape::set_offset(uint64_t offset) {
|
|||||||
|
|
||||||
// MARK: - Player
|
// MARK: - Player
|
||||||
|
|
||||||
bool TapePlayer::is_sleeping() {
|
ClockingHint::Preference TapePlayer::preferred_clocking() {
|
||||||
return !tape_ || tape_->is_at_end();
|
return (!tape_ || tape_->is_at_end()) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TapePlayer::set_tape(std::shared_ptr<Storage::Tape::Tape> tape) {
|
void TapePlayer::set_tape(std::shared_ptr<Storage::Tape::Tape> tape) {
|
||||||
tape_ = tape;
|
tape_ = tape;
|
||||||
reset_timer();
|
reset_timer();
|
||||||
get_next_pulse();
|
get_next_pulse();
|
||||||
update_sleep_observer();
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Storage::Tape::Tape> TapePlayer::get_tape() {
|
std::shared_ptr<Storage::Tape::Tape> TapePlayer::get_tape() {
|
||||||
@ -88,7 +88,7 @@ void TapePlayer::get_next_pulse() {
|
|||||||
// get the new pulse
|
// get the new pulse
|
||||||
if(tape_) {
|
if(tape_) {
|
||||||
current_pulse_ = tape_->get_next_pulse();
|
current_pulse_ = tape_->get_next_pulse();
|
||||||
if(tape_->is_at_end()) update_sleep_observer();
|
if(tape_->is_at_end()) update_clocking_observer();
|
||||||
} else {
|
} else {
|
||||||
current_pulse_.length.length = 1;
|
current_pulse_.length.length = 1;
|
||||||
current_pulse_.length.clock_rate = 1;
|
current_pulse_.length.clock_rate = 1;
|
||||||
@ -119,14 +119,15 @@ BinaryTapePlayer::BinaryTapePlayer(unsigned int input_clock_rate) :
|
|||||||
TapePlayer(input_clock_rate)
|
TapePlayer(input_clock_rate)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool BinaryTapePlayer::is_sleeping() {
|
ClockingHint::Preference BinaryTapePlayer::preferred_clocking() {
|
||||||
return !motor_is_running_ || TapePlayer::is_sleeping();
|
if(!motor_is_running_) return ClockingHint::Preference::None;
|
||||||
|
return TapePlayer::preferred_clocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryTapePlayer::set_motor_control(bool enabled) {
|
void BinaryTapePlayer::set_motor_control(bool enabled) {
|
||||||
if(motor_is_running_ != enabled) {
|
if(motor_is_running_ != enabled) {
|
||||||
motor_is_running_ = enabled;
|
motor_is_running_ = enabled;
|
||||||
update_sleep_observer();
|
update_clocking_observer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
#include "../../ClockReceiver/Sleeper.hpp"
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||||
|
|
||||||
#include "../TimedEventLoop.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
|
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.
|
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:
|
public:
|
||||||
TapePlayer(unsigned int input_clock_rate);
|
TapePlayer(unsigned int input_clock_rate);
|
||||||
|
|
||||||
@ -107,10 +107,10 @@ class TapePlayer: public TimedEventLoop, public Sleeper {
|
|||||||
|
|
||||||
void run_for_input_pulse();
|
void run_for_input_pulse();
|
||||||
|
|
||||||
bool is_sleeping();
|
ClockingHint::Preference preferred_clocking() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void process_next_event();
|
virtual void process_next_event() override;
|
||||||
virtual void process_input_pulse(const Tape::Pulse &pulse) = 0;
|
virtual void process_input_pulse(const Tape::Pulse &pulse) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -145,11 +145,11 @@ class BinaryTapePlayer: public TapePlayer {
|
|||||||
};
|
};
|
||||||
void set_delegate(Delegate *delegate);
|
void set_delegate(Delegate *delegate);
|
||||||
|
|
||||||
bool is_sleeping();
|
ClockingHint::Preference preferred_clocking() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Delegate *delegate_ = nullptr;
|
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 input_level_ = false;
|
||||||
bool motor_is_running_ = false;
|
bool motor_is_running_ = false;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user