#include "VirtualIODevices.hpp" #include #include "VirtualHardDrive.hpp" #include "MemoryMapper.hpp" #include "VirtualCPU.hpp" #include "VirtualRAM.hpp" namespace dragon { namespace hw { void VirtualBIOS::init(const ostd::String& biosFilePath) { bool loaded = ostd::Utils::loadByteStreamFromFile(biosFilePath, m_bios); if (!loaded) data::ErrorHandler::pushError(data::ErrorCodes::BIOS_FailedToLoad, "Failed to load BIOS data."); if (m_bios.size() != 4096) //TODO: Hardcoded data::ErrorHandler::pushError(data::ErrorCodes::BIOS_InvalidSize, ostd::String("Invalid BIOS size: ").add(ostd::Utils::getHexStr(m_bios.size(), true, 2))); m_initialized = true; } int8_t VirtualBIOS::read8(uint16_t addr) { if (addr >= m_bios.size()) data::ErrorHandler::pushError(data::ErrorCodes::BIOS_InvalidAddress, ostd::String("Invalid Byte BIOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return m_bios[addr]; } int16_t VirtualBIOS::read16(uint16_t addr) { if (addr >= m_bios.size() - 1) data::ErrorHandler::pushError(data::ErrorCodes::BIOS_InvalidAddress, ostd::String("Invalid Word BIOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return ((m_bios[addr + 0] << 8) & 0xFF00U) | ( m_bios[addr + 1] & 0x00FFU); } int8_t VirtualBIOS::write8(uint16_t addr, int8_t value) { data::ErrorHandler::pushError(data::ErrorCodes::BIOS_WriteAttempt, "Attempting to write to BIOS memory map."); return 0x00; } int16_t VirtualBIOS::write16(uint16_t addr, int16_t value) { data::ErrorHandler::pushError(data::ErrorCodes::BIOS_WriteAttempt, "Attempting to write to BIOS memory map."); return 0x0000; } ostd::ByteStream* VirtualBIOS::getByteStream(void) { return &m_bios; } InterruptVector::InterruptVector(void) { uint32_t dataSize = data::MemoryMapAddresses::IntVector_End - data::MemoryMapAddresses::IntVector_Start; for (int32_t i = 0; i < dataSize; i++) m_data.push_back(0x00); } int8_t InterruptVector::read8(uint16_t addr) { if (addr >= m_data.size()) data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Byte IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return m_data[addr]; } int16_t InterruptVector::read16(uint16_t addr) { if (addr >= m_data.size() - 1) data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return ((m_data[addr + 0] << 8) & 0xFF00U) | ( m_data[addr + 1] & 0x00FFU); } int8_t InterruptVector::write8(uint16_t addr, int8_t value) { if (addr >= m_data.size()) data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); m_data[addr] = value; return value; } int16_t InterruptVector::write16(uint16_t addr, int16_t value) { if (addr >= m_data.size() - 1) data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); m_data[addr + 0] = (value >> 8) & 0xFF; m_data[addr + 1] = value & 0xFF; return value; } ostd::ByteStream* InterruptVector::getByteStream(void) { return &m_data; } VirtualKeyboard::VirtualKeyboard(void) { } int8_t VirtualKeyboard::read8(uint16_t addr) { return 0x00; } int16_t VirtualKeyboard::read16(uint16_t addr) { return 0x0000; } int8_t VirtualKeyboard::write8(uint16_t addr, int8_t value) { return 0x00; } int16_t VirtualKeyboard::write16(uint16_t addr, int16_t value) { return 0x0000; } ostd::ByteStream* VirtualKeyboard::getByteStream(void) { return nullptr; } VirtualMouse::VirtualMouse(void) { } int8_t VirtualMouse::read8(uint16_t addr) { return 0x00; } int16_t VirtualMouse::read16(uint16_t addr) { return 0x0000; } int8_t VirtualMouse::write8(uint16_t addr, int8_t value) { return 0x00; } int16_t VirtualMouse::write16(uint16_t addr, int16_t value) { return 0x0000; } ostd::ByteStream* VirtualMouse::getByteStream(void) { return nullptr; } VirtualBootloader::VirtualBootloader(void) { for (int32_t i = 0; i < 512; i++) m_mbr.push_back(0); } int8_t VirtualBootloader::read8(uint16_t addr) { if (addr >= m_mbr.size()) data::ErrorHandler::pushError(data::ErrorCodes::BIOS_InvalidAddress, ostd::String("Invalid Byte MBR location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return m_mbr[addr]; } int16_t VirtualBootloader::read16(uint16_t addr) { if (addr >= m_mbr.size() - 1) data::ErrorHandler::pushError(data::ErrorCodes::BIOS_InvalidAddress, ostd::String("Invalid Word MBR location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return ((m_mbr[addr + 0] << 8) & 0xFF00U) | ( m_mbr[addr + 1] & 0x00FFU); } int8_t VirtualBootloader::write8(uint16_t addr, int8_t value) { if (addr >= m_mbr.size()) data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); m_mbr[addr] = value; return value; } int16_t VirtualBootloader::write16(uint16_t addr, int16_t value) { if (addr >= m_mbr.size() - 1) data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); m_mbr[addr + 0] = (value >> 8) & 0xFF; m_mbr[addr + 1] = value & 0xFF; return value; } ostd::ByteStream* VirtualBootloader::getByteStream(void) { return &m_mbr; } namespace interface { Disk::Disk(MemoryMapper& memory, VirtualCPU& cpu) : m_memory(memory), m_cpu(cpu) { m_data.w_Byte(tRegisters::Signal, tSignalValues::Ignore); } int8_t Disk::read8(uint16_t addr) { int8_t value = 0; if (!m_data.r_Byte(addr, value)) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ControllerReadFailed, "Failed to read byte from HardDrive Controller"); return 0; } return value; } int16_t Disk::read16(uint16_t addr) { int16_t value = 0; if (!m_data.r_Word(addr, value)) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ControllerReadFailed, "Failed to read word from HardDrive Controller"); return 0; } return value; } int8_t Disk::write8(uint16_t addr, int8_t value) { if (addr >= tRegisters::FirstReadOnly) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ControllerWriteFailed, "Attempt to write byte to ReadOnly part of HardDrive Controller"); return 0; } if (!m_data.w_Byte(addr, value)) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ControllerWriteFailed, "Failed to write byte to HardDrive Controller"); return 0; } return value; } int16_t Disk::write16(uint16_t addr, int16_t value) { if (addr >= tRegisters::FirstReadOnly) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ControllerWriteFailed, "Attempt to write word to ReadOnly part of HardDrive Controller"); return 0; } if (!m_data.w_Word(addr, value)) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ControllerWriteFailed, "Failed to write word to HardDrive Controller"); return 0; } return value; } ostd::ByteStream* Disk::getByteStream(void) { return &m_data.getData(); } void Disk::cycleStep(void) { uint8_t signal = tSignalValues::Ignore; m_data.r_Byte(tRegisters::Signal, (int8_t&)signal); if (m_busy) { if (signal == tSignalValues::Cancel) { m_data.w_Byte(tRegisters::Status, tStatusValues::Free); m_data.w_Byte(tRegisters::Signal, tSignalValues::Ignore); m_busy = false; return; } if (signal != tSignalValues::Ignore) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_InvalidConfiguration, "Invalid HardDrive configuration: register must be set to while busy."); m_busy = false; return; } uint8_t status = 0; m_data.r_Byte(tRegisters::Status, (int8_t&)status); if (status == tStatusValues::Free) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_InvalidConfiguration, "Invalid HardDrive configuration: register set to while busy."); m_busy = false; return; } uint8_t currentDisk = 0; uint16_t currentSector = 0, currentAddress = 0, restDataSize = 0, memoryAddress = 0; m_data.r_Byte(tRegisters::CurrentDisk, (int8_t&)currentDisk); m_data.r_Word(tRegisters::CurrentSector, (int16_t&)currentSector); m_data.r_Word(tRegisters::CurrentAddress, (int16_t&)currentAddress); m_data.r_Word(tRegisters::RestDataSize, (int16_t&)restDataSize); m_data.r_Word(tRegisters::SourceData, (int16_t&)memoryAddress); if (m_connectedDisks.count((data::VDiskID)currentDisk) == 0) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_InvalidDiskSelected, "Invalid HardDrive configuration: selected Disk not found."); m_busy = false; return; } auto& disk = *m_connectedDisks[currentDisk]; uint32_t hddAddress = 0; if (currentAddress == 0xFFFF) { if (currentSector == 0xFFFF) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_EndOfDisk, "HardDrive Error: Reached end of selected Disk."); m_busy = false; return; } currentSector++; currentAddress = 0x0000; } hddAddress = (currentSector << 16) | currentAddress; if (status == tStatusValues::Reading) { ostd::ByteStream _data; if (!disk.read(hddAddress, 1, _data)) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ReadFailed, "HardDrive Error: Failed to read data."); m_busy = false; return; } m_memory.write8(memoryAddress, _data[0]); } else if (status == tStatusValues::Writing) { int8_t dataRead = m_memory.read8(memoryAddress); if (!disk.write(hddAddress, dataRead)) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_WriteFailed, "HardDrive Error: Failed to write data."); m_busy = false; return; } } memoryAddress++; if (memoryAddress == 0xFFFF) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_MemoryOverflow, "HardDrive Error: Reached end of Memory."); m_busy = false; return; } restDataSize--; if (restDataSize == 0) { m_data.w_Byte(tRegisters::Status, tStatusValues::Free); m_data.w_Byte(tRegisters::Signal, tSignalValues::Ignore); m_busy = false; m_cpu.handleInterrupt(data::InterruptCodes::DiskInterfaceFFinished); return; } currentAddress++; m_data.w_Word(tRegisters::CurrentSector, currentSector); m_data.w_Word(tRegisters::CurrentAddress, currentAddress); m_data.w_Word(tRegisters::RestDataSize, restDataSize); m_data.w_Word(tRegisters::SourceData, memoryAddress); return; } if (signal != tSignalValues::Start) return; uint8_t mode = 0, disk = 0; uint16_t sector = 0, address = 0, size = 0, srcAddr = 0; m_data.r_Byte(tRegisters::ModeSelector, (int8_t&)mode); m_data.r_Byte(tRegisters::DiskSelector, (int8_t&)disk); m_data.r_Word(tRegisters::SectorSelector, (int16_t&)sector); m_data.r_Word(tRegisters::AddressSelector, (int16_t&)address); m_data.r_Word(tRegisters::DataSize, (int16_t&)size); m_data.r_Word(tRegisters::DataSourceAddress, (int16_t&)srcAddr); if (mode == tModeValues::Read) m_data.w_Byte(tRegisters::Status, tStatusValues::Reading); else if (mode == tModeValues::Write) m_data.w_Byte(tRegisters::Status, tStatusValues::Writing); else { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_InvalidConfiguration, "Invalid HardDrive configuration: must be set to or befor starting operations."); m_busy = false; return; } m_data.w_Byte(tRegisters::CurrentDisk, disk); m_data.w_Word(tRegisters::CurrentSector, sector); m_data.w_Word(tRegisters::CurrentAddress, address); m_data.w_Word(tRegisters::RestDataSize, size); m_data.w_Word(tRegisters::SourceData, srcAddr); m_data.w_Byte(tRegisters::Signal, tSignalValues::Ignore); m_busy = true; } data::VDiskID Disk::connectDisk(VirtualHardDrive& hdd) { for (auto& disk : m_connectedDisks) { if (disk.second->isSame(hdd)) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_DiskAlreadyConnected, "Attempt to connect already connected Disk to Controller"); return 0; } } m_connectedDisks[Disk::s_nextDiskID] = &hdd; return Disk::s_nextDiskID++; } bool Disk::disconnectDisk(data::VDiskID diskID) { if (m_connectedDisks.count(diskID) == 0) { data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_DisconnectInvalid, "Attempt to disconnect invalid Disk from Controller"); return false; } m_connectedDisks.erase(diskID); return true; } Graphics::Graphics(void) { } int8_t Graphics::read8(uint16_t addr) { return 0x00; } int16_t Graphics::read16(uint16_t addr) { return 0x0000; } int8_t Graphics::write8(uint16_t addr, int8_t value) { return 0x00; } int16_t Graphics::write16(uint16_t addr, int16_t value) { return 0x0000; } ostd::ByteStream* Graphics::getByteStream(void) { return nullptr; } SerialPort::SerialPort(void) { } int8_t SerialPort::read8(uint16_t addr) { return 0x00; } int16_t SerialPort::read16(uint16_t addr) { return 0x0000; } int8_t SerialPort::write8(uint16_t addr, int8_t value) { return 0x00; } int16_t SerialPort::write16(uint16_t addr, int16_t value) { return 0x0000; } ostd::ByteStream* SerialPort::getByteStream(void) { return nullptr; } void CMOS::init(const ostd::String& cmosFilePath) { m_size = data::MemoryMapAddresses::CMOS_End - data::MemoryMapAddresses::CMOS_Start + 1; m_dataFile.open(cmosFilePath.cpp_str(), std::ios::out | std::ios::in | std::ios::binary); if(!m_dataFile) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_UnableToMount, "Unable to mount virtual CMOS chip."); return; } m_fileSize = m_dataFile.tellg(); m_dataFile.seekg( 0, std::ios::end ); m_fileSize = (int64_t)m_dataFile.tellg() - m_fileSize; m_dataFile.seekg( 0, std::ios::beg ); if (m_fileSize != m_size) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_InvalidSize, ostd::String("Invalid virtual CMOS chhip size: ").add(m_fileSize)); return; } m_initialized = true; } int8_t CMOS::read8(uint16_t addr) { if (!m_initialized) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_Uninitialized, "Attempt to read uninitialized CMOS chip."); return false; } if (addr >= m_size) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_InvalidAddress, ostd::String("Invalid Byte CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return false; } int8_t value = 0; m_dataFile.seekg(addr); m_dataFile.read((char*)&value, sizeof(value)); return value; } int16_t CMOS::read16(uint16_t addr) { if (!m_initialized) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_Uninitialized, "Attempt to read uninitialized CMOS chip."); return false; } if (addr >= m_size - 1) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_InvalidAddress, ostd::String("Invalid Word CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return 0; } int8_t b1 = read8(addr); int8_t b2 = read8(addr + 1); return ((b1 << 8) & 0xFF00U) | (b2 & 0x00FFU); } int8_t CMOS::write8(uint16_t addr, int8_t value) { if (!m_initialized) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_Uninitialized, "Attempt to read uninitialized CMOS chip."); return false; } if (addr >= m_size) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_InvalidAddress, ostd::String("Invalid Byte CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return 0; } m_dataFile.seekp(addr); m_dataFile.write((char*)(&value), sizeof(value)); return value; } int16_t CMOS::write16(uint16_t addr, int16_t value) { if (!m_initialized) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_Uninitialized, "Attempt to read uninitialized CMOS chip."); return false; } if (addr >= m_size - 1) { data::ErrorHandler::pushError(data::ErrorCodes::CMOS_InvalidAddress, ostd::String("Invalid Word CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return 0; } int8_t b1 = (value >> 8) & 0xFF; int8_t b2 = (value & 0xFF); write8(addr, b1); write8(addr + 1, b2); return value; } ostd::ByteStream* CMOS::getByteStream(void) { return &m_data; } } } }