mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-10 08:04:45 +00:00
Takes a first bash at implementing the new SCSI::Bus timing infrastructure.
This commit is contained in:
parent
310c722cc0
commit
2f8e31bc8b
@ -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_{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
|
Loading…
Reference in New Issue
Block a user