From 2f8e31bc8bf6d963ca99a4263bf83047ca1518ad Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 2 Sep 2019 13:00:01 -0400 Subject: [PATCH] Takes a first bash at implementing the new SCSI::Bus timing infrastructure. --- Machines/Apple/Macintosh/Macintosh.cpp | 1 + Storage/MassStorage/SCSI/SCSI.cpp | 50 ++++++++++++++++++++++++-- Storage/MassStorage/SCSI/SCSI.hpp | 24 ++++++++++--- 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index fee1ad738..e426ac4f9 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -75,6 +75,7 @@ template class ConcreteMachin video_(audio_, drive_speed_accumulator_), via_(via_port_handler_), via_port_handler_(*this, clock_, keyboard_, video_, audio_, iwm_, mouse_), + scsi_bus_(CLOCK_RATE * 2), scsi_(scsi_bus_, CLOCK_RATE * 2), hard_drive_(scsi_bus_, 6 /* SCSI ID */), drives_{ diff --git a/Storage/MassStorage/SCSI/SCSI.cpp b/Storage/MassStorage/SCSI/SCSI.cpp index 7490d32a0..9b3b9da64 100644 --- a/Storage/MassStorage/SCSI/SCSI.cpp +++ b/Storage/MassStorage/SCSI/SCSI.cpp @@ -10,6 +10,23 @@ using namespace SCSI; +Bus::Bus(HalfCycles clock_rate) { + cycles_to_time_ = 1.0 / double(clock_rate.as_int()); + + // NB: note that the dispatch times below are **ORDERED** + // from least to greatest. Each box should contain the number + // of whole clock periods it will take to get the the first + // discrete moment after the required delay interval has been met. + dispatch_times_[0] = 1 + int(CableSkew / cycles_to_time_); + dispatch_times_[1] = 1 + int(DeskewDelay / cycles_to_time_); + dispatch_times_[2] = 1 + int(BusFreeDelay / cycles_to_time_); + dispatch_times_[3] = 1 + int(BusSettleDelay / cycles_to_time_); + dispatch_times_[4] = 1 + int(BusClearDelay / cycles_to_time_); + dispatch_times_[5] = 1 + int(BusSetDelay / cycles_to_time_); + dispatch_times_[6] = 1 + int(ArbitrationDelay / cycles_to_time_); + dispatch_times_[7] = 1 + int(ResetHoldTime / cycles_to_time_); +} + size_t Bus::add_device() { const auto slot = device_states_.size(); device_states_.push_back(DefaultBusState); @@ -41,9 +58,10 @@ void Bus::set_device_output(size_t device, BusState output) { (state_ & Line::Request) ? 'q' : '-' ); - for(auto &observer: observers_) { - observer->scsi_bus_did_change(this, state_, 0.0); - } + bool was_asleep = preferred_clocking() == ClockingHint::Preference::None; + dispatch_index_ = 0; + time_in_state_ = HalfCycles(0); + if(was_asleep) update_clocking_observer(); } BusState Bus::get_state() { @@ -53,3 +71,29 @@ BusState Bus::get_state() { void Bus::add_observer(Observer *observer) { observers_.push_back(observer); } + +ClockingHint::Preference Bus::preferred_clocking() { + return (dispatch_index_ < sizeof(dispatch_times_)) ? ClockingHint::Preference::RealTime : ClockingHint::Preference::None; +} + +void Bus::run_for(HalfCycles time) { + if(dispatch_index_ < sizeof(dispatch_times_)) { + time_in_state_ += time; + + const auto old_index = dispatch_index_; + const auto time_as_int = time_in_state_.as_int(); + while(time_as_int >= dispatch_times_[dispatch_index_] && dispatch_index_ < sizeof(dispatch_times_)) { + ++dispatch_index_; + } + + if(dispatch_index_ != old_index) { + for(auto &observer: observers_) { + observer->scsi_bus_did_change(this, state_, double(time_as_int) * cycles_to_time_); + } + } + + if(preferred_clocking() == ClockingHint::Preference::None) { + update_clocking_observer(); + } + } +} diff --git a/Storage/MassStorage/SCSI/SCSI.hpp b/Storage/MassStorage/SCSI/SCSI.hpp index e602d0e5b..d1d0415bc 100644 --- a/Storage/MassStorage/SCSI/SCSI.hpp +++ b/Storage/MassStorage/SCSI/SCSI.hpp @@ -57,6 +57,9 @@ enum Line: BusState { #define us(x) (x) / 1000000.0 #define ns(x) (x) / 1000000000.0 +/// The minimum amount of time that reset must be held for. +constexpr double ResetHoldTime = us(25.0); + /// The minimum amount of time a SCSI device must wait after asserting ::Busy /// until the data bus can be inspected to see whether arbitration has been won. constexpr double ArbitrationDelay = us(1.7); @@ -81,9 +84,6 @@ constexpr double BusFreeDelay = ns(100.0); /// The minimum amount of time required for deskew of "certain signals". TODO: which? constexpr double DeskewDelay = ns(45.0); -/// The minimum amount of time that reset must be held for. -constexpr double ResetHoldTime = us(25.0); - /// The maximum amount of time that propagation of a SCSI bus signal can take between /// any two devices. constexpr double CableSkew = ns(10.0); @@ -91,8 +91,9 @@ constexpr double CableSkew = ns(10.0); #undef ns #undef us -class Bus { +class Bus: public ClockingHint::Source { public: + Bus(HalfCycles clock_rate); /*! Adds a device to the bus, returning the index it should use @@ -123,7 +124,22 @@ class Bus { */ void add_observer(Observer *); + /*! + SCSI buses don't have a clock. But devices on the bus are concerned with time-based factors, + and `run_for` is the way that time propagates within this emulator. So please permit this + fiction. + */ + void run_for(HalfCycles); + + /// As per ClockingHint::Source. + ClockingHint::Preference preferred_clocking() final; + private: + HalfCycles time_in_state_; + double cycles_to_time_ = 1.0; + size_t dispatch_index_ = 0; + int dispatch_times_[8]; + std::vector device_states_; BusState state_ = DefaultBusState; std::vector observers_;