1
0
mirror of synced 2026-01-19 01:06:49 +00:00
2020-09-09 15:11:45 -07:00

663 lines
28 KiB
C++

#include "ui.h"
#include "iop_bmx.h"
#include "cray_mainframe.h"
#include <algorithm>
#include <time.h>
//////////////////////////////////////////////////////////////////////////////////////////
// IopChannelBmx_c
//////////////////////////////////////////////////////////////////////////////////////////
void IopChannelBmx_c::Setup(const Configuration_c &aConfig) {
mDevices.clear();
mDelayLimit = aConfig.get<size_t>("DelayLimit", 10);
size_t DeviceCount = aConfig.get<size_t>("DeviceCount", 255);
mDevices.resize(DeviceCount);
for(const auto &DeviceConfig: aConfig.get_child_safe("Devices")) {
std::string DeviceType = DeviceConfig.first;
std::shared_ptr<IopBmxDevice_i> Device;
const Configuration_c &DeviceParams = DeviceConfig.second;
// Create device
if (DeviceType == "Tape") {
// Create a tape device
Device = std::make_shared<IopBmxTape_c>(DeviceParams, *this);
} else {
throw InvalidParameter_x(boost::format("Unknown device type: %1%") % DeviceType);
}
// Add device to all channels (these are smart pointers, so adding a device multiple times is OK)
uint8_t DeviceAddress = Device->GetDeviceAddress();
if (mDevices[DeviceAddress].Device != nullptr) {
throw InvalidParameter_x(boost::format("expander channel index %1% is already in use") % DeviceAddress);
}
if (DeviceAddress >= mDevices.size()) {
throw InvalidParameter_x(boost::format("expander channel index %1% is too large") % DeviceAddress);
}
mDevices[DeviceAddress].Device = Device;
if (Device->NeedsTick()) {
mTickedDevices.emplace_back(Device.get());
}
}
}
IopInt_t IopChannelBmx_c::DoIo(IopIoFunction_t aFunction, IopInt_t aData) {
uint16_t RetVal;
switch (aFunction) {
case 000:
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " CLEAR_CHANNEL_BUSY/DONE with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mBusy = false;
mDone = false;
mInterruptEnabled = false;
//TODO: this should disable DMAs too, but there's no function to enabled it again?!!!
return 0;
case 001:
switch (aData & 0x3) {
case 0: // Clear output tag lines
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " CLEAR_OUTPUT_TAG_LINES with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mDone = true;
mBusy = false;
break;
case 1: // Interface disconnect
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " INTERFACE_DISCONNECT with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
CRAY_ASSERT(GetActiveDevice() != nullptr);
if (GetActiveDevice()->InterfaceDisconnect()) {
// Successfull disconnect
mDone = true;
mBusy = false;
} else {
// Error in disconnect
mDone = true;
mBusy = true;
}
break;
case 2: // Selective reset
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " SELECTIVE_RESET with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
if (GetActiveDevice() != nullptr) GetActiveDevice()->Reset();
mDone = true;
mBusy = false;
break;
case 3: // System reset
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " SYSTEM_RESET with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
for(auto &Device: mDevices) if (Device.Device != nullptr) Device.Device->Reset();
mDone = true;
mBusy = false;
break;
}
return 0;
case 002: { // CHANNEL COMMAND
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " CHANNEL_COMMAND with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
uint8_t Command = aData & 0xff;
mLogger << setloglevel(LogLevel_IoActivity) << "Command: " << HexPrinter(Command) << std::endl;
// This operation is a BMX-initiated transfer
CRAY_ASSERT(GetActiveDevice() != nullptr);
IopBmxDevice_i &ActiveDevice = *GetActiveDevice();
// Test I/O
if (Command == 0) {
mDeviceStatus = ActiveDevice.TestIo(Command);
} else {
std::vector<uint8_t> Data;
size_t BytesTransferred = 0;
size_t MaxSize = mByteCounter;
IopInt_t ByteCounter = mByteCounter;
IopInt_t LocalMemoryAdddress = mLocalMemoryAdddress[mActiveMemoryAddressIdx];
LogLine_c TraceLine = mLogger << setloglevel(LogLevel_IoTrace);
switch (Command & 0xf) {
case 0x1: // Write
case 0x5: // Write
case 0x9: // Write
case 0xd: // Write
case 0x3: // Control
case 0x7: // Control
case 0xb: // Control
case 0xf: // Control
while (ByteCounter != 0) {
for(size_t Parcel=0;Parcel<4;++Parcel) {
IopInt_t Word = mParent.GetDma().ReadWord((LocalMemoryAdddress & 0xfffc) | Parcel);
Data.push_back(Word >> 8);
--ByteCounter;
if (ByteCounter == 0) break;
Data.push_back(Word & 0xff);
--ByteCounter;
if (ByteCounter == 0) break;
}
LocalMemoryAdddress += 4;
}
switch (Command & 0x3) {
case 0x1: mDeviceStatus = ActiveDevice.Write(Command,Data,BytesTransferred); break;
case 0x3: mDeviceStatus = ActiveDevice.Control(Command,Data,BytesTransferred); break;
default: CRAY_ASSERT(false); break;
}
CRAY_ASSERT(BytesTransferred <= mByteCounter);
mLocalMemoryAdddress[mActiveMemoryAddressIdx] += IopInt_t(BytesTransferred / 8);
mByteCounter -= IopInt_t(BytesTransferred);
break;
case 0x2: // Read
case 0x6: // Read
case 0xa: // Read
case 0xe: // Read
case 0x4: // Sense
case 0xc: // Read backward
switch (Command & 0xf) {
case 0x2:
case 0x6:
case 0xa:
case 0xe: mDeviceStatus = ActiveDevice.Read(Command,Data,MaxSize); break;
case 0x4: mDeviceStatus = ActiveDevice.Sense(Command,Data,MaxSize); break;
case 0xc: mDeviceStatus = ActiveDevice.ReadBackward(Command,Data,MaxSize); break;
default: CRAY_ASSERT(false); break;
}
mLogger << setloglevel(LogLevel_IoActivity) << "received " << DecPrinter(Data.size()) << " bytes of data with command: " << HexPrinter(Command) << " to address: " << HexPrinter(mLocalMemoryAdddress[mActiveMemoryAddressIdx]) << std::endl;
size_t i;
for(i=0; i<Data.size(); ++i) {
if (i%16 == 0) {
if (i != 0) TraceLine << std::endl;
} else {
TraceLine << " ";
}
TraceLine << HexPrinter(Data[i]);
}
if (i%16 != 0) {
if (i != 0) TraceLine << std::endl;
}
CRAY_ASSERT(Data.size() <= mByteCounter);
BytesTransferred = 0;
while (BytesTransferred != Data.size()) {
for(size_t Parcel=0;Parcel<4;++Parcel) {
IopInt_t Word = Data[BytesTransferred] << 8;
--mByteCounter;
++BytesTransferred;
if (BytesTransferred != Data.size()) {
Word |= Data[BytesTransferred];
--mByteCounter;
++BytesTransferred;
}
mParent.GetDma().WriteWord((mLocalMemoryAdddress[mActiveMemoryAddressIdx] & 0xfffc) | Parcel,Word);
if (BytesTransferred == Data.size()) break;
}
mLocalMemoryAdddress[mActiveMemoryAddressIdx] += 4;
}
break;
default: CRAY_ASSERT(false); break;
}
}
mDone = true;
mBusy = false;
} return 0;
case 003: { // READ REQUEST-IN ADDRESS
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " READ REQUEST-IN ADDRESS with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
bool HaveInterrupt = false;
// This operation figures out the address of the interrupting I/O device.
for (auto &Device : mDevices) {
if (Device.Device != nullptr && Device.Device->GetRequestIn()) {
mActiveDevice = Device.Device->GetDeviceAddress();
mDeviceStatus = Device.Device->GetStatus();
HaveInterrupt = true;
break;
}
}
if (HaveInterrupt) {
mDone = true;
mBusy = false;
} else {
mDone = true;
mBusy = true;
}
} return 0;
case 004: // ASYNCHRONOUS I/O
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " ASYNC_IO with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
CRAY_ASSERT(false);
//GetActiveDevice().GetData(mStackStatusFlag);
mDone = true;
mBusy = false;
return 0;
case 005: // DELAY COUNTER DIAGNOSTICS
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " DELAY_COUNTER_DIAG with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mLogger << setloglevel(LogLevel_IoActivity) << "Starting delay counter: " << DecPrinter(mDelayLimit) << std::endl;
mDelayCounter = mDelayLimit;
mDone = false;
mBusy = true;
return 0;
case 006:
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " INTERRUPT_DISABLE with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mInterruptEnabled = false;
return 0;
case 007:
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " INTERRUPT_ENABLE with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mInterruptEnabled = true;
return 0;
case 010: // READ LOCAL MEMORY ADDRESS
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " READ_LOCAL_MEM_ADDRESS with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
RetVal = (mLocalMemoryAdddress[aData & 1] & 0xfffe) | (aData & 1);
mChainingInterruptActive = false;
mLogger << setloglevel(LogLevel_IoActivity) << "\treturning: " << HexPrinter(RetVal) << " (" << DecPrinter(RetVal) << ")" << std::endl;
return RetVal;
case 011: // READ BYTE COUNTER
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " READ_BYTE_COUNTER with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
if (mByteCountStatusRegValid) {
mByteCountStatusRegValid = false;
RetVal = mByteCountStatusReg;
} else {
RetVal = mByteCounter;
}
mLogger << setloglevel(LogLevel_IoActivity) << "\treturning: " << HexPrinter(RetVal) << " (" << DecPrinter(RetVal) << ")" << std::endl;
return RetVal;
case 012: // READ STATUS/ADDRESS
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " READ_STATUS/ADDRESS with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
if (mStatusRegValid) {
mStatusRegValid = false;
RetVal = mStatusReg;
} else {
RetVal = mDeviceStatus | (mActiveDevice << 8);
}
mLogger << setloglevel(LogLevel_IoActivity) << "\treturning: " << HexPrinter(RetVal) << " (" << DecPrinter(RetVal) << ")" << std::endl;
return RetVal;
case 013: // READ INPUT TAGS
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " READ_INPUT_TAGS with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
RetVal = // TODO: do we have to simulate other bits too?
(mByteCounter == 0) ? (1 << 13) : 0 |
(mActiveMemoryAddressIdx << 11) |
(GetDeviceInterrupt() << 4);
mLogger << setloglevel(LogLevel_IoActivity) << "\treturning: " << HexPrinter(RetVal) << " (" << DecPrinter(RetVal) << ")" << std::endl;
return RetVal;
case 014: // ENTER LOCAL MEMORY ADDRESS
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " ENTER_LOCAL_MEM_ADDRES with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mLocalMemoryAdddress[aData & 1] = aData & 0xfffe;
//CRAY_ASSERT((aData & 2) == 0); // For now, make sure chaining is not enabled
mLoadByteCounter = true;
return 0;
case 015: // ENTER BYTE COUNT
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " ENTER_BYTE_COUNT with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mByteCountStatusReg = aData;
mByteCountStatusRegValid = true;
if (mLoadByteCounter) mByteCounter = aData; else mNextByteCounter = aData;
mLoadByteCounter = false;
return 0;
case 016: // ENTER DEVICE ADDRESS/MODE
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " ENTER_DEVICE_ADDRESS/MODE with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mActiveDevice = (aData & 0xff);
mSkipFlag = GetBit((uint32_t)aData,8) != 0;
mStackStatusFlag = GetBit((uint32_t)aData,9) != 0;
mChainingMode = (ChainingMode_e)GetBits((uint32_t)aData,10,11);
mInterruptMode = (InterruptMode_e)GetBits((uint32_t)aData,12,13);
//CRAY_ASSERT(mInterruptMode != InterruptMode_StatusIn);
mStatusReg = aData;
mStatusRegValid = true;
return 0;
case 017: // ENTER OUTPUT TAGS
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " ENTER_OUTPUT_TAGS with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
//CRAY_ASSERT(false);
return 0;
default:
mLogger << setloglevel(LogLevel_IoActivity) << "Processing function " << OctPrinter(aFunction) << " with active device: " << DecPrinter(mActiveDevice) << " and accumulater: " << HexPrinter(aData) << " (" << DecPrinter(aData) << ")" << std::endl;
mLogger << setloglevel(LogLevel_Error) << SideEffectIndent << "ERROR: Invalid function code for channel" << std::endl;
return 0;
}
}
IopBit_t IopChannelBmx_c::GetInterrupt() {
if (mInterruptEnabled && mDone) {
mLogger << setloglevel(LogLevel_IoActivity) << "Sending DONE interrupt" << std::endl;
return 1;
}
return GetDeviceInterrupt();
}
// These could be further optimized but not all that interesting at the moment...
void IopChannelBmx_c::RequestInChanged(uint8_t aDeviceAddr, bool aValue) {
UpdateXxxInCnts();
}
void IopChannelBmx_c::StatusInChanged(uint8_t aDeviceAddr, bool aValue) {
UpdateXxxInCnts();
}
void IopChannelBmx_c::DisconnectInChanged(uint8_t aDeviceAddr, bool aValue) {
UpdateXxxInCnts();
}
void IopChannelBmx_c::UpdateXxxInCnts() {
mRequestInCnt = 0;
mStatusInCnt = 0;
mDisconnectInCnt = 0;
for (auto &Device : mDevices) {
if (Device.Device != nullptr) {
if (Device.Device->GetRequestIn()) ++mRequestInCnt;
if (Device.Device->GetStatusIn()) ++mStatusInCnt;
if (Device.Device->GetDisconnectIn()) ++mDisconnectInCnt;
}
}
}
IopBit_t IopChannelBmx_c::GetDeviceInterrupt() {
if (mInterruptMode == InterruptMode_RequestIn) return mRequestInCnt > 0 ? 1 : 0;
if (mInterruptMode == InterruptMode_StatusIn) return mStatusInCnt > 0 ? 1 : 0;
if (mInterruptMode == InterruptMode_DisconnectIn) return mDisconnectInCnt > 0 ? 1 : 0;
return 0;
}
std::string IopChannelBmx_c::GetName() const {
return mName;
}
void IopChannelBmx_c::Tick() {
for (auto &Device : mTickedDevices) {
Device->Tick();
}
if (mDelayCounter != 0) {
--mDelayCounter;
if (mDelayCounter == 0) {
mLogger << setloglevel(LogLevel_IoActivity) << "Delay counter expired" << std::endl;
mDone = true;
mBusy = false;
}
}
}
void IopChannelBmx_c::GetStatus(StatusReport_c &aStatus, PeripheralType_e aFilter, bool aLongFormat) const {
for (auto &Device : mDevices) {
if (Device.Device != nullptr && Device.Device->GetType() == aFilter) {
StatusReport_c Status;
Device.Device->GetStatus(Status, aFilter, aLongFormat);
aStatus.put_child(Device.Device->GetName(), Status);
}
}
}
void IopChannelBmx_c::RegisterCommands(CommandHooks_t &aHooks) {
for (auto &Device : mDevices) {
if (Device.Device != nullptr) {
Device.Device->RegisterCommands(aHooks);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// IopBmxTape_c
//////////////////////////////////////////////////////////////////////////////////////////
// First byte is the channel status byte
// Second byte is the unit status byte
// Bit Designation
// 0 Attention
// not used
// 1 Status modifier
// present with busy to indicate interrupt pending
// 2 Control unit end
// signals completion (with or without error) of an operation
// 3 Busy
// busy
// 4 Channel end
// 5 Device end
// 6 Unit check
// set if any bytes of the channel status byte is set or bit 7 (below) is set
// 7 Unit exception
enum BmxStat_e {
BmxStat_Attention = 1 << 0,
BmxStat_StatusModifier = 1 << 1,
BmxStat_ControlUnitEnd = 1 << 2,
BmxStat_Busy = 1 << 3,
BmxStat_ChannelEnd = 1 << 4,
BmxStat_DeviceEnd = 1 << 5,
BmxStat_UnitCheck = 1 << 6,
BmxStat_UnitException = 1 << 7
};
IopBmxTape_c::IopBmxTape_c(const Configuration_c &aConfig, IopChannelBmx_c &aParent):
mParent(aParent),
mLogger(aConfig, "BMT",aConfig.get<size_t>("DeviceAddress")),
mDeviceAddress(aConfig.get<uint8_t>("DeviceAddress")),
mReads(0),
mWrites(0),
mTapeImage(mLogger, aConfig),
mIsReadOnly(aConfig.get<bool>("ReadOnly", false))
{
boost::optional<std::string> Name = aConfig.get_optional<std::string>("DeviceName");
mName = (Name.is_initialized()) ? Name.get() : ModuleNameFormatter("BMT", aConfig.get<size_t>("DeviceAddress"));
mLogger.SetHeader(mName.c_str());
mLogger.SetParent(mParent.GetLogger());
ClearState();
mTapeImage.open(aConfig.get<std::string>("Tape"));
}
uint8_t IopBmxTape_c::TestIo(uint8_t aCommand) {
return BmxStat_DeviceEnd | BmxStat_ControlUnitEnd;
}
uint8_t IopBmxTape_c::Write(uint8_t aCommand, std::vector<uint8_t> &aData, size_t &aBytesTransferred) {
mLogger << setloglevel(LogLevel_IoActivity) << "writing " << DecPrinter(aData.size()) << " bytes of data with command: " << HexPrinter(aCommand) << std::endl;
CRAY_ASSERT(!mIsReadOnly);
LogLine_c TraceLine = mLogger << setloglevel(LogLevel_IoTrace);
size_t i;
for(i=0; i<aData.size(); ++i) {
if (i%16 == 0) {
if (i != 0) TraceLine << std::endl;
} else {
TraceLine << " ";
}
TraceLine << HexPrinter(aData[i]);
}
if (i%16 != 0) {
if (i != 0) TraceLine << std::endl;
}
mTapeImage.ReOpen();
mTapeImage.WriteRecord(aData);
aBytesTransferred = aData.size();
mWrites += aData.size();
uint8_t RetVal = BmxStat_ControlUnitEnd;
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfFile) RetVal |= BmxStat_UnitException;
return RetVal;
}
uint8_t IopBmxTape_c::Control(uint8_t aCommand, std::vector<uint8_t> &aData, size_t &aBytesTransferred) {
mLogger << setloglevel(LogLevel_IoActivity) << "control with " << DecPrinter(aData.size()) << " bytes of data and command: " << HexPrinter(aCommand) << std::endl;
LogLine_c TraceLine = mLogger << setloglevel(LogLevel_IoTrace);
size_t i;
for(i=0; i<aData.size(); ++i) {
if (i%16 == 0) {
if (i != 0) TraceLine << std::endl;
} else {
TraceLine << " ";
}
TraceLine << HexPrinter(aData[i]);
}
if (i%16 != 0) {
if (i != 0) TraceLine << std::endl;
}
uint8_t RetVal = BmxStat_DeviceEnd | BmxStat_ControlUnitEnd;
switch ((aCommand >> 2 ) & 1) {
case 0: // mode setting operations
mLogger << setloglevel(LogLevel_IoActivity) << "Mode setting" << std::endl; break;
break;
case 1: // Tape motion operations
switch ((aCommand >> 3) & 7) {
case 0: mLogger << setloglevel(LogLevel_IoActivity) << "Rewind" << std::endl;
mTapeImage.ReOpen();
mTapeImage.SeekToBeginningOfTape();
mTapeImage.close();
break;
case 1: mLogger << setloglevel(LogLevel_IoActivity) << "Rewind and Unload" << std::endl;
mTapeImage.ReOpen();
mTapeImage.SeekToBeginningOfTape();
mTapeImage.close();
if (mTapeImage.GetState() == TapFile_c::State_e::BeginningOfTape) RetVal |= BmxStat_UnitCheck;
break;
case 2: mLogger << setloglevel(LogLevel_IoActivity) << "Erase Gap" << std::endl; break;
case 3: mLogger << setloglevel(LogLevel_IoActivity) << "Write Tape Mark" << std::endl;
CRAY_ASSERT(!mIsReadOnly);
mTapeImage.ReOpen();
mTapeImage.WriteEndOfFile();
break;
case 4: mLogger << setloglevel(LogLevel_IoActivity) << "Backspace Block" << std::endl;
mTapeImage.ReOpen();
mTapeImage.SeekToPrevRecord();
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfFile) RetVal |= BmxStat_UnitException;
if (mTapeImage.GetState() == TapFile_c::State_e::BeginningOfTape) RetVal |= BmxStat_UnitCheck;
break;
case 5: mLogger << setloglevel(LogLevel_IoActivity) << "Backspace File" << std::endl;
mTapeImage.ReOpen();
do {
mTapeImage.SeekToPrevRecord();
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfTape) {
mLogger << setloglevel(LogLevel_IoActivity) << "spacing cut short by EOT condition after " << DecPrinter(i) << " records." << std::endl;
break;
}
} while (mTapeImage.GetState() != TapFile_c::State_e::EndOfFile);
if (mTapeImage.GetState() == TapFile_c::State_e::BeginningOfTape) RetVal |= BmxStat_UnitCheck;
break;
case 6: mLogger << setloglevel(LogLevel_IoActivity) << "Forward Space Block" << std::endl;
mTapeImage.ReOpen();
mTapeImage.SeekToNextRecord();
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfFile) RetVal |= BmxStat_UnitException;
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfTape) RetVal |= BmxStat_UnitCheck;
break;
case 7: mLogger << setloglevel(LogLevel_IoActivity) << "Forward Space File" << std::endl;
mTapeImage.ReOpen();
do {
mTapeImage.SeekToNextRecord();
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfTape) {
mLogger << setloglevel(LogLevel_IoActivity) << "spacing cut short by EOT condition after " << DecPrinter(i) << " records." << std::endl;
break;
}
} while (mTapeImage.GetState() != TapFile_c::State_e::EndOfFile);
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfTape) RetVal |= BmxStat_UnitCheck;
break;
default: CRAY_ASSERT(false); break;
}
break;
default: CRAY_ASSERT(false); break;
}
aBytesTransferred = aData.size();
return RetVal;
}
uint8_t IopBmxTape_c::Read(uint8_t aCommand, std::vector<uint8_t> &aData, size_t aMaxSize) {
mLogger << setloglevel(LogLevel_IoActivity) << "read with " << DecPrinter(aMaxSize) << " bytes of buffer and command: " << HexPrinter(aCommand) << std::endl;
uint8_t RetVal = BmxStat_ControlUnitEnd;
mTapeImage.ReOpen();
aData = mTapeImage.Read(uint32_t(aMaxSize));
mReads += aData.size();
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfFile) RetVal |= BmxStat_UnitException;
if (mTapeImage.GetState() == TapFile_c::State_e::EndOfTape) RetVal |= BmxStat_UnitCheck;
return RetVal;
}
uint8_t IopBmxTape_c::Sense(uint8_t aCommand, std::vector<uint8_t> &aData, size_t aMaxSize) {
mLogger << setloglevel(LogLevel_IoActivity) << "sense with " << DecPrinter(aMaxSize) << " bytes of buffer and command: " << HexPrinter(aCommand) << std::endl;
// Sense data is documented in
// A22-6866-4_2400_Tape_Unit_2803_2804_Tape_Controls_Component_Description_Sep68.pdf
// page 29-32
CRAY_ASSERT(aMaxSize >= 6);
aData.resize(6);
aData[0] = 0;
// TU Status A File Protect
aData[1] = (1 << 1) | ((mIsReadOnly ? 1:0) << 6);
aData[2] = 0;
aData[3] = 0;
aData[4] = 0;
aData[5] = 0;
/* aData[0] = 0x36;
// TU Status A File Protect
aData[1] = (1 << 1) | (1 << 6);
aData[2] = 0x47;
aData[3] = 0x58;
aData[4] = 0x69;
aData[5] = 0x7a;*/
return BmxStat_ControlUnitEnd;
}
uint8_t IopBmxTape_c::ReadBackward(uint8_t aCommand, std::vector<uint8_t> &aData, size_t aMaxSize) {
mLogger << setloglevel(LogLevel_IoActivity) << "read backward " << DecPrinter(aMaxSize) << " bytes of buffer and command: " << HexPrinter(aCommand) << " - UNIMPLEMENTED!!!" << std::endl;
CRAY_ASSERT(false); // This is not implemented
aData.resize(aMaxSize);
for(size_t i=0; i<aData.size(); ++i) {
aData[i] = i % 255;
}
return BmxStat_ControlUnitEnd;
}
// This is only called if the device is requesting an interrupt. Since we never do it, it won't be called ever.
uint8_t IopBmxTape_c::GetStatus() const {
uint8_t RetVal = BmxStat_DeviceEnd | BmxStat_ControlUnitEnd;
return RetVal;
}
bool IopBmxTape_c::GetRequestIn() const {
return false;
}
bool IopBmxTape_c::GetStatusIn() const {
return false;
}
bool IopBmxTape_c::GetDisconnectIn() const {
return false;
}
void IopBmxTape_c::GetStatus(StatusReport_c &aStatus, PeripheralType_e aFilter, bool aLongFormat) const {
aStatus.put(aLongFormat ? "Reads" : "R", mReads);
aStatus.put(aLongFormat ? "Writes" : "W", mWrites);
if (mTapeImage.is_open()) aStatus.put(aLongFormat ? "Position" : "Pos", const_cast<PokedTapFile_c*>(&mTapeImage)->tellg()); else aStatus.put(aLongFormat ? "Position" : "Pos", "-");
aStatus.put(aLongFormat ? "File Name" : "FName", mTapeImage.GetFileName());
}
std::string IopBmxTape_c::GetName() const {
return mName;
}
void IopBmxTape_c::MountTape(const std::string &aTapeFileName) {
if (mTapeImage.is_open()) throw Generic_x("Can't unmount tape while it's in use.");
mTapeImage.open(aTapeFileName);
mTapeImage.close();
}
void IopBmxTape_c::RegisterCommands(CommandHooks_t &aHooks) {
class CmdMount_c : public CmdFactoryBase_i {
public:
explicit CmdMount_c(IopBmxTape_c &aParent) : mParent(aParent) {}
virtual bool ParseAndExec(TokenStream_t::const_iterator aBegin, TokenStream_t::const_iterator aEnd) override {
bool DoHelp = false;
if (aBegin->mValue == "help") {
DoHelp = true;
++aBegin; if (aBegin == aEnd) return false;
}
if (aBegin->mValue != "mount") return false;
++aBegin; if (aBegin == aEnd) return false;
if (aBegin->mValue != mParent.GetName()) return false;
if (DoHelp) {
std::cout << "mount " << mParent.GetName() << " <file name>" << std::endl;
std::cout << " Mounts a new virtual tape on the tape drive" << std::endl;
return true;
}
else {
++aBegin; if (aBegin == aEnd) return false;
std::string FileName = aBegin->mValue;
++aBegin;
if (aBegin != aEnd && aBegin->mTokenId != TokenTypes_e::EndOfLine) return false;
// TODO: add warning about existing file
try {
mParent.MountTape(FileName);
std::cout << "New virtual tape " << FileName << " mounted" << std::endl;
}
catch (std::exception &Ex) {
std::cout << "Mount failed" << std::endl;
std::cout << Ex.what() << std::endl;
}
return true;
}
}
virtual std::string GetCommandName() const override { return "mount"; }
virtual std::string GetDeviceName() const override { return mParent.GetName(); }
protected:
IopBmxTape_c &mParent;
};
aHooks.emplace_back(std::make_unique<CmdMount_c>(*this));
}