From 4aa5e318f0c04da467b9ea70782c2f15f6119f6b Mon Sep 17 00:00:00 2001 From: OmniaX-Dev Date: Fri, 22 May 2026 21:28:32 +0200 Subject: [PATCH] Testing backport of V2 components --- CMakeLists.txt | 2 +- other/build.nr | 2 +- src/hardware/BUS.cpp | 143 +++++++++++++++++++++++ src/hardware/BUS.hpp | 142 +++++++++++++++++++++++ src/hardware/RAM.cpp | 172 +++++++++++++++++++++++++++ src/hardware/RAM.hpp | 51 ++++++++ src/tools/GlobalData.hpp | 243 ++++++++++++++++++++++++--------------- 7 files changed, 661 insertions(+), 94 deletions(-) create mode 100644 src/hardware/BUS.cpp create mode 100644 src/hardware/BUS.hpp create mode 100644 src/hardware/RAM.cpp create mode 100644 src/hardware/RAM.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a2a5618..0eec9cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_C_COMPILER "clang") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) cmake_minimum_required(VERSION 3.18) project(${PROJ_NAME} LANGUAGES C CXX) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) file(STRINGS "./other/build.nr" BUILD_NUMBER) #----------------------------------------------------------------------------------------- diff --git a/other/build.nr b/other/build.nr index da66d92..8f8453a 100644 --- a/other/build.nr +++ b/other/build.nr @@ -1 +1 @@ -1646 +1648 diff --git a/src/hardware/BUS.cpp b/src/hardware/BUS.cpp new file mode 100644 index 0000000..6b3eb40 --- /dev/null +++ b/src/hardware/BUS.cpp @@ -0,0 +1,143 @@ +/* + DragonV2 - A collection of useful functionality + Copyright (C) 2026 OmniaX-Dev + + This file is part of DragonV2. + + DragonV2 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DragonV2 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DragonV2. If not, see . +*/ + +#include "BUS.hpp" + +namespace dragon +{ + AtomicStorageMemoryDevice::AtomicStorageMemoryDevice(u64 size) + { + if (size == 0) // Special case, for classes like BUS, that are MemoryDevices but don't own any data + { + m_wordCount = 0; + m_size = 0; + m_words = nullptr; + return; + } + m_wordCount = (size + 7) / 8; // Round up to multiple of 8 bytes for the word-granular backing + m_size = size; + m_words = new std::atomic[m_wordCount]; + for (u64 i = 0; i < m_wordCount; ++i) + m_words[i].store(0, std::memory_order_relaxed); + } + + AtomicStorageMemoryDevice::~AtomicStorageMemoryDevice(void) + { + delete[] m_words; + } + + bool MemoryDevice::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired) + { + data::ErrorHandler::pushError(data::ErrorCodes::MM_AtomicNotSupported, "MemoryDevice::guest_cas_u32: atomic not supported: "); + fault(addr, data::ExceptionCause::AtomicNotSupported, "MemoryDevice::guest_cas_u32"); + return false; + } + + + + + + namespace hw + { + bool BUS::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired) + { + auto region = findRegion(addr); + if (!region) { /* fault */ return false; } + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->guest_cas_u32(finalAddr, expected, actual, desired); + } + + u8 BUS::load_u8(AddressType addr) const + { + auto region = findRegion(addr); + if (region == nullptr) return 0x00; //TODO: Error + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->load_u8(finalAddr); + } + + u16 BUS::load_u16(AddressType addr) const + { + auto region = findRegion(addr); + if (region == nullptr) return 0x0000; //TODO: Error + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->load_u16(finalAddr); + } + + u32 BUS::load_u32(AddressType addr) const + { + auto region = findRegion(addr); + if (region == nullptr) return 0x0000'0000; //TODO: Error + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->load_u32(finalAddr); + } + + u64 BUS::load_u64(AddressType addr) const + { + auto region = findRegion(addr); + if (region == nullptr) return 0x0000'0000'0000'0000; //TODO: Error + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->load_u64(finalAddr); + } + + bool BUS::store_u8(AddressType addr, u8 value) + { + auto region = findRegion(addr); + if (region == nullptr) return false; + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->store_u8(finalAddr, value); + } + + bool BUS::store_u16(AddressType addr, u16 value) + { + auto region = findRegion(addr); + if (region == nullptr) return false; + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->store_u16(finalAddr, value); + } + + bool BUS::store_u32(AddressType addr, u32 value) + { + auto region = findRegion(addr); + if (region == nullptr) return false; + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->store_u32(finalAddr, value); + } + + bool BUS::store_u64(AddressType addr, u64 value) + { + auto region = findRegion(addr); + if (region == nullptr) return false; + AddressType finalAddr = (region->remap ? addr - region->startAddress : addr); + return region->device->store_u64(finalAddr, value); + } + + void BUS::mapDevice(MemoryDevice& device, AddressType startAddr, AddressType endAddr, bool remap, ostd::String name) + { + m_regions.push_back({ &device, startAddr, endAddr, remap, name }); + } + + ostd::String BUS::getMemoryRegionName(AddressType address) + { + tMemoryRegion* region = findRegion(address); + if (region == nullptr) return "Invalid"; + return region->name; + } + } +} diff --git a/src/hardware/BUS.hpp b/src/hardware/BUS.hpp new file mode 100644 index 0000000..b28b4f2 --- /dev/null +++ b/src/hardware/BUS.hpp @@ -0,0 +1,142 @@ +/* + DragonV2 - A collection of useful functionality + Copyright (C) 2026 OmniaX-Dev + + This file is part of DragonV2. + + DragonV2 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DragonV2 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DragonV2. If not, see . +*/ + +#include +#include +#include +#include "../tools/GlobalData.hpp" + +#pragma once + +namespace dragon +{ + class MemoryDevice : public ostd::BaseObject + { + public: + MemoryDevice(void) = default; + MemoryDevice(const MemoryDevice&) = delete; + MemoryDevice& operator=(const MemoryDevice&) = delete; + MemoryDevice(MemoryDevice&&) = delete; + MemoryDevice& operator=(MemoryDevice&&) = delete; + virtual ~MemoryDevice() = default; + + virtual bool guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired); + + // ---- Aligned loads ---- + virtual u64 load_u64(AddressType addr) const = 0; + virtual u32 load_u32(AddressType addr) const = 0; + virtual u16 load_u16(AddressType addr) const = 0; + virtual u8 load_u8 (AddressType addr) const = 0; + + inline i64 load_i64(AddressType addr) const { return cast(load_u64(addr)); } + inline i32 load_i32(AddressType addr) const { return cast(load_u32(addr)); } + inline i16 load_i16(AddressType addr) const { return cast(load_u16(addr)); } + inline i8 load_i8 (AddressType addr) const { return cast(load_u8 (addr)); } + + // ---- Aligned stores ---- + virtual bool store_u64(AddressType addr, u64 value) = 0; + virtual bool store_u32(AddressType addr, u32 value) = 0; + virtual bool store_u16(AddressType addr, u16 value) = 0; + virtual bool store_u8 (AddressType addr, u8 value) = 0; + + inline bool store_i64(AddressType addr, u64 value) { return store_u64(addr, cast(value)); } + inline bool store_i32(AddressType addr, u32 value) { return store_u32(addr, cast(value)); } + inline bool store_i16(AddressType addr, u16 value) { return store_u16(addr, cast(value)); } + inline bool store_i8 (AddressType addr, u8 value) { return store_u8 (addr, cast(value)); } + + [[noreturn]] virtual void fault(AddressType addr, data::ExceptionCause cause, const String& msg = "") const { throw data::GuestException{ cause, addr, msg }; }; // Message can be useful for debugging + }; + + class AtomicStorageMemoryDevice : public MemoryDevice + { + public: + explicit AtomicStorageMemoryDevice(u64 size); + virtual ~AtomicStorageMemoryDevice(void); + AtomicStorageMemoryDevice(const AtomicStorageMemoryDevice&) = delete; + AtomicStorageMemoryDevice& operator=(const AtomicStorageMemoryDevice&) = delete; + AtomicStorageMemoryDevice(AtomicStorageMemoryDevice&&) = delete; + AtomicStorageMemoryDevice& operator=(AtomicStorageMemoryDevice&&) = delete; + + // ---- Helpers ---- + inline u64 getWordCount(void) const { return m_wordCount; } + inline u64 getSize(void) const { return m_size; } + inline virtual std::span> getRawData(void) { return { m_words, m_size }; } + inline virtual std::span> getRawData(void) const { return { m_words, m_size }; } + // Both getRawData() can be overridden to return empty spans for devices where internal memory shouldn't be exposed + + protected: + std::atomic* m_words; + u64 m_wordCount; + u64 m_size; + }; + namespace hw + { + class BUS : public MemoryDevice + { + public: + private: struct tMemoryRegion + { + MemoryDevice* device { nullptr }; + AddressType startAddress { 0x0000'0000 }; + AddressType endAddress { 0x0000'0000 }; + bool remap { false }; + ostd::String name { "" }; + }; + + public: + BUS(void) = default; + + bool guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired) override; + + // ---- Aligned loads ---- + u64 load_u64(AddressType addr) const override; + u32 load_u32(AddressType addr) const override; + u16 load_u16(AddressType addr) const override; + u8 load_u8 (AddressType addr) const override; + + // ---- Aligned stores ---- + bool store_u64(AddressType addr, u64 value) override; + bool store_u32(AddressType addr, u32 value) override; + bool store_u16(AddressType addr, u16 value) override; + bool store_u8 (AddressType addr, u8 value) override; + + void mapDevice(MemoryDevice& device, AddressType startAddr, AddressType endAddr, bool remap = false, ostd::String name = ""); + ostd::String getMemoryRegionName(AddressType address); + + private: + template + auto findRegion(this Self&& self, AddressType address) + { + for (auto& region : self.m_regions) + { // works whether self is const or not + if (address >= region.startAddress && address <= region.endAddress) + return ®ion; + } + data::ErrorHandler::pushError(data::ErrorCodes::MM_RegionNotFound, "Memory device not found"); + return decltype(&self.m_regions[0]){nullptr}; + } + + private: + std::vector m_regions; + + private: + }; + } +} diff --git a/src/hardware/RAM.cpp b/src/hardware/RAM.cpp new file mode 100644 index 0000000..055acca --- /dev/null +++ b/src/hardware/RAM.cpp @@ -0,0 +1,172 @@ +/* + DragonV2 - A collection of useful functionality + Copyright (C) 2026 OmniaX-Dev + + This file is part of DragonV2. + + DragonV2 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DragonV2 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DragonV2. If not, see . +*/ + +#include "RAM.hpp" +#include + +#define DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(size, msg) \ + if (addr + size > m_size) \ + { \ + fault(addr, data::ExceptionCause::LoadBounds, msg); \ + return 0; \ + } \ + if (addr & (size - 1)) \ + { \ + fault(addr, data::ExceptionCause::MisalignedLoad, msg); \ + return 0; \ + } \ + +#define DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(size, msg) \ + if (addr + size > m_size) \ + { \ + fault(addr, data::ExceptionCause::StoreBounds, msg); \ + return false; \ + } \ + if (addr & (size - 1)) \ + { \ + fault(addr, data::ExceptionCause::MisalignedStore, msg); \ + return false; \ + } \ + +namespace dragon +{ + namespace hw + { + bool RAM::guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired) + { + DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(4, "RAM::guest_cas_u32"); + + u32 shift = (addr & 4) * 8; + u64 mask = u64(0xFFFFFFFFu) << shift; + std::atomic& word = m_words[addr >> 3]; + + u64 word_expected = word.load(std::memory_order_seq_cst); + while (true) + { + u32 current_in_slot = static_cast(word_expected >> shift); + if (current_in_slot != expected) + { + // CAS fails: report what we saw, don't modify + actual = current_in_slot; + return false; + } + // CAS could succeed: try to update + u64 word_desired = (word_expected & ~mask) | (u64(desired) << shift); + if (word.compare_exchange_weak(word_expected, word_desired, std::memory_order_seq_cst, std::memory_order_seq_cst)) + { + actual = expected; // tell caller we succeeded + return true; + } + // CAS spuriously failed or someone else changed the word; word_expected + // is updated. Loop and re-check. + } + } + + u64 RAM::load_u64(AddressType addr) const + { + DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(8, "RAM::load_u64"); + return m_words[addr >> 3].load(std::memory_order_seq_cst); + } + + u32 RAM::load_u32(AddressType addr) const + { + DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(4, "RAM::load_u32"); + u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst); + u32 shift = (addr & 4) * 8; // 0 or 32 + return static_cast(word >> shift); + } + + u16 RAM::load_u16(AddressType addr) const + { + DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_R(2, "RAM::load_u16"); + u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst); + u32 shift = (addr & 6) * 8; // 0, 16, 32, or 48 + return static_cast(word >> shift); + } + + u8 RAM::load_u8(AddressType addr) const + { + if (addr >= m_size) + { + fault(addr, data::ExceptionCause::LoadBounds, "RAM::load_u8"); + return 0; + } + u64 word = m_words[addr >> 3].load(std::memory_order_seq_cst); + u32 shift = (addr & 7) * 8; + return static_cast(word >> shift); + } + + bool RAM::store_u64(AddressType addr, u64 value) + { + DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(8, "RAM::store_u64"); + m_words[addr >> 3].store(value, std::memory_order_seq_cst); + return true; + } + + bool RAM::store_u32(AddressType addr, u32 value) + { + DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(4, "RAM::store_u32"); + u32 shift = (addr & 4) * 8; + u64 mask = u64(0xFFFFFFFFu) << shift; + std::atomic& word = m_words[addr >> 3]; + u64 expected = word.load(std::memory_order_relaxed); + u64 desired; + do + { + desired = (expected & ~mask) | (u64(value) << shift); + } while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed)); + return true; + } + + bool RAM::store_u16(AddressType addr, u16 value) + { + DRAGON_TEST_FOR_MISALIGNED_OR_BOUNDS_FAULT_W(2, "RAM::store_u16"); + u32 shift = (addr & 6) * 8; + u64 mask = u64(0xFFFFu) << shift; + std::atomic& word = m_words[addr >> 3]; + u64 expected = word.load(std::memory_order_relaxed); + u64 desired; + do + { + desired = (expected & ~mask) | (u64(value) << shift); + } while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed)); + return true; + } + + bool RAM::store_u8(AddressType addr, u8 value) + { + if (addr >= m_size) + { + fault(addr, data::ExceptionCause::StoreBounds, "RAM::store_u8"); + return false; + } + u32 shift = (addr & 7) * 8; + u64 mask = u64(0xFFu) << shift; + std::atomic& word = m_words[addr >> 3]; + u64 expected = word.load(std::memory_order_relaxed); + u64 desired; + do + { + desired = (expected & ~mask) | (u64(value) << shift); + } while (!word.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_relaxed)); + return true; + } + } +} diff --git a/src/hardware/RAM.hpp b/src/hardware/RAM.hpp new file mode 100644 index 0000000..de4ff22 --- /dev/null +++ b/src/hardware/RAM.hpp @@ -0,0 +1,51 @@ +/* + DragonV2 - A collection of useful functionality + Copyright (C) 2026 OmniaX-Dev + + This file is part of DragonV2. + + DragonV2 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DragonV2 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DragonV2. If not, see . +*/ + +#pragma once + +#include + +#include "BUS.hpp" + +namespace dragon +{ + namespace hw + { + class RAM : public AtomicStorageMemoryDevice + { + public: + explicit RAM(u64 size) : AtomicStorageMemoryDevice(size) { } + + bool guest_cas_u32(AddressType addr, u32 expected, u32& actual, u32 desired) override; + + // ---- Aligned loads ---- + u64 load_u64(AddressType addr) const override; + u32 load_u32(AddressType addr) const override; + u16 load_u16(AddressType addr) const override; + u8 load_u8 (AddressType addr) const override; + + // ---- Aligned stores ---- + bool store_u64(AddressType addr, u64 value) override; + bool store_u32(AddressType addr, u32 value) override; + bool store_u16(AddressType addr, u16 value) override; + bool store_u8 (AddressType addr, u8 value) override; + }; + } +} diff --git a/src/tools/GlobalData.hpp b/src/tools/GlobalData.hpp index 15e3d36..fe673b7 100644 --- a/src/tools/GlobalData.hpp +++ b/src/tools/GlobalData.hpp @@ -5,6 +5,7 @@ namespace dragon { + using AddressType = u16; namespace hw { class VirtualCPU; } namespace data { @@ -13,51 +14,109 @@ namespace dragon class ErrorCodes { public: - inline static constexpr uint64_t NoError = 0x0000000000000000; - inline static constexpr uint64_t AccessViolation_BiosModeRequired = 0x0000000000000001; + inline static constexpr uint64_t NoError = 0x0000000000000000; + inline static constexpr uint64_t AccessViolation_BiosModeRequired = 0x0000000000000001; - inline static constexpr uint64_t MM_RegionNotFound = 0x1000000000000000; + inline static constexpr uint64_t MM_RegionNotFound = 0x1000000000000000; + inline static constexpr uint64_t MM_AtomicNotSupported = 0x1000000000000001; - inline static constexpr uint64_t CPU_UnknownInstruction = 0x2000000000000000; - inline static constexpr uint64_t CPU_UnsupportedExtension = 0x2000000000000001; - inline static constexpr uint64_t CPU_StackOverflow = 0x2000000000000002; + inline static constexpr uint64_t CPU_UnknownInstruction = 0x2000000000000000; + inline static constexpr uint64_t CPU_UnsupportedExtension = 0x2000000000000001; + inline static constexpr uint64_t CPU_StackOverflow = 0x2000000000000002; - inline static constexpr uint64_t BIOS_FailedToLoad = 0x3000000000000000; - inline static constexpr uint64_t BIOS_InvalidSize = 0x3000000000000001; - inline static constexpr uint64_t BIOS_WriteAttempt = 0x3000000000000002; - inline static constexpr uint64_t BIOS_InvalidAddress = 0x3000000000000003; + inline static constexpr uint64_t BIOS_FailedToLoad = 0x3000000000000000; + inline static constexpr uint64_t BIOS_InvalidSize = 0x3000000000000001; + inline static constexpr uint64_t BIOS_WriteAttempt = 0x3000000000000002; + inline static constexpr uint64_t BIOS_InvalidAddress = 0x3000000000000003; - inline static constexpr uint64_t HardDrive_UnableToMount = 0x4000000000000000; - inline static constexpr uint64_t HardDrive_Uninitialized = 0x4000000000000001; - inline static constexpr uint64_t HardDrive_ReadOverflow = 0x4000000000000002; - inline static constexpr uint64_t HardDrive_WriteOverflow = 0x4000000000000003; - inline static constexpr uint64_t HardDrive_BuffWriteOverflow = 0x4000000000000004; - inline static constexpr uint64_t HardDrive_EmptyBuffer = 0x4000000000000005; - inline static constexpr uint64_t HardDrive_InvalidConfiguration = 0x4000000000000006; - inline static constexpr uint64_t HardDrive_ReadFailed = 0x4000000000000007; - inline static constexpr uint64_t HardDrive_WriteFailed = 0x4000000000000008; - inline static constexpr uint64_t HardDrive_MemoryOverflow = 0x4000000000000009; - inline static constexpr uint64_t HardDrive_InvalidDiskSelected = 0x400000000000000A; - inline static constexpr uint64_t HardDrive_EndOfDisk = 0x400000000000000B; - inline static constexpr uint64_t HardDrive_ControllerReadFailed = 0x400000000000000C; - inline static constexpr uint64_t HardDrive_ControllerWriteFailed = 0x400000000000000D; - inline static constexpr uint64_t HardDrive_DisconnectInvalid = 0x400000000000000E; - inline static constexpr uint64_t HardDrive_DiskAlreadyConnected = 0x400000000000000F; + inline static constexpr uint64_t HardDrive_UnableToMount = 0x4000000000000000; + inline static constexpr uint64_t HardDrive_Uninitialized = 0x4000000000000001; + inline static constexpr uint64_t HardDrive_ReadOverflow = 0x4000000000000002; + inline static constexpr uint64_t HardDrive_WriteOverflow = 0x4000000000000003; + inline static constexpr uint64_t HardDrive_BuffWriteOverflow = 0x4000000000000004; + inline static constexpr uint64_t HardDrive_EmptyBuffer = 0x4000000000000005; + inline static constexpr uint64_t HardDrive_InvalidConfiguration = 0x4000000000000006; + inline static constexpr uint64_t HardDrive_ReadFailed = 0x4000000000000007; + inline static constexpr uint64_t HardDrive_WriteFailed = 0x4000000000000008; + inline static constexpr uint64_t HardDrive_MemoryOverflow = 0x4000000000000009; + inline static constexpr uint64_t HardDrive_InvalidDiskSelected = 0x400000000000000A; + inline static constexpr uint64_t HardDrive_EndOfDisk = 0x400000000000000B; + inline static constexpr uint64_t HardDrive_ControllerReadFailed = 0x400000000000000C; + inline static constexpr uint64_t HardDrive_ControllerWriteFailed = 0x400000000000000D; + inline static constexpr uint64_t HardDrive_DisconnectInvalid = 0x400000000000000E; + inline static constexpr uint64_t HardDrive_DiskAlreadyConnected = 0x400000000000000F; - inline static constexpr uint64_t CMOS_InvalidAddress = 0x5000000000000000; - inline static constexpr uint64_t CMOS_UnableToMount = 0x5000000000000001; - inline static constexpr uint64_t CMOS_InvalidSize = 0x5000000000000002; - inline static constexpr uint64_t CMOS_Uninitialized = 0x5000000000000003; + inline static constexpr uint64_t CMOS_InvalidAddress = 0x5000000000000000; + inline static constexpr uint64_t CMOS_UnableToMount = 0x5000000000000001; + inline static constexpr uint64_t CMOS_InvalidSize = 0x5000000000000002; + inline static constexpr uint64_t CMOS_Uninitialized = 0x5000000000000003; - inline static constexpr uint64_t BIOSVideo_InvalidAddress = 0x6000000000000000; + inline static constexpr uint64_t BIOSVideo_InvalidAddress = 0x6000000000000000; - inline static constexpr uint64_t IntVector_InvalidAddress = 0x7000000000000000; + inline static constexpr uint64_t IntVector_InvalidAddress = 0x7000000000000000; - inline static constexpr uint64_t Graphics_MemoryReadFailed = 0x8000000000000000; - inline static constexpr uint64_t Graphics_MemoryWriteFailed = 0x8000000000000001; + inline static constexpr uint64_t Graphics_MemoryReadFailed = 0x8000000000000000; + inline static constexpr uint64_t Graphics_MemoryWriteFailed = 0x8000000000000001; }; + enum class ExceptionCause : u32 + { + // === Instruction fetch faults === + MisalignedFetch = 0x0000'0000, // PC not aligned to instruction boundary + InstructionBounds = 0x0000'0001, // PC outside any mapped region + InstructionPageFault = 0x0000'0002, // virt page not mapped (MMU on) or no X permission + InstructionPrivPageFault = 0x0000'0003, // privileged virt page not mapped (MMU on) or no X permission + UnknownInstruction = 0x0000'0004, // decoded opcode is not valid + + // === Privilege === + PrivilegeViolation = 0x0000'1000, // user mode executed a [PRIV] instruction + + // === Memory access faults (loads) === + MisalignedLoad = 0x0000'2000, // load address not naturally aligned + LoadBounds = 0x0000'2001, // load address outside mapped region + LoadPageFault = 0x0000'2002, // virt page not mapped or no R permission + LoadPrivPageFault = 0x0000'2003, // virt page not mapped or no R permission + + // === Memory access faults (stores) === + MisalignedStore = 0x0000'3000, + StoreBounds = 0x0000'3001, + StorePageFault = 0x0000'3002, // virt page not mapped or no W permission + StorePrivPageFault = 0x0000'3003, // virt page not mapped or no W permission + + // === Atomic op faults === + AtomicNotSupported = 0x0000'4000, // CAS/XADD/XCHG on a device that can't (ROM, MMIO) + + // === Arithmetic === + DivideByZero = 0x0000'5000, + + // === MMIO === + BusUnmapped = 0x0000'6000, + ReadOnlyViolation = 0x0000'6001, + + // === Deliberate guest-initiated traps === + Syscall = 0x0000'7000, // syscall instruction + SoftwareInt = 0x0000'7001, // int N instruction + TrapInstruction = 0x0000'7002, // explicit trap instr + Breakpoint = 0x0000'7003, // brk for debugger + + // === Asynchronous (external) === + HardwareInterrupt = 0x0000'F000, // generic external IRQ; details in INT controller + + // === Catastrophic === + DoubleFault = 0x000F'FFFF, // exception while handling exception + + // Just in case + NoFault = 0xFFFF'FFFF, + }; + + struct GuestException + { + ExceptionCause cause; + AddressType fault_addr; // VA for translation faults, PA for bus faults, 0 otherwise + String msg; // For debug + }; + class ErrorHandler { public: struct tError @@ -127,22 +186,22 @@ namespace dragon public: inline BiosVideoDefaultPalette(void) { - m_colors.push_back({ 0, 0, 0 }); // Black - m_colors.push_back({ 157, 157, 157 }); // Gray - m_colors.push_back({ 255, 255, 255 }); // White - m_colors.push_back({ 190, 38, 51 }); // Red - m_colors.push_back({ 224, 111, 139 }); // Pink - m_colors.push_back({ 73, 60, 43 }); // Brown - m_colors.push_back({ 164, 100, 34 }); // Dark Orange - m_colors.push_back({ 235, 137, 49 }); // Orange - m_colors.push_back({ 247, 226, 107 }); // Yellow - m_colors.push_back({ 47, 80, 42 }); // Dark Green - m_colors.push_back({ 68, 137, 26 }); // Green - m_colors.push_back({ 163, 206, 39 }); // Lime - m_colors.push_back({ 27, 38, 50 }); // Dark Blue - m_colors.push_back({ 0, 87, 132 }); // Blue - m_colors.push_back({ 49, 162, 242 }); // Light Blue - m_colors.push_back({ 178, 220, 239 }); // Sky + m_colors.push_back({ 0, 0, 0 }); // Black + m_colors.push_back({ 157, 157, 157 }); // Gray + m_colors.push_back({ 255, 255, 255 }); // White + m_colors.push_back({ 190, 38, 51 }); // Red + m_colors.push_back({ 224, 111, 139 }); // Pink + m_colors.push_back({ 73, 60, 43 }); // Brown + m_colors.push_back({ 164, 100, 34 }); // Dark Orange + m_colors.push_back({ 235, 137, 49 }); // Orange + m_colors.push_back({ 247, 226, 107 }); // Yellow + m_colors.push_back({ 47, 80, 42 }); // Dark Green + m_colors.push_back({ 68, 137, 26 }); // Green + m_colors.push_back({ 163, 206, 39 }); // Lime + m_colors.push_back({ 27, 38, 50 }); // Dark Blue + m_colors.push_back({ 0, 87, 132 }); // Blue + m_colors.push_back({ 49, 162, 242 }); // Light Blue + m_colors.push_back({ 178, 220, 239 }); // Sky } inline ostd::Color getColor(uint8_t col) override @@ -203,12 +262,12 @@ namespace dragon { switch (code) { - case DiskInterfaceFFinished: return "Disk_Interface_Finished"; - case KeyPressed: return "Key_Pressed"; - case KeyReleased: return "Key_Released"; - case TextEntered: return "Text_Entered"; - case Text16ModeScreenRefreshed: return "Text16_Mode_Screen_Refreshed"; - default: return "UNKNOWN"; + case DiskInterfaceFFinished: return "Disk_Interface_Finished"; + case KeyPressed: return "Key_Pressed"; + case KeyReleased: return "Key_Released"; + case TextEntered: return "Text_Entered"; + case Text16ModeScreenRefreshed: return "Text16_Mode_Screen_Refreshed"; + default: return "UNKNOWN"; } return "UNKNOWN"; } @@ -217,59 +276,59 @@ namespace dragon class CMOSRegisters { public: - inline static constexpr uint8_t MemoryStart = 0x00; - inline static constexpr uint8_t MemorySize = 0x02; - inline static constexpr uint8_t ClockSpeed = 0x04; - inline static constexpr uint8_t ScreenRedrawRate = 0x06; - inline static constexpr uint8_t ScreenWidth = 0x07; - inline static constexpr uint8_t ScreenHeight = 0x09; - inline static constexpr uint8_t BootDisk = 0x10; - inline static constexpr uint8_t StackSize = 0x11; + inline static constexpr uint8_t MemoryStart = 0x00; + inline static constexpr uint8_t MemorySize = 0x02; + inline static constexpr uint8_t ClockSpeed = 0x04; + inline static constexpr uint8_t ScreenRedrawRate = 0x06; + inline static constexpr uint8_t ScreenWidth = 0x07; + inline static constexpr uint8_t ScreenHeight = 0x09; + inline static constexpr uint8_t BootDisk = 0x10; + inline static constexpr uint8_t StackSize = 0x11; - inline static constexpr uint8_t DiskList = 0x7E; + inline static constexpr uint8_t DiskList = 0x7E; }; class DPTStructure { public: struct tFlags { - inline static constexpr uint8_t Boot = 0; + inline static constexpr uint8_t Boot = 0; }; public: - inline static constexpr uint16_t DPTID = 0x000; - inline static constexpr uint16_t DPTVersionMaj = 0x002; - inline static constexpr uint16_t DPTVersionMin = 0x003; - inline static constexpr uint16_t PartitionCount = 0x004; + inline static constexpr uint16_t DPTID = 0x000; + inline static constexpr uint16_t DPTVersionMaj = 0x002; + inline static constexpr uint16_t DPTVersionMin = 0x003; + inline static constexpr uint16_t PartitionCount = 0x004; - inline static constexpr uint16_t EntriesStart = 0x00C; + inline static constexpr uint16_t EntriesStart = 0x00C; - inline static constexpr uint16_t EntryStartAddress = 0x000; - inline static constexpr uint16_t EntryPartitionSize = 0x004; - inline static constexpr uint16_t EntryFlags = 0x008; - inline static constexpr uint16_t EntryPartitionLabel = 0x024; + inline static constexpr uint16_t EntryStartAddress = 0x000; + inline static constexpr uint16_t EntryPartitionSize = 0x004; + inline static constexpr uint16_t EntryFlags = 0x008; + inline static constexpr uint16_t EntryPartitionLabel = 0x024; - inline static constexpr uint16_t HeaderReservedSizeBytes = 7; - inline static constexpr uint16_t DiskAddress = 0x200; - inline static constexpr uint16_t DPT_ID_CODE = 0xF1CA; - inline static constexpr uint16_t EntrySizeBytes = 100; - inline static constexpr uint16_t EntryLabelSizeBytes = 64; - inline static constexpr uint16_t EntryReservedSizeBytes = 26; - inline static constexpr uint16_t HeaderSizeBytes = 12; - inline static constexpr uint16_t DPTBlockSizeBytes = 512; - inline static constexpr uint8_t CurrentDPTVersionMaj = 0x00; - inline static constexpr uint8_t CurrentDPTVersionMin = 0x02; - inline static constexpr uint32_t DiskStartAddr = 0x00000400; - inline static constexpr uint8_t MaxPartCount = 5; + inline static constexpr uint16_t HeaderReservedSizeBytes = 7; + inline static constexpr uint16_t DiskAddress = 0x200; + inline static constexpr uint16_t DPT_ID_CODE = 0xF1CA; + inline static constexpr uint16_t EntrySizeBytes = 100; + inline static constexpr uint16_t EntryLabelSizeBytes = 64; + inline static constexpr uint16_t EntryReservedSizeBytes = 26; + inline static constexpr uint16_t HeaderSizeBytes = 12; + inline static constexpr uint16_t DPTBlockSizeBytes = 512; + inline static constexpr uint8_t CurrentDPTVersionMaj = 0x00; + inline static constexpr uint8_t CurrentDPTVersionMin = 0x02; + inline static constexpr uint32_t DiskStartAddr = 0x00000400; + inline static constexpr uint8_t MaxPartCount = 5; - inline static constexpr uint16_t BootPart_IDAddr = 0x0000; - inline static constexpr uint16_t BootPart_CodeStart = 0x0020; - inline static constexpr uint16_t BootPart_ID_CODE = 0xF1C4; - inline static constexpr uint16_t BootPart_CodeSizeBytes = 1024; - inline static constexpr uint8_t BootPart_HeaderSizeBytes = 32; + inline static constexpr uint16_t BootPart_IDAddr = 0x0000; + inline static constexpr uint16_t BootPart_CodeStart = 0x0020; + inline static constexpr uint16_t BootPart_ID_CODE = 0xF1C4; + inline static constexpr uint16_t BootPart_CodeSizeBytes = 1024; + inline static constexpr uint8_t BootPart_HeaderSizeBytes = 32; }; class CPUExtension