1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-04-15 16:10:41 +00:00

Slow progress. RH11 can now boot 2.11BSD and load the kernel into memory; XP driver then fails

to read the label block, though it looks like the proper transfers take place.

Minor code cleanup.
This commit is contained in:
Josh Dersch
2020-01-29 02:03:35 +01:00
parent 311b5f48a7
commit a72a845623
6 changed files with 263 additions and 104 deletions

View File

@@ -34,11 +34,11 @@ massbus_rp_c::massbus_rp_c(
_workerState(WorkerState::Idle),
_workerWakeupCond(PTHREAD_COND_INITIALIZER),
_workerMutex(PTHREAD_MUTEX_INITIALIZER),
_vv(false),
_err(false),
_ata(false),
_ready(true),
_ned(false)
_ned(false),
_attnSummary(0)
{
name.value="MASSBUS_RP";
type_name.value = "massbus_rp_c";
@@ -135,7 +135,15 @@ massbus_rp_c::WriteRegister(
UpdateDesiredCylinder();
INFO("Desired Cylinder Address o%o (o%o)", _desiredCylinder, value);
break;
break;
case Registers::AttentionSummary:
// Clear bits in the Attention Summary register specified in the
// written value:
_attnSummary &= ~(value & 0xff);
_controller->WriteRegister(static_cast<uint32_t>(Registers::AttentionSummary), _attnSummary);
INFO("Attention Summary write o%o, value is now o%o", value, _attnSummary);
break;
default:
FATAL("Unimplemented RP register write.");
@@ -155,7 +163,7 @@ void massbus_rp_c::DoCommand(
FunctionCode function = static_cast<FunctionCode>((command & RP_FUNC) >> 1);
_selectedUnit = unit;
INFO("RP function 0%o, unit %o", function, _selectedUnit);
if (!SelectedDrive()->IsAvailable())
@@ -163,9 +171,10 @@ void massbus_rp_c::DoCommand(
// Early return for disconnected drives;
// set NED and ERR bits
_err = true;
_ata = true;
_ned = true; // TODO: should be done at RH11 level!
_vv = false;
UpdateStatus();
SelectedDrive()->ClearVolumeValid();
UpdateStatus(true);
return;
}
@@ -175,6 +184,8 @@ void massbus_rp_c::DoCommand(
{
case FunctionCode::Nop:
// Nothing.
_ata = false;
UpdateStatus(true);
break;
case FunctionCode::ReadInPreset:
@@ -184,11 +195,14 @@ void massbus_rp_c::DoCommand(
// sector/track address register, and clears the FMT, HCI, and ECI
// bits in the offset register. It is used to bootstrap the device."
//
_vv = true;
SelectedDrive()->SetVolumeValid();
SelectedDrive()->SetDriveReady();
_desiredSector = 0;
_desiredTrack = 0;
_offset = 0;
UpdateStatus();
_ata = false;
_ready = true;
UpdateStatus(true);
UpdateDesiredSectorTrack();
UpdateOffset();
break;
@@ -199,17 +213,6 @@ void massbus_rp_c::DoCommand(
case FunctionCode::ReadHeaderAndData:
case FunctionCode::Search:
INFO("RP Read/Write Data or Search");
// Ensure the drive is ready, error if not
if (!SelectedDrive()->IsDriveReady())
{
_vv = false;
_err = true;
_ata = true;
_ready = true;
UpdateStatus();
}
else
{
// Clear the unit's DRY bit
SelectedDrive()->ClearDriveReady();
@@ -222,18 +225,20 @@ void massbus_rp_c::DoCommand(
// Clear ATA and READY
_ata = false;
_ready = false;
UpdateStatus();
UpdateStatus(false);
pthread_mutex_lock(&_workerMutex);
// Save a copy of command data for the worker to consume
_newCommand.unit = _selectedUnit;
_newCommand.function = function;
_newCommand.cylinder = _desiredCylinder;
_newCommand.track = _desiredTrack;
_newCommand.sector = _desiredSector;
_newCommand.bus_address = _controller->GetBusAddress();
_newCommand.word_count = _controller->GetWordCount();
_newCommand.ready = true;
// Wake the worker
pthread_cond_signal(&_workerWakeupCond);
pthread_mutex_unlock(&_workerMutex);
@@ -252,9 +257,9 @@ massbus_rp_c::ReadRegister(
uint32_t unit,
uint32_t reg)
{
INFO("RP reg read: unit %d register 0%o", unit, reg);
INFO("*** RP reg read: unit %d register 0%o", unit, reg);
FATAL("Unimplemented RP register read.");
FATAL("Unimplemented register read %o", reg);
return 0;
}
@@ -263,7 +268,8 @@ massbus_rp_c::ReadRegister(
// Register update functions
//
void
massbus_rp_c::UpdateStatus()
massbus_rp_c::UpdateStatus(
bool complete)
{
_error1 =
@@ -274,6 +280,7 @@ massbus_rp_c::UpdateStatus()
if (_error1 != 0)
{
_err = true;
_ata = true;
}
else
{
@@ -281,7 +288,7 @@ massbus_rp_c::UpdateStatus()
}
_status =
(_vv ? 0100 : 0) |
(SelectedDrive()->GetVolumeValid() ? 0100 : 0) |
(SelectedDrive()->IsDriveReady() ? 0200 : 0) |
(0400) | // Drive preset -- always set for a single-controller disk
(SelectedDrive()->GetReadLastSector() ? 02000 : 0) | // Last sector read
@@ -291,11 +298,22 @@ massbus_rp_c::UpdateStatus()
(_err ? 040000 : 0) | // Composite error
(_ata ? 0100000 : 0);
INFO("Unit %d Status: o%o", _selectedUnit, _status);
_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()->IsAvailable())
{
_attnSummary |= (0x1 << _selectedUnit); // TODO: these only get set, and are latched until
// manually cleared?
}
_controller->WriteRegister(static_cast<uint32_t>(Registers::AttentionSummary), _attnSummary);
// Inform controller of status update.
_controller->BusStatus(_ready, _ata, _err, _ned);
_controller->BusStatus(complete, _ready, _ata, _err, SelectedDrive()->IsAvailable(), _ned);
}
void
@@ -303,6 +321,10 @@ massbus_rp_c::UpdateDesiredSectorTrack()
{
uint16_t desiredSectorTrack = (_desiredSector | (_desiredTrack << 8));
_controller->WriteRegister(static_cast<uint32_t>(Registers::DesiredSectorTrackAddress), desiredSectorTrack);
// Fudge: We update the look-ahead sector value to be the last-requested sector - 1
uint16_t lookAhead = (((_desiredSector - 5) % 22) << 6);
_controller->WriteRegister(static_cast<uint32_t>(Registers::LookAhead), lookAhead);
}
void
@@ -317,6 +339,13 @@ massbus_rp_c::UpdateOffset()
_controller->WriteRegister(static_cast<uint32_t>(Registers::Offset), _offset);
}
void
massbus_rp_c::UpdateCurrentCylinder()
{
_controller->WriteRegister(static_cast<uint32_t>(Registers::CurrentCylinderAddress),
SelectedDrive()->GetCurrentCylinder());
}
void
massbus_rp_c::Reset()
{
@@ -325,7 +354,8 @@ massbus_rp_c::Reset()
// Reset registers to their defaults
_ata = false;
_ready = true;
UpdateStatus();
_attnSummary = 0;
UpdateStatus(false);
_desiredSector = 0;
_desiredTrack = 0;
@@ -334,6 +364,8 @@ massbus_rp_c::Reset()
_desiredCylinder = 0;
UpdateDesiredCylinder();
UpdateCurrentCylinder();
_offset = 0;
UpdateOffset();
@@ -380,7 +412,6 @@ massbus_rp_c::Worker()
pthread_mutex_unlock(&_workerMutex);
_workerState = WorkerState::Execute;
INFO("worker awoken.");
break;
case WorkerState::Execute:
@@ -389,24 +420,24 @@ massbus_rp_c::Worker()
case FunctionCode::ReadData:
{
INFO("READ CHS %d/%d/%d, %d words to address o%o",
_desiredCylinder,
_desiredTrack,
_desiredSector,
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
_newCommand.bus_address);
uint16_t* buffer = nullptr;
if (SelectedDrive()->Read(
_desiredCylinder,
_desiredTrack,
_desiredSector,
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
&buffer))
{
//
// Data read: do DMA transfer to memory.
//
_controller->DMAWrite(
_controller->DiskReadTransfer(
_newCommand.bus_address,
_newCommand.word_count,
buffer);
@@ -418,15 +449,14 @@ massbus_rp_c::Worker()
{
// Read failed:
INFO("Read failed.");
_ata = true;
}
// Return drive to ready state
SelectedDrive()->SetDriveReady();
// Signal attention and update status.
_ata = true;
// Signal ready status.
_ready = true;
UpdateStatus();
_workerState = WorkerState::Finish;
}
break;
@@ -434,25 +464,29 @@ massbus_rp_c::Worker()
case FunctionCode::WriteData:
{
INFO("WRITE CHS %d/%d/%d, %d words from address o%o",
_desiredCylinder,
_desiredTrack,
_desiredSector,
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
_newCommand.bus_address);
uint16_t* buffer = new uint16_t[_newCommand.word_count];
assert(buffer);
//
// Data write: do DMA transfer from memory.
//
uint16_t* buffer = _controller->DiskWriteTransfer(
_newCommand.bus_address,
_newCommand.word_count);
if (!SelectedDrive()->Write(
_desiredCylinder,
_desiredTrack,
_desiredSector,
if (!buffer || !SelectedDrive()->Write(
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
buffer))
{
// Write failed:
INFO("Write failed.");
_ata = true;
}
delete buffer;
@@ -460,10 +494,8 @@ massbus_rp_c::Worker()
// Return drive to ready state
SelectedDrive()->SetDriveReady();
// Signal attention and update status.
_ata = true;
// Signal ready status.
_ready = true;
UpdateStatus();
_workerState = WorkerState::Finish;
}
break;
@@ -471,24 +503,24 @@ massbus_rp_c::Worker()
case FunctionCode::Search:
{
INFO("SEARCH CHS %d/%d/%d",
_desiredCylinder,
_desiredTrack,
_desiredSector);
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector);
if (!SelectedDrive()->Search(
_desiredCylinder,
_desiredTrack,
_desiredSector))
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector))
{
// Search failed
INFO("Search failed");
}
// Return to ready state
// Return to ready state, set attention bit.
SelectedDrive()->SetDriveReady();
_ata = true;
_ready = true;
UpdateStatus();
_workerState = WorkerState::Finish;
}
break;
@@ -503,7 +535,8 @@ massbus_rp_c::Worker()
case WorkerState::Finish:
_workerState = WorkerState::Idle;
SelectedDrive()->SetDriveReady();
UpdateStatus();
UpdateCurrentCylinder();
UpdateStatus(true);
break;
}

View File

@@ -108,6 +108,9 @@ private:
volatile uint32_t bus_address;
volatile uint32_t word_count;
volatile FunctionCode function;
volatile uint32_t cylinder;
volatile uint32_t track;
volatile uint32_t sector;
volatile bool ready;
} _newCommand;
@@ -123,10 +126,11 @@ private:
void on_power_changed(void) override;
void on_init_changed(void) override;
void UpdateStatus();
void UpdateStatus(bool completion);
void UpdateDesiredSectorTrack();
void UpdateDesiredCylinder();
void UpdateOffset();
void UpdateCurrentCylinder();
rp_drive_c* SelectedDrive();
@@ -140,7 +144,7 @@ private:
{ "CS1", false, 0, 0177700 }, // Status
{ "ER1", false, true , 0, 0177777 }, // Error #1 - writable by diagnostics
{ "MR" , false, true , 0, 0177777 }, // Maintenance
{ "ATN", false, false, 0, 0 }, // Attention summary
{ "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
@@ -155,29 +159,28 @@ private:
};
// Unit selected by last command register write
uint32_t _selectedUnit;
volatile uint32_t _selectedUnit;
// Register data
uint16_t _status;
uint16_t _error1;
uint16_t _maint;
uint16_t _attn;
uint16_t _desiredSector;
uint16_t _desiredTrack;
uint16_t _offset;
uint16_t _desiredCylinder;
uint16_t _currentCylinder;
uint16_t _error2;
uint16_t _error3;
volatile uint16_t _status;
volatile uint16_t _error1;
volatile uint16_t _maint;
volatile uint16_t _attnSummary;
volatile uint16_t _desiredSector;
volatile uint16_t _desiredTrack;
volatile uint16_t _offset;
volatile uint16_t _desiredCylinder;
volatile uint16_t _currentCylinder;
volatile uint16_t _error2;
volatile uint16_t _error3;
// Status bits that we track
bool _vv;
bool _err;
bool _ata;
volatile bool _err;
volatile bool _ata;
// RH11 ready signal (ugly: this should be in the rh11 code!)
bool _ready;
bool _ned; // ditto
volatile bool _ready;
volatile bool _ned; // ditto
// Worker thread
pthread_t _workerThread;

View File

@@ -107,7 +107,7 @@ rh11_c::rh11_c() :
// base addr, intr-vector, intr level
// TODO: make configurable based on type (fixed, tape, moving-head disk)
// right now it is hardcoded for moving-head disks.
set_default_bus_params(0776700, 11, 0254, 5);
set_default_bus_params(0776700, 21, 0254, 6);
_massbus.reset(new massbus_rp_c(this));
@@ -146,7 +146,7 @@ rh11_c::rh11_c() :
RH_reg[i]->active_on_dati = false;
RH_reg[i]->active_on_dato = true;
RH_reg[i]->reset_value = 0;
RH_reg[i]->writable_bits = 0xffff;
RH_reg[i]->writable_bits = 0xfffe;
break;
case RHCS2:
@@ -212,11 +212,14 @@ rh11_c::~rh11_c()
{
}
// TODO: RENAME! This is invoked when the drive is finished with the given command.
void
rh11_c::BusStatus(
bool completion,
bool ready,
bool attention,
bool error,
bool avail,
bool ned)
{
INFO("Massbus status update attn %d, error %d, ned %d", attention, error, ned);
@@ -226,12 +229,19 @@ rh11_c::BusStatus(
INFO("RHCS1 is currently o%o", currentStatus);
currentStatus &= ~(0140200); // move to constant
currentStatus &= ~(0140300); // 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 (should clear when busy)
if (completion)
{
// clear the GO bit from the CS word if the drive is finished.
currentStatus &= ~(01);
}
set_register_dati_value(
RH_reg[RHCS1],
currentStatus,
@@ -254,7 +264,7 @@ rh11_c::BusStatus(
INFO("RHCS2 is now o%o", currentStatus);
if (attention)
if ((attention || ready) && completion)
{
Interrupt();
}
@@ -294,12 +304,100 @@ rh11_c::GetBusAddress()
return _busAddress;
}
//
// Transfers data read from disk to the Unibus.
// Updates RHBA and RHWC registers appropriately.
//
bool
rh11_c::DiskReadTransfer(
uint32_t address,
size_t lengthInWords,
uint16_t* diskBuffer)
{
// Write the disk data to memory
if (DMAWrite(
address,
lengthInWords,
diskBuffer))
{
IncrementBusAddress(lengthInWords);
DecrementWordCount(lengthInWords);
return true;
}
else
{
return false;
}
}
//
// Transfers data from the Unibus to be written to disk.
// Updates RHBA and RHWC registers appropriately.
//
uint16_t*
rh11_c::DiskWriteTransfer(
uint32_t address,
size_t lengthInWords)
{
uint16_t* buffer = DMARead(
address,
lengthInWords);
if (buffer)
{
IncrementBusAddress(lengthInWords);
DecrementWordCount(lengthInWords);
}
return buffer;
}
void
rh11_c::IncrementBusAddress(uint32_t delta)
{
_busAddress += delta;
uint16_t currentStatus = get_register_dato_value(RH_reg[RHCS1]);
// Clear extended address bits
currentStatus &= ~(0x300);
currentStatus |= ((_busAddress & 0x30000) >> 8);
set_register_dati_value(
RH_reg[RHCS1],
currentStatus,
"SetBusAddress");
set_register_dati_value(
RH_reg[RHBA],
(_busAddress & 0xffff),
"IncrementBusAddress");
INFO("BA Reg incr: o%o", _busAddress);
}
uint16_t
rh11_c::GetWordCount()
{
return -get_register_dato_value(RH_reg[RHWC]);
}
void
rh11_c::DecrementWordCount(uint16_t delta)
{
uint16_t currentWordCount = get_register_dato_value(RH_reg[RHWC]);
currentWordCount += delta;
set_register_dati_value(
RH_reg[RHWC],
currentWordCount,
"DecrementWordCount");
INFO("WC Reg decr: o%o", currentWordCount);
}
bool
rh11_c::DMAWrite(
uint32_t address,
@@ -323,6 +421,7 @@ rh11_c::DMAWrite(
lengthInWords);
INFO("Success: %d", dma_request.success);
assert(dma_request.success);
return dma_request.success;
}
@@ -339,8 +438,6 @@ rh11_c::DMARead(
uint16_t* buffer = new uint16_t[lengthInWords];
assert (buffer);
memset(reinterpret_cast<uint8_t*>(buffer), 0xc3, lengthInWords * 2);
unibusadapter->DMA(
dma_request,
true,
@@ -355,6 +452,7 @@ rh11_c::DMARead(
}
else
{
delete buffer;
return nullptr;
}
}
@@ -412,8 +510,6 @@ void rh11_c::on_after_register_access(
unibusdevice_register_t *device_reg,
uint8_t unibus_control)
{
UNUSED(unibus_control);
uint16_t value = device_reg->active_dato_flipflops;
switch(device_reg->index)
@@ -423,15 +519,15 @@ void rh11_c::on_after_register_access(
if (UNIBUS_CONTROL_DATO == unibus_control)
{
// IE bit
_interruptEnable = !!(value & 0x40);
_interruptEnable = ((value & 0x40) != 0);
// Extended bus address bits
_busAddress = (_busAddress & 0xffff) | ((value & 0x300) << 8);
// Let the massbus device take a crack at the shared bits
_massbus->WriteRegister(_unit, RHCS1, value & 0x3f);
INFO("RHCS1: IE %d BA 0%o", _interruptEnable, _busAddress);
INFO("RHCS1: IE %x BA 0%o", _interruptEnable, _busAddress);
// Let the massbus device take a crack at the shared bits
_massbus->WriteRegister(_unit, RHCS1, value & 0x3f);
}
}
break;
@@ -447,12 +543,16 @@ void rh11_c::on_after_register_access(
// TODO: handle System Register Clear (bit 5)
if (value & 040) // controller clear
{
INFO("Controller Clear");
// clear error bits
value &= ~(010040);
set_register_dati_value(
RH_reg[RHCS2],
value,
"Reset");
"Reset");
_interruptEnable = false;
_massbus->Reset();
}
INFO("RHCS2: unit %d", _unit);
}
@@ -489,6 +589,7 @@ void rh11_c::on_after_register_access(
}
else
{
INFO("massbus reg read %o", device_reg->index);
set_register_dati_value(
device_reg,
_massbus->ReadRegister(_unit, _unibusToMassbusRegisterMap[device_reg->index]),
@@ -506,10 +607,15 @@ void rh11_c::on_after_register_access(
void rh11_c::Interrupt(void)
{
INFO("interrupt enable is %d", _interruptEnable);
if(_interruptEnable)
{
INFO("Interrupt!");
unibusadapter->INTR(intr_request, nullptr, 0);
// IE is cleared after the interrupt is raised
// Actual bit is cleared in BusStatus, this should be fixed.
_interruptEnable = false;
}
}

View File

@@ -45,25 +45,31 @@ private:
void Interrupt(void);
void reset_controller(void);
void IncrementBusAddress(uint32_t delta);
void DecrementWordCount(uint16_t delta);
bool DMAWrite(uint32_t address, size_t lengthInWords, uint16_t* buffer);
uint16_t* DMARead(uint32_t address, size_t lengthInWords);
private:
std::unique_ptr<massbus_device_c> _massbus;
// Control & Status reg 1 bits
bool _interruptEnable;
uint32_t _busAddress;
volatile bool _interruptEnable;
volatile uint32_t _busAddress;
// Control & Status reg 2 bits
uint16_t _unit;
bool _busAddressIncrementProhibit;
bool _parityTest;
volatile uint16_t _unit;
volatile bool _busAddressIncrementProhibit;
volatile bool _parityTest;
public:
rh11_c();
virtual ~rh11_c();
void BusStatus(bool ready, bool attention, bool error, bool ned);
void BusStatus(bool completion, bool ready, bool attention, bool error, bool avail, bool ned);
// Unibus register access (for devices on massbus)
void WriteRegister(uint32_t reg, uint16_t value);
@@ -73,9 +79,9 @@ public:
uint32_t GetBusAddress();
uint16_t GetWordCount();
bool DMAWrite(uint32_t address, size_t lengthInWords, uint16_t* buffer);
uint16_t* DMARead(uint32_t address, size_t lengthInWords);
bool DiskReadTransfer(uint32_t address, size_t lengthInWords, uint16_t* buffer);
uint16_t* DiskWriteTransfer(uint32_t address, size_t lengthInWords);
public:
// background worker function

View File

@@ -23,7 +23,8 @@ rp_drive_c::rp_drive_c(storagecontroller_c *controller, uint32_t driveNumber) :
_aoe(false),
_iae(false),
_wle(false),
_pip(false)
_pip(false),
_vv(false)
{
set_workers_count(0) ; // needs no worker()
log_label = "RP";
@@ -142,6 +143,7 @@ rp_drive_c::Write(
}
else
{
_currentCylinder = cylinder;
uint32_t offset = GetSectorForCHS(cylinder, track, sector);
file_write(reinterpret_cast<uint8_t*>(buffer), offset * GetSectorSize(), countInWords * 2);
return true;
@@ -173,6 +175,8 @@ rp_drive_c::Read(
}
else
{
_currentCylinder = cylinder;
*buffer = new uint16_t[countInWords];
assert(nullptr != *buffer);
@@ -180,6 +184,7 @@ rp_drive_c::Read(
uint32_t offset = GetSectorForCHS(cylinder, track, sector);
INFO("Read from sector offset o%o", offset);
file_read(reinterpret_cast<uint8_t*>(*buffer), offset * GetSectorSize(), countInWords * 2);
return true;
}
}
@@ -205,9 +210,11 @@ rp_drive_c::Search(
INFO("Search commencing.");
_pip = true;
timeout.wait_ms(50);
timeout.wait_ms(500);
_pip = false;
_currentCylinder = cylinder;
return true;
}

View File

@@ -39,7 +39,10 @@ public:
bool GetWriteLockError(void) { return _wle; }
void SetDriveReady(void) { _ready = true; }
void ClearDriveReady(void) { _ready = false; }
void SetVolumeValid(void) { _vv = true; }
void ClearVolumeValid(void) { _vv = false; }
bool GetVolumeValid(void) { return _vv; }
bool SeekTo(uint32_t cylinder);
uint32_t GetCurrentCylinder();
@@ -60,6 +63,7 @@ private:
bool _iae;
bool _wle;
bool _pip;
bool _vv;
bool ValidateCHS(uint32_t cylinder, uint32_t track, uint32_t sector);
uint32_t GetSectorForCHS(uint32_t cylinder, uint32_t track, uint32_t sector);