From 6bc7703e635e0e4dc9a58acb3b5e5c0161b06e9b Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Tue, 24 Mar 2020 01:39:31 +0100 Subject: [PATCH] Cleanup, modifications while trying to get diagnostics to pass. --- 10.02_devices/2_src/massbus_rp.cpp | 64 +++++++--- 10.02_devices/2_src/massbus_rp.hpp | 32 ++--- 10.02_devices/2_src/rh11.cpp | 190 ++++++++++++++++++++--------- 10.02_devices/2_src/rh11.hpp | 18 +++ 10.02_devices/2_src/rp_drive.cpp | 22 ++-- 10.02_devices/2_src/rp_drive.hpp | 5 +- 6 files changed, 228 insertions(+), 103 deletions(-) diff --git a/10.02_devices/2_src/massbus_rp.cpp b/10.02_devices/2_src/massbus_rp.cpp index 03468c6..7f02e3f 100644 --- a/10.02_devices/2_src/massbus_rp.cpp +++ b/10.02_devices/2_src/massbus_rp.cpp @@ -122,7 +122,7 @@ massbus_rp_c::WriteRegister( // while the drive is busy is invalid. INFO("Register modification while drive busy."); _rmr = true; - UpdateStatus(false); + UpdateStatus(false, false); return; } @@ -160,11 +160,14 @@ massbus_rp_c::WriteRegister( break; case Registers::Error1: - // Clear any bits in the Error 1 Register specified in the - // written value: - _error1 &= ~(value & 0xff); - _controller->WriteRegister(reg, _error1); + // Pg. 2-20 of EK-RP056-MM-01: + // "The register can also be written by the controller for diagnostic purposes. + // Setting any bit in this register sets the composite error bit in the status register." + // + // Based on diagnostic (ZRJGE0) behavior, writing ANY value here forces an error. + // INFO("Error 1 Reg write o%o, value is now o%o", value, _error1); + UpdateStatus(false, true); // Force composite error. break; default: @@ -188,7 +191,7 @@ void massbus_rp_c::DoCommand( INFO("RP function 0%o, unit %o", function, _selectedUnit); - if (!SelectedDrive()->IsAvailable()) + if (!SelectedDrive()->IsConnected()) { // Early return for disconnected drives; // set NED and ERR bits @@ -196,7 +199,7 @@ void massbus_rp_c::DoCommand( _ata = true; _ned = true; // TODO: should be done at RH11 level! SelectedDrive()->ClearVolumeValid(); - UpdateStatus(true); + UpdateStatus(true, false); return; } @@ -213,7 +216,7 @@ void massbus_rp_c::DoCommand( { case FunctionCode::Nop: // Nothing. - UpdateStatus(true); + UpdateStatus(true, false); break; case FunctionCode::ReadInPreset: @@ -230,7 +233,7 @@ void massbus_rp_c::DoCommand( _offset = 0; UpdateDesiredSectorTrack(); UpdateOffset(); - UpdateStatus(false); /* do not interrupt */ + UpdateStatus(false, false); /* do not interrupt */ break; case FunctionCode::ReadData: @@ -249,7 +252,7 @@ void massbus_rp_c::DoCommand( } // Clear READY - UpdateStatus(false); + UpdateStatus(false, false); pthread_mutex_lock(&_workerMutex); @@ -282,9 +285,28 @@ massbus_rp_c::ReadRegister( uint32_t reg) { INFO("*** RP reg read: unit %d register 0%o", unit, reg); - - FATAL("Unimplemented register read %o", reg); + + switch(static_cast(reg)) + { + + case Registers::DriveType: + return SelectedDrive()->GetDriveType() | 020000; // Moving head (MOVE TO CONSTANT) + break; + + case Registers::SerialNo: + return SelectedDrive()->GetSerialNumber(); + break; + + case Registers::AttentionSummary: + INFO("attn: o%o", _attnSummary); + return _attnSummary; + break; + default: + FATAL("Unimplemented register read %o", reg); + break; + + } return 0; } @@ -293,7 +315,8 @@ massbus_rp_c::ReadRegister( // void massbus_rp_c::UpdateStatus( - bool complete) + bool complete, + bool diagForceError) { _error1 = @@ -307,6 +330,11 @@ massbus_rp_c::UpdateStatus( _err = true; _ata = true; } + else if (diagForceError) + { + _err = false; + _ata = true; + } else { _err = false; @@ -318,7 +346,7 @@ massbus_rp_c::UpdateStatus( (0400) | // Drive preset -- always set for a single-controller disk (SelectedDrive()->GetReadLastSector() ? 02000 : 0) | // Last sector read (SelectedDrive()->IsWriteLocked() ? 04000 : 0) | // Write lock - (SelectedDrive()->IsAvailable() ? 010000 : 0) | // Medium online + (SelectedDrive()->IsPackLoaded() ? 010000 : 0) | // Medium online (SelectedDrive()->IsPositioningInProgress() ? 020000 : 0) | // PIP (_err ? 040000 : 0) | // Composite error (_ata ? 0100000 : 0); @@ -329,7 +357,7 @@ massbus_rp_c::UpdateStatus( // Update the Attention Summary register if this disk is online: - if (_ata && SelectedDrive()->IsAvailable()) + if (_ata && SelectedDrive()->IsConnected()) { _attnSummary |= (0x1 << _selectedUnit); // TODO: these only get set, and are latched until // manually cleared? @@ -339,7 +367,7 @@ massbus_rp_c::UpdateStatus( _controller->WriteRegister(static_cast(Registers::AttentionSummary), _attnSummary); // Inform controller of status update. - _controller->BusStatus(complete, SelectedDrive()->IsDriveReady(), _ata, _err, SelectedDrive()->IsAvailable(), _ned); + _controller->BusStatus(complete, SelectedDrive()->IsDriveReady(), _ata, _err, SelectedDrive()->IsConnected(), _ned); } void @@ -382,7 +410,7 @@ massbus_rp_c::Reset() _attnSummary = 0; _error1 = 0; _rmr = false; - UpdateStatus(false); + UpdateStatus(false, false); _desiredSector = 0; _desiredTrack = 0; @@ -558,7 +586,7 @@ massbus_rp_c::Worker() _workerState = WorkerState::Idle; SelectedDrive()->SetDriveReady(); UpdateCurrentCylinder(); - UpdateStatus(true); + UpdateStatus(true, false); break; } diff --git a/10.02_devices/2_src/massbus_rp.hpp b/10.02_devices/2_src/massbus_rp.hpp index 53c16db..4eaeb85 100644 --- a/10.02_devices/2_src/massbus_rp.hpp +++ b/10.02_devices/2_src/massbus_rp.hpp @@ -132,7 +132,7 @@ private: void on_power_changed(void) override; void on_init_changed(void) override; - void UpdateStatus(bool completion); + void UpdateStatus(bool completion, bool diagForceError); void UpdateDesiredSectorTrack(); void UpdateDesiredCylinder(); void UpdateOffset(); @@ -147,21 +147,21 @@ private: { // Name DATI DATO { "INV", false, false, 0, 0 }, // 0, not used - { "CS1", false, true, 0, 0401477 }, // Status - { "ER1", false, true, 0, 0177777 }, // Error #1 - writable by diagnostics - { "MR" , false, true, 0, 0177777 }, // Maintenance - { "ATN", false, true, 0, 0377 }, // Attention summary - { "DA" , false, true, 0, 0017437 }, // Desired Sector/Track - { "LA" , false, false, 0, 0 }, // Look Ahead - { "DT" , false, false, 0, 0 }, // Drive Type - { "SN" , false, false, 0, 0 }, // Serial Number - { "OFF", false, false, 0, 0177777 }, // Offset - { "DCY", false, true, 0, 0001777 }, // Desired Cylinder - { "CCY", false, false, 0, 0 }, // Current Cylinder - { "ER2", false, false, 0, 0 }, // Error #2 - { "ER3", false, false, 0, 0 }, // Error #3 - { "EPO", false, false, 0, 0 }, // ECC Position - { "EPA", false, false, 0, 0 }, // ECC Pattern + { "CS1", false, true, 0, 0041577 }, // 1, Status + { "ER1", false, true, 0, 0177777 }, // 2, Error #1 - writable by diagnostics + { "MR" , false, true, 0, 0177777 }, // 3, Maintenance + { "ATN", true, true, 0, 0377 }, // 4, Attention summary + { "DA" , false, true, 0, 0017437 }, // 5, Desired Sector/Track + { "DT" , true, true, 0, 0 }, // 6, Drive Type + { "LA" , false, false, 0, 0 }, // 7, Look Ahead + { "ER2", false, false, 0, 0 }, // 10, Error #2 + { "OFF", false, false, 0, 0177777 }, // 11, Offset + { "DCY", false, true, 0, 0001777 }, // 12, Desired Cylinder + { "CCY", false, false, 0, 0 }, // 13, Current Cylinder + { "SN" , true, true, 0, 0 }, // 14, Serial Number + { "ER3", false, false, 0, 0 }, // 15, Error #3 + { "EPO", false, false, 0, 0 }, // 16, ECC Position + { "EPA", false, false, 0, 0 }, // 17, ECC Pattern }; // Unit selected by last command register write diff --git a/10.02_devices/2_src/rh11.cpp b/10.02_devices/2_src/rh11.cpp index 890d4ea..0322651 100755 --- a/10.02_devices/2_src/rh11.cpp +++ b/10.02_devices/2_src/rh11.cpp @@ -34,11 +34,11 @@ int32_t _unibusToMassbusRegisterMap[] = -1, // 776722 03, // 776724 06, // 776726 - 010, // 776730 + 014, // 776730 011, // 776732 012, // 776734 013, // 776736 - 014, // 776740 + 010, // 776740 015, // 776742 016, // etc. 017, @@ -95,7 +95,23 @@ int32_t _massbusToUnibusRegisterMap[] = rh11_c::rh11_c() : storagecontroller_c(), _massbus(nullptr), + _attention(false), + _error(false), + _mcbParityError(false), + _avail(false), + _ready(false), _interruptEnable(false), + _function(0), + _go(false), + _parityError(false), + _ned(false), + _nxm(false), + _progError(false), + _outputReady(false), + _inputReady(false), + _controllerClear(false), + _busAddressIncrementProhibit(false), + _parityTest(false), _busAddress(0), _unit(0) { @@ -224,45 +240,24 @@ rh11_c::BusStatus( { INFO("Massbus status update attn %d, error %d, ned %d", attention, error, ned); - uint16_t currentStatus = get_register_dato_value( - RH_reg[RHCS1]); - - INFO("RHCS1 is currently o%o", currentStatus); - - currentStatus &= ~(0144300); // clear status bits, IE, and GO bit : move to constant - currentStatus |= - (attention ? 0100000 : 0) | // check when this actually gets set? - (error ? 0040000 : 0) | - (avail ? 0004000 : 0) | // drive available - (ready ? 0000200 : 0); // controller ready bit + _attention = attention; + _error = error; + _avail = avail; + _ready = ready; if (completion) { // clear the GO bit from the CS word if the drive is finished. - currentStatus &= ~(01); + _go = false; } - set_register_dati_value( - RH_reg[RHCS1], - currentStatus, - "BusStatus"); - - INFO("RHCS1 is now o%o", currentStatus); - - currentStatus = get_register_dato_value(RH_reg[RHCS2]); + UpdateCS1(); - INFO("RHCS2 is currently o%o", currentStatus); - - currentStatus &= ~(010000); - currentStatus |= - (ned ? 010000 : 0); // non-existent drive - - set_register_dati_value( - RH_reg[RHCS2], - currentStatus, - "BusStatus"); - - INFO("RHCS2 is now o%o", currentStatus); + if (!_controllerClear) + { + _ned = ned; + UpdateCS2(); + } if ((attention || ready) && completion) { @@ -270,6 +265,49 @@ rh11_c::BusStatus( } } +void +rh11_c::UpdateCS1() +{ + uint16_t newStatus = + (_attention ? 0100000 : 0) | + (_error ? 0040000 : 0) | + (_mcbParityError ? 0020000 : 0) | + (_avail ? 0004000 : 0) | // drive available + ((_busAddress & 0600000) >> 8) | + (_ready ? 0000200 : 0) | // controller ready bit + (_interruptEnable ? 0000100 : 0) | + (_function << 1) | + (_go ? 0000001 : 0); + + INFO("RHCS1 is now o%o", newStatus); + + set_register_dati_value( + RH_reg[RHCS1], + newStatus, + "UpdateCS1"); +} + +void rh11_c::UpdateCS2() +{ + uint16_t newStatus = + (_parityError ? 0020000 : 0) | + (_ned ? 0010000 : 0) | + (_nxm ? 0004000 : 0) | + (_progError ? 0002000 : 0) | + (_outputReady ? 0000200 : 0) | + (_inputReady ? 0000100 : 0) | + (_parityTest ? 0000020 : 0) | + (_busAddressIncrementProhibit ? 0000010 : 0) | + (_unit); + + INFO("RHCS2 is now o%o", newStatus); + + set_register_dati_value( + RH_reg[RHCS2], + newStatus, + "UpdateCS2"); +} + void rh11_c::WriteRegister( uint32_t reg, @@ -525,16 +563,35 @@ void rh11_c::on_after_register_access( { if (UNIBUS_CONTROL_DATO == unibus_control) { - // IE bit - _interruptEnable = ((value & 0x40) != 0); - - // Extended bus address bits - _busAddress = (_busAddress & 0xffff) | ((value & 0x300) << 8); - - INFO("RHCS1: IE %d BA 0%o", _interruptEnable, _busAddress); + INFO("RHCS1: DATO o%o", value); + _error = !!(value & 0040000); + _busAddress = (_busAddress & 0177777) | ((value & 01400) << 8); + _interruptEnable = !!(value & 0100); + _function = (value & 076) >> 1; + _go = (value & 01); + // Bit 14 (Transfer Error) is writeable; the function of this is not documented + // In the handbook or engineering manuals. The schematics are eluding me as well. + // 2.11bsd writes this bit, as do diagnostics (and they expect it to be read back..) + // SIMH suggests this is used to clear error bits, so we'll do that here. + if (_error) + { + _error = false; + _parityError = false; + _mcbParityError = false; + _ned = false; + _nxm = false; + } + + INFO("RHCS1: IE %d BA o%o func o%o go %d", _interruptEnable, _busAddress, _function, _go); // Let the massbus device take a crack at the shared bits - _massbus->WriteRegister(_unit, RHCS1, value & 0x3f); + _massbus->WriteRegister(_unit, RHCS1, value & 077); + + // TODO: Per RH11-AB_OptionDescr.pdf (p 4-17): + // "The program can force an interrupt by loading bits D06 H (IE) and D07 H (RDY) in the CS1 + // register which direct sets the INTR flip-flop." + // + UpdateCS1(); } } break; @@ -543,25 +600,33 @@ void rh11_c::on_after_register_access( { if (UNIBUS_CONTROL_DATO == unibus_control) { - _unit = (value & 0x7); - _busAddressIncrementProhibit = !!(value & 0x8); - _parityTest = !!(value & 0x10); - + _unit = (value & 07); + _busAddressIncrementProhibit = !!(value & 010); + _parityTest = !!(value & 020); + _controllerClear = !!(value & 040); + _parityError = !!(value & 0020000); + + INFO("RHCS2 write: o%o", value); + INFO("RHCS2: perror %d, unit %d inc %d ptest %d clear %d", + _parityError, _unit, _busAddressIncrementProhibit, _parityTest, _controllerClear); + // TODO: handle System Register Clear (bit 5) - if (value & 040) // controller clear + if (_controllerClear) { INFO("Controller Clear"); - // clear error bits - value &= ~(010040); - set_register_dati_value( - RH_reg[RHCS2], - value, - "Reset"); - _interruptEnable = false; - _massbus->Reset(); + + _massbus->Reset(); + + // clear error and the clear bits + _ned = false; + _nxm = false; + + INFO("RHCS2: is now o%o", value); + _controllerClear = false; } - INFO("RHCS2: unit %d", _unit); + + UpdateCS2(); } } break; @@ -571,8 +636,12 @@ void rh11_c::on_after_register_access( if (UNIBUS_CONTROL_DATO == unibus_control) { INFO("RHWC: o%o", value); - } + set_register_dati_value( + RH_reg[RHWC], + value, + "HACK"); + } } break; @@ -582,6 +651,11 @@ void rh11_c::on_after_register_access( { _busAddress = (_busAddress & 0x30000) | value; INFO("RHBA: o%o", _busAddress); + + set_register_dati_value( + RH_reg[RHWC], + value, + "HACK"); } } break; @@ -593,6 +667,7 @@ void rh11_c::on_after_register_access( if (UNIBUS_CONTROL_DATO == unibus_control) { _massbus->WriteRegister(_unit, _unibusToMassbusRegisterMap[device_reg->index], value); + // Massbus is responsible for writing back the appropriate dati value. } else { @@ -622,6 +697,7 @@ void rh11_c::Interrupt(void) // IE is cleared after the interrupt is raised // Actual bit is cleared in BusStatus, this should be fixed. _interruptEnable = false; + UpdateCS1(); } } diff --git a/10.02_devices/2_src/rh11.hpp b/10.02_devices/2_src/rh11.hpp index d387a67..d5530a1 100755 --- a/10.02_devices/2_src/rh11.hpp +++ b/10.02_devices/2_src/rh11.hpp @@ -51,18 +51,36 @@ private: bool DMAWrite(uint32_t address, size_t lengthInWords, uint16_t* buffer); uint16_t* DMARead(uint32_t address, size_t lengthInWords); + void UpdateCS1(); + void UpdateCS2(); + private: std::unique_ptr _massbus; // Control & Status reg 1 bits + volatile bool _attention; + volatile bool _error; + volatile bool _mcbParityError; + volatile bool _avail; + volatile bool _ready; volatile bool _interruptEnable; + volatile uint16_t _function; + volatile bool _go; + volatile uint32_t _busAddress; // Control & Status reg 2 bits + volatile bool _parityError; + volatile bool _ned; + volatile bool _nxm; + volatile bool _progError; + volatile bool _outputReady; + volatile bool _inputReady; volatile uint16_t _unit; volatile bool _busAddressIncrementProhibit; volatile bool _parityTest; + volatile bool _controllerClear; public: diff --git a/10.02_devices/2_src/rp_drive.cpp b/10.02_devices/2_src/rp_drive.cpp index 3c76499..b7d8504 100644 --- a/10.02_devices/2_src/rp_drive.cpp +++ b/10.02_devices/2_src/rp_drive.cpp @@ -79,12 +79,12 @@ rp_drive_c::GetSectorSize() } // -// IsAvailable(): -// Indicates whether this drive is available (i.e. has an image -// assigned to it and can thus be used by the controller.) +// IsPackLoaded(): +// Indicates whether this drive has a pack loaded (i.e. has an image +// assigned to it) // bool -rp_drive_c::IsAvailable() +rp_drive_c::IsPackLoaded() { return file_is_open(); } @@ -97,7 +97,7 @@ rp_drive_c::SeekTo( _iae = !(destinationCylinder < _driveInfo.Cylinders); - if (IsAvailable() && !_iae) + if (IsConnected() && IsPackLoaded() && !_iae) { _currentCylinder = destinationCylinder; return true; @@ -137,7 +137,7 @@ rp_drive_c::Write( // TODO: handle address overflow - if (!IsAvailable() || _iae || _wle) + if (!IsConnected() || !IsPackLoaded() || _iae || _wle) { return false; } @@ -167,10 +167,10 @@ rp_drive_c::Read( _iae = !ValidateCHS(cylinder, track, sector); _wle = false; - if (!IsAvailable() || _iae) + if (!IsConnected() || !IsPackLoaded() || _iae) { *buffer = nullptr; - INFO("Failure: avail %d valid %d", IsAvailable(), ValidateCHS(cylinder, track, sector)); + INFO("Failure: connected %d loaded %d valid %d", IsConnected(), IsPackLoaded(), ValidateCHS(cylinder, track, sector)); return false; } else @@ -197,9 +197,9 @@ rp_drive_c::Search( { _iae = !ValidateCHS(cylinder, track, sector); - if (!IsAvailable() || _iae) + if (!IsConnected() || !IsPackLoaded() || _iae) { - INFO("Failure: avail %d valid %d", IsAvailable(), ValidateCHS(cylinder, track, sector)); + INFO("Failure: connected &d loaded %d valid %d", IsConnected(), IsPackLoaded(), ValidateCHS(cylinder, track, sector)); return false; } else @@ -208,7 +208,7 @@ rp_drive_c::Search( timeout_c timeout; INFO("Search commencing."); - timeout.wait_ms(30); + timeout.wait_ms(250); _pip = false; INFO("Search completed."); _currentCylinder = cylinder; diff --git a/10.02_devices/2_src/rp_drive.hpp b/10.02_devices/2_src/rp_drive.hpp index 3c64531..51d7eb2 100644 --- a/10.02_devices/2_src/rp_drive.hpp +++ b/10.02_devices/2_src/rp_drive.hpp @@ -28,7 +28,8 @@ public: uint32_t GetSectorSize(void); uint32_t GetType(void); - bool IsAvailable(void); + bool IsConnected(void) { return _driveNumber == 0; /* todo: make config. parameter */ } + bool IsPackLoaded(void); bool IsDriveReady(void) { return _ready; } bool IsWriteLocked(void) { return false; /* for now */ } bool IsPositioningInProgress(void) { return _pip; } @@ -42,6 +43,8 @@ public: void SetVolumeValid(void) { _vv = true; } void ClearVolumeValid(void) { _vv = false; } bool GetVolumeValid(void) { return _vv; } + uint16_t GetDriveType(void) { return _driveInfo.TypeNumber; } + uint16_t GetSerialNumber(void) { return 012345; } // TODO: Make configurable parameter bool SeekTo(uint32_t cylinder); uint32_t GetCurrentCylinder();