From 0e0c789b0261c9561a2291541bd58491a5dafd4a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Aug 2019 23:43:42 -0400 Subject: [PATCH] Starts attempting to introduce a direct access device. Without having access to the SCSI-1 standard, a lot of this is guesswork. --- Components/5380/DirectAccessDevice.cpp | 57 +++++++++++++++++++ Components/5380/DirectAccessDevice.hpp | 44 ++++++++++++++ Components/5380/SCSI.cpp | 22 ++++--- Components/5380/SCSI.hpp | 10 +++- Components/5380/ncr5380.cpp | 40 ++++++++----- Components/5380/ncr5380.hpp | 7 ++- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++ 7 files changed, 164 insertions(+), 22 deletions(-) create mode 100644 Components/5380/DirectAccessDevice.cpp create mode 100644 Components/5380/DirectAccessDevice.hpp diff --git a/Components/5380/DirectAccessDevice.cpp b/Components/5380/DirectAccessDevice.cpp new file mode 100644 index 000000000..6d776836e --- /dev/null +++ b/Components/5380/DirectAccessDevice.cpp @@ -0,0 +1,57 @@ +// +// DirectAccessDevice.cpp +// Clock Signal +// +// Created by Thomas Harte on 17/08/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#include "DirectAccessDevice.hpp" + +using namespace SCSI; + +DirectAccessDevice::DirectAccessDevice(Bus &bus, int scsi_id) : + bus_(bus), + scsi_id_mask_(BusState(1 << scsi_id)), + scsi_bus_device_id_(bus.add_device()) { + bus.add_observer(this); +} + +void DirectAccessDevice::scsi_bus_did_change(Bus *, BusState new_state) { + /* + "The target determines that it is selected when the SEL# signal + and its SCSI ID bit are active and the BSY# and I#/O signals + are false. It then asserts the signal within a selection abort + time." + */ + + switch(state_) { + case State::Inactive: + if( + (new_state & scsi_id_mask_) && + ((new_state & (Line::SelectTarget | Line::Busy | Line::Input)) == Line::SelectTarget) + ) { + state_ = State::Selected; + bus_state_ |= Line::Busy | Line::Request; + bus_.set_device_output(scsi_bus_device_id_, bus_state_); + } + break; + + case State::Selected: + switch(new_state & (Line::Request | Line::Acknowledge)) { + case Line::Request | Line::Acknowledge: + bus_state_ &= ~Line::Request; + printf("Got %02x maybe?\n", bus_state_ & 0xff); + break; + + case Line::Acknowledge: + case 0: + bus_state_ |= Line::Request; + break; + + default: break; + } + bus_.set_device_output(scsi_bus_device_id_, bus_state_); + break; + } +} diff --git a/Components/5380/DirectAccessDevice.hpp b/Components/5380/DirectAccessDevice.hpp new file mode 100644 index 000000000..e92d486ce --- /dev/null +++ b/Components/5380/DirectAccessDevice.hpp @@ -0,0 +1,44 @@ +// +// DirectAccessDevice.hpp +// Clock Signal +// +// Created by Thomas Harte on 17/08/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#ifndef DirectAccessDevice_hpp +#define DirectAccessDevice_hpp + +#include "SCSI.hpp" + +namespace SCSI { + +/*! + Models a SCSI direct access device, ordinarily some sort of + hard drive. +*/ +class DirectAccessDevice: public Bus::Observer { + public: + /*! + Instantiates a direct access device attached to @c bus, + with SCSI ID @c scsi_id — a number in the range 0 to 7. + */ + DirectAccessDevice(Bus &bus, int scsi_id); + + private: + void scsi_bus_did_change(Bus *, BusState new_state) final; + + Bus &bus_; + const BusState scsi_id_mask_; + const size_t scsi_bus_device_id_; + + enum class State { + Inactive, + Selected + } state_ = State::Inactive; + BusState bus_state_ = DefaultBusState; +}; + +} + +#endif /* DirectAccessDevice_hpp */ diff --git a/Components/5380/SCSI.cpp b/Components/5380/SCSI.cpp index 1ae837b05..a6ba9a0a0 100644 --- a/Components/5380/SCSI.cpp +++ b/Components/5380/SCSI.cpp @@ -17,19 +17,27 @@ size_t Bus::add_device() { } void Bus::set_device_output(size_t device, BusState output) { - printf("%08x output\n", output); + if(device_states_[device] == output) return; device_states_[device] = output; - state_is_valid_ = false; -} -BusState Bus::get_state() { - if(!state_is_valid_) return state_; - - state_is_valid_ = true; + const auto previous_state = state_; state_ = DefaultBusState; for(auto state: device_states_) { state_ |= state; } + if(state_ == previous_state) return; + printf("SCSI bus: %08x\n", state_); + + for(auto &observer: observers_) { + observer->scsi_bus_did_change(this, state_); + } +} + +BusState Bus::get_state() { return state_; } + +void Bus::add_observer(Observer *observer) { + observers_.push_back(observer); +} diff --git a/Components/5380/SCSI.hpp b/Components/5380/SCSI.hpp index 69d087a6e..9e36fd586 100644 --- a/Components/5380/SCSI.hpp +++ b/Components/5380/SCSI.hpp @@ -70,10 +70,18 @@ class Bus { */ BusState get_state(); + struct Observer { + virtual void scsi_bus_did_change(Bus *, BusState new_state) = 0; + }; + /*! + Adds an observer. + */ + void add_observer(Observer *); + private: std::vector device_states_; BusState state_ = DefaultBusState; - bool state_is_valid_ = false; + std::vector observers_; }; } diff --git a/Components/5380/ncr5380.cpp b/Components/5380/ncr5380.cpp index 82fcbe75e..6f40ee9fb 100644 --- a/Components/5380/ncr5380.cpp +++ b/Components/5380/ncr5380.cpp @@ -12,7 +12,9 @@ using namespace NCR::NCR5380; -NCR5380::NCR5380(int clock_rate) : clock_rate_(clock_rate) { +NCR5380::NCR5380(int clock_rate) : + device_(bus_, 6), + clock_rate_(clock_rate) { device_id_ = bus_.add_device(); } @@ -28,16 +30,17 @@ void NCR5380::write(int address, uint8_t value) { LOG("[SCSI 1] Initiator command register set: " << PADHEX(2) << int(value)); initiator_command_ = value; - SCSI::BusState mask = SCSI::DefaultBusState; - if(value & 0x80) mask |= Line::Reset; - test_mode_ = value & 0x40; + bus_output_ &= ~(Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention); + if(value & 0x80) bus_output_ |= Line::Reset; + if(value & 0x10) bus_output_ |= Line::Acknowledge; + if(value & 0x08) bus_output_ |= Line::Busy; + if(value & 0x04) bus_output_ |= Line::SelectTarget; + if(value & 0x02) bus_output_ |= Line::Attention; + /* 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; + + test_mode_ = value & 0x40; assert_data_bus_ = value & 0x01; - bus_output_ = (bus_output_ & ~(Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention)) | mask; } break; case 2: @@ -74,9 +77,14 @@ void NCR5380::write(int address, uint8_t value) { } break; - case 3: + case 3: { LOG("[SCSI 3] Set target command: " << PADHEX(2) << int(value)); - break; + bus_output_ &= ~(Line::Request | Line::Message | Line::Control | Line::Input); + if(value & 0x08) bus_output_ |= Line::Request; + if(value & 0x04) bus_output_ |= Line::Message; + if(value & 0x02) bus_output_ |= Line::Control; + if(value & 0x01) bus_output_ |= Line::Input; + } break; case 4: LOG("[SCSI 4] Set select enabled: " << PADHEX(2) << int(value)); @@ -133,9 +141,15 @@ uint8_t NCR5380::read(int address) { LOG("[SCSI 2] Get mode"); return mode_; - case 3: + case 3: { LOG("[SCSI 3] Get target command"); - return 0xff; + const auto bus_state = bus_.get_state(); + return + ((bus_state & SCSI::Line::Request) ? 0x08 : 0x00) | + ((bus_state & SCSI::Line::Message) ? 0x04 : 0x00) | + ((bus_state & SCSI::Line::Control) ? 0x02 : 0x00) | + ((bus_state & SCSI::Line::Input) ? 0x01 : 0x00); + } case 4: { LOG("[SCSI 4] Get current bus state"); diff --git a/Components/5380/ncr5380.hpp b/Components/5380/ncr5380.hpp index 57ce6510a..b9b551a7b 100644 --- a/Components/5380/ncr5380.hpp +++ b/Components/5380/ncr5380.hpp @@ -12,6 +12,7 @@ #include #include "SCSI.hpp" +#include "DirectAccessDevice.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" #include "../../ClockReceiver/ClockingHintSource.hpp" @@ -52,8 +53,12 @@ class NCR5380 final: public ClockingHint::Source { ClockingHint::Preference preferred_clocking() final; private: - const int clock_rate_; + // TEMPORARY. For development expediency, the 5380 owns its own + // SCSI bus and direct access device. These will be moved out. SCSI::Bus bus_; + SCSI::DirectAccessDevice device_; + + const int clock_rate_; size_t device_id_; SCSI::BusState bus_output_ = SCSI::DefaultBusState; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index e95f436e2..58c2dc5f7 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -619,6 +619,7 @@ 4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BB73EAA1B587A5100552FC2 /* MainMenu.xib */; }; 4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */; }; 4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; }; + 4BB8F5272308E5A50015C2A6 /* DirectAccessDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB8F5252308E5A50015C2A6 /* DirectAccessDevice.cpp */; }; 4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; }; 4BBB70A5202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; }; 4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; }; @@ -1407,6 +1408,8 @@ 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clock_SignalUITests.swift; sourceTree = ""; }; 4BB73EC31B587A5100552FC2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4BB73ECF1B587A6700552FC2 /* Clock Signal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Clock Signal.entitlements"; sourceTree = ""; }; + 4BB8F5252308E5A50015C2A6 /* DirectAccessDevice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DirectAccessDevice.cpp; sourceTree = ""; }; + 4BB8F5262308E5A50015C2A6 /* DirectAccessDevice.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DirectAccessDevice.hpp; sourceTree = ""; }; 4BBB709C2020109C002FE009 /* DynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DynamicMachine.hpp; sourceTree = ""; }; 4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMediaTarget.hpp; sourceTree = ""; }; 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMediaTarget.cpp; sourceTree = ""; }; @@ -3333,6 +3336,8 @@ 4BDACBEB22FFA5D20045EF7E /* ncr5380.hpp */, 4B89BCFA23024BB500EA0782 /* SCSI.cpp */, 4B89BCFB23024BB500EA0782 /* SCSI.hpp */, + 4BB8F5252308E5A50015C2A6 /* DirectAccessDevice.cpp */, + 4BB8F5262308E5A50015C2A6 /* DirectAccessDevice.hpp */, ); path = 5380; sourceTree = ""; @@ -4143,6 +4148,7 @@ 4BCE0060227D39AB000CA200 /* Video.cpp in Sources */, 4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, + 4BB8F5272308E5A50015C2A6 /* DirectAccessDevice.cpp in Sources */, 4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */, 4B7913CC1DFCD80E00175A82 /* Video.cpp in Sources */, 4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */,