Added first draft of Runtime class

This commit is contained in:
OmniaX-Dev 2026-05-24 06:48:16 +02:00
parent 678bbf6381
commit 9a8d7d15a3
11 changed files with 449 additions and 33 deletions

View file

@ -35,6 +35,9 @@ list(APPEND RUNTIME_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/RAM.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/MMU.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 list(APPEND DEBUGGER_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/debugger/debugger_main.cpp ${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/RAM.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/BUS.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/MMU.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 list(APPEND ASSEMBLER_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/assembler/assembler_main.cpp ${CMAKE_CURRENT_LIST_DIR}/src/assembler/assembler_main.cpp

2
build
View file

@ -61,7 +61,7 @@ else
printf "\n\033[0;32mRunning DragonV2 runtime...\n\n\033[0m" printf "\n\033[0;32mRunning DragonV2 runtime...\n\n\033[0m"
cd bin cd bin
# ./reload_env.sh # ./reload_env.sh
./dvm config/testMachine.dvm ./dvm config/testMachine.dvm --verbose-load
# backtrack_changes # backtrack_changes
cd .. cd ..
elif [[ "$1" == "ddb" ]]; then elif [[ "$1" == "ddb" ]]; then

View file

@ -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

View file

@ -1 +1 @@
13 14

View file

@ -31,6 +31,19 @@ namespace dragon
using PhysicalAddress = AddressType; 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 { enum class RegAccess {
UserReadWrite, // FL (if you split out I) UserReadWrite, // FL (if you split out I)
UserReadOnly, // CORE_ID, CYCLE UserReadOnly, // CORE_ID, CYCLE

View file

@ -28,11 +28,7 @@ namespace dragon
// Core // Core
// ===================================================================== // =====================================================================
Core::Core(CPU& cpu, BUS& bus, u32 core_id) Core::Core(CPU& cpu, BUS& bus, u32 core_id) : m_cpu(cpu), m_bus(bus), m_coreId(core_id), m_mmu(bus, &m_accessMode)
: m_cpu(cpu)
, m_bus(bus)
, m_coreId(core_id)
, m_mmu(bus, &m_accessMode)
{ {
reset(); reset();
} }
@ -159,8 +155,7 @@ namespace dragon
// CPU // CPU
// ===================================================================== // =====================================================================
CPU::CPU(BUS& bus, u32 num_cores) CPU::CPU(BUS& bus, u32 num_cores) : m_bus(bus)
: m_bus(bus)
{ {
m_cores.reserve(num_cores); m_cores.reserve(num_cores);
for (u32 i = 0; i < num_cores; ++i) for (u32 i = 0; i < num_cores; ++i)

View file

@ -0,0 +1,69 @@
#include "ConfigLoader.hpp"
#include <ostd/io/File.hpp>
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;
}
}

View file

@ -0,0 +1,32 @@
#pragma once
#include <ostd/data/Color.hpp>
#include <map>
namespace dragon
{
struct tMachineConfig
{
std::map<int32_t, ostd::String> 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);
};
}

238
src/runtime/Runtime.cpp Normal file
View file

@ -0,0 +1,238 @@
#include "Runtime.hpp"
#include <ogfx/render/PixelRenderer.hpp>
#include <ostd/io/Memory.hpp>
#include <ostd/utils/Time.hpp>
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<int16_t>(ogfx::PixelRenderer::TextRenderer::CONSOLE_CHARS_H));
// vCMOS.write16(CMOSRegisters::ScreenHeight, static_cast<int16_t>(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 <binary-file> <ram-offset>";
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 <machine-config-file> [...options...]").reset().nl();
out.nl();
}
}

53
src/runtime/Runtime.hpp Normal file
View file

@ -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;
};
}

View file

@ -1,28 +1,24 @@
/* #include <ostd/utils/Defines.hpp>
DragonV2 - A collection of useful functionality #include "Runtime.hpp"
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 <https://www.gnu.org/licenses/>.
*/
#include <ostd/ostd.hpp>
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
ostd::ConsoleOutputHandler out; //Loading commandline arguments
out.fg(ostd::ConsoleColors::Green).p("TODO: Runtime!!").nl().reset(); dragon::DragonRuntime::tCommandLineArgs args;
int32_t rValue = dragon::DragonRuntime::loadArguments(argc, argv, args);
if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_RUNTIME)
return 0; 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;
} }