1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-03-07 11:26:51 +00:00

Small cleanup, implemented most of the remaining rp04/05/06 functions. Prep for supporting multiple drives.

This commit is contained in:
Josh Dersch
2020-03-27 19:12:51 +01:00
parent 828e4f5371
commit b6f933a4a2
3 changed files with 145 additions and 41 deletions

View File

@@ -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<FunctionCode>((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<uint32_t>(Registers::Status), _status);
_controller->WriteRegister(static_cast<uint32_t>(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<uint32_t>(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;
}

View File

@@ -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;

View File

@@ -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;
}