diff --git a/Components/5380/ncr5380.cpp b/Components/5380/ncr5380.cpp index 990d80515..7669b24d9 100644 --- a/Components/5380/ncr5380.cpp +++ b/Components/5380/ncr5380.cpp @@ -18,6 +18,7 @@ NCR5380::NCR5380(SCSI::Bus &bus, int clock_rate) : bus_(bus), clock_rate_(clock_rate) { device_id_ = bus_.add_device(); + bus_.add_observer(this); } void NCR5380::write(int address, uint8_t value, bool dma_acknowledge) { @@ -73,13 +74,14 @@ void NCR5380::write(int address, uint8_t value, bool dma_acknowledge) { case 0x1: arbitration_in_progress_ = true; - set_execution_state(ExecutionState::WatchingBusy); + set_execution_state(ExecutionState::WaitingForBusy); lost_arbitration_ = false; break; default: - assert_data_bus_ = false; // TODO: proper logic for this. + assert_data_bus_ = false; set_execution_state(ExecutionState::PerformingDMA); + bus_.update_observers(); break; } update_control_output(); @@ -229,11 +231,7 @@ void NCR5380::update_control_output() { } } -void NCR5380::run_for(Cycles cycles) { - if(state_ == ExecutionState::None) return; - - const auto bus_state = bus_.get_state(); - ++time_in_state_; +void NCR5380::scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) { switch(state_) { default: break; @@ -258,23 +256,20 @@ void NCR5380::run_for(Cycles cycles) { (iii) check that BSY and SEL are inactive. */ + case ExecutionState::WaitingForBusy: + if(!(new_state & SCSI::Line::Busy) || time_since_change < SCSI::DeskewDelay) return; + state_ = ExecutionState::WatchingBusy; + case ExecutionState::WatchingBusy: - if(bus_state & SCSI::Line::Busy) { - // Arbitration is lost only if a non-busy state had previously been observed. - if(time_in_state_ > 1) { - lost_arbitration_ = true; - set_execution_state(ExecutionState::None); - } else { - time_in_state_ = 0; - } - } /* else { - arbitration_in_progress_ = true; - }*/ + if(!(new_state & SCSI::Line::Busy)) { + lost_arbitration_ = true; + set_execution_state(ExecutionState::None); + } // Check for having hit 400ns (more or less) since BSY was inactive. - if(!lost_arbitration_ && time_in_state_ == int(int64_t(400) * int64_t(clock_rate_) / int64_t(1000000000))) { + if(time_since_change >= SCSI::BusSettleDelay) { // arbitration_in_progress_ = false; - if(bus_.get_state() & SCSI::Line::SelectTarget) { + if(new_state & SCSI::Line::SelectTarget) { lost_arbitration_ = true; set_execution_state(ExecutionState::None); } else { @@ -287,9 +282,11 @@ void NCR5380::run_for(Cycles cycles) { break; case ExecutionState::PerformingDMA: + if(time_since_change < SCSI::DeskewDelay) return; + // Signal a DMA request if the request line is active, i.e. meaningful data is // on the bus, and this device hasn't yet acknowledged it. - switch(bus_state & (SCSI::Line::Request | SCSI::Line::Acknowledge)) { + switch(new_state & (SCSI::Line::Request | SCSI::Line::Acknowledge)) { case 0: dma_request_ = false; break; @@ -311,15 +308,6 @@ void NCR5380::run_for(Cycles cycles) { } void NCR5380::set_execution_state(ExecutionState state) { - time_in_state_ = 0; state_ = state; if(state != ExecutionState::PerformingDMA) dma_operation_ = DMAOperation::Ready; - update_clocking_observer(); -} - -ClockingHint::Preference NCR5380::preferred_clocking() { - // Request real-time clocking if any sort of timed bus watching is ongoing, - // given that there's no knowledge in here as to what clocking other devices - // on the SCSI bus might be enjoying. - return (state_ < ExecutionState::WatchingBusy) ? ClockingHint::Preference::None : ClockingHint::Preference::RealTime; } diff --git a/Components/5380/ncr5380.hpp b/Components/5380/ncr5380.hpp index 2842dfdf1..8e7874d38 100644 --- a/Components/5380/ncr5380.hpp +++ b/Components/5380/ncr5380.hpp @@ -12,8 +12,6 @@ #include #include "../../Storage/MassStorage/SCSI/SCSI.hpp" -#include "../../ClockReceiver/ClockReceiver.hpp" -#include "../../ClockReceiver/ClockingHintSource.hpp" namespace NCR { @@ -22,7 +20,7 @@ namespace NCR5380 { /*! Models the NCR 5380, a SCSI interface chip. */ -class NCR5380 final: public ClockingHint::Source { +class NCR5380 final: public SCSI::Bus::Observer { public: NCR5380(SCSI::Bus &bus, int clock_rate); @@ -32,25 +30,6 @@ class NCR5380 final: public ClockingHint::Source { /*! Reads from @c address. */ uint8_t read(int address, bool dma_acknowledge = false); - /*! - As per its design manual: - - "The NCR 5380 is a clockless device. Delays such as bus - free delay, bus set delay and bus settle delay are - implemented using gate delays." - - Therefore this fictitious implementation of an NCR5380 needs - a way to track time even though the real one doesn't take in - a clock. This is provided by `run_for`. - - Nevertheless, the clocking doesn't need to be very precise. - Please provide a clock that is close to 200,000 Hz. - */ - void run_for(Cycles); - - /// As per ClockingHint::Source. - ClockingHint::Preference preferred_clocking() final; - private: SCSI::Bus &bus_; @@ -70,7 +49,7 @@ class NCR5380 final: public ClockingHint::Source { enum class ExecutionState { None, - + WaitingForBusy, WatchingBusy, PerformingDMA, } state_ = ExecutionState::None; @@ -80,13 +59,14 @@ class NCR5380 final: public ClockingHint::Source { TargetReceive, InitiatorReceive } dma_operation_ = DMAOperation::Ready; - int time_in_state_ = 0; bool lost_arbitration_ = false, arbitration_in_progress_ = false; void set_execution_state(ExecutionState state); SCSI::BusState target_output(); void update_control_output(); + + void scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) final; }; } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index d972ec8b3..e0053ccb1 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -140,7 +140,6 @@ template class ConcreteMachin // Also watch for changes in clocking requirement from the SCSI chip. if(model == Analyser::Static::Macintosh::Target::Model::MacPlus) { - scsi_.set_clocking_hint_observer(this); scsi_bus_.set_clocking_hint_observer(this); } @@ -505,7 +504,6 @@ template class ConcreteMachin private: void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override { - scsi_is_clocked_ = scsi_.preferred_clocking() != ClockingHint::Preference::None; scsi_bus_is_clocked_ = scsi_bus_.preferred_clocking() != ClockingHint::Preference::None; } @@ -599,7 +597,6 @@ template class ConcreteMachin // Update the SCSI if currently active. if(model == Analyser::Static::Macintosh::Target::Model::MacPlus) { - if(scsi_is_clocked_) scsi_.run_for(Cycles(duration.as_int())); if(scsi_bus_is_clocked_) scsi_bus_.run_for(duration); } } @@ -744,7 +741,6 @@ template class ConcreteMachin SCSI::Bus scsi_bus_; NCR::NCR5380::NCR5380 scsi_; SCSI::Target::Target hard_drive_; - bool scsi_is_clocked_ = false; bool scsi_bus_is_clocked_ = false; HalfCycles via_clock_; diff --git a/Storage/MassStorage/SCSI/SCSI.cpp b/Storage/MassStorage/SCSI/SCSI.cpp index fc750493e..446f14875 100644 --- a/Storage/MassStorage/SCSI/SCSI.cpp +++ b/Storage/MassStorage/SCSI/SCSI.cpp @@ -76,6 +76,13 @@ ClockingHint::Preference Bus::preferred_clocking() { return (dispatch_index_ < dispatch_times_.size()) ? ClockingHint::Preference::RealTime : ClockingHint::Preference::None; } +void Bus::update_observers() { + const auto time_elapsed = double(time_in_state_.as_int()) * cycles_to_time_; + for(auto &observer: observers_) { + observer->scsi_bus_did_change(this, state_, time_elapsed); + } +} + void Bus::run_for(HalfCycles time) { if(dispatch_index_ < dispatch_times_.size()) { time_in_state_ += time; @@ -87,9 +94,7 @@ void Bus::run_for(HalfCycles time) { } if(dispatch_index_ != old_index) { - for(auto &observer: observers_) { - observer->scsi_bus_did_change(this, state_, double(time_as_int) * cycles_to_time_); - } + update_observers(); } if(preferred_clocking() == ClockingHint::Preference::None) { diff --git a/Storage/MassStorage/SCSI/SCSI.hpp b/Storage/MassStorage/SCSI/SCSI.hpp index 9325eaf92..ddc8703d6 100644 --- a/Storage/MassStorage/SCSI/SCSI.hpp +++ b/Storage/MassStorage/SCSI/SCSI.hpp @@ -132,6 +132,11 @@ class Bus: public ClockingHint::Source { */ void run_for(HalfCycles); + /*! + Forces a `scsi_bus_did_change` propagation now. + */ + void update_observers(); + /// As per ClockingHint::Source. ClockingHint::Preference preferred_clocking() final;