From 9a8d7d15a351a5951091f4c8a069a04f96094d2b Mon Sep 17 00:00:00 2001 From: OmniaX-Dev Date: Sun, 24 May 2026 06:48:16 +0200 Subject: [PATCH] Added first draft of Runtime class --- CMakeLists.txt | 6 + build | 2 +- extra/config/testMachine.dvm | 14 +++ other/build.nr | 2 +- src/common/Data.hpp | 13 ++ src/hardware/CPU.cpp | 9 +- src/runtime/ConfigLoader.cpp | 69 ++++++++++ src/runtime/ConfigLoader.hpp | 32 +++++ src/runtime/Runtime.cpp | 238 +++++++++++++++++++++++++++++++++++ src/runtime/Runtime.hpp | 53 ++++++++ src/runtime/runtime_main.cpp | 44 +++---- 11 files changed, 449 insertions(+), 33 deletions(-) create mode 100644 extra/config/testMachine.dvm create mode 100644 src/runtime/ConfigLoader.cpp create mode 100644 src/runtime/ConfigLoader.hpp create mode 100644 src/runtime/Runtime.cpp create mode 100644 src/runtime/Runtime.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 68ba209..db62c68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,9 @@ list(APPEND RUNTIME_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/MMU.cpp + + ${CMAKE_CURRENT_LIST_DIR}/src/runtime/Runtime.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/runtime/ConfigLoader.cpp ) list(APPEND DEBUGGER_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/debugger/debugger_main.cpp @@ -43,6 +46,9 @@ list(APPEND DEBUGGER_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/MMU.cpp + + ${CMAKE_CURRENT_LIST_DIR}/src/runtime/Runtime.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/runtime/ConfigLoader.cpp ) list(APPEND ASSEMBLER_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/assembler/assembler_main.cpp diff --git a/build b/build index f54ac83..992469a 100755 --- a/build +++ b/build @@ -61,7 +61,7 @@ else printf "\n\033[0;32mRunning DragonV2 runtime...\n\n\033[0m" cd bin # ./reload_env.sh - ./dvm config/testMachine.dvm + ./dvm config/testMachine.dvm --verbose-load # backtrack_changes cd .. elif [[ "$1" == "ddb" ]]; then diff --git a/extra/config/testMachine.dvm b/extra/config/testMachine.dvm new file mode 100644 index 0000000..ded9b57 --- /dev/null +++ b/extra/config/testMachine.dvm @@ -0,0 +1,14 @@ +Disks = dragon/disk1.dr +Bios = dragon/bios.bin +CMOS = dragon/cmos.dr + +cpuext = extmov, extalu +fixed_clock = true +clock_rate_sec = 5000 +memory_extension_pages = 16 + +screen_redraw_rate_per_second = 10 + +SingleColor_foreground = #009900FF +SingleColor_background = #111111FF +16Color_Palette = 0 diff --git a/other/build.nr b/other/build.nr index b1bd38b..8351c19 100644 --- a/other/build.nr +++ b/other/build.nr @@ -1 +1 @@ -13 +14 diff --git a/src/common/Data.hpp b/src/common/Data.hpp index d3a3de4..4168cba 100644 --- a/src/common/Data.hpp +++ b/src/common/Data.hpp @@ -31,6 +31,19 @@ namespace dragon using PhysicalAddress = AddressType; // -------------------------------- + class MemoryMapAddresses + { + public: + inline static constexpr AddressType BIOS_Start = 0x0000'0000; + inline static constexpr AddressType BIOS_End = 0x0001'0000; // 1 MiB + + inline static constexpr AddressType CMOS_Start = 0x0001'0000; + inline static constexpr AddressType CMOS_End = 0x0001'1000; // 4 KiB + + inline static constexpr AddressType Memory_Start = 0x0004'0000; // 4 Gib - 4 Mib + inline static constexpr AddressType Memory_End = 0xFFFF'FFFF; + }; + enum class RegAccess { UserReadWrite, // FL (if you split out I) UserReadOnly, // CORE_ID, CYCLE diff --git a/src/hardware/CPU.cpp b/src/hardware/CPU.cpp index 2962b2c..8490125 100644 --- a/src/hardware/CPU.cpp +++ b/src/hardware/CPU.cpp @@ -28,11 +28,7 @@ namespace dragon // Core // ===================================================================== - Core::Core(CPU& cpu, BUS& bus, u32 core_id) - : m_cpu(cpu) - , m_bus(bus) - , m_coreId(core_id) - , m_mmu(bus, &m_accessMode) + Core::Core(CPU& cpu, BUS& bus, u32 core_id) : m_cpu(cpu), m_bus(bus), m_coreId(core_id), m_mmu(bus, &m_accessMode) { reset(); } @@ -159,8 +155,7 @@ namespace dragon // CPU // ===================================================================== - CPU::CPU(BUS& bus, u32 num_cores) - : m_bus(bus) + CPU::CPU(BUS& bus, u32 num_cores) : m_bus(bus) { m_cores.reserve(num_cores); for (u32 i = 0; i < num_cores; ++i) diff --git a/src/runtime/ConfigLoader.cpp b/src/runtime/ConfigLoader.cpp new file mode 100644 index 0000000..ee07e2b --- /dev/null +++ b/src/runtime/ConfigLoader.cpp @@ -0,0 +1,69 @@ +#include "ConfigLoader.hpp" +#include + +namespace dragon +{ + const tMachineConfig MachineConfigLoader::loadConfig(const ostd::String& configFilePath) + { + tMachineConfig config; + ostd::TextFileBuffer file(configFilePath.cpp_str()); + if (!file.exists()) return config; //TODO: Error + auto lines = file.getLines(); + for (auto& line : lines) + { + ostd::String lineEdit = line; + if (!lineEdit.contains("=")) continue; //TODO: Warning + auto tokens = lineEdit.tokenize("="); + if (tokens.count() != 2) continue; //TODO: Warning + lineEdit = tokens.next(); + lineEdit = lineEdit.toLower(); + if (lineEdit == "disks") + { + lineEdit = tokens.next(); + tokens = lineEdit.tokenize(","); + if (tokens.count() == 0) continue; //TODO: Warning + int32_t disk_nr = 0; + while (tokens.hasNext()) + { + lineEdit = tokens.next(); + config.vdisk_paths[disk_nr++] = lineEdit; + } + } + else if (lineEdit == "bios") + { + lineEdit = tokens.next(); + config.bios_path = lineEdit; + } + else if (lineEdit == "cmos") + { + lineEdit = tokens.next(); + config.cmos_path = lineEdit; + } + else if (lineEdit == "clock_rate_sec") + { + lineEdit = tokens.next(); + lineEdit.trim().toLower(); + if (!lineEdit.isNumeric()) continue; //TODO: Error + config.clock_rate_sec = lineEdit.toInt(); + } + else if (lineEdit == "fixed_clock") + { + lineEdit = tokens.next(); + lineEdit.trim().toLower(); + if (lineEdit == "true") + config.fixed_clock = true; + else if (lineEdit == "false") + config.fixed_clock = false; + else continue; //TODO: Error + } + else continue; //TODO: Warning + } + return validate_machine_config(config); + } + + const tMachineConfig& MachineConfigLoader::validate_machine_config(tMachineConfig& config) //TODO: Implement config validation + { + config.m_valid = true; + return config; + } +} diff --git a/src/runtime/ConfigLoader.hpp b/src/runtime/ConfigLoader.hpp new file mode 100644 index 0000000..e4025f3 --- /dev/null +++ b/src/runtime/ConfigLoader.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace dragon +{ + struct tMachineConfig + { + std::map vdisk_paths; + int32_t clock_rate_sec { 500 }; + bool fixed_clock { true }; + ostd::String bios_path; + ostd::String cmos_path; + + inline bool isValid(void) const { return m_valid; } + + private: + bool m_valid { false }; + + friend class MachineConfigLoader; + }; + + class MachineConfigLoader + { + public: + static const tMachineConfig loadConfig(const ostd::String& configFilePath); + + private: + static const tMachineConfig& validate_machine_config(tMachineConfig& config); + }; +} diff --git a/src/runtime/Runtime.cpp b/src/runtime/Runtime.cpp new file mode 100644 index 0000000..53fc43e --- /dev/null +++ b/src/runtime/Runtime.cpp @@ -0,0 +1,238 @@ +#include "Runtime.hpp" +#include +#include +#include + +namespace dragon +{ + int32_t DragonRuntime::loadArguments(int argc, char** argv, tCommandLineArgs& args) + { + ostd::ConsoleOutputHandler out; + if (argc < 2) + { + out.fg(ostd::ConsoleColors::Red).p("Error: too few arguments.").nl(); + out.fg(ostd::ConsoleColors::Red).p("Use the --help option for more info.").reset().nl(); + return dragon::DragonRuntime::RETURN_VAL_TOO_FEW_ARGUMENTS; + } + else + { + args.machine_config_path = argv[1]; + if (args.machine_config_path == "--help") + { + __print_application_help(); + return DragonRuntime::RETURN_VAL_CLOSE_RUNTIME; + } + for (int32_t i = 2; i < argc; i++) + { + ostd::String edit(argv[i]); + if (edit == "--verbose-load") + args.verbose_load = true; + else if (edit == "--force-load") + { + if ((argc - 1) - i < 2) + return RETURN_VAL_MISSING_PARAM; + i++; + args.force_load_file = argv[i]; + i++; + edit = argv[i]; + if (!edit.isNumeric()) + return RETURN_VAL_PARAMETER_NOT_NUMERIC; + args.force_load_mem_offset = (AddressType)edit.toInt(); + args.force_load = true; + } + else if (edit == "--help") + { + __print_application_help(); + return RETURN_VAL_CLOSE_RUNTIME; + } + } + } + return RETURN_VAL_EXIT_SUCCESS; + } + + int32_t DragonRuntime::initMachine(const ostd::String& configFilePath, + bool verbose, + bool debugModeEnabled) + { + ostd::ConsoleOutputHandler out; + if (verbose) + out.fg(ostd::ConsoleColors::Magenta).p("Loading machine config: ").fg(ostd::ConsoleColors::BrightYellow).p(configFilePath.cpp_str()).nl(); + machine_config = dragon::MachineConfigLoader::loadConfig(configFilePath); + if (!machine_config.isValid()) return RETURN_VAL_INVALID_MACHINE_CONFIG; //TODO: Error + + if (machine_config.vdisk_paths.size() == 0) return RETURN_VAL_NO_DISK; //TODO: Error + // if (verbose) + // out.fg(ostd::ConsoleColors::Magenta).p(" Initializing virtual disks:").nl(); + // for (auto const& disk_path : machine_config.vdisk_paths) + // { + // vDisks[disk_path.first] = dragon::hw::VirtualHardDrive(disk_path.second); + // vDiskInterface.connectDisk(vDisks[disk_path.first], disk_path.first); + // if (verbose) + // out.fg(ostd::ConsoleColors::BrightYellow).p(" Disk").p(disk_path.first).p(" connected: ").p(disk_path.second.cpp_str()).nl(); + // } + + // if (verbose) + // out.fg(ostd::ConsoleColors::Magenta).p(" Loading vBIOS file: ").fg(ostd::ConsoleColors::BrightYellow).p(machine_config.bios_path.cpp_str()).nl(); + // vBIOS.init(machine_config.bios_path); + + // if (verbose) + // out.fg(ostd::ConsoleColors::Magenta).p(" Loading vCMOS file: ").fg(ostd::ConsoleColors::BrightYellow).p(machine_config.cmos_path.cpp_str()).nl(); + // vCMOS.init(machine_config.cmos_path); + + // if (verbose) + // { + // out.fg(ostd::ConsoleColors::Magenta).p(" Initializing BUS:").nl(); + // out.fg(ostd::ConsoleColors::Magenta).p(" vBIOS: "); + // out.fg(ostd::ConsoleColors::BrightYellow); + // out.p(ostd::String::getHexStr(dragon::MemoryMapAddresses::BIOS_Start, true, 2).cpp_str()); + // out.p(" to "); + // out.p(ostd::String::getHexStr(dragon::MemoryMapAddresses::BIOS_End, true, 2).cpp_str()); + // out.p(" (remap=false)").nl(); + // } + // bus.mapDevice(vBIOS, dragon::MemoryMapAddresses::BIOS_Start, dragon::MemoryMapAddresses::BIOS_End, false, "BIOS"); + // if (verbose) + // { + // out.fg(ostd::ConsoleColors::Magenta).p(" vCMOS: "); + // out.fg(ostd::ConsoleColors::BrightYellow); + // out.p(ostd::String::getHexStr(dragon::MemoryMapAddresses::CMOS_Start, true, 2).cpp_str()); + // out.p(" to "); + // out.p(ostd::String::getHexStr(dragon::MemoryMapAddresses::CMOS_End, true, 2).cpp_str()); + // out.p(" (remap=true)").nl(); + // } + // memMap.mapDevice(vCMOS, dragon::MemoryMapAddresses::CMOS_Start, dragon::MemoryMapAddresses::CMOS_End, true, "CMOS"); + if (verbose) + { + out.fg(ostd::ConsoleColors::Magenta).p(" RAM: "); + out.fg(ostd::ConsoleColors::BrightYellow); + out.p(ostd::String::getHexStr(dragon::MemoryMapAddresses::Memory_Start, true, 4).cpp_str()); + out.p(" to "); + out.p(ostd::String::getHexStr(dragon::MemoryMapAddresses::Memory_End, true, 4).cpp_str()); + out.p(" (remap=false)").nl(); + } + bus.mapDevice(ram, dragon::MemoryMapAddresses::Memory_Start, dragon::MemoryMapAddresses::Memory_End, false, "RAM"); + + if (verbose) + { + out.fg(ostd::ConsoleColors::BrightYellow).p(" Fixed clock enabled: ").p(STR_BOOL(machine_config.fixed_clock)).nl(); + if (machine_config.fixed_clock) + out.fg(ostd::ConsoleColors::BrightYellow).p(" Clock speed: ").p(machine_config.clock_rate_sec).p(" Hz").nl(); + } + + // vCMOS.write16(CMOSRegisters::MemoryStart, MemoryMapAddresses::Memory_Start); + // vCMOS.write16(CMOSRegisters::MemorySize, MemoryMapAddresses::Memory_End); + // vCMOS.write16(CMOSRegisters::ClockSpeed, machine_config.clock_rate_sec); + // vCMOS.write8(CMOSRegisters::ScreenRedrawRate, machine_config.screen_redraw_rate_per_second); + // vCMOS.write16(CMOSRegisters::ScreenWidth, static_cast(ogfx::PixelRenderer::TextRenderer::CONSOLE_CHARS_H)); + // vCMOS.write16(CMOSRegisters::ScreenHeight, static_cast(ogfx::PixelRenderer::TextRenderer::CONSOLE_CHARS_V)); + // vCMOS.write16(CMOSRegisters::StackSize, 0x1000); + // ostd::BitField_16 disk_list_bitfield; + // disk_list_bitfield.value = 0; + // for (int32_t i = 0; i < 16; i++) + // { + // if (vDisks.count(i) > 0) + // ostd::Bits::set(disk_list_bitfield, i); + // } + // vCMOS.write16(CMOSRegisters::DiskList, disk_list_bitfield.value); + // if (verbose) + // out.fg(ostd::ConsoleColors::BrightYellow).p(" Loading CMOS Machine info").nl(); + + out.nl().nl(); + return RETURN_VAL_EXIT_SUCCESS; + } + + void DragonRuntime::runMachine(void) + { + cpu.run(); //TODO: CPU needs to run a single step, not have an internal loop + // double clock_speed_us = 1000000.0 / machine_config.clock_rate_sec; + // double acc = 0; + // double acc2 = 0; + // uint64_t avg_count = 0; + // uint64_t _time = 0; + // double avg_tot = 0; + // ostd::Counter clock_timer; + // bool running = true; + // bool fixed_clock = machine_config.fixed_clock; + // ostd::Counter _timer; + // while (running) + // { + // clock_timer.startCount(ostd::eTimeUnits::Microseconds); + // ostd::SignalHandler::handleDelegateSignals(); + // AddressType addr = cpu.readRegister(dragon::Registers::IP); + // AddressType spAddr = cpu.readRegister(dragon::Registers::SP); + // uint8_t screenRedrawRate = vCMOS.read8(CMOSRegisters::ScreenRedrawRate); + // running = cpu.execute() && vDisplay.isRunning(); + // if (acc == 500) + // { + // avg_count++; + // avg_tot += _time; + // s_avgInstTime = (uint64_t)std::round(avg_tot / avg_count); + // // out.fg(ostd::ConsoleColors::Red).p(getAvgClockSpeed()).nl().reset(); + // acc = 0; + // } + // _time = clock_timer.endCount(); + // acc++; + // acc2++; + // if (_time < clock_speed_us && fixed_clock) + // ostd::Time::sleep(clock_speed_us - _time, ostd::eTimeUnits::Microseconds); + // } + } + + bool DragonRuntime::runStep(void) + { + return false; + // std::sort(trackedAddresses.begin(), trackedAddresses.end()); + // __get_machine_footprint(&s_machineInfo, trackedAddresses, true); + // __track_call_stack(&s_machineInfo); + // bool running = cpu.execute() && vDisplay.isRunning(); + // uint8_t screenRedrawRate = vCMOS.read8(CMOSRegisters::ScreenRedrawRate); + // vDisplay.mainLoop(); + // if (s_enableScreenRedrawDelay && s_stepAcc2 == (1000.0 / screenRedrawRate)) + // { + // vDisplay.redrawScreen(); + // s_stepAcc2 = 0; + // } + // else if (!s_enableScreenRedrawDelay) + // { + // vDisplay.redrawScreen(); + // } + // s_stepAcc2++; + // // vDisplay.redrawScreen(); // This is slow...maybe it should be on a 100ms rate, like in normal runtime mode + // vDiskInterface.cycleStep(); + // __get_machine_footprint(&s_machineInfo, trackedAddresses, false); + // return running || vDiskInterface.isBusy(); + } + + void DragonRuntime::forceLoad(const ostd::String& filePath, AddressType loadAddress) + { + ostd::ByteStream code; + ostd::Memory::loadByteStreamFromFile(filePath, code); + + int16_t index = 0; + for (auto& b : code) + { + ram.store_i8(dragon::MemoryMapAddresses::Memory_Start + loadAddress + index, b); + index++; + } + } + + void DragonRuntime::__print_application_help(void) + { + ostd::ConsoleOutputHandler out; + + int32_t commandLength = 46; + + out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available parameters:").reset().nl(); + ostd::String tmpCommand = "--verbose-load"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show more information while loading the virtual machine.").reset().nl(); + tmpCommand = "--force-load "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Injects the specified binary into RAM at the specified offset.").reset().nl(); + tmpCommand = "--help"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Displays this help message.").reset().nl(); + + out.nl().fg(ostd::ConsoleColors::Magenta).p("Usage: ./dvm [...options...]").reset().nl(); + out.nl(); + } +} diff --git a/src/runtime/Runtime.hpp b/src/runtime/Runtime.hpp new file mode 100644 index 0000000..0edf398 --- /dev/null +++ b/src/runtime/Runtime.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "../hardware/CPU.hpp" +#include "../hardware/RAM.hpp" +#include "ConfigLoader.hpp" + +namespace dragon +{ + class DragonRuntime + { + public: struct tCommandLineArgs + { + ostd::String machine_config_path = ""; + bool verbose_load = false; + bool force_load = false; + ostd::String force_load_file = ""; + uint16_t force_load_mem_offset = 0x00; + }; + public: + static int32_t loadArguments(int argc, char** argv, tCommandLineArgs& args); + static int32_t initMachine(const ostd::String& configFilePath, + bool verbose = false, + bool debugModeEnabled = false); + static void runMachine(void); + static bool runStep(void); + static void forceLoad(const ostd::String& filePath, AddressType loadAddress); + + inline static uint64_t getAvgClockSpeed(void) { return (uint64_t)std::round(1000000.0 / s_avgInstTime); } + + private: + static void __print_application_help(void); + + public: + inline static hw::BUS bus; + inline static hw::CPU cpu { bus, 4 }; //TODO: hardcoded 4 cores, need a way to load core count from config file + inline static hw::RAM ram { 0xFFFFFF }; //TODO: 16 MiB hardcoded, need a way to configure from config file + + inline static tMachineConfig machine_config; + + inline static uint64_t s_avgInstTime { 0 }; + + + public: + inline static const int32_t RETURN_VAL_CLOSE_DEBUGGER = 128; + inline static const int32_t RETURN_VAL_CLOSE_RUNTIME = 256; + inline static const int32_t RETURN_VAL_INVALID_MACHINE_CONFIG = 1; + inline static const int32_t RETURN_VAL_NO_DISK = 2; + inline static const int32_t RETURN_VAL_TOO_FEW_ARGUMENTS = 3; + inline static const int32_t RETURN_VAL_MISSING_PARAM = 4; + inline static const int32_t RETURN_VAL_PARAMETER_NOT_NUMERIC = 5; + inline static const int32_t RETURN_VAL_EXIT_SUCCESS = 0; + }; +} diff --git a/src/runtime/runtime_main.cpp b/src/runtime/runtime_main.cpp index 03bd023..72f6b70 100644 --- a/src/runtime/runtime_main.cpp +++ b/src/runtime/runtime_main.cpp @@ -1,28 +1,24 @@ -/* - 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 "Runtime.hpp" int main(int argc, char** argv) { - ostd::ConsoleOutputHandler out; - out.fg(ostd::ConsoleColors::Green).p("TODO: Runtime!!").nl().reset(); - return 0; + //Loading commandline arguments + dragon::DragonRuntime::tCommandLineArgs args; + int32_t rValue = dragon::DragonRuntime::loadArguments(argc, argv, args); + if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_RUNTIME) + return 0; + if (rValue != 0) return rValue; + + //Initializing the runtime + rValue = dragon::DragonRuntime::initMachine(args.machine_config_path, args.verbose_load); + if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_RUNTIME) + return 0; + if (rValue != 0) return rValue; + + //Executing the runtime + if (args.force_load) + dragon::DragonRuntime::forceLoad(args.force_load_file, args.force_load_mem_offset); + dragon::DragonRuntime::runMachine(); + return dragon::DragonRuntime::RETURN_VAL_EXIT_SUCCESS; }