mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
Starts thinking out the mechanics of emulating a SCSI-1 bus.
This commit is contained in:
parent
0f67e490e8
commit
ce1c96d68c
34
Components/5380/SCSI.cpp
Normal file
34
Components/5380/SCSI.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// SCSI.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 12/08/2019.
|
||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "SCSI.hpp"
|
||||
|
||||
using namespace SCSI;
|
||||
|
||||
size_t Bus::add_device() {
|
||||
const auto slot = device_states_.size();
|
||||
device_states_.push_back(DefaultBusState);
|
||||
return slot;
|
||||
}
|
||||
|
||||
void Bus::set_device_output(size_t device, BusState output) {
|
||||
device_states_[device] = output;
|
||||
state_is_valid_ = false;
|
||||
}
|
||||
|
||||
BusState Bus::get_state() {
|
||||
if(!state_is_valid_) return state_;
|
||||
|
||||
state_is_valid_ = true;
|
||||
state_ = DefaultBusState;
|
||||
for(auto state: device_states_) {
|
||||
state_ &= state;
|
||||
}
|
||||
|
||||
return state_;
|
||||
}
|
79
Components/5380/SCSI.hpp
Normal file
79
Components/5380/SCSI.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// SCSI.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 12/08/2019.
|
||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SCSI_hpp
|
||||
#define SCSI_hpp
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace SCSI {
|
||||
|
||||
typedef int BusState;
|
||||
|
||||
static const BusState DefaultBusState = std::numeric_limits<BusState>::max();
|
||||
|
||||
/*!
|
||||
SCSI bus state is encoded entirely within an int.
|
||||
Bits correlate mostly but not exactly to the real SCSI bus.
|
||||
|
||||
TODO: validate levels below. The bus uses open collector logic,
|
||||
so active low needs to be respected.
|
||||
*/
|
||||
enum Line: BusState {
|
||||
/// Provides the value currently on the data lines.
|
||||
Data = 0xff,
|
||||
/// Parity of the data lines.
|
||||
Parity = 1 << 8,
|
||||
/// 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.
|
||||
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.
|
||||
Busy = 1 << 12,
|
||||
/// Reset if acknowledging a data transfer request. Set otherwise.
|
||||
Acknowledge = 1 << 13,
|
||||
/// Reset if a bus reset is being requested. Set otherwise.
|
||||
Reset = 1 << 14,
|
||||
/// Set if data is currently input. Reset if it is an output.
|
||||
Input = 1 << 15,
|
||||
/// Set during the message phase. Reset otherwise.
|
||||
MessagePhase = 1 << 16
|
||||
};
|
||||
|
||||
|
||||
class Bus {
|
||||
public:
|
||||
/*!
|
||||
Adds a device to the bus, returning the index it should use
|
||||
to refer to itself in subsequent calls to set_device_output.
|
||||
*/
|
||||
size_t add_device();
|
||||
|
||||
/*!
|
||||
Sets the current output for @c device.
|
||||
*/
|
||||
void set_device_output(size_t device, BusState output);
|
||||
|
||||
/*!
|
||||
@returns the current state of the bus.
|
||||
*/
|
||||
BusState get_state();
|
||||
|
||||
private:
|
||||
std::vector<BusState> device_states_;
|
||||
BusState state_ = DefaultBusState;
|
||||
bool state_is_valid_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SCSI_hpp */
|
@ -12,19 +12,56 @@
|
||||
|
||||
using namespace NCR::NCR5380;
|
||||
|
||||
NCR5380::NCR5380() {
|
||||
device_id_ = bus_.add_device();
|
||||
}
|
||||
|
||||
void NCR5380::write(int address, uint8_t value) {
|
||||
using Line = SCSI::Line;
|
||||
switch(address & 7) {
|
||||
case 0:
|
||||
LOG("[SCSI 0] Set current SCSI bus state to " << PADHEX(2) << int(value));
|
||||
data_bus_ = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 1: {
|
||||
LOG("[SCSI 1] Initiator command register set: " << PADHEX(2) << int(value));
|
||||
break;
|
||||
initiator_command_ = value;
|
||||
|
||||
SCSI::BusState mask = SCSI::DefaultBusState;
|
||||
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;
|
||||
} break;
|
||||
|
||||
case 2:
|
||||
LOG("[SCSI 2] Set mode: " << PADHEX(2) << int(value));
|
||||
mode_ = value;
|
||||
|
||||
// bit 7: 1 = use block mode DMA mode (if DMA mode is also enabled)
|
||||
// bit 6: 1 = be a SCSI target; 0 = be an initiator
|
||||
// bit 5: 1 = check parity
|
||||
// bit 4: 1 = generate an interrupt if parity checking is enabled and an error is found
|
||||
// bit 3: 1 = generate an interrupt when an EOP is received from the DMA controller
|
||||
// bit 2: 1 = generate an interrupt and reset low 6 bits of register 1 if an unexpected loss of Line::Busy occurs
|
||||
// bit 1: 1 = use DMA mode
|
||||
// bit 0: 1 = begin arbitration mode (device ID should be in register 0)
|
||||
|
||||
/*
|
||||
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), an arbitration delay of 2.2 /Lsec must elapse before the data bus
|
||||
can be examined to deter- mine if arbitration has been won. This delay must be
|
||||
implemented in the controlling software driver.
|
||||
*/
|
||||
break;
|
||||
|
||||
case 3:
|
||||
@ -47,17 +84,32 @@ void NCR5380::write(int address, uint8_t value) {
|
||||
LOG("[SCSI 7] Start DMA initiator receive: " << PADHEX(2) << int(value));
|
||||
break;
|
||||
}
|
||||
|
||||
// Data is output only if the data bus is asserted.
|
||||
if(assert_data_bus_) {
|
||||
bus_output_ &= data_bus_;
|
||||
} else {
|
||||
bus_output_ |= SCSI::Line::Data;
|
||||
}
|
||||
|
||||
// In test mode, still nothing is output. Otherwise throw out
|
||||
// the current value of bus_output_.
|
||||
if(test_mode_) {
|
||||
bus_.set_device_output(device_id_, SCSI::DefaultBusState);
|
||||
} else {
|
||||
bus_.set_device_output(device_id_, bus_output_);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t NCR5380::read(int address) {
|
||||
switch(address & 7) {
|
||||
case 0:
|
||||
LOG("[SCSI 0] Get current SCSI bus state");
|
||||
return 0xff;
|
||||
return uint8_t(bus_.get_state());
|
||||
|
||||
case 1:
|
||||
LOG("[SCSI 1] Initiator command register get");
|
||||
return 0xff;
|
||||
return initiator_command_;
|
||||
|
||||
case 2:
|
||||
LOG("[SCSI 2] Get mode");
|
||||
|
@ -11,6 +11,10 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "SCSI.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
|
||||
namespace NCR {
|
||||
namespace NCR5380 {
|
||||
|
||||
@ -19,14 +23,40 @@ namespace NCR5380 {
|
||||
*/
|
||||
class NCR5380 {
|
||||
public:
|
||||
NCR5380();
|
||||
|
||||
/*! Writes @c value to @c address. */
|
||||
void write(int address, uint8_t value);
|
||||
|
||||
/*! Reads from @c address. */
|
||||
uint8_t read(int address);
|
||||
|
||||
/*!
|
||||
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);
|
||||
|
||||
private:
|
||||
SCSI::Bus bus_;
|
||||
size_t device_id_;
|
||||
|
||||
SCSI::BusState bus_output_ = SCSI::DefaultBusState;
|
||||
uint8_t mode_ = 0xff;
|
||||
uint8_t initiator_command_ = 0xff;
|
||||
uint8_t data_bus_ = 0xff;
|
||||
bool test_mode_ = false;
|
||||
bool assert_data_bus_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -300,6 +300,8 @@
|
||||
4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894516201967B4007DE474 /* StaticAnalyser.cpp */; };
|
||||
4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; };
|
||||
4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; };
|
||||
4B89BCFC23024BB500EA0782 /* SCSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B89BCFA23024BB500EA0782 /* SCSI.cpp */; };
|
||||
4B89BCFD23024BB500EA0782 /* SCSI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B89BCFA23024BB500EA0782 /* SCSI.cpp */; };
|
||||
4B8FE21B1DA19D5F0090D3CE /* Atari2600Options.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */; };
|
||||
4B8FE21C1DA19D5F0090D3CE /* MachineDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */; };
|
||||
4B8FE21D1DA19D5F0090D3CE /* QuickLoadCompositeOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B8FE2171DA19D5F0090D3CE /* QuickLoadCompositeOptions.xib */; };
|
||||
@ -1057,6 +1059,8 @@
|
||||
4B894516201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
4B894517201967B4007DE474 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = "<group>"; };
|
||||
4B89BCFA23024BB500EA0782 /* SCSI.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SCSI.cpp; sourceTree = "<group>"; };
|
||||
4B89BCFB23024BB500EA0782 /* SCSI.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SCSI.hpp; sourceTree = "<group>"; };
|
||||
4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DeferredQueue.hpp; sourceTree = "<group>"; };
|
||||
4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = "<group>"; };
|
||||
4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyboardMachine.hpp; sourceTree = "<group>"; };
|
||||
@ -3327,6 +3331,8 @@
|
||||
children = (
|
||||
4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */,
|
||||
4BDACBEB22FFA5D20045EF7E /* ncr5380.hpp */,
|
||||
4B89BCFA23024BB500EA0782 /* SCSI.cpp */,
|
||||
4B89BCFB23024BB500EA0782 /* SCSI.hpp */,
|
||||
);
|
||||
path = 5380;
|
||||
sourceTree = "<group>";
|
||||
@ -4037,6 +4043,7 @@
|
||||
4B8318B822D3E566006DB630 /* IWM.cpp in Sources */,
|
||||
4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */,
|
||||
4B894535201967B4007DE474 /* AddressMapper.cpp in Sources */,
|
||||
4B89BCFD23024BB500EA0782 /* SCSI.cpp in Sources */,
|
||||
4B055AD41FAE9B0B0060FFFF /* Oric.cpp in Sources */,
|
||||
4B055A921FAE85B50060FFFF /* PRG.cpp in Sources */,
|
||||
4B055AAF1FAE85FD0060FFFF /* UnformattedTrack.cpp in Sources */,
|
||||
@ -4194,6 +4201,7 @@
|
||||
4B894528201967B4007DE474 /* Disk.cpp in Sources */,
|
||||
4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */,
|
||||
4B89453A201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4B89BCFC23024BB500EA0782 /* SCSI.cpp in Sources */,
|
||||
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */,
|
||||
4BDACBEC22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
|
||||
4B54C0C21F8D91CD0050900F /* Keyboard.cpp in Sources */,
|
||||
|
Loading…
Reference in New Issue
Block a user