diff --git a/10.02_devices/2_src/massbus_rp.cpp b/10.02_devices/2_src/massbus_rp.cpp index 7135d90..9b41343 100644 --- a/10.02_devices/2_src/massbus_rp.cpp +++ b/10.02_devices/2_src/massbus_rp.cpp @@ -140,7 +140,7 @@ massbus_rp_c::WriteRegister( // while the drive is busy is invalid. DEBUG("Register modification while drive busy."); _rmr = true; - UpdateStatus(false, false); + UpdateStatus(_selectedUnit, false, false); return; } @@ -183,7 +183,7 @@ massbus_rp_c::WriteRegister( // Based on diagnostic (ZRJGE0) behavior, writing ANY value here forces an error. // DEBUG("Error 1 Reg write o%o, value is now o%o", value, _error1); - UpdateStatus(false, true); // Force composite error. + UpdateStatus(_selectedUnit, false, true); // Force composite error. break; default: @@ -196,15 +196,17 @@ void massbus_rp_c::DoCommand( uint32_t unit, uint16_t command) { - // check for GO bit; if unset we have nothing to do here. - if ((command & RP_GO) == 0) - { - return; - } - FunctionCode function = static_cast((command & RP_FUNC) >> 1); _selectedUnit = unit; + // check for GO bit; if unset we have nothing to do here, + // but we will update status in case the drive unit has changed. + if ((command & RP_GO) == 0) + { + UpdateStatus(_selectedUnit, false, false); + return; + } + DEBUG("RP function 0%o, unit %o", function, _selectedUnit); if (!SelectedDrive()->IsConnected()) @@ -215,7 +217,7 @@ void massbus_rp_c::DoCommand( _ata = true; _ned = true; // TODO: should be done at RH11 level! SelectedDrive()->ClearVolumeValid(); - UpdateStatus(true, false); + UpdateStatus(_selectedUnit, true, false); return; } @@ -232,9 +234,51 @@ void massbus_rp_c::DoCommand( { case FunctionCode::Nop: // Nothing. - UpdateStatus(true, false); + UpdateStatus(_selectedUnit, true, false); break; + case FunctionCode::Unload: + // Unsure how best to implement this: + // This puts the drive in STANDBY state, meaning the heads are + // retracted and the spindle powers down. The command doesn't actually *finish* + // until the pack is manually spun back up. This could be implemented + // by unloading the disk image and waiting until a new one is loaded. + // Right now I'm just treating it as a no-op, at least until I can find a good + // way to test it using real software. + DEBUG("RP Unload"); + _ata = true; + UpdateStatus(_selectedUnit, true, false); + break; + + + case FunctionCode::DriveClear: + // p. 2-11 of EK-RP056-MM-01: + // "The following registers and conditions within the DCL are cleared + // by this command: + // - Status Register (ATA and ERR status bits) + // - All three Error registers + // - Attention Summary Register + // - ECC Position and Pattern Registers + // - The Diagnostic Mode Bit + + DEBUG("RP Drive Clear"); + _ata = false; + _attnSummary = 0; + _error1 = 0; + _error2 = 0; + _error3 = 0; + _rmr = false; + _ned = false; + _maint = false; + UpdateStatus(_selectedUnit, false, false); + break; + + case FunctionCode::Release: + DEBUG("RP Release"); + // This is a no-op, this only applies to dual-ported configurations, + // which we are not. + break; + case FunctionCode::ReadInPreset: DEBUG("RP Read-In Preset"); // @@ -249,7 +293,13 @@ void massbus_rp_c::DoCommand( _offset = 0; UpdateDesiredSectorTrack(); UpdateOffset(); - UpdateStatus(false, false); /* do not interrupt */ + UpdateStatus(_selectedUnit, false, false); /* do not interrupt */ + break; + + case FunctionCode::PackAcknowledge: + DEBUG("RP Pack Acknowledge"); + SelectedDrive()->SetVolumeValid(); + UpdateStatus(_selectedUnit, false, false); break; case FunctionCode::ReadData: @@ -257,23 +307,31 @@ void massbus_rp_c::DoCommand( case FunctionCode::WriteHeaderAndData: case FunctionCode::ReadHeaderAndData: case FunctionCode::Search: - DEBUG("RP Read/Write Data or Search"); + case FunctionCode::Seek: + case FunctionCode::Recalibrate: + case FunctionCode::Offset: + case FunctionCode::ReturnToCenterline: + DEBUG("RP Read/Write Data or head-motion command"); { // Clear the unit's DRY bit SelectedDrive()->ClearDriveReady(); - if (function == FunctionCode::Search) + if (function == FunctionCode::Search || + function == FunctionCode::Seek || + function == FunctionCode::Recalibrate || + function == FunctionCode::Offset || + function == FunctionCode::ReturnToCenterline) { SelectedDrive()->SetPositioningInProgress(); } - // Clear READY - UpdateStatus(false, false); + UpdateStatus(_selectedUnit, false, false); pthread_mutex_lock(&_workerMutex); // Save a copy of command data for the worker to consume _newCommand.unit = _selectedUnit; + _newCommand.drive = SelectedDrive(); _newCommand.function = function; _newCommand.cylinder = _desiredCylinder; _newCommand.track = _desiredTrack; @@ -326,15 +384,20 @@ massbus_rp_c::ReadRegister( // void massbus_rp_c::UpdateStatus( + uint16_t unit, bool complete, bool diagForceError) { + // Most of these status bits (except possibly ATTN) + // are for the currently selected drive. + rp_drive_c* drive = SelectedDrive(); + _error1 = (_rmr ? 04 : 0) | - (SelectedDrive()->GetAddressOverflow() ? 01000 : 0) | - (SelectedDrive()->GetInvalidAddress() ? 02000 : 0) | - (SelectedDrive()->GetWriteLockError() ? 04000 : 0); + (drive->GetAddressOverflow() ? 01000 : 0) | + (drive->GetInvalidAddress() ? 02000 : 0) | + (drive->GetWriteLockError() ? 04000 : 0); if (_error1 != 0) { @@ -352,13 +415,13 @@ massbus_rp_c::UpdateStatus( } _status = - (SelectedDrive()->GetVolumeValid() ? 0100 : 0) | - (SelectedDrive()->IsDriveReady() ? 0200 : 0) | + (drive->GetVolumeValid() ? 0100 : 0) | + (drive->IsDriveReady() ? 0200 : 0) | (0400) | // Drive preset -- always set for a single-controller disk - (SelectedDrive()->GetReadLastSector() ? 02000 : 0) | // Last sector read - (SelectedDrive()->IsWriteLocked() ? 04000 : 0) | // Write lock - (SelectedDrive()->IsPackLoaded() ? 010000 : 0) | // Medium online - (SelectedDrive()->IsPositioningInProgress() ? 020000 : 0) | // PIP + (drive->GetReadLastSector() ? 02000 : 0) | // Last sector read + (drive->IsWriteLocked() ? 04000 : 0) | // Write lock + (drive->IsPackLoaded() ? 010000 : 0) | // Medium online + (drive->IsPositioningInProgress() ? 020000 : 0) | // PIP (_err ? 040000 : 0) | // Composite error (_ata ? 0100000 : 0); @@ -366,11 +429,11 @@ massbus_rp_c::UpdateStatus( _controller->WriteRegister(static_cast(Registers::Status), _status); _controller->WriteRegister(static_cast(Registers::Error1), _error1); - // Update the Attention Summary register if this disk is online: - - if (_ata && SelectedDrive()->IsConnected()) + // Update the Attention Summary register if the requested disk is online: + // Note that we may be setting ATTN for a drive other than the currently selected one. + if (_ata && GetDrive(unit)->IsConnected()) { - _attnSummary |= (0x1 << _selectedUnit); // TODO: these only get set, and are latched until + _attnSummary |= (0x1 << unit); // TODO: these only get set, and are latched until // manually cleared? DEBUG("Attention Summary is now o%o", _attnSummary); } @@ -378,7 +441,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()->IsConnected(), _ned); + _controller->BusStatus(complete, drive->IsDriveReady(), _ata, _err, drive->IsConnected(), _ned); } void @@ -416,8 +479,9 @@ massbus_rp_c::Reset() _ata = false; _attnSummary = 0; _error1 = 0; - _rmr = false; - UpdateStatus(false, false); + _rmr = false; + _selectedUnit = 0; + UpdateStatus(_selectedUnit, false, false); _desiredSector = 0; _desiredTrack = 0; @@ -442,6 +506,13 @@ massbus_rp_c::SelectedDrive() return _controller->GetDrive(_selectedUnit); } +rp_drive_c* +massbus_rp_c::GetDrive(uint16_t unit) +{ + assert(unit < 8); + return _controller->GetDrive(unit); +} + // Background worker function void massbus_rp_c::Worker() @@ -489,7 +560,7 @@ massbus_rp_c::Worker() _newCommand.bus_address); uint16_t* buffer = nullptr; - if (SelectedDrive()->Read( + if (_newCommand.drive->Read( _newCommand.cylinder, _newCommand.track, _newCommand.sector, @@ -515,7 +586,7 @@ massbus_rp_c::Worker() } // Return drive to ready state - SelectedDrive()->SetDriveReady(); + _newCommand.drive->SetDriveReady(); _workerState = WorkerState::Finish; } @@ -537,7 +608,7 @@ massbus_rp_c::Worker() _newCommand.bus_address, _newCommand.word_count); - if (!buffer || !SelectedDrive()->Write( + if (!buffer || !_newCommand.drive->Write( _newCommand.cylinder, _newCommand.track, _newCommand.sector, @@ -552,7 +623,7 @@ massbus_rp_c::Worker() delete buffer; // Return drive to ready state - SelectedDrive()->SetDriveReady(); + _newCommand.drive->SetDriveReady(); _workerState = WorkerState::Finish; } @@ -565,7 +636,7 @@ massbus_rp_c::Worker() _newCommand.track, _newCommand.sector); - if (!SelectedDrive()->Search( + if (!_newCommand.drive->Search( _newCommand.cylinder, _newCommand.track, _newCommand.sector)) @@ -576,12 +647,37 @@ massbus_rp_c::Worker() // Return to ready state, set attention bit. - SelectedDrive()->SetDriveReady(); + _newCommand.drive->SetDriveReady(); _ata = true; _workerState = WorkerState::Finish; } break; + case FunctionCode::Seek: + DEBUG("SEEK Cylinder %d", _newCommand.cylinder); + if (!_newCommand.drive->SeekTo(_newCommand.cylinder)) + { + // Seek failed + DEBUG("Seek failed"); + } + _newCommand.drive->SetDriveReady(); + _ata = true; + _workerState = WorkerState::Finish; + break; + + case FunctionCode::Offset: + case FunctionCode::ReturnToCenterline: + // We don't support adjusting the head position between + // cylinders so these are both effectively no-ops, but + // they're no-ops that need to take a small amount of time + // to complete. + DEBUG("OFFSET/RETURN TO CL"); + timeout.wait_ms(10); + _newCommand.drive->SetDriveReady(); + _ata = true; + _workerState = WorkerState::Finish; + break; + default: FATAL("Unimplemented drive function %d", command.function); break; @@ -591,9 +687,9 @@ massbus_rp_c::Worker() case WorkerState::Finish: _workerState = WorkerState::Idle; - SelectedDrive()->SetDriveReady(); + _newCommand.drive->SetDriveReady(); UpdateCurrentCylinder(); - UpdateStatus(true, false); + UpdateStatus(_newCommand.unit, true, false); break; } diff --git a/10.02_devices/2_src/massbus_rp.hpp b/10.02_devices/2_src/massbus_rp.hpp index 5f86027..b59f6d0 100644 --- a/10.02_devices/2_src/massbus_rp.hpp +++ b/10.02_devices/2_src/massbus_rp.hpp @@ -112,7 +112,8 @@ public: private: struct WorkerCommand { - volatile uint32_t unit; + uint16_t unit; + rp_drive_c* drive; volatile uint32_t bus_address; volatile uint32_t word_count; volatile FunctionCode function; @@ -134,13 +135,14 @@ private: void on_power_changed(void) override; void on_init_changed(void) override; - void UpdateStatus(bool completion, bool diagForceError); + void UpdateStatus(uint16_t unit, bool completion, bool diagForceError); void UpdateDesiredSectorTrack(); void UpdateDesiredCylinder(); void UpdateOffset(); void UpdateCurrentCylinder(); rp_drive_c* SelectedDrive(); + rp_drive_c* GetDrive(uint16_t unit); private: rh11_c* _controller; diff --git a/10.02_devices/2_src/rp_drive.cpp b/10.02_devices/2_src/rp_drive.cpp index ebf7c5a..a09a96d 100644 --- a/10.02_devices/2_src/rp_drive.cpp +++ b/10.02_devices/2_src/rp_drive.cpp @@ -55,6 +55,12 @@ rp_drive_c::on_param_changed(parameter_c *param) if (file_open(image_filepath.new_value, true)) { image_filepath.value = image_filepath.new_value; + + // New image; this is analogous to a new pack being spun up. + // Set the Volume Valid bit so software knows things have changed. + // TODO: should alert massbus_rp so that it can update status regs immediately. + SetVolumeValid(); + return true; }