diff --git a/10.01_base/2_src/arm/storagedrive.cpp b/10.01_base/2_src/arm/storagedrive.cpp index 9c261c5..92b1e0d 100644 --- a/10.01_base/2_src/arm/storagedrive.cpp +++ b/10.01_base/2_src/arm/storagedrive.cpp @@ -145,6 +145,12 @@ void storagedrive_c::file_write(uint8_t *buffer, uint64_t position, unsigned len f.flush(); } +uint64_t storagedrive_c::file_size(void) +{ + f.seekp(0, ios::end); + return f.tellp(); +} + void storagedrive_c::file_close(void) { assert(file_is_open()); f.close(); diff --git a/10.01_base/2_src/arm/storagedrive.hpp b/10.01_base/2_src/arm/storagedrive.hpp index fae8267..4c63287 100644 --- a/10.01_base/2_src/arm/storagedrive.hpp +++ b/10.01_base/2_src/arm/storagedrive.hpp @@ -67,6 +67,7 @@ public: bool file_is_open(void); void file_read(uint8_t *buffer, uint64_t position, unsigned len); void file_write(uint8_t *buffer, uint64_t position, unsigned len); + uint64_t file_size(void); void file_close(void); storagedrive_c(storagecontroller_c *controller); diff --git a/10.02_devices/2_src/mscp_drive.cpp b/10.02_devices/2_src/mscp_drive.cpp index 47fe260..03e17ab 100644 --- a/10.02_devices/2_src/mscp_drive.cpp +++ b/10.02_devices/2_src/mscp_drive.cpp @@ -3,6 +3,7 @@ */ #include +#include using namespace std; @@ -14,7 +15,8 @@ using namespace std; mscp_drive_c::mscp_drive_c( storagecontroller_c *controller, uint32_t driveNumber) : - storagedrive_c(controller) + storagedrive_c(controller), + _useImageSize(false) { log_label = "MSCPD"; SetDriveType("RA81"); @@ -22,7 +24,11 @@ mscp_drive_c::mscp_drive_c( // Calculate the unit's ID: // drive number in upper 32 bits, class/model in lower. - _unitID = (static_cast(0xffffffff) << 32) | 0x02020000; + _unitID = (static_cast(driveNumber) << 32) | 0x02020000; + + // Initialize the RCT area + _rctData.reset(new uint8_t[GetBlockSize()]); + memset(reinterpret_cast(_rctData.get()), 0, GetBlockSize()); } mscp_drive_c::~mscp_drive_c() @@ -43,9 +49,27 @@ uint32_t mscp_drive_c::GetBlockSize() uint32_t mscp_drive_c::GetBlockCount() { - // TODO: need to be able to handle drives of arbitrary size, not just - // DEC-branded units. - return _driveInfo.BlockCount; + if (_useImageSize) + { + // Return the image size / Block size (rounding down). + return file_size() / GetBlockSize(); + } + else + { + // + // Use the size defined by the drive type. + // + return _driveInfo.BlockCount; + } +} + +uint32_t mscp_drive_c::GetRCTBlockCount() +{ + // + // We provide only a single RCT block, required by the MSCP spec for the volume + // write-protect flags. + // + return 1; } uint32_t mscp_drive_c::GetMediaID() @@ -123,6 +147,50 @@ uint8_t* mscp_drive_c::Read( return buffer; } +// +// Writes a single block's worth of data from the provided buffer into the +// RCT area at the specified RCT block. Buffer must be at least as large +// as the disk's block size. +// +void mscp_drive_c::WriteRCTBlock( + uint32_t rctBlockNumber, + uint8_t* buffer) +{ + assert (rctBlockNumber < GetRCTBlockCount()); + + memcpy( + reinterpret_cast(_rctData.get() + rctBlockNumber * GetBlockSize()), + reinterpret_cast(buffer), + GetBlockSize()); +} + +// +// Reads a single block's worth of data from the RCT area (at the specified +// block offset). Returns a pointer to a buffer containing the data read. +// Caller is responsible for freeing this buffer. +// +uint8_t* mscp_drive_c::ReadRCTBlock( + uint32_t rctBlockNumber) +{ + assert (rctBlockNumber < GetRCTBlockCount()); + + uint8_t* buffer = new uint8_t[GetBlockSize()]; + assert (nullptr != buffer); + + memcpy( + reinterpret_cast(buffer), + reinterpret_cast(_rctData.get() + rctBlockNumber * GetBlockSize()), + GetBlockSize()); + + return buffer; +} + +void mscp_drive_c::UpdateCapacity() +{ + capacity.value = + GetBlockCount() * GetBlockSize(); +} + bool mscp_drive_c::on_param_changed( parameter_c *param) { @@ -138,12 +206,20 @@ bool mscp_drive_c::on_param_changed( if (file_open(image_filepath.new_value, true)) { image_filepath.value = image_filepath.new_value; + UpdateCapacity(); return true; } // // TODO: if file is a nonstandard size? } + else if (&use_image_size == param) + { + _useImageSize = use_image_size.new_value; + use_image_size.value = use_image_size.new_value; + UpdateCapacity(); + return true; + } return false; } @@ -161,7 +237,7 @@ bool mscp_drive_c::SetDriveType(const char* typeName) { _driveInfo = g_driveTable[index]; type_name.value = _driveInfo.TypeName; - capacity.value = GetBlockCount() * GetBlockSize(); + UpdateCapacity(); return true; } diff --git a/10.02_devices/2_src/mscp_drive.hpp b/10.02_devices/2_src/mscp_drive.hpp index 8bb856a..318dab5 100644 --- a/10.02_devices/2_src/mscp_drive.hpp +++ b/10.02_devices/2_src/mscp_drive.hpp @@ -20,6 +20,7 @@ public: uint32_t GetBlockSize(void); uint32_t GetBlockCount(void); + uint32_t GetRCTBlockCount(void); uint32_t GetMediaID(void); uint64_t GetUnitID(void); @@ -35,7 +36,14 @@ public: uint8_t* Read( uint32_t blockNumber, - size_t lengthInBytes); + size_t lengthInBytes); + + void WriteRCTBlock( + uint32_t rctBlockNumber, + uint8_t* buffer); + + uint8_t* ReadRCTBlock( + uint32_t rctBlockNumber); public: bool on_param_changed(parameter_c *param) override; @@ -44,6 +52,10 @@ public: void worker(void) override; +public: + parameter_bool_c use_image_size = parameter_bool_c( + this, "useimagesize", "uis", false, "Determine unit size from image file instead of drive type"); + private: struct DriveInfo @@ -55,7 +67,7 @@ private: bool ReadOnly; }; - DriveInfo g_driveTable[23] + DriveInfo g_driveTable[22] { { "RX50", 800, 0x25658032, true, false }, { "RX33", 2400, 0x25658021, true, false }, @@ -78,12 +90,23 @@ private: { "RA90", 2376153, 0x2564105a, false, false }, { "RA92", 2940951, 0x2564105c, false, false }, { "RA73", 3920490, 0x25641049, false, false }, - { "JD90", 2376153, 0x2564105d, false, false }, { "", 0, 0, false, false } }; bool SetDriveType(const char* typeName); + void UpdateCapacity(void); DriveInfo _driveInfo; bool _online; uint64_t _unitID; + bool _useImageSize; + + // + // RCT ("Replacement and Caching Table") data: + // At this time we provide the minimum required by the MSCP spec, + // a single block used to provide the volume write-protect flags. + // We do not require additional RCT space because the underlying media + // (an image file) doesn't have bad sectors. + // This data is not persisted to disk as it is unnecessary. + // + unique_ptr _rctData; }; diff --git a/10.02_devices/2_src/mscp_server.cpp b/10.02_devices/2_src/mscp_server.cpp index 3bf63af..a95d06b 100644 --- a/10.02_devices/2_src/mscp_server.cpp +++ b/10.02_devices/2_src/mscp_server.cpp @@ -1,7 +1,38 @@ +/* + mscp_server.cpp: Implementation a simple MSCP server. + + This provides an implementation of the Minimal MSCP subset outlined + in AA-L619A-TK (Chapter 6). It takes a few liberties and errs on + the side of implementation simplicity. + + In particular: + All commands are executed sequentially, as they appear in the + command ring. This includes any commands in the "Immediate" + category. Technically this is incorrect: Immediate commands + should execute as soon as possible, before any other commands. + In practice I have yet to find code that cares. + + This simplifies the implementation significantly, and apart + from maintaining fealty to the MSCP spec for Immediate commands, + there's no good reason to make it more complex: real MSCP + controllers (like the original UDA50) would resequence commands + to allow optimal throughput across multiple units, etc. On the + unibone, the underlying storage and the execution speed of the + processor is orders of magnitude faster, so even a brute-force + braindead implementation like this can saturate the Unibus. + + TODO: + Some commands aren't checked as thoroughly for errors as they could be, + and at this time NXM (attempts to address non-existent memory) are + almost completely unhandled. + + +*/ #include #include #include +#include using namespace std; @@ -163,11 +194,24 @@ mscp_server::Poll(void) header->ReferenceNumber); uint32_t cmdStatus = 0; + uint16_t modifiers = header->Word3.Command.Modifiers; switch (header->Word3.Command.Opcode) { + case Opcodes::ABORT: + cmdStatus = Abort(message); + break; + + case Opcodes::ACCESS: + cmdStatus = Access(message, header->UnitNumber); + break; + case Opcodes::AVAILABLE: - cmdStatus = Available(message, header->UnitNumber, header->Word3.Command.Modifiers); + cmdStatus = Available(message, header->UnitNumber, modifiers); + break; + + case Opcodes::COMPARE_HOST_DATA: + cmdStatus = CompareHostData(message, header->UnitNumber); break; case Opcodes::DETERMINE_ACCESS_PATHS: @@ -175,15 +219,27 @@ mscp_server::Poll(void) break; case Opcodes::ERASE: - cmdStatus = Erase(message, header->UnitNumber, header->Word3.Command.Modifiers); + cmdStatus = Erase(message, header->UnitNumber, modifiers); + break; + + case Opcodes::GET_COMMAND_STATUS: + cmdStatus = GetCommandStatus(message); break; case Opcodes::GET_UNIT_STATUS: - cmdStatus = GetUnitStatus(message, header->UnitNumber, header->Word3.Command.Modifiers); + cmdStatus = GetUnitStatus(message, header->UnitNumber, modifiers); break; case Opcodes::ONLINE: - cmdStatus = Online(message, header->UnitNumber, header->Word3.Command.Modifiers); + cmdStatus = Online(message, header->UnitNumber, modifiers); + break; + + case Opcodes::READ: + cmdStatus = Read(message, header->UnitNumber, modifiers); + break; + + case Opcodes::REPLACE: + cmdStatus = Replace(message, header->UnitNumber); break; case Opcodes::SET_CONTROLLER_CHARACTERISTICS: @@ -191,15 +247,11 @@ mscp_server::Poll(void) break; case Opcodes::SET_UNIT_CHARACTERISTICS: - cmdStatus = SetUnitCharacteristics(message, header->UnitNumber, header->Word3.Command.Modifiers); - break; - - case Opcodes::READ: - cmdStatus = Read(message, header->UnitNumber, header->Word3.Command.Modifiers); + cmdStatus = SetUnitCharacteristics(message, header->UnitNumber, modifiers); break; case Opcodes::WRITE: - cmdStatus = Write(message, header->UnitNumber, header->Word3.Command.Modifiers); + cmdStatus = Write(message, header->UnitNumber, modifiers); break; default: @@ -260,13 +312,14 @@ mscp_server::Poll(void) } // - // This delay works around an issue in the VMS bootstrap -- unsure of the + // This delay works around an issue in various bootstraps -- unsure of the // exact cause, it appears to be a race condition exacerbated by the speed // at which we're able to process commands in the ring buffer (we are much - // faster than any real MSCP device ever was.) + // faster than any real MSCP device ever was) and the liberties that bootstrap + // code takes with the MSCP spec to save code space. // This is likely a hack, we may be papering over a bug somewhere. // - timer.wait_us(100); + timer.wait_us(2000); // // Go around and pick up the next one. @@ -296,6 +349,25 @@ mscp_server::Poll(void) DEBUG("MSCP Polling thread exiting."); } +uint32_t +mscp_server::Abort( + shared_ptr message) +{ + INFO("MSCP ABORT"); + + // + // Since we do not reorder messages and in fact pick up and execute + // them one at a time, sequentially as they appear in the ring buffer, + // by the time we've gotten this command, the command it's referring + // to is long gone. + // This is legal behavior and it's legal for us to ignore ABORT in this + // case. + // + // We just return SUCCESS here. + return STATUS(Status::SUCCESS, 0, 0); +} + + uint32_t mscp_server::Available( shared_ptr message, @@ -322,6 +394,33 @@ mscp_server::Available( return STATUS(Status::SUCCESS, 0x40, 0); // still connected } +uint32_t +mscp_server::Access( + shared_ptr message, + uint16_t unitNumber) +{ + INFO("MSCP ACCESS"); + + return DoDiskTransfer( + Opcodes::ACCESS, + message, + unitNumber, + 0); +} + +uint32_t +mscp_server::CompareHostData( + shared_ptr message, + uint16_t unitNumber) +{ + INFO("MSCP COMPARE HOST DATA"); + return DoDiskTransfer( + Opcodes::COMPARE_HOST_DATA, + message, + unitNumber, + 0); +} + uint32_t mscp_server::DetermineAccessPaths( shared_ptr message, @@ -342,66 +441,40 @@ mscp_server::Erase( uint16_t unitNumber, uint16_t modifiers) { + return DoDiskTransfer( + Opcodes::ERASE, + message, + unitNumber, + modifiers); +} + +uint32_t +mscp_server::GetCommandStatus( + shared_ptr message) +{ + INFO("MSCP GET COMMAND STATUS"); + #pragma pack(push,1) - struct EraseParameters + struct GetCommandStatusResponseParameters { - uint32_t ByteCount; - uint32_t Unused0; - uint32_t Unused1; - uint32_t Unused2; - uint32_t LBN; + uint32_t OutstandingReferenceNumber; + uint32_t CommandStatus; }; #pragma pack(pop) - // TODO: Factor this code out (shared w/Read and Write) - EraseParameters* params = - reinterpret_cast(GetParameterPointer(message)); + message->MessageLength = sizeof(GetCommandStatusResponseParameters) + + HEADER_SIZE; - DEBUG("MSCP ERASE unit %d chan count %d lbn %d", - unitNumber, - params->ByteCount, - params->LBN); - - // Adjust message length for response - message->MessageLength = sizeof(EraseParameters) + - HEADER_SIZE; - - mscp_drive_c* drive = GetDrive(unitNumber); - - // Check unit - if (nullptr == drive || - !drive->IsAvailable()) - { - return STATUS(Status::UNIT_OFFLINE, 0x3, 0); - } - - // Check LBN - if (params->LBN > drive->GetBlockCount() + 1) // + 1 for RCT - { - return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0, 0); // TODO: set sub-code - } - - // Check byte count - if (params->ByteCount > ((drive->GetBlockCount() + 1) - params->LBN) * drive->GetBlockSize()) - { - return STATUS(Status::INVALID_COMMAND + (0x0c << 8), 0, 0); // TODO: as above - } + + GetCommandStatusResponseParameters* params = + reinterpret_cast( + GetParameterPointer(message)); // - // OK: do the transfer from a zero'd out buffer to disk + // This will always return zero; as with the ABORT command, at this + // point the command being referenced has already been executed. // - unique_ptr memBuffer(new uint8_t[params->ByteCount]); - memset(reinterpret_cast(memBuffer.get()), 0, params->ByteCount); - - drive->Write(params->LBN, - params->ByteCount, - memBuffer.get()); - - // Set parameters for response. - // We leave ByteCount as is (for now anyway) - // And set First Bad Block to 0. (This is unnecessary since we're - // not reporting a bad block, but we're doing it for completeness.) - params->LBN = 0; + params->CommandStatus = 0; return STATUS(Status::SUCCESS, 0, 0); } @@ -592,6 +665,31 @@ mscp_server::Online( (alreadyOnline ? SuccessSubcodes::ALREADY_ONLINE : SuccessSubcodes::NORMAL), 0, 0); } +uint32_t +mscp_server::Replace( + shared_ptr message, + uint16_t unitNumber) +{ + INFO("MSCP REPLACE"); + // + // We treat this as a success for valid units as we do no block replacement at all. + // Best just to smile and nod. We could be more vigilant and check LBNs, etc... + // + message->MessageLength = HEADER_SIZE; + + mscp_drive_c* drive = GetDrive(unitNumber); + + if (nullptr == drive || + !drive->IsAvailable()) + { + return STATUS(Status::UNIT_OFFLINE, 0x3, 0); // unknown -- todo move to enum + } + else + { + return STATUS(Status::SUCCESS, 0, 0); + } +} + uint32_t mscp_server::SetControllerCharacteristics( shared_ptr message) @@ -715,71 +813,11 @@ mscp_server::Read( uint16_t unitNumber, uint16_t modifiers) { - #pragma pack(push,1) - struct ReadParameters - { - uint32_t ByteCount; - uint32_t BufferPhysicalAddress; // upper 8 bits are channel address for VAXen - uint32_t Unused0; - uint32_t Unused1; - uint32_t LBN; - }; - #pragma pack(pop) - - ReadParameters* params = - reinterpret_cast(GetParameterPointer(message)); - - DEBUG("MSCP READ unit %d chan o%o pa o%o count %d lbn %d", + return DoDiskTransfer( + Opcodes::READ, + message, unitNumber, - params->BufferPhysicalAddress >> 24, - params->BufferPhysicalAddress & 0x00ffffff, - params->ByteCount, - params->LBN); - - // Adjust message length for response - message->MessageLength = sizeof(ReadParameters) + - HEADER_SIZE; - - mscp_drive_c* drive = GetDrive(unitNumber); - - // Check unit - if (nullptr == drive || - !drive->IsAvailable()) - { - return STATUS(Status::UNIT_OFFLINE, 0x3, 0); - } - - // TODO: Need to rectify reads/writes to RCT area more cleanly - // and enforce block size of 512 for RCT area. - - // Check LBN and byte count - if (params->LBN >= drive->GetBlockCount() + 1) // + 1 for RCT write protect flag - { - return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0, 0); // TODO: set sub-code - } - - if (params->ByteCount > ((drive->GetBlockCount() + 1) - params->LBN) * drive->GetBlockSize()) - { - return STATUS(Status::INVALID_COMMAND + (0xc << 8), 0, 0); // TODO: as above - } - - // - // OK: do the transfer to memory - // - unique_ptr diskBuffer(drive->Read(params->LBN, params->ByteCount)); - - _port->DMAWrite( - params->BufferPhysicalAddress & 0x00ffffff, - params->ByteCount, - diskBuffer.get()); - - // Set parameters for response. - // We leave ByteCount as is (for now anyway) - // And set First Bad Block to 0. (This is unnecessary since we're - // not reporting a bad block, but we're doing it for completeness.) - params->LBN = 0; - - return STATUS(Status::SUCCESS, 0, 0); + modifiers); } uint32_t @@ -787,9 +825,23 @@ mscp_server::Write( shared_ptr message, uint16_t unitNumber, uint16_t modifiers) +{ + return DoDiskTransfer( + Opcodes::WRITE, + message, + unitNumber, + modifiers); +} + +uint32_t +mscp_server::DoDiskTransfer( + uint16_t operation, + shared_ptr message, + uint16_t unitNumber, + uint16_t modifiers) { #pragma pack(push,1) - struct WriteParameters + struct ReadWriteEraseParameters { uint32_t ByteCount; uint32_t BufferPhysicalAddress; // upper 8 bits are channel address for VAXen @@ -799,11 +851,11 @@ mscp_server::Write( }; #pragma pack(pop) - // TODO: Factor this code out (shared w/Read) - WriteParameters* params = - reinterpret_cast(GetParameterPointer(message)); + ReadWriteEraseParameters* params = + reinterpret_cast(GetParameterPointer(message)); - DEBUG("MSCP WRITE unit %d chan o%o pa o%o count %d lbn %d", + DEBUG("MSCP RWE 0x%x unit %d chan o%o pa o%o count %d lbn %d", + operation, unitNumber, params->BufferPhysicalAddress >> 24, params->BufferPhysicalAddress & 0x00ffffff, @@ -811,7 +863,7 @@ mscp_server::Write( params->LBN); // Adjust message length for response - message->MessageLength = sizeof(WriteParameters) + + message->MessageLength = sizeof(ReadWriteEraseParameters) + HEADER_SIZE; mscp_drive_c* drive = GetDrive(unitNumber); @@ -823,29 +875,130 @@ mscp_server::Write( return STATUS(Status::UNIT_OFFLINE, 0x3, 0); } - // Check LBN - if (params->LBN > drive->GetBlockCount() + 1) // + 1 for RCT + // Are we accessing the RCT area? + bool rctAccess = params->LBN >= drive->GetBlockCount(); + uint32_t rctBlockNumber = params->LBN - drive->GetBlockCount(); + + // Check that the LBN is valid + if (params->LBN >= drive->GetBlockCount() + drive->GetRCTBlockCount()) { return STATUS(Status::INVALID_COMMAND + (0x1c << 8), 0, 0); // TODO: set sub-code } - // Check byte count - if (params->ByteCount > ((drive->GetBlockCount() + 1) - params->LBN) * drive->GetBlockSize()) + // Check byte count: + if (params->ByteCount > ((drive->GetBlockCount() + drive->GetRCTBlockCount()) - params->LBN) * drive->GetBlockSize()) { return STATUS(Status::INVALID_COMMAND + (0x0c << 8), 0, 0); // TODO: as above } + // If this is an RCT access, byte count must equal the block size. + if (rctAccess && params->ByteCount != drive->GetBlockSize()) + { + return STATUS(Status::INVALID_COMMAND + (0x0c << 8), 0, 0); // TODO: again + } + // // OK: do the transfer from the PDP-11 to a buffer // - unique_ptr memBuffer(_port->DMARead( - params->BufferPhysicalAddress & 0x00ffffff, - params->ByteCount, - params->ByteCount)); + switch (operation) + { + case Opcodes::ACCESS: + // We don't need to actually do any sort of transfer; ACCESS merely checks + // That the data can be read -- we checked the LBN, etc. above and we + // will never encounter a read error, so there's nothing left to do. + break; - drive->Write(params->LBN, - params->ByteCount, - memBuffer.get()); + case Opcodes::COMPARE_HOST_DATA: + { + // Read the data in from disk, read the data in from memory, and compare. + unique_ptr diskBuffer; + + if (rctAccess) + { + diskBuffer.reset(drive->ReadRCTBlock(rctBlockNumber)); + } + else + { + diskBuffer.reset(drive->Read(params->LBN, params->ByteCount)); + } + + unique_ptr memBuffer(_port->DMARead( + params->BufferPhysicalAddress & 0x00ffffff, + params->ByteCount, + params->ByteCount)); + + if (!memcmp(diskBuffer.get(), memBuffer.get(), params->ByteCount)) + { + // TODO: maybe not do an early return, make code not as ugly? Hm. + return STATUS(Status::COMPARE_ERROR, 0, 0); + } + } + + case Opcodes::ERASE: + { + unique_ptr memBuffer(new uint8_t[params->ByteCount]); + memset(reinterpret_cast(memBuffer.get()), 0, params->ByteCount); + + if (rctAccess) + { + drive->WriteRCTBlock(rctBlockNumber, + memBuffer.get()); + } + else + { + drive->Write(params->LBN, + params->ByteCount, + memBuffer.get()); + } + } + break; + + case Opcodes::READ: + { + unique_ptr diskBuffer; + + if (rctAccess) + { + diskBuffer.reset(drive->ReadRCTBlock(rctBlockNumber)); + } + else + { + diskBuffer.reset(drive->Read(params->LBN, params->ByteCount)); + } + + _port->DMAWrite( + params->BufferPhysicalAddress & 0x00ffffff, + params->ByteCount, + diskBuffer.get()); + } + break; + + case Opcodes::WRITE: + { + unique_ptr memBuffer(_port->DMARead( + params->BufferPhysicalAddress & 0x00ffffff, + params->ByteCount, + params->ByteCount)); + + if (rctAccess) + { + drive->WriteRCTBlock(rctBlockNumber, + memBuffer.get()); + } + else + { + drive->Write(params->LBN, + params->ByteCount, + memBuffer.get()); + } + } + break; + + default: + // Should never happen. + assert(false); + break; + } // Set parameters for response. // We leave ByteCount as is (for now anyway) diff --git a/10.02_devices/2_src/mscp_server.hpp b/10.02_devices/2_src/mscp_server.hpp index 9110ea4..73373a0 100644 --- a/10.02_devices/2_src/mscp_server.hpp +++ b/10.02_devices/2_src/mscp_server.hpp @@ -134,16 +134,22 @@ public: bool on_param_changed(parameter_c *param) override { return true; } private: + uint32_t Abort(std::shared_ptr message); + uint32_t Access(std::shared_ptr message, uint16_t unitNumber); uint32_t Available(std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); + uint32_t CompareHostData(std::shared_ptr message, uint16_t unitNumber); uint32_t DetermineAccessPaths(std::shared_ptr message, uint16_t unitNumber); uint32_t Erase(std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); + uint32_t GetCommandStatus(std::shared_ptr message); uint32_t GetUnitStatus(std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); uint32_t Online(std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); uint32_t SetControllerCharacteristics(std::shared_ptr message); uint32_t SetUnitCharacteristics(std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); uint32_t Read(std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); + uint32_t Replace(std::shared_ptr message, uint16_t unitNumber); uint32_t Write(std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); + uint32_t DoDiskTransfer(uint16_t operation, std::shared_ptr message, uint16_t unitNumber, uint16_t modifiers); uint8_t* GetParameterPointer(std::shared_ptr message); mscp_drive_c* GetDrive(uint32_t unitNumber); diff --git a/10.02_devices/2_src/uda.cpp b/10.02_devices/2_src/uda.cpp index cf49b26..3bf85fd 100644 --- a/10.02_devices/2_src/uda.cpp +++ b/10.02_devices/2_src/uda.cpp @@ -1,3 +1,14 @@ +/* + uda.cpp: Implementation of the MSCP port (unibus interface). + + This provides logic for the UDA50's SA and IP registers, + the four-step initialization handshake, DMA transfers to and + from the Unibus, and the command/response ring protocols. + + At this time it acts as the port for an MSCP controller. + It would be trivial to extend this to TMSCP at a future date. +*/ + #include #include