1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Removes clock for NCR 5380.

It doesn't have one in real life, and now can live off the time counting that occurs on the SCSI bus.
This commit is contained in:
Thomas Harte 2019-09-18 20:17:47 -04:00
parent 3002ac8a4a
commit 962275c22a
5 changed files with 35 additions and 61 deletions

View File

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

View File

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

View File

@ -140,7 +140,6 @@ template <Analyser::Static::Macintosh::Target::Model model> 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 <Analyser::Static::Macintosh::Target::Model model> 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 <Analyser::Static::Macintosh::Target::Model model> 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 <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
SCSI::Bus scsi_bus_;
NCR::NCR5380::NCR5380 scsi_;
SCSI::Target::Target<SCSI::DirectAccessDevice> hard_drive_;
bool scsi_is_clocked_ = false;
bool scsi_bus_is_clocked_ = false;
HalfCycles via_clock_;

View File

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

View File

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