1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-04-13 23:44:17 +00:00

Code cleanup/simplification. Moved stuff out to rp_drive_c that belonged there.

This commit is contained in:
Josh Dersch
2020-03-30 21:13:15 +02:00
parent b6f933a4a2
commit 8906fe3485
6 changed files with 149 additions and 189 deletions

View File

@@ -44,12 +44,14 @@ public:
// Writable bits
virtual uint16_t RegisterWritableBits(uint32_t register) = 0;
// Selects the specified unit
virtual void SelectUnit(uint32_t uint) = 0;
//
// MASSBUS Register reads and writes
// MASSBUS Register reads and writes (to the unit selected by SelectUnit)
//
virtual void WriteRegister(uint32_t unit, uint32_t register, uint16_t value) = 0;
virtual uint16_t ReadRegister(uint32_t unit, uint32_t register) = 0;
virtual void WriteRegister(uint32_t register, uint16_t value) = 0;
virtual uint16_t ReadRegister(uint32_t register) = 0;
//
// Block transfers

View File

@@ -36,9 +36,6 @@ massbus_rp_c::massbus_rp_c(
device_c(),
_controller(controller),
_selectedUnit(0),
_desiredSector(0),
_desiredTrack(0),
_desiredCylinder(0),
_workerState(WorkerState::Idle),
_workerWakeupCond(PTHREAD_COND_INITIALIZER),
_workerMutex(PTHREAD_MUTEX_INITIALIZER),
@@ -124,21 +121,33 @@ massbus_rp_c::RegisterWritableBits(
return _registerMetadata[reg].WritableBits;
}
void
massbus_rp_c::SelectUnit(
uint32_t unit)
{
_selectedUnit = unit;
UpdateStatus(_selectedUnit, false, false);
UpdateDriveRegisters();
}
void
massbus_rp_c::WriteRegister(
uint32_t unit,
uint32_t reg,
uint16_t value)
{
DEBUG("RP reg write: unit %d register 0%o value 0%o", unit, reg, value);
DEBUG("RP reg write: unit %d register 0%o value 0%o", _selectedUnit, reg, value);
if (!SelectedDrive()->IsDriveReady() &&
rp_drive_c* drive = SelectedDrive();
if (!drive->IsDriveReady() &&
reg != 0 && // CS1 is allowed as long as GO isn't set (will be checked in DoCommand)
reg != (uint32_t)Registers::AttentionSummary)
{
// Any attempt to modify a drive register other than Attention Summary
// while the drive is busy is invalid.
DEBUG("Register modification while drive busy.");
// while the drive is busy is invalid.
// For now treat this as fatal (it's not but real code shouldn't be doing it so this
// is a diagnostic at the moment.)
FATAL("Register modification while drive busy.");
_rmr = true;
UpdateStatus(_selectedUnit, false, false);
return;
@@ -147,24 +156,18 @@ massbus_rp_c::WriteRegister(
switch(static_cast<Registers>(reg))
{
case Registers::Control:
DoCommand(unit, value);
DoCommand(value);
break;
case Registers::DesiredSectorTrackAddress:
_desiredTrack = (value & 0x1f00) >> 8;
_desiredSector = (value & 0x1f);
UpdateDesiredSectorTrack();
DEBUG("Desired Sector Track Address: track %d, sector %d",
_desiredTrack,
_desiredSector);
drive->SetDesiredTrack((value & 0x1f00) >> 8);
drive->SetDesiredSector(value & 0x1f);
UpdateDriveRegisters();
break;
case Registers::DesiredCylinderAddress:
_desiredCylinder = value & 0x3ff;
UpdateDesiredCylinder();
DEBUG("Desired Cylinder Address o%o (o%o)", _desiredCylinder, value);
drive->SetDesiredCylinder(value & 0x3ff);
UpdateDriveRegisters();
break;
case Registers::AttentionSummary:
@@ -193,30 +196,27 @@ massbus_rp_c::WriteRegister(
}
void massbus_rp_c::DoCommand(
uint32_t unit,
uint16_t command)
{
FunctionCode function = static_cast<FunctionCode>((command & RP_FUNC) >> 1);
_selectedUnit = unit;
rp_drive_c* drive = SelectedDrive();
// 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())
if (!drive->IsConnected())
{
// Early return for disconnected drives;
// set NED and ERR bits
_err = true;
_ata = true;
_ned = true; // TODO: should be done at RH11 level!
SelectedDrive()->ClearVolumeValid();
drive->ClearVolumeValid();
UpdateStatus(_selectedUnit, true, false);
return;
}
@@ -286,19 +286,18 @@ 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."
//
SelectedDrive()->SetVolumeValid();
SelectedDrive()->SetDriveReady();
_desiredSector = 0;
_desiredTrack = 0;
_offset = 0;
UpdateDesiredSectorTrack();
UpdateOffset();
drive->SetVolumeValid();
drive->SetDriveReady();
drive->SetDesiredSector(0);
drive->SetDesiredTrack(0);;
drive->SetOffset(0);
UpdateDriveRegisters();
UpdateStatus(_selectedUnit, false, false); /* do not interrupt */
break;
case FunctionCode::PackAcknowledge:
DEBUG("RP Pack Acknowledge");
SelectedDrive()->SetVolumeValid();
drive->SetVolumeValid();
UpdateStatus(_selectedUnit, false, false);
break;
@@ -314,7 +313,7 @@ void massbus_rp_c::DoCommand(
DEBUG("RP Read/Write Data or head-motion command");
{
// Clear the unit's DRY bit
SelectedDrive()->ClearDriveReady();
drive->ClearDriveReady();
if (function == FunctionCode::Search ||
function == FunctionCode::Seek ||
@@ -322,7 +321,7 @@ void massbus_rp_c::DoCommand(
function == FunctionCode::Offset ||
function == FunctionCode::ReturnToCenterline)
{
SelectedDrive()->SetPositioningInProgress();
drive->SetPositioningInProgress();
}
UpdateStatus(_selectedUnit, false, false);
@@ -331,11 +330,7 @@ void massbus_rp_c::DoCommand(
// 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;
_newCommand.sector = _desiredSector;
_newCommand.bus_address = _controller->GetBusAddress();
_newCommand.word_count = _controller->GetWordCount();
_newCommand.ready = true;
@@ -355,10 +350,9 @@ void massbus_rp_c::DoCommand(
uint16_t
massbus_rp_c::ReadRegister(
uint32_t unit,
uint32_t reg)
{
DEBUG("RP reg read: unit %d register 0%o", unit, reg);
DEBUG("RP reg read: unit %d register 0%o", _selectedUnit, reg);
switch(static_cast<Registers>(reg))
{
@@ -384,7 +378,7 @@ massbus_rp_c::ReadRegister(
//
void
massbus_rp_c::UpdateStatus(
uint16_t unit,
uint32_t unit,
bool complete,
bool diagForceError)
{
@@ -445,36 +439,26 @@ massbus_rp_c::UpdateStatus(
}
void
massbus_rp_c::UpdateDesiredSectorTrack()
massbus_rp_c::UpdateDriveRegisters()
{
uint16_t desiredSectorTrack = (_desiredSector | (_desiredTrack << 8));
_controller->WriteRegister(static_cast<uint32_t>(Registers::DesiredSectorTrackAddress), desiredSectorTrack);
}
rp_drive_c* drive = SelectedDrive();
void
massbus_rp_c::UpdateDesiredCylinder()
{
_controller->WriteRegister(static_cast<uint32_t>(Registers::DesiredCylinderAddress), _desiredCylinder);
}
_controller->WriteRegister(static_cast<uint32_t>(Registers::CurrentCylinderAddress),
drive->GetCurrentCylinder());
void
massbus_rp_c::UpdateOffset()
{
_controller->WriteRegister(static_cast<uint32_t>(Registers::Offset), _offset);
}
_controller->WriteRegister(static_cast<uint32_t>(Registers::DesiredCylinderAddress),
drive->GetDesiredCylinder());
void
massbus_rp_c::UpdateCurrentCylinder()
{
_controller->WriteRegister(static_cast<uint32_t>(Registers::CurrentCylinderAddress),
SelectedDrive()->GetCurrentCylinder());
uint16_t desiredSectorTrack = drive->GetDesiredSector() | (drive->GetDesiredTrack() << 8);
_controller->WriteRegister(static_cast<uint32_t>(Registers::DesiredSectorTrackAddress),
desiredSectorTrack);
_controller->WriteRegister(static_cast<uint32_t>(Registers::Offset), drive->GetOffset());
}
void
massbus_rp_c::Reset()
{
// TODO: reset all drives
// Reset registers to their defaults
_ata = false;
_attnSummary = 0;
@@ -483,19 +467,8 @@ massbus_rp_c::Reset()
_selectedUnit = 0;
UpdateStatus(_selectedUnit, false, false);
_desiredSector = 0;
_desiredTrack = 0;
UpdateDesiredSectorTrack();
UpdateDriveRegisters();
_desiredCylinder = 0;
UpdateDesiredCylinder();
UpdateCurrentCylinder();
_offset = 0;
UpdateOffset();
_selectedUnit = 0;
_newCommand.ready = false;
}
@@ -548,31 +521,23 @@ massbus_rp_c::Worker()
break;
case WorkerState::Execute:
{
rp_drive_c* drive = GetDrive(command.unit);
switch(command.function)
{
case FunctionCode::ReadData:
{
DEBUG("READ CHS %d/%d/%d, %d words to address o%o",
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
_newCommand.bus_address);
uint16_t* buffer = nullptr;
if (_newCommand.drive->Read(
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
if (drive->Read(
command.word_count,
&buffer))
{
//
// Data read: do DMA transfer to memory.
//
_controller->DiskReadTransfer(
_newCommand.bus_address,
_newCommand.word_count,
command.bus_address,
command.word_count,
buffer);
// Free buffer
@@ -586,7 +551,7 @@ massbus_rp_c::Worker()
}
// Return drive to ready state
_newCommand.drive->SetDriveReady();
drive->SetDriveReady();
_workerState = WorkerState::Finish;
}
@@ -594,25 +559,15 @@ massbus_rp_c::Worker()
case FunctionCode::WriteData:
{
DEBUG("WRITE CHS %d/%d/%d, %d words from address o%o",
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
_newCommand.bus_address);
//
// Data write: do DMA transfer from memory.
//
uint16_t* buffer = _controller->DiskWriteTransfer(
_newCommand.bus_address,
_newCommand.word_count);
command.bus_address,
command.word_count);
if (!buffer || !_newCommand.drive->Write(
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector,
_newCommand.word_count,
if (!buffer || !drive->Write(
command.word_count,
buffer))
{
// Write failed:
@@ -623,7 +578,7 @@ massbus_rp_c::Worker()
delete buffer;
// Return drive to ready state
_newCommand.drive->SetDriveReady();
drive->SetDriveReady();
_workerState = WorkerState::Finish;
}
@@ -631,15 +586,7 @@ massbus_rp_c::Worker()
case FunctionCode::Search:
{
DEBUG("SEARCH CHS %d/%d/%d",
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector);
if (!_newCommand.drive->Search(
_newCommand.cylinder,
_newCommand.track,
_newCommand.sector))
if (!drive->Search())
{
// Search failed
DEBUG("Search failed");
@@ -647,20 +594,19 @@ massbus_rp_c::Worker()
// Return to ready state, set attention bit.
_newCommand.drive->SetDriveReady();
drive->SetDriveReady();
_ata = true;
_workerState = WorkerState::Finish;
}
break;
case FunctionCode::Seek:
DEBUG("SEEK Cylinder %d", _newCommand.cylinder);
if (!_newCommand.drive->SeekTo(_newCommand.cylinder))
if (!drive->SeekTo())
{
// Seek failed
DEBUG("Seek failed");
}
_newCommand.drive->SetDriveReady();
drive->SetDriveReady();
_ata = true;
_workerState = WorkerState::Finish;
break;
@@ -673,7 +619,7 @@ massbus_rp_c::Worker()
// to complete.
DEBUG("OFFSET/RETURN TO CL");
timeout.wait_ms(10);
_newCommand.drive->SetDriveReady();
drive->SetDriveReady();
_ata = true;
_workerState = WorkerState::Finish;
break;
@@ -682,14 +628,14 @@ massbus_rp_c::Worker()
FATAL("Unimplemented drive function %d", command.function);
break;
}
}
break;
case WorkerState::Finish:
_workerState = WorkerState::Idle;
_newCommand.drive->SetDriveReady();
UpdateCurrentCylinder();
GetDrive(_newCommand.unit)->SetDriveReady();
UpdateStatus(_newCommand.unit, true, false);
UpdateDriveRegisters();
break;
}

View File

@@ -102,8 +102,10 @@ public:
uint16_t RegisterResetValue(uint32_t register) override;
uint16_t RegisterWritableBits(uint32_t register) override;
void WriteRegister(uint32_t unit, uint32_t register, uint16_t value) override;
uint16_t ReadRegister(uint32_t unit, uint32_t register) override;
void WriteRegister(uint32_t register, uint16_t value) override;
uint16_t ReadRegister(uint32_t register) override;
void SelectUnit(uint32_t unit);
// Background worker functions
void Worker();
@@ -113,13 +115,9 @@ private:
struct WorkerCommand
{
uint16_t unit;
rp_drive_c* drive;
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;
@@ -130,16 +128,13 @@ private:
Finish = 2,
} _workerState;
void DoCommand(uint32_t unit, uint16_t command);
void DoCommand(uint16_t command);
void on_power_changed(void) override;
void on_init_changed(void) override;
void UpdateStatus(uint16_t unit, bool completion, bool diagForceError);
void UpdateDesiredSectorTrack();
void UpdateDesiredCylinder();
void UpdateOffset();
void UpdateCurrentCylinder();
void UpdateStatus(uint32_t unit, bool completion, bool diagForceError);
void UpdateDriveRegisters();
rp_drive_c* SelectedDrive();
rp_drive_c* GetDrive(uint16_t unit);
@@ -176,11 +171,6 @@ private:
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;

View File

@@ -615,7 +615,7 @@ void rh11_c::on_after_register_access(
if ((dato_mask & 0x00ff) == 0x00ff)
{
// Let the massbus device take a crack at the shared bits.
_massbus->WriteRegister(_unit, RHCS1, value & 077);
_massbus->WriteRegister(RHCS1, value & 077);
}
}
}
@@ -625,7 +625,7 @@ void rh11_c::on_after_register_access(
{
if (UNIBUS_CONTROL_DATO == unibus_control)
{
_unit = (value & 07);
uint16_t newUnit = (value & 07);
_busAddressIncrementProhibit = !!(value & 010);
_parityTest = !!(value & 020);
_controllerClear = !!(value & 040);
@@ -634,13 +634,25 @@ void rh11_c::on_after_register_access(
DEBUG("RHCS2 write: o%o", value);
DEBUG("RHCS2: perror %d, unit %d inc %d ptest %d clear %d",
_parityError, _unit, _busAddressIncrementProhibit, _parityTest, _controllerClear);
// On unit change, select new drive
if (newUnit != _unit)
{
_unit = newUnit;
_massbus->SelectUnit(_unit);
}
// TODO: handle System Register Clear (bit 5)
if (_controllerClear)
{
DEBUG("Controller Clear");
_interruptEnable = false;
for (uint32_t i=0; i<drivecount; i++)
{
GetDrive(i)->Reset();
}
_massbus->Reset();
// clear error and the clear bits
@@ -691,7 +703,7 @@ void rh11_c::on_after_register_access(
{
if (UNIBUS_CONTROL_DATO == unibus_control)
{
_massbus->WriteRegister(_unit, _unibusToMassbusRegisterMap[device_reg->index], value);
_massbus->WriteRegister(_unibusToMassbusRegisterMap[device_reg->index], value);
// Massbus is responsible for writing back the appropriate dati value.
}
else
@@ -699,7 +711,7 @@ void rh11_c::on_after_register_access(
DEBUG("massbus reg read %o", device_reg->index);
set_register_dati_value(
device_reg,
_massbus->ReadRegister(_unit, _unibusToMassbusRegisterMap[device_reg->index]),
_massbus->ReadRegister(_unibusToMassbusRegisterMap[device_reg->index]),
"on_after_register_access");
}
}
@@ -715,6 +727,8 @@ void rh11_c::on_after_register_access(
void rh11_c::reset_controller(void)
{
reset_unibus_registers();
// TODO: do more
}

View File

@@ -18,6 +18,10 @@ using namespace std;
rp_drive_c::rp_drive_c(storagecontroller_c *controller, uint32_t driveNumber) :
storagedrive_c(controller),
_driveNumber(driveNumber),
_desiredCylinder(0),
_currentCylinder(0),
_desiredTrack(0),
_offset(0),
_ready(true),
_lst(false),
_aoe(false),
@@ -39,6 +43,12 @@ rp_drive_c::~rp_drive_c()
}
}
void
rp_drive_c::Reset()
{
}
// on_param_changed():
// Handles configuration parameter changes.
bool
@@ -96,20 +106,19 @@ rp_drive_c::IsPackLoaded()
}
bool
rp_drive_c::SeekTo(
uint32_t destinationCylinder)
rp_drive_c::SeekTo()
{
// TODO: delay by appropriate amount
timeout_c timeout;
_iae = !(destinationCylinder < _driveInfo.Cylinders);
_iae = !(_desiredCylinder < _driveInfo.Cylinders);
if (IsConnected() && IsPackLoaded() && !_iae)
{
timeout.wait_ms(20);
_currentCylinder = destinationCylinder;
_currentCylinder = _desiredCylinder;
return true;
}
else
@@ -118,12 +127,6 @@ rp_drive_c::SeekTo(
}
}
uint32_t
rp_drive_c::GetCurrentCylinder()
{
return _currentCylinder;
}
//
// TODO: on all reads/writes, an implied seek takes place if the
@@ -136,13 +139,10 @@ rp_drive_c::GetCurrentCylinder()
//
bool
rp_drive_c::Write(
uint32_t cylinder,
uint32_t track,
uint32_t sector,
size_t countInWords,
uint16_t* buffer)
{
_iae = !ValidateCHS(cylinder, track, sector);
_iae = !ValidateCHS(_desiredCylinder, _desiredTrack, _desiredSector);
_wle = IsWriteLocked();
// TODO: handle address overflow
@@ -153,11 +153,11 @@ rp_drive_c::Write(
}
else
{
_currentCylinder = cylinder;
uint32_t offset = GetSectorForCHS(cylinder, track, sector);
_currentCylinder = _desiredCylinder;
uint32_t offset = GetSectorForCHS(_currentCylinder, _desiredTrack, _desiredSector);
file_write(reinterpret_cast<uint8_t*>(buffer), offset * GetSectorSize(), countInWords * 2);
//timeout_c timeout;
//timeout.wait_ms(20);
timeout_c timeout;
timeout.wait_us(500);
return true;
}
@@ -170,51 +170,45 @@ rp_drive_c::Write(
//
bool
rp_drive_c::Read(
uint32_t cylinder,
uint32_t track,
uint32_t sector,
size_t countInWords,
uint16_t** buffer)
{
_iae = !ValidateCHS(cylinder, track, sector);
_iae = !ValidateCHS(_desiredCylinder, _desiredTrack, _desiredSector);
_wle = false;
if (!IsConnected() || !IsPackLoaded() || _iae)
{
*buffer = nullptr;
DEBUG("Failure: connected %d loaded %d valid %d", IsConnected(), IsPackLoaded(), ValidateCHS(cylinder, track, sector));
DEBUG("Failure: connected %d loaded %d valid %d", IsConnected(), IsPackLoaded(), _iae);
return false;
}
else
{
_currentCylinder = cylinder;
_currentCylinder = _desiredCylinder;
*buffer = new uint16_t[countInWords];
assert(nullptr != *buffer);
uint32_t offset = GetSectorForCHS(cylinder, track, sector);
uint32_t offset = GetSectorForCHS(_currentCylinder, _desiredTrack, _desiredSector);
DEBUG("Read from sector offset o%o", offset);
file_read(reinterpret_cast<uint8_t*>(*buffer), offset * GetSectorSize(), countInWords * 2);
//timeout_c timeout;
//timeout.wait_ms(20);
timeout_c timeout;
timeout.wait_us(500);
return true;
}
}
bool
rp_drive_c::Search(
uint32_t cylinder,
uint32_t track,
uint32_t sector)
rp_drive_c::Search(void)
{
_iae = !ValidateCHS(cylinder, track, sector);
_iae = !ValidateCHS(_desiredCylinder, _desiredTrack, _desiredSector);
if (!IsConnected() || !IsPackLoaded() || _iae)
{
DEBUG("Failure: connected &d loaded %d valid %d", IsConnected(), IsPackLoaded(), ValidateCHS(cylinder, track, sector));
DEBUG("Failure: connected &d loaded %d valid %d", IsConnected(), IsPackLoaded(), _iae);
return false;
}
else
@@ -226,7 +220,7 @@ rp_drive_c::Search(
timeout.wait_ms(20);
_pip = false;
DEBUG("Search completed.");
_currentCylinder = cylinder;
_currentCylinder = _desiredCylinder;
return true;
}

View File

@@ -23,12 +23,24 @@ public:
rp_drive_c(storagecontroller_c *controller, uint32_t driveNumber);
~rp_drive_c(void);
void Reset();
bool on_param_changed(parameter_c *param) override;
uint32_t GetSectorSize(void);
uint32_t GetType(void);
bool IsConnected(void) { return _driveNumber == 0; /* todo: make config. parameter */ }
void SetDesiredCylinder(uint32_t cylinder) { _desiredCylinder = cylinder; }
void SetDesiredTrack(uint32_t track) { _desiredTrack = track; }
void SetDesiredSector(uint32_t sector) { _desiredSector = sector; }
void SetOffset(uint16_t offset) { _offset = offset; }
uint32_t GetDesiredCylinder(void) { return _desiredCylinder; }
uint32_t GetDesiredTrack(void) { return _desiredTrack; }
uint32_t GetDesiredSector(void) { return _desiredSector; }
uint16_t GetOffset(void) { return _offset; }
uint32_t GetCurrentCylinder(void) { return _currentCylinder; }
bool IsConnected(void) { return true; /* todo: make config. parameter */ }
bool IsPackLoaded(void);
bool IsDriveReady(void) { return _ready; }
bool IsWriteLocked(void) { return false; /* for now */ }
@@ -46,18 +58,20 @@ public:
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();
bool Write(uint32_t cylinder, uint32_t track, uint32_t sector, uint32_t countInWords, uint16_t* buffer);
bool Read(uint32_t cylinder, uint32_t track, uint32_t sector, uint32_t countInWords, uint16_t** outBuffer);
bool Search(uint32_t cylinder, uint32_t track, uint32_t sector);
bool SeekTo();
bool Write(uint32_t countInWords, uint16_t* buffer);
bool Read(uint32_t countInWords, uint16_t** outBuffer);
bool Search();
public:
void on_power_changed(void) override;
void on_init_changed(void) override;
private:
uint32_t _desiredCylinder;
uint32_t _desiredTrack;
uint32_t _desiredSector;
uint16_t _offset;
uint32_t _currentCylinder;
uint32_t _driveNumber;
bool _ready;