1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-05 08:26:28 +00:00

Makes an attempt at getting the 5380 past arbitration.

Not entirely successful. Also gets a bit smarter with `final` on ClockingHint::Sources.
This commit is contained in:
Thomas Harte
2019-08-15 23:14:40 -04:00
parent ce1c96d68c
commit f668e4a54c
9 changed files with 146 additions and 32 deletions

View File

@@ -27,7 +27,7 @@ BusState Bus::get_state() {
state_is_valid_ = true;
state_ = DefaultBusState;
for(auto state: device_states_) {
state_ &= state;
state_ |= state;
}
return state_;

View File

@@ -16,7 +16,7 @@ namespace SCSI {
typedef int BusState;
static const BusState DefaultBusState = std::numeric_limits<BusState>::max();
static const BusState DefaultBusState = 0;
/*!
SCSI bus state is encoded entirely within an int.
@@ -33,20 +33,22 @@ enum Line: BusState {
/// Set if the SEL line is currently selecting a target.
/// Reset if it is selecting an initiator.
SelectTarget = 1 << 9,
/// Reset to indicate an attention condition. Set otherwise.
/// Set to indicate an attention condition. Reset otherwise.
Attention = 1 << 10,
/// Set if control is on the bus. Reset if data is on the bus.
Control = 1 << 11,
/// Reset if the bus is busy. Set otherwise.
/// Set if the bus is busy. Reset otherwise.
Busy = 1 << 12,
/// Reset if acknowledging a data transfer request. Set otherwise.
/// Set if acknowledging a data transfer request. Reset otherwise.
Acknowledge = 1 << 13,
/// Reset if a bus reset is being requested. Set otherwise.
/// Set if a bus reset is being requested. Reset otherwise.
Reset = 1 << 14,
/// Set if data is currently input. Reset if it is an output.
/// Set if data is currently an input to the initiator. Reset if it is an output.
Input = 1 << 15,
/// Set during the message phase. Reset otherwise.
MessagePhase = 1 << 16
Message = 1 << 16,
/// Set if requesting a data transfer. Reset otherwise.
Request = 1 << 17,
};

View File

@@ -12,7 +12,7 @@
using namespace NCR::NCR5380;
NCR5380::NCR5380() {
NCR5380::NCR5380(int clock_rate) : clock_rate_(clock_rate) {
device_id_ = bus_.add_device();
}
@@ -29,15 +29,15 @@ void NCR5380::write(int address, uint8_t value) {
initiator_command_ = value;
SCSI::BusState mask = SCSI::DefaultBusState;
if(value & 0x80) mask &= ~Line::Reset;
test_mode_ = !!(value & 0x40);
if(value & 0x80) mask |= Line::Reset;
test_mode_ = value & 0x40;
/* bit 5 = differential enable if this were a 5381 */
if(value & 0x10) mask &= ~Line::Acknowledge;
if(value & 0x08) mask &= ~Line::Busy;
if(value & 0x04) mask &= ~Line::SelectTarget;
if(value & 0x02) mask &= ~Line::Attention;
assert_data_bus_ = (value & 0x01);
bus_output_ = (bus_output_ | Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention) & mask;
if(value & 0x10) mask |= Line::Acknowledge;
if(value & 0x08) mask |= Line::Busy;
if(value & 0x04) mask |= Line::SelectTarget;
if(value & 0x02) mask |= Line::Attention;
assert_data_bus_ = value & 0x01;
bus_output_ = (bus_output_ & ~(Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention)) | mask;
} break;
case 2:
@@ -62,6 +62,16 @@ void NCR5380::write(int address, uint8_t value) {
can be examined to deter- mine if arbitration has been won. This delay must be
implemented in the controlling software driver.
*/
if(mode_ & 1) {
if(state_ == ExecutionState::None) {
set_execution_state(ExecutionState::WatchingBusy);
arbitration_in_progress_ = false;
lost_arbitration_ = false;
}
} else {
set_execution_state(ExecutionState::None);
}
break;
case 3:
@@ -109,7 +119,15 @@ uint8_t NCR5380::read(int address) {
case 1:
LOG("[SCSI 1] Initiator command register get");
return initiator_command_;
return
// Bits repeated as they were set.
(initiator_command_ & ~0x60) |
// Arbitration in progress.
(arbitration_in_progress_ ? 0x40 : 0x00) |
// Lost arbitration.
(lost_arbitration_ ? 0x20 : 0x00);
case 2:
LOG("[SCSI 2] Get mode");
@@ -119,13 +137,27 @@ uint8_t NCR5380::read(int address) {
LOG("[SCSI 3] Get target command");
return 0xff;
case 4:
case 4: {
LOG("[SCSI 4] Get current bus state");
return 0xff;
const auto bus_state = bus_.get_state();
return
((bus_state & SCSI::Line::Reset) ? 0x80 : 0x00) |
((bus_state & SCSI::Line::Busy) ? 0x40 : 0x00) |
((bus_state & SCSI::Line::Request) ? 0x20 : 0x00) |
((bus_state & SCSI::Line::Message) ? 0x10 : 0x00) |
((bus_state & SCSI::Line::Control) ? 0x08 : 0x00) |
((bus_state & SCSI::Line::Input) ? 0x04 : 0x00) |
((bus_state & SCSI::Line::SelectTarget) ? 0x02 : 0x00) |
((bus_state & SCSI::Line::Parity) ? 0x01 : 0x00);
}
case 5:
case 5: {
LOG("[SCSI 5] Get bus and status");
return 0x03;
const auto bus_state = bus_.get_state();
return
((bus_state & SCSI::Line::Attention) ? 0x02 : 0x00) |
((bus_state & SCSI::Line::Acknowledge) ? 0x01 : 0x00);
}
case 6:
LOG("[SCSI 6] Get input data");
@@ -137,3 +169,50 @@ uint8_t NCR5380::read(int address) {
}
return 0;
}
void NCR5380::run_for(Cycles cycles) {
if(state_ == ExecutionState::None) return;
++time_in_state_;
switch(state_) {
default: break;
case ExecutionState::WatchingBusy:
/*
Arbitration is accomplished using a bus-free filter to continuously monitor BSY.
If BSY remains inactive for at least 400 nsec then the SCSI bus is considered free
and arbitration may begin. Arbitration will begin if the bus is free, SEL is inactive
and the ARBITRATION bit (port 2, bit 0) is active. Once arbitration has begun
(BSY asserted)...
*/
if(bus_.get_state() & SCSI::Line::Busy) {
lost_arbitration_ = true;
set_execution_state(ExecutionState::None);
}
// Check for having hit the 400ns state.
if(time_in_state_ == 400 * clock_rate_ / 1000000000) {
arbitration_in_progress_ = false;
if(bus_.get_state() & SCSI::Line::SelectTarget) {
lost_arbitration_ = true;
set_execution_state(ExecutionState::None);
} else {
bus_output_ |= SCSI::Line::Busy;
set_execution_state(ExecutionState::None);
}
}
break;
}
}
void NCR5380::set_execution_state(ExecutionState state) {
time_in_state_ = 0;
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::None) ? ClockingHint::Preference::RealTime : ClockingHint::Preference::None;
}

View File

@@ -13,6 +13,7 @@
#include "SCSI.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../ClockReceiver/ClockingHintSource.hpp"
namespace NCR {
@@ -21,9 +22,9 @@ namespace NCR5380 {
/*!
Models the NCR 5380, a SCSI interface chip.
*/
class NCR5380 {
class NCR5380 final: public ClockingHint::Source {
public:
NCR5380();
NCR5380(int clock_rate);
/*! Writes @c value to @c address. */
void write(int address, uint8_t value);
@@ -47,7 +48,11 @@ class NCR5380 {
*/
void run_for(Cycles);
/// As per ClockingHint::Source.
ClockingHint::Preference preferred_clocking() final;
private:
const int clock_rate_;
SCSI::Bus bus_;
size_t device_id_;
@@ -57,6 +62,16 @@ class NCR5380 {
uint8_t data_bus_ = 0xff;
bool test_mode_ = false;
bool assert_data_bus_ = false;
enum class ExecutionState {
None,
WatchingBusy,
} state_ = ExecutionState::None;
int time_in_state_ = 0;
bool lost_arbitration_ = false, arbitration_in_progress_ = false;
void set_execution_state(ExecutionState state);
};
}