1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-03 11:30:02 +00:00

Takes a first bash at implementing the new SCSI::Bus timing infrastructure.

This commit is contained in:
Thomas Harte 2019-09-02 13:00:01 -04:00
parent 310c722cc0
commit 2f8e31bc8b
3 changed files with 68 additions and 7 deletions

View File

@ -75,6 +75,7 @@ template <Analyser::Static::Macintosh::Target::Model model> 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_{

View File

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

View File

@ -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<BusState> device_states_;
BusState state_ = DefaultBusState;
std::vector<Observer *> observers_;