First Commit

This commit is contained in:
OmniaX 2023-12-27 17:21:07 +01:00
parent 7be98df33e
commit 43e9e82264
69 changed files with 7707 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
bin
tools/win-release-tools
win-release

31
.vscode/c_cpp_properties.json vendored Normal file
View file

@ -0,0 +1,31 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${default}",
"${workspaceRoot}/../omnia-framework/src"
],
"defines": [],
"compilerPath": "/usr/bin/clang++",
"cStandard": "c17",
"cppStandard": "c++20",
"intelliSenseMode": "linux-clang-x64"
//"compileCommands": "${workspaceFolder}/compile_commands.json"
},
{
"name": "Windows",
"includePath": [
"${default}",
"${workspaceRoot}/../omnia-framework/src"
],
"defines": [],
"compilerPath": "C:\\msys64\\ucrt64\\bin\\clang++",
"cStandard": "c17",
"cppStandard": "c++20",
"intelliSenseMode": "windows-clang-x64"
//"compileCommands": "${workspaceFolder}/compile_commands.json"
}
],
"version": 4
}

143
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,143 @@
{
"C_Cpp.errorSquiggles": "Enabled",
"files.associations": {
"functional": "cpp",
"thread": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"csetjmp": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"hash_map": "cpp",
"barrier": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cfenv": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"coroutine": "cpp",
"cstdint": "cpp",
"cuchar": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"source_location": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"latch": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"ranges": "cpp",
"scoped_allocator": "cpp",
"semaphore": "cpp",
"shared_mutex": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"syncstream": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp",
"*.rh": "cpp",
"hash_set": "cpp",
"strstream": "cpp",
"expected": "cpp",
"spanstream": "cpp",
"stacktrace": "cpp",
"*.ipp": "cpp",
"format": "cpp",
"stdfloat": "cpp"
},
"workbench.colorCustomizations": {
"editor.background": "#131313",
"tab.hoverBackground": "#111",
"tab.inactiveBackground": "#000",
"tab.activeBackground": "#1b1b1b",
"tab.activeBorder": "#bb5400",
"editorIndentGuide.background": "#252525",
"editorWhitespace.foreground": "#683700",
"editor.lineHighlightBorder": "#1a1a1a",
"editor.lineHighlightBackground": "#490050",
"editorBracketMatch.background": "#444",
"editorBracketMatch.border": "#b80318",
"editor.selectionBackground": "#3b3b3b",
"editor.selectionHighlightBackground": "#702900",
"statusBar.background": "#2e2850",
"minimap.background" : "#1b1b1b",
"sideBar.background": "#1b1b1b"
},
"workbench.editor.wrapTabs": true,
"todohighlight.isEnable": true,
"todohighlight.keywords": [
{
"text": "TODO:",
"color": "red",
"backgroundColor": "rgba(0, 0, 0, 0)"
},
{
"text": "INFO:",
"color": "blue",
"backgroundColor": "rgba(0, 0, 0, 0)"
},
{
"text": "HACK:",
"color": "purple",
"backgroundColor": "rgba(0, 0, 0, 0)"
}
],
"editor.insertSpaces": false,
"workbench.list.openMode": "doubleClick"
}

181
CMakeLists.txt Normal file
View file

@ -0,0 +1,181 @@
#Setup
#-----------------------------------------------------------------------------------------
set(CMAKE_CXX_COMPILER "clang++")
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_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 20)
file(STRINGS "./tools/build.nr" BUILD_NUMBER)
#-----------------------------------------------------------------------------------------
#Variables
#-----------------------------------------------------------------------------------------
set(PROJ_NAME DragonVM)
set(MAJOR_VER 0)
set(MINOR_VER 1)
message("** Building ${PROJ_NAME} ${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}")
#-----------------------------------------------------------------------------------------
#Sources
#-----------------------------------------------------------------------------------------
list(APPEND INCLUDE_DIRS
${CMAKE_CURRENT_LIST_DIR}/src
${CMAKE_CURRENT_LIST_DIR}/../omnia-framework/src
)
list(APPEND RUNTIME_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/runtime/runtime_main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/runtime/DragonRuntime.cpp
${CMAKE_CURRENT_LIST_DIR}/src/runtime/ConfigLoader.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/Window.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/widgets/VirtualConsoleWidget.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/RawTextRenderer.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/VirtualConsoleOutputHandler.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/VirtualConsole.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualCPU.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualRAM.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/MemoryMapper.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualIODevices.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp
${CMAKE_CURRENT_LIST_DIR}/src/tools/Utils.cpp
)
list(APPEND DEBUGGER_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/debugger/debugger_main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/debugger/DisassemblyLoader.cpp
${CMAKE_CURRENT_LIST_DIR}/src/debugger/Debugger.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/DebuggerWindow.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/Window.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/widgets/VirtualConsoleWidget.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/RawTextRenderer.cpp
${CMAKE_CURRENT_LIST_DIR}/src/gui/VirtualConsoleOutputHandler.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualCPU.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualRAM.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/MemoryMapper.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualIODevices.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp
${CMAKE_CURRENT_LIST_DIR}/src/runtime/DragonRuntime.cpp
${CMAKE_CURRENT_LIST_DIR}/src/runtime/ConfigLoader.cpp
${CMAKE_CURRENT_LIST_DIR}/src/assembler/Assembler.cpp
${CMAKE_CURRENT_LIST_DIR}/src/tools/Utils.cpp
)
list(APPEND ASSEMBLER_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/assembler/assembler_main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/assembler/Assembler.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp
${CMAKE_CURRENT_LIST_DIR}/src/tools/Utils.cpp
)
list(APPEND TOOLS_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/src/tools/tools_main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/tools/Utils.cpp
${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp
)
#-----------------------------------------------------------------------------------------
#Targets
#-----------------------------------------------------------------------------------------
set(RUNTIME_TARGET dvm)
add_executable(${RUNTIME_TARGET} ${RUNTIME_SOURCE_FILES})
target_include_directories(${RUNTIME_TARGET} PUBLIC ${INCLUDE_DIRS})
set(DEBUGGER_TARGET ddb)
add_executable(${DEBUGGER_TARGET} ${DEBUGGER_SOURCE_FILES})
target_include_directories(${DEBUGGER_TARGET} PUBLIC ${INCLUDE_DIRS})
set(ASSEMBLER_TARGET dasm)
add_executable(${ASSEMBLER_TARGET} ${ASSEMBLER_SOURCE_FILES})
target_include_directories(${ASSEMBLER_TARGET} PUBLIC ${INCLUDE_DIRS})
set(TOOLS_TARGET dtools)
add_executable(${TOOLS_TARGET} ${TOOLS_SOURCE_FILES})
target_include_directories(${TOOLS_TARGET} PUBLIC ${INCLUDE_DIRS})
target_compile_definitions(${RUNTIME_TARGET} PUBLIC BUILD_NR=${BUILD_NUMBER} MAJ_V=${MAJOR_VER} MIN_V=${MINOR_VER} VERSION_STR="${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER} - Alpha")
#TODO: Different flags for Release/Debug
add_compile_options(-O3 -m32 -MMD -MP -Wall -ggdb)
if (UNIX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'")
#target_link_libraries(${RUNTIME_TARGET} X11 GL)
target_link_libraries(${RUNTIME_TARGET} xcb xcb-randr)
target_link_libraries(${ASSEMBLER_TARGET} xcb xcb-randr)
target_link_libraries(${TOOLS_TARGET} xcb xcb-randr)
target_link_libraries(${DEBUGGER_TARGET} xcb xcb-randr)
endif (UNIX)
if (WIN32)
target_link_libraries(${RUNTIME_TARGET} mingw32)
target_link_libraries(${ASSEMBLER_TARGET} mingw32)
target_link_libraries(${TOOLS_TARGET} mingw32)
target_link_libraries(${DEBUGGER_TARGET} mingw32)
endif (WIN32)
target_link_libraries(${RUNTIME_TARGET} SDL2main SDL2 SDL2_mixer SDL2_image)
target_link_libraries(${DEBUGGER_TARGET} SDL2main SDL2 SDL2_mixer SDL2_image)
# target_link_libraries(${RUNTIME_TARGET} sfml-system sfml-window sfml-graphics)
#-----------------------------------------------------------------------------------------
#Linking
#-----------------------------------------------------------------------------------------
find_library(OMNIA_STD_LIB
NAMES ostd
HINTS "${CMAKE_CURRENT_SOURCE_DIR}/../omnia-framework/bin/"
NO_CACHE
)
target_link_libraries(${RUNTIME_TARGET} ${OMNIA_STD_LIB})
target_link_libraries(${DEBUGGER_TARGET} ${OMNIA_STD_LIB})
target_link_libraries(${ASSEMBLER_TARGET} ${OMNIA_STD_LIB})
target_link_libraries(${TOOLS_TARGET} ${OMNIA_STD_LIB})
#-----------------------------------------------------------------------------------------
#BuildNumber Target
#-----------------------------------------------------------------------------------------
if (WIN32)
add_custom_command ( OUTPUT ./tools/build.nr
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/inc_bnr.exe
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tools/
)
endif (WIN32)
if (UNIX)
add_custom_command ( OUTPUT ./tools/build.nr
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/inc_bnr
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tools/
)
endif (UNIX)
add_custom_target(
IncBnr ALL
DEPENDS ./tools/build.nr
)
#-----------------------------------------------------------------------------------------
add_custom_command(TARGET ${RUNTIME_TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/extra/ $<TARGET_FILE_DIR:${RUNTIME_TARGET}>
VERBATIM
)

22
README.md Normal file
View file

@ -0,0 +1,22 @@
# DragonVM
Compile instructions on Windows
**Step 1:**
download MSYS2 from https://www.msys2.org/ and install it
**Step 2:**
run MSYS2, and in the terminal run:
```
pacman -Syuu
pacman -S --needed base-devel mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-gdb mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-sfml mingw-w64-ucrt-x86_64-make mingw-w64-ucrt-x86_64-SDL2
```
**Step 3:**
open a UCRT64/MSYS2 command prompt inside the root directory of the project
**Step 4:**
execute this command:
```
./compile
```

16
compile Normal file
View file

@ -0,0 +1,16 @@
#!/bin/bash
rm -r bin
if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
cmake -B bin -S ./
cd bin
make
cd ..
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
cmake -B bin -S ./ -G "MinGW Makefiles"
cd bin
mingw32-make.exe
cd ..
cp ../omnia-framework/bin/libostd.dll ./bin
fi

View file

@ -0,0 +1,3 @@
Disks = dragon/disk1.dr
Bios = dragon/bios.bin
CMOS = dragon/cmos.dr

View file

@ -0,0 +1,79 @@
0x0000
BIOS (1024 Bytes)
0x03FF
-------
0x0400
CMOS (128 Bytes)
BootDisk: 0x0010
0x047F
-------
0x0480
INTERRUPT VECTOR (512 Bytes)
0x067F
-------
0x0680
KEYBOARD MAPPING (224 Bytes)
0x075F
-------
0x0760
MOUSE MAPPING (32 Bytes)
0x077F
-------
0x0780
BIOS VIDEO MEMORY (3840 Bytes)
0x000: Clear Color (1 Byte)
0x001: Palette (1 Byte)
0x002: UseTransparencyOn0x5 (1 Byte)
0x003: Signal (1 Byte)
0x00: Continue
0x01: Clear Screen
0x03C - 0xEFF: 3780 Bytes
Structure:
90x21 characters
1 Byte ascii
1 byte: 4 Bits background/4 bits foreground
0x167F
-------
0x1680
BOOTLOADER (512 Bytes)
0x187F
-------
0x1880
DISK INTERFACE (128 Bytes)
Read/Write:
Signal: 1 Byte
0x00: Start Operation
0x01: Cancel Current Operation
0xFF: Ignore
Mode: 1 Byte
0x00: Read
0x01: Write
Disk: 1 Byte
Sector: 2 Bytes
Address: 2 Bytes
DataSize: 2 Bytes
DataAddress: 2 Byte
ReadOnly:
Status: 1 Byte
0x00: Free
0x01: Writing
0x02: Reading
CurrentDisk: 1 Byte
CurrentSector: 2 Bytes
CurrentAddress: 2 Bytes
RestDataSize: 2 Bytes
SourceData: 2 Bytes
Free: 107 Byte
0x18FF
-------
0x1900
VIDEO CARD INTERFACE (256 Bytes)
0x19FF
-------
0x1A00
GENERIC SERIAL INTERFACE (64 Bytes)
0x1A3F
-------
0x1A40
RAM (58815 Bytes)
0xFFFF

View file

@ -0,0 +1,70 @@
Software Interrupts:
====================
0x20: BIOS Interrupt (Uses R10 register for specific functionality)
0x00: Set Interrupt Handler
0x01: Clear Interrupt Handler
0x10: Disk Interface
0x30: BIOS Video Interrupt (Uses R10 register for specific functionality)
0x00: Clear Screen
Hardware Interrupts:
====================
0x80: Disk Interface Finished
0x81: BiosVideo Screen Refreshed
Default Palette BIOS Colors:
====================
0x0: Black
0, 0, 0
#000000
0x1: Gray
157, 157, 157
#9D9D9D
0x2: White
255, 255, 255
#FFFFFF
0x3: Red
190, 38, 51
#BE2633
0x4: Pimk
224, 111, 139
#E06F8B
OLD -- 0x5: DarkBrown
OLD -- 73, 60, 43
OLD -- #493C2B
0x5: Transparent
--
0x6: Brown
164, 100, 34
#A46422
0x7: Orange
235, 137, 49
#EB8931
0x8: Yellow
247, 226, 107
#F7E26B
0x9: Dark Green
47, 80, 42
#2F502A
0xA: Green
68, 137, 26
#44891A
0xB: Slime Green
163, 206, 39
#A3CE27
0xC: Night Blue
27, 38, 50
#1B2632
0xD: Sea Blue
0, 87, 132
#005784
0xE: Sky Blue
49, 162, 242
#31A2F2
0xF: Cloud Blue
178, 220, 239
#B2DCEF

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View file

@ -0,0 +1,10 @@
Regex: Javascript
Number Constants
0x[0-9A-Fa-f]+|0b[0-1]+|(?<!\w)[0-9]+(?!\w)
Labels
\$\w+
Registers (case-insensitive)
(?<!\w)(r[1-9]|r10|fl|pp|rv|fp|sp|ip|acc)(?!\w)

BIN
extra/dragon/bios.bin Normal file

Binary file not shown.

BIN
extra/dragon/cmos.dr Normal file

Binary file not shown.

BIN
extra/dragon/disk1.dr Normal file

Binary file not shown.

140
extra/dss/bios.dss Normal file
View file

@ -0,0 +1,140 @@
## ============================= Memory Mapped Devices and Registers =============================
@define MBR_START_ADDRESS 0x1680
@define INT_VEC_START_ADDRESS 0x0480
@define DISK_INTERFACE_START_ADDRESS 0x1880
@define MEMORY_START_ADDRESS 0x1A40
@define CMOS_START_ADDRESS 0x0400
@define BIOS_VIDEO_START_ADDRESS 0x0780
@define BIOS_VIDEO_MEMORY_START_ADDRESS 0x07BC
@define BIOS_VIDEO_MEMORY_END_ADDRESS 0x167F
@define _BIOS_VIDEO_REG_CLEAR_COLOR {BIOS_VIDEO_START_ADDRESS + 0x0000}
@define _BIOS_VIDEO_REG_SIGNAL {BIOS_VIDEO_START_ADDRESS + 0x0003}
## ===============================================================================================
@define _CMOS_REG_BOOT_DISK {CMOS_START_ADDRESS + 0x0010}
.load 0x0000 ## BIOS is mapped to address 0x0000 in memory
.data
$bios_version_number 0x00, 0x01 ## BIOS Version stored in memory
## ============================= BIOS Program =============================
.code
mov FL, 0b0000000000000001 ## Zero the FL Register and enable interrupts
movb [{INT_VEC_START_ADDRESS + (0x20 * 3)}], 0xFF ## Setting up int 0x20 handler
mov [{INT_VEC_START_ADDRESS + (0x20 * 3) + 1}], $_int_20_handler ## --
mov R10, 0x00 ## Setting up int 0x30 handler by using int 0x20 functionality
mov R9, $_int_30_handler ## -Passing the handler's address
mov R8, 0x30 ## -Passing the interrupt's code to setup
int 0x20 ## -Calling int 0x20 with 0x00 as parameter, to set new handler up
## TODO: Probably move this code before interrupts are enabled
## to prevent an interrupt being fired while loading MBR
## data from disk
## ----
## MBR Loading
push 0
call $_load_mbr_data_block
## ----
jmp MBR_START_ADDRESS ## Jump to start of MBR in memory
hlt ## Just in case somehow execution reaches this point
## ========================================================================
## ============================= BIOS Interrupt handler =============================
_int_20_handler:
mov ACC, R10
jeq $_int_20_disk_interface, 0x0010
jeq $_int_20_set_new_interrupt_handler, 0x0000
jeq $_int_20_clear_interrupt, 0x0001
jmp $_int_20_end
_int_20_disk_interface:
movb [{DISK_INTERFACE_START_ADDRESS + 0x1}], *R9 ## Mode
inc R9
movb [{DISK_INTERFACE_START_ADDRESS + 0x2}], *R9 ## Disk
inc R9
mov [{DISK_INTERFACE_START_ADDRESS + 0x3}], *R9 ## Sector
inc R9
inc R9
mov [{DISK_INTERFACE_START_ADDRESS + 0x5}], *R9 ## Address
inc R9
inc R9
mov [{DISK_INTERFACE_START_ADDRESS + 0x7}], *R9 ## Size
inc R9
inc R9
mov [{DISK_INTERFACE_START_ADDRESS + 0x9}], *R9 ## Memory Address
movb [DISK_INTERFACE_START_ADDRESS], 0x00 ## Signal set to "Start Operation"
jmp $_int_20_end
_int_20_set_new_interrupt_handler:
push R8 ## Interrupt Code
push R9 ## Handler Address
push 2
call $_set_interrupt_vector_entry
jmp $_int_20_end
_int_20_clear_interrupt:
push R9
push 0x0000
push 2
call $_set_interrupt_vector_entry
_int_20_end:
rti
## ==================================================================================
## ========================== BIOS Video Interrupt handler =========================
_int_30_handler:
mov ACC, R10
jeq $_int_30_clear_screen, 0x0000
jmp $_int_30_end
_int_30_clear_screen:
movb [_BIOS_VIDEO_REG_SIGNAL], 0x01 ## Signal = ClearScreen
_int_30_end:
rti
## ==================================================================================
_load_mbr_data_block:
movb [{DISK_INTERFACE_START_ADDRESS + 0x1}], 0x00 ## Mode = Read
movb R1, [_CMOS_REG_BOOT_DISK]
movb [{DISK_INTERFACE_START_ADDRESS + 0x2}], R1 ## Disk = Default
mov [{DISK_INTERFACE_START_ADDRESS + 0x3}],0x0000 ## Sector = 0x0000
mov [{DISK_INTERFACE_START_ADDRESS + 0x5}],0x0000 ## Address = 0x0000 (Start of Disk)
##//TODO: currently set to 20 instead of 512, for debugging purposes
mov [{DISK_INTERFACE_START_ADDRESS + 0x7}], 20 ## Size = 512 (Size of MBR is 512 bytes)
mov [{DISK_INTERFACE_START_ADDRESS + 0x9}], MBR_START_ADDRESS ## MemoryAddress = MBR Address in memory
movb [DISK_INTERFACE_START_ADDRESS], 0x00 ## Signal = Start
_load_mbr_data_block_wait_loop:
mov ACC, [{DISK_INTERFACE_START_ADDRESS + 0xB}] ## Moving <Status> register into ACC
jne $_load_mbr_data_block_wait_loop, 0x00 ## If <Status> register not Free, loop around and wait
ret
_calc_interrupt_vector_address:
mov R1, *PP
mul R1, 0x03 ## Multiply the interrupt code by 3 for memory alignment
mov R1, ACC
add R1, INT_VEC_START_ADDRESS ## Add Interrupt Vector base address to Interrupt code
mov RV, ACC
ret
_set_interrupt_vector_entry:
mov R1, *PP
push R1
push 1
call $_calc_interrupt_vector_address
mov R1, RV
dec PP
dec PP
mov ACC, *PP
jeq $_set_interrupt_vector_entry_disable, 0x0000
movb *R1, 0xFF
jmp $_set_interrupt_vector_entry_end
_set_interrupt_vector_entry_disable:
movb *R1, 0x00
_set_interrupt_vector_entry_end:
inc R1
mov *R1, ACC
ret
.fixed 1024, 0x00 ## BIOS Needs to be 1024 Bytes exactly

94
extra/dss/handlerTest.dss Normal file
View file

@ -0,0 +1,94 @@
.load 0x1A40
##@struct Rectangle {
## width:2,
## height:2,
## x:2,
## y:2
##
## ## Static properties
## StructSize
##}
.data
$cursor_pos 0x00, 0x00
$color 0x02
$current_video_addr 0x07, 0xBC
$string "Hello World!!"
## $rect => Rectangle
.code
debug_break
infinite_loop:
push 0
call $clear_screen
## mov $rect.x, 200
## debug_break
push $string
push 1
call $print_string
jmp $infinite_loop
hlt
print_string:
mov R1, *PP
dec PP
dec PP
push R1
push 1
call $strlen
mov R2, RV
push 65
push 1
call $print_char
ret
strlen:
mov R1, *PP
dec PP
dec PP
mov R2, 0
_strlen_loop:
movb ACC, *R1
jeq $_strlen_end_loop, 0
inc R2
inc R1
jmp $_strlen_loop
_strlen_end_loop:
mov RV, R2
ret
print_char:
mov R2, *PP
dec PP
dec PP
mov R1, [$current_video_addr]
mov R3, [$cursor_pos]
mul R3, 2
mov R3, ACC
add R1, R3
mov R1, ACC
movb *R1, R2
inc R1
movb *R1, [$color]
mov R3, [$cursor_pos]
inc R3
mov [$cursor_pos], R3
ret
clear_screen:
mov R10, 0x00
int 0x30
ret
.fixed 512, 0x00

7
extra/dss/mbr.dss Normal file
View file

@ -0,0 +1,7 @@
.load 0x1680
.code
jmp 0x1A40
hlt
.fixed 512, 0x00

15
extra/dss/newTest.dss Normal file
View file

@ -0,0 +1,15 @@
.load 0x1A40
.data
$v1 0x00, 0x01
$v2 0x00, 0x01
$v3 0x00, 0x01, 0x00, 0x01, 0x00, 0x01
.code
mov R1, 0xAA
mov R2, 0xBB
mov R3, 0xCC
debug_break
hlt
.fixed 512, 0x00

8
extra/dss/test.dss Normal file
View file

@ -0,0 +1,8 @@
.load 0x1A40
.code
mov R1, 0xABCD
debug_break
hlt
.fixed 512, 0x00

BIN
extra/font.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

25
extra/make_and_run Normal file
View file

@ -0,0 +1,25 @@
#!/bin/bash
printf "${green}\n============================================[ Building Application ]============================================\n\n${clear}"
\cp -r ../extra/* ./
if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
make
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
mingw32-make.exe
cp ../../omnia-framework/bin/libostd.dll ./
fi
printf "${green}\n=================================================================================================================\n\n"
if [ $? -eq 0 ]; then
cd scripts
/bin/bash ./bios_flash
/bin/bash ./load_mbr
cd ..
printf "${green}Compiling Test Program...\n"
./dasm dss/handlerTest.dss -o handlerTest.bin --save-disassembly disassembly/handlerTest.dds
printf "\n${green}Running Application...\n\n${clear}"
./ddb config/testMachine.dvm --force-load handlerTest.bin 0x00 --verbose-load --track-step-diff --hide-vdisplay
#./dvm config/testMachine.dvm --debug --force-load newTest.bin 0x00 --verbose-load
cp dragon/disk1.dr ../extra/dragon/disk1.dr
cp dragon/cmos.dr ../extra/dragon/cmos.dr
fi

10
extra/scripts/bios_flash Normal file
View file

@ -0,0 +1,10 @@
#!/bin/bash
green='\033[0;32m'
clear='\033[0m'
printf "${green}Reloading BIOS...\n${clear}"
cd ..
cp ../extra/dss/bios.dss ./dss/bios.dss
./dasm dss/bios.dss -o dragon/bios.bin --save-disassembly disassembly/bios.dds
cp dragon/bios.bin ../extra/dragon/bios.bin

13
extra/scripts/load_mbr Normal file
View file

@ -0,0 +1,13 @@
#!/bin/bash
green='\033[0;32m'
clear='\033[0m'
printf "\n${green}Reloading MBR Block...\n${clear}"
cd ..
cp ../extra/dss/mbr.dss ./dss/mbr.dss
./dasm dss/mbr.dss -o dragon/mbr.bin --save-disassembly disassembly/mbr.dds
./dtools load-program dragon/disk1.dr dragon/mbr.bin 0x00000000
printf "\n"
cp dragon/disk1.dr ../extra/dragon/disk1.dr
rm dragon/mbr.bin

1152
src/assembler/Assembler.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
#pragma once
#include <ostd/Utils.hpp>
#include <unordered_map>
namespace dragon
{
namespace hw
{
class VirtualHardDrive;
}
namespace code
{
class Assembler
{
public: struct tDisassemblyLine {
uint32_t addr = 0;
ostd::String code = "";
};
public: struct tSymbol
{
std::vector<ostd::UByte> bytes;
uint16_t address { 0 };
};
public: struct tLabel
{
std::vector<uint16_t> references;
uint16_t address { 0 };
};
public: enum class eOperandType {
Register = 0,
Immediate,
DerefMemory,
DerefRegister,
Label,
Error
};
public:
static ostd::ByteStream assembleFromFile(ostd::String fileName);
static void loadSource(ostd::String source);
static ostd::ByteStream assembleToFile(ostd::String sourceFileName, ostd::String binaryFileName);
static ostd::ByteStream assembleToVirtualDisk(ostd::String fileName, hw::VirtualHardDrive& vhdd, uint32_t address);
static bool saveDisassemblyToFile(ostd::String fileName);
static void tempPrint(void);
private:
static void removeComments(void);
static void replaceDefines(void);
static void parseSections(void);
static void parseDataSection(void);
static void parseCodeSection(void);
static void parseDebugOperands(ostd::String line);
static void parse0Operand(ostd::String line);
static void parse1Operand(ostd::String line);
static void parse2Operand(ostd::String line);
static void parse3Operand(ostd::String line);
static void combineDataAndCode(void);
static ostd::String replaceSymbols(ostd::String line);
static void replaceLabelRefs(void);
static eOperandType parseOperand(ostd::String op, int16_t& outOp);
static uint8_t parseRegister(ostd::String op);
private:
inline static ostd::String m_rawSource { "" };
inline static ostd::ByteStream m_code;
inline static std::vector<ostd::String> m_lines;
inline static std::vector<ostd::String> m_rawDataSection;
inline static std::vector<ostd::String> m_rawCodeSection;
inline static std::unordered_map<ostd::String, tSymbol> m_symbolTable;
inline static std::unordered_map<ostd::String, tLabel> m_labelTable;
inline static uint16_t m_fixedSize { 0 };
inline static uint8_t m_fixedFillValue { 0x00 };
inline static uint16_t m_loadAddress { 0x0000 };
inline static uint16_t m_currentDataAddr { 0x0000 };
inline static uint16_t m_dataSize { 0x0000 };
inline static uint16_t m_programSize { 0x0000 };
inline static std::vector<tDisassemblyLine> m_disassembly;
};
}
}

View file

@ -0,0 +1,60 @@
#include "Assembler.hpp"
#include <ostd/Utils.hpp>
#include <ostd/IOHandlers.hpp>
ostd::legacy::ConsoleOutputHandler out;
struct tCommandLineArgs
{
ostd::String source_file_path = "";
ostd::String dest_file_path = "";
bool save_disassembly = false;
bool verbose = false;
ostd::String disassembly_file_path = "";
};
int main(int argc, char** argv)
{
tCommandLineArgs args;
if (argc < 4)
{
out.col("red").p("Error: too few arguments.").nl();
out.col("red").p(" Usage: ./dasm <source> -o <destination> [...options...]").resetColors().nl();
return 1;
}
else
{
args.source_file_path = argv[1];
for (int32_t i = 2; i < argc; i++)
{
ostd::StringEditor edit(argv[i]);
if (edit.str() == "-o")
{
if (i == argc - 1)
break; //TODO: Warning
i++;
args.dest_file_path = argv[i];
}
else if (edit.str() == "--save-disassembly")
{
if (i == argc - 1)
break; //TODO: Warning
i++;
args.disassembly_file_path = argv[i];
args.save_disassembly = true;
}
else if (edit.str() == "--verbose")
args.verbose = true;
}
}
dragon::code::Assembler::assembleToFile(args.source_file_path, args.dest_file_path);
if (args.verbose)
dragon::code::Assembler::tempPrint();
if (args.save_disassembly)
dragon::code::Assembler::saveDisassemblyToFile(args.disassembly_file_path);
return 0;
}

829
src/debugger/Debugger.cpp Normal file
View file

@ -0,0 +1,829 @@
#include "Debugger.hpp"
#include "../runtime/DragonRuntime.hpp"
#include "DisassemblyLoader.hpp"
#include <ostd/Defines.hpp>
namespace dragon
{
//Debugger::Utils
DisassemblyList Debugger::Utils::findCodeRegion(const DisassemblyList& code, uint16_t address, uint16_t codeRegionMargin)
{
if (code.size() <= (codeRegionMargin * 2) + 1) return code;
std::vector<dragon::code::Assembler::tDisassemblyLine> codeRegion;
uint16_t start = 0;
uint16_t end = (codeRegionMargin * 2);
for (int32_t i = 0; i < code.size(); i++)
{
if (code[i].addr != address) continue;
if (i + 1 <= codeRegionMargin) break;
if (code.size() - (i + 1) < codeRegionMargin)
{
end = code.size() - 1;
start = end - ((codeRegionMargin * 2) + 1);
break;
}
start = i - codeRegionMargin;
end = i + codeRegionMargin;
break;
}
for (int16_t i = start; i <= end; i++)
codeRegion.push_back(code[i]);
return codeRegion;
}
ostd::String Debugger::Utils::findSymbol(const DisassemblyList& labels, uint16_t address)
{
for (auto& label : labels)
{
if (label.addr == address)
return label.code;
}
return "";
}
uint16_t Debugger::Utils::findSymbol(const DisassemblyList& labels, const ostd::StringEditor& symbol)
{
for (auto& label : labels)
{
if (label.code == symbol.str())
return label.addr;
}
return 0x0000;
}
bool Debugger::Utils::isValidLabelNameChar(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_');
}
ostd::StringEditor Debugger::Utils::fillString(const ostd::StringEditor& str, char fill, int32_t totalLength)
{
int32_t fillLen = totalLength - str.len();
if (fillLen < 1) return str;
ostd::StringEditor newStr = str;
newStr.add(ostd::Utils::duplicateChar(fill, fillLen));
return newStr;
}
//Debugger::Display
void Debugger::Display::colorizeInstructionBody(const ostd::String& instBody, bool currentLine, const DisassemblyList& labelList)
{
ostd::RegexRichString rgxrstr(instBody);
rgxrstr.fg("\\{|\\}|\\+|\\*|\\-|\\/|\\(|\\)|\\[|\\]", "Red"); //Operators
rgxrstr.fg("0x[0-9A-Fa-f]+|0b[0-1]+|(?<!\\w)[0-9]+(?!\\w)", "BrightYellow"); //Number Constants
rgxrstr.fg("(?<!\\w)(r[1-9]|r10|fl|pp|rv|fp|sp|ip|acc)(?!\\w)", "BrightGreen", true); //Registers
rgxrstr.col("\\$\\w+", "Cyan", "Black"); //Labels
ostd::StringEditor instEdit = rgxrstr.getRawString();
for (auto& label : labelList)
{
int32_t index = -1;
ostd::StringEditor labelEdit = label.code;
labelEdit.trim();
while ((index = instEdit.indexOf(labelEdit.str(), index + 1)) != -1)
{
if (index + labelEdit.str().length() < instEdit.len() && Utils::isValidLabelNameChar(instEdit.at(index + labelEdit.str().length())))
continue;
ostd::String instStr = instEdit.str();
instStr.replace(index, labelEdit.str().length(), labelEdit.str() + "[@@ style foreground:brightgray](" + ostd::Utils::getHexStr(label.addr, true, 2) + ")[@@/]");
instEdit = instStr;
}
}
rgxrstr.setRawString(instEdit);
out.p("\t").pStyled(rgxrstr);
if (currentLine)
out.p(" ").col(ostd::legacy::ConsoleCol::OnYellow).col(ostd::legacy::ConsoleCol::Black).p(" <-- ");
out.nl();
out.reset().resetColors();
}
void Debugger::Display::colorCodeInstructions(const ostd::String& inst, bool currentLine, const DisassemblyList& labelList)
{
ostd::StringEditor instEditor = inst;
ostd::StringEditor instBody = "";
ostd::StringEditor instHead = inst;
instEditor.trim();
if (instEditor.contains(" "))
{
instHead = instEditor.substr(0, instEditor.indexOf(" "));
instBody = instEditor.substr(instEditor.indexOf(" "));
}
if (currentLine)
{
out.col(ostd::legacy::ConsoleCol::OnYellow).col(ostd::legacy::ConsoleCol::Black);
}
else if (instHead.str() == "call" || instHead.str() == "ret" || instHead.str() == "int" || instHead.str() == "rti")
out.col(ostd::legacy::ConsoleCol::OnBrightRed).col(ostd::legacy::ConsoleCol::White);
else if (instHead.str() == "hlt" || instHead.str() == "debug_break")
out.col(ostd::legacy::ConsoleCol::Red);
else if (instHead.str() == "nop")
out.col(ostd::legacy::ConsoleCol::Gray);
else
out.col(ostd::legacy::ConsoleCol::Blue);
out.p(instHead);
out.reset().resetColors();
colorizeInstructionBody(instBody.str(), currentLine, labelList);
}
void Debugger::Display::printPrompt(void)
{
out.col(ostd::legacy::ConsoleCol::Magenta).p(" #/> ").col(ostd::legacy::ConsoleCol::White);
}
void Debugger::Display::printStep(void)
{
out.clear();
int32_t codeRegionSpan = 15;
auto codeRegion = Utils::findCodeRegion(debugger.code, debugger.currentAddress, codeRegionSpan);
for (int32_t i = 0; i < codeRegion.size(); i++)
{
auto& _da = codeRegion[i];
bool currentLine = _da.addr == debugger.currentAddress;
ostd::String label = Utils::findSymbol(debugger.labels, _da.addr);
bool specialSection = _da.code.starts_with("[") &&_da.code.ends_with("]");
if (label.length() < debugger.labelLineLength)
{
label += ostd::Utils::duplicateChar(' ', debugger.labelLineLength - label.length());
}
else if (label.length() > debugger.labelLineLength)
{
ostd::StringEditor edit(label);
edit = edit.substr(0, debugger.labelLineLength - 3);
edit.add("...");
label = edit.str();
}
out.col(ostd::legacy::ConsoleCol::Gray).p(label).p(" ");
if (currentLine)
{
out.col(ostd::legacy::ConsoleCol::Black).col(ostd::legacy::ConsoleCol::OnYellow).p(ostd::Utils::getHexStr(_da.addr, true, 2)).p(" ").reset();;
}
else
{
if (specialSection)
out.col(ostd::legacy::ConsoleCol::Cyan);
else
out.col(ostd::legacy::ConsoleCol::BrightGray);
out.p(ostd::Utils::getHexStr(_da.addr, true, 2)).p(" ");
}
if (specialSection)
out.col(ostd::legacy::ConsoleCol::Cyan).p(_da.code).nl();
else
colorCodeInstructions(_da.code, currentLine, debugger.labels);
out.reset();
}
int32_t cw = ostd::Utils::getConsoleWidth();
ostd::String str = ostd::Utils::duplicateChar('#', cw);
out.col(ostd::legacy::ConsoleCol::OnYellow).col(ostd::legacy::ConsoleCol::Black).p(str).reset().nl();
}
void Debugger::Display::printDiff(void)
{
out.clear();
ostd::StringEditor str;
str.add("|===============|================PREV================|================CURR================|=====|====REG====|====REG====|");
str.add("\n");
str.add("| InstAddr: |*%PREV_INST_ADDR%*******************|*%CURR_INST_ADDR%*******************| R1 |*%PREV_R1%*|*%CURR_R1%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| Code: | ---- |*%CURR_CODE%************************| R2 |*%PREV_R2%*|*%CURR_R2%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| StackFrame: |*%PREV_STACK_FRAME%*****************|*%CURR_STACK_FRAME%*****************| R3 |*%PREV_R3%*|*%CURR_R3%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| DBG BRK: |*%PREV_DBG_BRK%*********************|*%CURR_DBG_BRK%*********************| R4 |*%PREV_R4%*|*%CURR_R4%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| INT Handler: |*%PREV_INT_HANDLER%*****************|*%CURR_INT_HANDLER%*****************| R5 |*%PREV_R5%*|*%CURR_R5%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| BIOS Mode: |*%PREV_BIOS_MODE%*******************|**%CURR_BIOS_MODE%******************| R6 |*%PREV_R6%*|*%CURR_R6%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| IP: |*%PREV_IP%**************************|**%CURR_IP%*************************| R7 |*%PREV_R7%*|*%CURR_R7%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| SP: |*%PREV_SP%**************************|**%CURR_SP%*************************| R8 |*%PREV_R8%*|*%CURR_R8%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| FP: |*%PREV_FP%**************************|**%CURR_FP%*************************| R9 |*%PREV_R9%*|*%CURR_R9%*|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|");
str.add("\n");
str.add("| RV: |*%PREV_RV%**************************|**%CURR_RV%*************************| R10 |*%PREV_R10%|*%CURR_R10%|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|=====|===========|===========|");
str.add("\n");
str.add("| PP: |*%PREV_PP%**************************|**%CURR_PP%*************************|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|");
str.add("\n");
str.add("| FL: |*%PREV_FL%**************************|**%CURR_FL%*************************|");
str.add("\n");
str.add("|---------------|------------------------------------|------------------------------------|");
str.add("\n");
str.add("| ACC: |*%PREV_ACC%*************************|**%CURR_ACC%************************|");
str.add("\n");
str.add("|===============|====================================|====================================|");
str.replaceAll("*", "");
int32_t item_len = 36;
const dragon::DragonRuntime::tMachineDebugInfo& minfo = dragon::DragonRuntime::getMachineInfoDiff();
ostd::StringEditor tmp = " ", tmpStyle = "";
//Instruction Address
{
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionAddress, true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_INST_ADDR%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionAddress, true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionAddress != minfo.previousInstructionAddress)
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_INST_ADDR%", tmpStyle.str());
}
//Code
{
tmp = " ", tmpStyle = "";
ostd::StringEditor prevCode = " (";
prevCode.add(minfo.previousInstructionOpCode).add(") ");
for (int32_t i = 0; i < minfo.previousInstructionFootprintSize; i++)
prevCode.add(ostd::Utils::getHexStr(minfo.previousInstructionFootprint[i], false, 1)).add(" ");
tmp.add(prevCode.str());
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Black,background:BrightGray]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_CODE%", tmpStyle.str());
// tmp = " ";
// ostd::StringEditor currCode = " (";
// currCode.add(minfo.currentInstructionOpCode).add(") ");
// for (int32_t i = 0; i < minfo.currentInstructionFootprintSize; i++)
// currCode.add(ostd::Utils::getHexStr(minfo.currentInstructionFootprint[i], false, 1)).add(" ");
// tmp.add(currCode.str());
// tmp = fillString(tmp, ' ', item_len);
// if (currCode.str() != prevCode.str())
// tmpStyle = "[@@style foreground:Black,background:Yellow]";
// else
// tmpStyle = "[@@style foreground:Blue]";
// tmpStyle.add(tmp.str()).add("[@@/]");
// str.replaceAll("%CURR_CODE%", tmpStyle.str());
}
//Stack Frame
{
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionStackFrameSize, true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_STACK_FRAME%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionStackFrameSize, true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionStackFrameSize != minfo.previousInstructionStackFrameSize)
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_STACK_FRAME%", tmpStyle.str());
}
//Debug Break
{
tmp = " ", tmpStyle = "";
tmp.add(STR_BOOL(minfo.previousInstructionDebugBreak));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_DBG_BRK%", tmpStyle.str());
tmp = " ";
tmp.add(STR_BOOL(minfo.currentInstructionDebugBreak));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionDebugBreak != minfo.previousInstructionDebugBreak)
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_DBG_BRK%", tmpStyle.str());
}
//INT Handler
{
tmp = " ", tmpStyle = "";
tmp.add(STR_BOOL(minfo.previousInstructionInterruptHandler));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_INT_HANDLER%", tmpStyle.str());
tmp = " ";
tmp.add(STR_BOOL(minfo.currentInstructionInterruptHandler));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionInterruptHandler != minfo.previousInstructionInterruptHandler)
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_INT_HANDLER%", tmpStyle.str());
}
//Bios Mode
{
tmp = " ", tmpStyle = "";
tmp.add(STR_BOOL(minfo.previousInstructionBiosMode));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_BIOS_MODE%", tmpStyle.str());
tmp = " ";
tmp.add(STR_BOOL(minfo.currentInstructionBiosMode));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionBiosMode != minfo.previousInstructionBiosMode)
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_BIOS_MODE%", tmpStyle.str());
}
//System Registers
{
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::IP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_IP%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::IP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::IP] != minfo.previousInstructionRegisters[dragon::data::Registers::IP])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_IP%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::SP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_SP%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::SP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::SP] != minfo.previousInstructionRegisters[dragon::data::Registers::SP])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_SP%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::FP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_FP%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::FP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::FP] != minfo.previousInstructionRegisters[dragon::data::Registers::FP])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_FP%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::RV], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_RV%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::RV], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::RV] != minfo.previousInstructionRegisters[dragon::data::Registers::RV])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_RV%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::PP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_PP%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::PP], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::PP] != minfo.previousInstructionRegisters[dragon::data::Registers::PP])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_PP%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::FL], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_FL%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::FL], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::FL] != minfo.previousInstructionRegisters[dragon::data::Registers::FL])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_FL%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::ACC], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_ACC%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::ACC], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::ACC] != minfo.previousInstructionRegisters[dragon::data::Registers::ACC])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_ACC%", tmpStyle.str());
}
item_len = 11;
//General Registers
{
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R1], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R1%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R1], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R1] != minfo.previousInstructionRegisters[dragon::data::Registers::R1])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R1%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R2], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R2%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R2], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R2] != minfo.previousInstructionRegisters[dragon::data::Registers::R2])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R2%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R3], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R3%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R3], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R3] != minfo.previousInstructionRegisters[dragon::data::Registers::R3])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R3%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R4], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R4%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R4], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R4] != minfo.previousInstructionRegisters[dragon::data::Registers::R4])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R4%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R5], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R5%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R5], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R5] != minfo.previousInstructionRegisters[dragon::data::Registers::R5])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R5%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R6], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R6%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R6], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R6] != minfo.previousInstructionRegisters[dragon::data::Registers::R6])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R6%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R7], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R7%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R7], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R7] != minfo.previousInstructionRegisters[dragon::data::Registers::R7])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R7%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R8], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R8%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R8], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R8] != minfo.previousInstructionRegisters[dragon::data::Registers::R8])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R8%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R9], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R9%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R9], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R9] != minfo.previousInstructionRegisters[dragon::data::Registers::R9])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R9%", tmpStyle.str());
tmp = " ", tmpStyle = "";
tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionRegisters[dragon::data::Registers::R10], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%PREV_R10%", tmpStyle.str());
tmp = " ";
tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionRegisters[dragon::data::Registers::R10], true, 2));
tmp = Utils::fillString(tmp, ' ', item_len);
if (minfo.currentInstructionRegisters[dragon::data::Registers::R10] != minfo.previousInstructionRegisters[dragon::data::Registers::R10])
tmpStyle = "[@@style foreground:Black,background:BrightRed]";
else
tmpStyle = "[@@style foreground:Blue]";
tmpStyle.add(tmp.str()).add("[@@/]");
str.replaceAll("%CURR_R10%", tmpStyle.str());
}
ostd::RegexRichString rgxstr(str);
rgxstr.fg("InstAddr|Code|StackFrame|DBG BRK|INT Handler|BIOS Mode", "Magenta");
rgxstr.fg("IP|SP|FP|RV|PP|FL|ACC", "Cyan");
rgxstr.fg("R10|R2|R3|R4|R5|R6|R7|R8|R9|R1", "BrightGreen");
rgxstr.fg("PREV", "Red");
rgxstr.fg("CURR", "Green");
out.pStyled(rgxstr);
out.nl().nl().col(ostd::legacy::ConsoleCol::Yellow).p("Pres <Enter> to continue execution...").nl().reset();
std::cin.get();
}
void Debugger::Display::printTrackedAddresses(const std::vector<uint16_t>& trackedAddresses)
{
}
ostd::StringEditor Debugger::Display::changeScreen(void)
{
if (debugger.command.str() == "diff")
{
printDiff();
printStep();
return getCommandInput();
}
else if (debugger.command.str() == "tracker")
{
printTrackedAddresses(debugger.trackedAddresses);
printStep();
return getCommandInput();
}
return "";
}
//Debugger
void Debugger::processErrors(void)
{
if (!dragon::DragonRuntime::hasError()) return;
while (dragon::data::ErrorHandler::hasError())
{
auto err = dragon::data::ErrorHandler::popError();
out.nl().col(ostd::legacy::ConsoleCol::Red).p("Error ").p(ostd::Utils::getHexStr(err.code, true, 8)).p(": ").p(err.text).nl();
}
debugger.args.step_exec = true;
}
int32_t Debugger::loadArguments(int argc, char** argv)
{
if (argc < 2)
{
out.col("red").p("Error, too few arguments.").resetColors().nl();
return 128;
}
else
{
debugger.args.machine_config_path = argv[1];
for (int32_t i = 2; i < argc; i++)
{
ostd::StringEditor edit(argv[i]);
if (edit.str() == "--verbose-load")
debugger.args.verbose_load = true;
else if (edit.str() == "--step-exec")
debugger.args.step_exec = true;
else if (edit.str() == "--track-step-diff")
debugger.args.track_step_diff = true;
else if (edit.str() == "--hide-vdisplay")
debugger.args.hide_virtual_display = true;
else if (edit.str() == "--auto-start")
debugger.args.auto_start_debug = true;
else if (edit.str() == "--force-load")
{
if ((argc - 1) - i < 2)
break; //TODO: Warning
i++;
debugger.args.force_load_file = argv[i];
i++;
edit = argv[i];
if (!edit.isNumeric())
continue; //TODO: Error
debugger.args.force_load_mem_offset = (uint16_t)edit.toInt();
debugger.args.force_load = true;
}
}
}
return 0;
}
int32_t Debugger::initRuntime(void)
{
int32_t init_state = dragon::DragonRuntime::initMachine(debugger.args.machine_config_path, debugger.args.verbose_load, debugger.args.track_step_diff, debugger.args.hide_virtual_display);
if (init_state != 0) return init_state; //TODO: Error
if (debugger.args.force_load)
dragon::DragonRuntime::forceLoad(debugger.args.force_load_file, debugger.args.force_load_mem_offset);
dragon::DisassemblyLoader::loadDirectory(debugger.disassemblyDirectory.str());
debugger.code = dragon::DisassemblyLoader::getCodeTable();
debugger.labels = dragon::DisassemblyLoader::getLabelTable();
debugger.data = dragon::DisassemblyLoader::getDataTable();
return 0;
}
ostd::StringEditor Debugger::getCommandInput(void)
{
ostd::String cmd;
std::getline(std::cin, cmd);
return cmd;
}
}

72
src/debugger/Debugger.hpp Normal file
View file

@ -0,0 +1,72 @@
#pragma once
#include <ostd/Utils.hpp>
#include <ostd/IOHandlers.hpp>
#include "../assembler/Assembler.hpp"
namespace dragon
{
typedef std::vector<dragon::code::Assembler::tDisassemblyLine> DisassemblyList;
class Debugger
{
public: struct tCommandLineArgs
{
inline tCommandLineArgs(void) { }
ostd::String machine_config_path = "";
bool verbose_load = false;
bool force_load = false;
bool step_exec = false;
bool track_step_diff = false;
bool auto_start_debug = false;
bool hide_virtual_display = false;
ostd::String force_load_file = "";
uint16_t force_load_mem_offset = 0x00;
};
public: struct tDebuggerData
{
inline tDebuggerData(void) { }
tCommandLineArgs args;
DisassemblyList code;
DisassemblyList labels;
DisassemblyList data;
std::vector<uint16_t> trackedAddresses;
ostd::StringEditor command;
int32_t labelLineLength { 20 };
uint16_t currentAddress { 0 };
bool userQuit { false };
ostd::StringEditor disassemblyDirectory { "disassembly" };
};
public: class Utils
{
public:
static DisassemblyList findCodeRegion(const DisassemblyList& code, uint16_t address, uint16_t codeRegionMargin);
static ostd::String findSymbol(const DisassemblyList& labels, uint16_t address);
static uint16_t findSymbol(const DisassemblyList& labels, const ostd::StringEditor& symbol);
static bool isValidLabelNameChar(char c);
static ostd::StringEditor fillString(const ostd::StringEditor& str, char fill, int32_t totalLength); //TODO: Implement in omnia-framework
};
public: class Display
{
public:
static void colorizeInstructionBody(const ostd::String& instBody, bool currentLine, const DisassemblyList& labelList);
static void colorCodeInstructions(const ostd::String& inst, bool currentLine, const DisassemblyList& labelList);
static void printPrompt(void);
static void printStep(void);
static void printDiff(void);
static void printTrackedAddresses(const std::vector<uint16_t>& trackedAddresses);
static ostd::StringEditor changeScreen(void);
};
public:
static void processErrors(void);
static int32_t loadArguments(int argc, char** argv);
static int32_t initRuntime(void);
static ostd::StringEditor getCommandInput(void);
static inline tDebuggerData& data(void) { return debugger; }
static inline ostd::legacy::ConsoleOutputHandler& output(void) { return out; }
private:
inline static tDebuggerData debugger;
inline static ostd::legacy::ConsoleOutputHandler out;
};
}

View file

@ -0,0 +1,137 @@
#include "DisassemblyLoader.hpp"
#include <ostd/File.hpp>
#include <ostd/Serial.hpp>
#include "../tools/Utils.hpp"
namespace dragon
{
const DisassemblyTable DisassemblyTable::DefaultObject;
void DisassemblyTable::init(const ostd::String& filePath)
{
ostd::ByteStream stream;
if (!ostd::Utils::loadByteStreamFromFile(filePath, stream))
{
m_initialized = false;
return;
}
load_data(stream);
}
void DisassemblyTable::load_data(ostd::ByteStream& stream)
{
constexpr int32_t MODE_CODE = 0, MODE_DATA = 1, MODE_LABELS = 2;
int32_t mode = MODE_CODE;
ostd::serial::SerialIO serializer(stream, ostd::serial::SerialIO::tEndianness::BigEndian);
ostd::StreamIndex addr = 0;
int32_t line_addr = 0;
int8_t line_code_char = 0;
ostd::String header_string = "";
serializer.r_NullTerminatedString(0, header_string);
if (header_string != "{ DRAGON_DEBUG_DISASSEMBLY }") return;
addr += (header_string.length() + 1) * ostd::tTypeSize::BYTE;
while (addr < serializer.size())
{
serializer.r_DWord(addr, line_addr);
addr += ostd::tTypeSize::DWORD;
// ostd::ByteStream str_stream;
// serializer.r_Byte(addr, line_code_char);
// addr += ostd::tTypeSize::BYTE;
// do
// {
// str_stream.push_back(line_code_char);
// serializer.r_Byte(addr, line_code_char);
// addr += ostd::tTypeSize::BYTE;
// } while(line_code_char != 0);
// ostd::String code_line = ostd::Utils::byteStreamToString(str_stream);
ostd::String code_line = "";
serializer.r_NullTerminatedString(addr, code_line);
addr += (code_line.length() + 1) * ostd::tTypeSize::BYTE;
if (code_line == "{ DATA }")
{
mode = MODE_DATA;
continue;
}
else if (code_line == "{ LABELS }")
{
mode = MODE_LABELS;
continue;
}
else if (code_line == "[----------DATA_SECTION----------]")
{
continue; //TODO: Store addresses for various data sections
}
code::Assembler::tDisassemblyLine line;
line.addr = line_addr;
line.code = code_line;
if (mode == MODE_CODE)
{
ostd::StringEditor codeEdit(line.code);
codeEdit.trim();
if (codeEdit.contains(" "))
{
ostd::StringEditor part1 = codeEdit.substr(0, codeEdit.indexOf(" "));
ostd::StringEditor part2 = codeEdit.substr(codeEdit.indexOf(" ") + 1);
part1.trim();
part2.trim();
int32_t opCodeLen = 10;
if (part1.len() < opCodeLen)
{
codeEdit = part1.str() + ostd::Utils::duplicateChar(' ', opCodeLen - part1.len()) + part2.str();
line.code = codeEdit.str();
}
}
m_code.push_back(line);
}
else if (mode == MODE_DATA)
m_data.push_back(line);
else if (mode == MODE_LABELS)
m_labels.push_back(line);
}
m_initialized = true;
}
void DisassemblyLoader::loadDirectory(const ostd::String& directoryPath)
{
auto list = ostd::Utils::listFilesInDirectory(directoryPath);
for (auto& path : list)
loadFile(path.string());
}
const DisassemblyTable& DisassemblyLoader::loadFile(const ostd::String& filePath)
{
DisassemblyTable table(filePath);
if (!table.isInitialized()) return DisassemblyTable::DefaultObject; //TODO: Error
m_tables.push_back(table);
return m_tables[m_tables.size() - 1];
}
std::vector<code::Assembler::tDisassemblyLine> DisassemblyLoader::getCodeTable(void)
{
std::vector<code::Assembler::tDisassemblyLine> fullTable;
for (auto& table : m_tables)
fullTable.insert(fullTable.end(), table.getCodeTable().begin(), table.getCodeTable().end());
return fullTable;
}
std::vector<code::Assembler::tDisassemblyLine> DisassemblyLoader::getDataTable(void)
{
std::vector<code::Assembler::tDisassemblyLine> fullTable;
for (auto& table : m_tables)
fullTable.insert(fullTable.end(), table.getDataTable().begin(), table.getDataTable().end());
return fullTable;
}
std::vector<code::Assembler::tDisassemblyLine> DisassemblyLoader::getLabelTable(void)
{
std::vector<code::Assembler::tDisassemblyLine> fullTable;
for (auto& table : m_tables)
fullTable.insert(fullTable.end(), table.getLabelTable().begin(), table.getLabelTable().end());
return fullTable;
}
}

View file

@ -0,0 +1,49 @@
#pragma once
#include "../assembler/Assembler.hpp"
#include <ostd/Types.hpp>
namespace dragon
{
class DisassemblyTable
{
public:
inline DisassemblyTable(void) { m_initialized = false; }
inline DisassemblyTable(const ostd::String& filePath) { init(filePath); }
void init(const ostd::String& filePath);
inline const std::vector<code::Assembler::tDisassemblyLine>& getCodeTable(void) const { return m_code; }
inline const std::vector<code::Assembler::tDisassemblyLine>& getDataTable(void) const { return m_data; }
inline const std::vector<code::Assembler::tDisassemblyLine>& getLabelTable(void) const { return m_labels; }
inline bool isInitialized(void) const { return m_initialized; }
private:
void load_data(ostd::ByteStream& stream);
private:
std::vector<code::Assembler::tDisassemblyLine> m_code;
std::vector<code::Assembler::tDisassemblyLine> m_labels;
std::vector<code::Assembler::tDisassemblyLine> m_data;
bool m_initialized { false };
ostd::String m_filePath { "" };
public:
static const DisassemblyTable DefaultObject;
};
class DisassemblyLoader
{
public:
static void loadDirectory(const ostd::String& directoryPath);
static const DisassemblyTable& loadFile(const ostd::String& filePath);
static std::vector<code::Assembler::tDisassemblyLine> getCodeTable(void);
static std::vector<code::Assembler::tDisassemblyLine> getDataTable(void);
static std::vector<code::Assembler::tDisassemblyLine> getLabelTable(void);
private:
inline static std::vector<DisassemblyTable> m_tables;
};
}

View file

@ -0,0 +1,100 @@
#include "Debugger.hpp"
#include "../runtime/DragonRuntime.hpp"
int main(int argc, char** argv)
{
using namespace dragon;
int32_t rValue = Debugger::loadArguments(argc, argv);
if (rValue != 0) return rValue;
rValue = Debugger::initRuntime();
if (rValue != 0) return rValue;
if (!Debugger::data().args.auto_start_debug)
{
while (true)
{
Debugger::Display::printPrompt();
Debugger::data().command = Debugger::getCommandInput();
Debugger::data().command.trim().toLower();
if (Debugger::data().command.str() == "r" || Debugger::data().command.str() == "run")
break;
else if (Debugger::data().command.str() == "q" || Debugger::data().command.str() == "quit")
return rValue;
else if (Debugger::data().command.startsWith("watch "))
{
Debugger::data().command = Debugger::data().command.substr(6);
Debugger::data().command.trim();
auto tokens = Debugger::data().command.tokenize();
for (auto& addr : tokens)
{
Debugger::data().command = addr;
if (Debugger::data().command.isNumeric())
Debugger::data().trackedAddresses.push_back((uint16_t)Debugger::data().command.toInt());
else if (Debugger::data().command.startsWith("$"))
{
uint16_t addr = Debugger::Utils::findSymbol(Debugger::data().data, Debugger::data().command);
if (addr > 0)
Debugger::data().trackedAddresses.push_back(addr);
else
{
addr = Debugger::Utils::findSymbol(Debugger::data().labels, Debugger::data().command);
if (addr > 0)
Debugger::data().trackedAddresses.push_back(addr);
}
}
}
}
}
}
constexpr int32_t labelLineLen = 20;
Debugger::data().currentAddress = dragon::DragonRuntime::cpu.readRegister(dragon::data::Registers::IP);
bool userQuit = false;
while (true)
{
Debugger::data().command.clr();
bool result = false;
bool hasError = false;
if (!userQuit)
{
result = dragon::DragonRuntime::runStep(Debugger::data().trackedAddresses);
hasError = dragon::DragonRuntime::hasError();
Debugger::Display::printStep();
Debugger::processErrors();
}
if (!result || userQuit)
{
Debugger::output().nl().col(ostd::legacy::ConsoleCol::Yellow).p("Execution Finished. Pres <Enter> to exit...").nl().reset();
std::cin.get();
break;
}
Debugger::Display::printPrompt();
if (Debugger::data().args.step_exec || dragon::DragonRuntime::cpu.isInDebugBreakPoint())
{
Debugger::data().command = Debugger::getCommandInput();
if (dragon::DragonRuntime::cpu.isInDebugBreakPoint())
Debugger::data().args.step_exec = true;
}
Debugger::data().command.trim().toLower();
while (Debugger::data().command.str() != "")
{
if (Debugger::data().command.str() == "q" || Debugger::data().command.str() == "quit")
{
userQuit = true;
Debugger::data().command = "";
}
else if (Debugger::data().command.str() == "c" || Debugger::data().command.str() == "continue")
{
Debugger::data().args.step_exec = false;
Debugger::data().command = "";
}
else
Debugger::data().command = Debugger::Display::changeScreen();
}
Debugger::data().currentAddress = dragon::DragonRuntime::cpu.readRegister(dragon::data::Registers::IP);
}
return rValue;
}

View file

@ -0,0 +1,6 @@
#include "DebuggerWindow.hpp"
namespace dragon
{
}

View file

@ -0,0 +1,9 @@
#pragma once
namespace dragon
{
class DebuggerWindow
{
};
} // namespace dragon

101
src/gui/RawTextRenderer.cpp Normal file
View file

@ -0,0 +1,101 @@
#include "RawTextRenderer.hpp"
#include <ostd/Utils.hpp>
#include <ostd/Geometry.hpp>
#include <ostd/Defines.hpp>
namespace dragon
{
void RawTextRenderer::initialize(void)
{
for (char c = ' '; c <= '~'; c++)
characterMap[c] = getCharacterIndex(c);
}
bool RawTextRenderer::drawString(ostd::String str, uint32_t column, uint32_t row, uint32_t* screenPixels, int32_t screenWidth, int32_t screenHeight, uint32_t* fontPixels, ostd::Color color, ostd::Color background)
{
ostd::StringEditor se(str);
if (se.str() == "") return false;
if (row >= CONSOLE_CHARS_V) return false;
if (column >= CONSOLE_CHARS_H) return false;
if (column + str.length() > CONSOLE_CHARS_H) return false;
int32_t x = column * FONT_CHAR_W;
int32_t y = row * FONT_CHAR_H;
for (auto& c : str)
{
drawCharacter((uint8_t*)screenPixels, screenWidth, screenHeight, (uint8_t*)fontPixels, x, y, c, color, background);
x += FONT_CHAR_W;
}
return true;
}
int32_t RawTextRenderer::getCharacterIndex(char c)
{
using namespace ostd;
int32_t charIndex = (int)c - 32;
IPoint charCoords = CONVERT_1D_2D(charIndex, FONT_H_CHARS);
charCoords.x *= FONT_CHAR_W * 4;
charCoords.y *= FONT_CHAR_H;
charIndex = CONVERT_2D_1D(charCoords.x, charCoords.y, (FONT_H_CHARS * FONT_CHAR_W * 4));
return charIndex;
}
ostd::Color RawTextRenderer::applyTint(ostd::Color baseColor, ostd::Color tintColor)
{
auto nBase = baseColor.getNormalizedColor();
auto nTint = tintColor.getNormalizedColor();
float r = nBase.r * nTint.r;
float g = nBase.r * nTint.g;
float b = nBase.r * nTint.b;
ostd::Color::FloatCol nTinted(r, g, b, 1.0f);
return ostd::Color(nTinted);
}
void RawTextRenderer::drawCharacter(uint8_t* screenPixels, int32_t screenWidth, int32_t screenHeight, uint8_t* fontPixels, int32_t x, int32_t y, char c, ostd::Color color, ostd::Color background)
{
using namespace ostd;
int32_t charIndex = characterMap[c];
IPoint charCoords = CONVERT_1D_2D(charIndex, (FONT_CHAR_W * FONT_H_CHARS * 4));
int32_t screenx = x * 4, screeny = y;
ostd::Color tintedColor;
bool applyBackground = false;
for (int32_t y = charCoords.y; y < charCoords.y + (FONT_CHAR_H); y += 1)
{
for (int32_t x = charCoords.x; x < charCoords.x + (FONT_CHAR_W * 4); x += 4)
{
int32_t index = CONVERT_2D_1D(x, y, (FONT_CHAR_W * FONT_H_CHARS * 4));
int32_t screenIndex = CONVERT_2D_1D(screenx, screeny, (screenWidth * 4));
screenx += 4;
if (fontPixels[index] == 0x00 && fontPixels[index + 1] == 0x00 && fontPixels[index + 2] == 0x00)
{
if (background.a == 0)
continue;
applyBackground = true;
}
if (applyBackground)
{
screenPixels[screenIndex] = 255;
screenPixels[screenIndex + 1] = background.b;
screenPixels[screenIndex + 2] = background.g;
screenPixels[screenIndex + 3] = background.r;
applyBackground = false;
continue;
}
tintedColor = applyTint({ fontPixels[index], fontPixels[index + 1], fontPixels[index + 2], 255 }, color);
screenPixels[screenIndex] = fontPixels[index + 3];
screenPixels[screenIndex + 1] = tintedColor.b;
screenPixels[screenIndex + 2] = tintedColor.g;
screenPixels[screenIndex + 3] = tintedColor.r;
}
screeny += 1;
screenx = x * 4;
}
}
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <unordered_map>
#include <ostd/Color.hpp>
namespace dragon
{
class RawTextRenderer
{
private:
inline static std::unordered_map<char, int32_t> characterMap;
public:
static void initialize(void);
static bool drawString(ostd::String str, uint32_t column, uint32_t row, uint32_t* screenPixels, int32_t screenWidth, int32_t screenHeight, uint32_t* fontPixels, ostd::Color color = { 255, 255, 255, 255 }, ostd::Color background = { 255, 255, 255, 0 });
private:
static int32_t getCharacterIndex(char c);
static ostd::Color applyTint(ostd::Color baseColor, ostd::Color tintColor);
static void drawCharacter(uint8_t* screenPixels, int32_t screenWidth, int32_t screenHeight, uint8_t* fontPixels, int32_t x, int32_t y, char c, ostd::Color color = { 255, 255, 255, 255 }, ostd::Color background = { 255, 255, 255, 0 });
public:
inline static constexpr int32_t FONT_CHAR_W = 11;
inline static constexpr int32_t FONT_CHAR_H = 26;
inline static constexpr int32_t FONT_V_CHARS = 6;
inline static constexpr int32_t FONT_H_CHARS = 16;
inline static constexpr int32_t CONSOLE_CHARS_H = 90;
inline static constexpr int32_t CONSOLE_CHARS_V = 21;
};
}

158
src/gui/VirtualConsole.cpp Normal file
View file

@ -0,0 +1,158 @@
#include "VirtualConsole.hpp"
#include "RawTextRenderer.hpp"
#include <ostd/Defines.hpp>
#include "../hardware/VirtualIODevices.hpp"
#include "../hardware/VirtualCPU.hpp"
namespace dragon
{
void VirtualConsole::initialize(void)
{
m_windowWidth = RawTextRenderer::CONSOLE_CHARS_H * RawTextRenderer::FONT_CHAR_W; //60 * 16;
m_windowHeight = RawTextRenderer::CONSOLE_CHARS_V * RawTextRenderer::FONT_CHAR_H; //60 * 9;
SDL_Init(SDL_INIT_VIDEO);
m_window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_windowWidth, m_windowHeight, SDL_WINDOW_RESIZABLE);
SDL_SetWindowResizable(m_window, SDL_FALSE);
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetWindowMinimumSize(m_window, m_windowWidth, m_windowHeight);
m_screenTexture = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, m_windowWidth, m_windowHeight);
m_fontSurface = SDL_LoadBMP("font.bmp");
if (m_fontSurface == NULL)
out.bg(ostd::ConsoleColors::Red).p("Error loading font.").nl();
m_fontPixels = (uint32_t*)m_fontSurface->pixels;
m_screenPixels = (uint32_t*)malloc(m_windowWidth * m_windowHeight * 4);
dragon::RawTextRenderer::initialize();
vout.init(m_screenPixels, m_windowWidth, m_windowHeight, m_fontPixels);
vout.enableFixedScreen();
m_palettes.push_back(new data::BiosVideoDefaultPalette); //TODO: Possible Memory Leak, palettes are never destroyed
m_initialized = true;
m_running = true;
}
void VirtualConsole::update(void)
{
if (!m_initialized) return;
Uint64 start = SDL_GetPerformanceCounter();
handleEvents();
if (m_redrawConsole)
{
uint8_t signal = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::Signal);
switch (signal)
{
case tSignals::ClearScreen:
m_clearMemory = true;
break;
case tSignals::Continue: break;
default: break;
}
m_biosVideo.write8(hw::VirtualBIOSVideo::tRegisters::Signal, tSignals::Continue);
drawConsole();
m_redrawConsole = false;
}
finalizeRender();
Uint64 end = SDL_GetPerformanceCounter();
float elapsed = (end - start) / (float)SDL_GetPerformanceFrequency();
m_redrawAccumulator += elapsed;
if (m_redrawAccumulator >= 0.2f)
{
m_redrawConsole = true;
m_redrawAccumulator = 0.0f;
}
end = SDL_GetPerformanceCounter();
elapsed = (end - start) / (float)SDL_GetPerformanceFrequency();
m_timeAccumulator += elapsed;
if (m_timeAccumulator >= 0.5f)
{
m_title.clr().add("FPS: ").addi((int)(1.0f / elapsed));
SDL_SetWindowTitle(m_window, m_title.c_str());
m_timeAccumulator = 0.0f;
}
}
void VirtualConsole::handleEvents(void)
{
if (!m_initialized) return;
SDL_Event event;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT) exit(0);
}
}
void VirtualConsole::finalizeRender(void)
{
SDL_RenderClear(m_renderer);
SDL_UpdateTexture(m_screenTexture, NULL, m_screenPixels, m_windowWidth * 4);
SDL_RenderCopy(m_renderer, m_screenTexture, NULL, NULL);
SDL_RenderPresent(m_renderer);
}
void VirtualConsole::drawConsole(void)
{
const uint8_t TRANSPARENCY_COLOR_INDEX = 0x5;
uint8_t clearColor = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::ClearColor);
uint8_t palette = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::Palette);
bool useTransparency = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::UseTransparencyOn0x5) != 0;
if (palette >= m_palettes.size())
palette = 0; //TODO: Error
auto& pal = *m_palettes[palette];
ostd::Color clrCol = pal.getColor(clearColor);
if (m_clearMemory)
{
for (int y = 0; y < m_windowHeight; ++y)
{
for (int x = 0; x < m_windowWidth; ++x)
{
m_screenPixels[x + y * m_windowWidth] = clrCol.asInteger();
}
}
m_clearMemory = false;
}
ostd::Color fgCol;
ostd::Color bgCol;
uint16_t index = hw::VirtualBIOSVideo::tRegisters::VideoMemoryStart;
// if (m_clearMemory)
// {
// for (int32_t i = index; i < (index + (RawTextRenderer::CONSOLE_CHARS_H * RawTextRenderer::CONSOLE_CHARS_V * 2)); i += 2)
// {
// m_biosVideo.write8((uint16_t)i, 0x00);
// m_biosVideo.write8((uint16_t)(i + 1), clearColor);
// }
// m_clearMemory = false;
// }
for (int32_t i = index; i < (index + (RawTextRenderer::CONSOLE_CHARS_H * RawTextRenderer::CONSOLE_CHARS_V * 2)); i += 2)
{
uint8_t character = m_biosVideo.read8((uint16_t)i);
uint8_t colors = m_biosVideo.read8((uint16_t)(i + 1));
uint8_t bgcol = (colors >> 4) & 0xF;
uint8_t fgcol = (colors & 0xF);
if (!isprint(character))
{
character = ' ';
fgCol = pal.getColor(fgcol);
bgCol = clrCol;
}
else
{
fgCol = pal.getColor(fgcol);
bgCol = pal.getColor(bgcol);
}
vout.bgcol(bgCol).col(fgCol).p((char)character).flush().bgcol(clrCol);
}
vout.refreshScreen();
m_cpu.handleInterrupt(data::InterruptCodes::BiosVideoScreenRefresh);
}
}

View file

@ -0,0 +1,64 @@
#pragma once
#include "../tools/SDLInclude.hpp"
#include <ostd/Utils.hpp>
#include "VirtualConsoleOutputHandler.hpp"
#include "../tools/GlobalData.hpp"
namespace dragon
{
namespace hw
{
class VirtualBIOSVideo;
class VirtualCPU;
}
class VirtualConsole
{
public: struct tSignals
{
inline static constexpr uint8_t Continue = 0x00;
inline static constexpr uint8_t ClearScreen = 0x01;
};
public:
inline VirtualConsole(hw::VirtualBIOSVideo& biosVideo, hw::VirtualCPU& cpu) : m_biosVideo(biosVideo), m_cpu(cpu) { }
inline bool isInitialized(void) const { return m_initialized; }
inline bool isRunning(void) const { return m_running; }
void initialize(void);
void update(void);
private:
void handleEvents(void);
void finalizeRender(void);
void drawConsole(void);
private:
ostd::ConsoleOutputHandler out;
dragon::VirtualConsoleOutputHandler vout;
SDL_Window* m_window { nullptr };
SDL_Renderer* m_renderer { nullptr };
SDL_Texture* m_screenTexture { nullptr };
SDL_Surface* m_fontSurface { nullptr };
int32_t m_windowWidth { 0 };
int32_t m_windowHeight { 0 };
uint32_t* m_fontPixels { nullptr };
uint32_t* m_screenPixels { nullptr };
bool m_redrawConsole { true };
float m_timeAccumulator { 0.0f };
bool m_running { false };
ostd::StringEditor m_title { "" };
float m_redrawAccumulator { 0.0f };
bool m_initialized { false };
bool m_clearMemory { false };
hw::VirtualBIOSVideo& m_biosVideo;
hw::VirtualCPU& m_cpu;
std::vector<data::IBiosVideoPalette*> m_palettes;
};
}

View file

@ -0,0 +1,303 @@
#include "VirtualConsoleOutputHandler.hpp"
#include "RawTextRenderer.hpp"
#include <ostd/Defines.hpp>
#include <ostd/Logger.hpp>
namespace dragon
{
tRichChar RichString::at(uint32_t index) const
{
if (index >= m_text.size())
{
OX_WARN("ox::RichString::at(...): Index out of bounds.");
return tRichChar();
}
return { (unsigned char)m_text[index], m_foreground[index], m_background[index] };
}
void RichString::add(tRichChar rchar)
{
m_text += rchar.ascii;
m_foreground.push_back(rchar.foreground);
m_background.push_back(rchar.background);
}
void RichString::add(ostd::String str, ostd::Color fg, ostd::Color bg)
{
for (auto& c : str)
{
m_text += c;
m_foreground.push_back(fg);
m_background.push_back(bg);
}
}
void RichString::add(ostd::String str)
{
ostd::Color fcol(255);
ostd::Color bcol(0, 0);
if (m_text.length() > 0)
{
fcol = m_foreground[m_foreground.size() - 1];
bcol = m_background[m_background.size() - 1];
}
add(str, fcol, bcol);
}
void RichString::clear(void)
{
m_text = "";
m_background.clear();
m_foreground.clear();
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::col(ostd::String color)
{
//TODO: Maybe implement??
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::col(const ostd::Color& color)
{
m_currentForegroundColor = color;
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::p(char c)
{
check_if_new_line();
m_currentBufferEditor.add(c);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::p(const ostd::StringEditor& se)
{
check_if_new_line();
m_currentBufferEditor.add(se.str());
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(uint8_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(int8_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(uint16_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(int16_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(uint32_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(int32_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(uint64_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pi(int64_t i)
{
check_if_new_line();
m_currentBufferEditor.addi(i);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pf(float f, uint8_t precision)
{
//TODO: Implement precision
check_if_new_line();
m_currentBufferEditor.addf(f);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::pf(double f, uint8_t precision)
{
//TODO: Implement precision
check_if_new_line();
m_currentBufferEditor.addf(f);
add_current_to_rich_buffer();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::nl(void)
{
m_newLine = true;
flush();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::flush(void)
{
if (!isInitialized()) return *this;
if (m_currentBuffer.getText() == "") return *this;
m_lines.push_back(m_currentBuffer);
m_currentBuffer.clear();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::reset(void)
{
m_currentBuffer.clear();
m_currentBufferEditor.clr();
m_lines.clear();
m_newLine = true;
resetColors();
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::clear(void)
{
//TODO: Implement
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::bgcol(const ostd::Color& color)
{
m_currentBackgroundColor = color;
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::bgcol(ostd::String color)
{
//TODO: Maybe implement
return *this;
}
ostd::legacy::IOutputHandler& VirtualConsoleOutputHandler::resetColors(void)
{
m_currentForegroundColor = m_defaultForegroundColor;
m_currentBackgroundColor = m_defaultBackgroundColor;
return *this;
}
void VirtualConsoleOutputHandler::init(uint32_t* screenPixels, int32_t screenWidth, int32_t screenHeight, uint32_t* fontPixels)
{
m_screenPixels = screenPixels;
m_fontPixels = fontPixels;
m_screenHeight = screenHeight;
m_screenWidth = screenWidth;
m_initialized = m_screenHeight > 0 && m_screenWidth > 0 && m_screenPixels != nullptr && m_fontPixels != nullptr;
}
void VirtualConsoleOutputHandler::add_current_to_rich_buffer(void)
{
if (m_currentBuffer.getText().length() + m_currentBufferEditor.len() <= RawTextRenderer::CONSOLE_CHARS_H)
{
m_currentBuffer.add(m_currentBufferEditor.str(), m_currentForegroundColor, m_currentBackgroundColor);
m_currentBufferEditor.clr();
if (m_currentBuffer.getText().length() == RawTextRenderer::CONSOLE_CHARS_H)
nl();
else
m_newLine = false;
return;
}
int32_t extraStringLength = (m_currentBuffer.getText().length() + m_currentBufferEditor.len()) - RawTextRenderer::CONSOLE_CHARS_H;
ostd::String excess = m_currentBufferEditor.substr(RawTextRenderer::CONSOLE_CHARS_H);
ostd::String end = m_currentBufferEditor.substr(0, RawTextRenderer::CONSOLE_CHARS_H);
m_currentBuffer.add(end, m_currentForegroundColor, m_currentBackgroundColor);
nl();
m_currentBufferEditor = excess;
add_current_to_rich_buffer();
}
void VirtualConsoleOutputHandler::check_if_new_line(void)
{
if (m_newLine) return;
if (m_lines.size() > 0)
{
m_currentBuffer = m_lines[m_lines.size() - 1];
m_lines.pop_back();
}
}
void VirtualConsoleOutputHandler::draw_rich_String(const RichString& rstr, uint32_t row)
{
for (int32_t i = 0; i < rstr.getText().length(); i++)
{
auto rchar = rstr.at(i);
RawTextRenderer::drawString(ostd::StringEditor().add(rchar.ascii).str(), i, row, m_screenPixels, m_screenWidth, m_screenHeight, m_fontPixels, rchar.foreground, rchar.background);
}
}
void VirtualConsoleOutputHandler::refreshScreen(void)
{
uint32_t row = 0;
if (m_lines.size() > RawTextRenderer::CONSOLE_CHARS_V - 1)
{
if (m_fixedScreen)
{
for (int32_t i = 0; i < RawTextRenderer::CONSOLE_CHARS_V; i++)
{
draw_rich_String(m_lines[i], row);
row++;
}
}
else
{
for (int32_t i = m_lines.size() - RawTextRenderer::CONSOLE_CHARS_V + 1; i < m_lines.size(); i++)
{
draw_rich_String(m_lines[i], row);
row++;
}
}
}
else
{
for (auto& line : m_lines)
{
draw_rich_String(line, row);
row++;
}
}
m_lines.clear();
m_currentBuffer.clear();
m_currentBufferEditor.clr();
}
}

View file

@ -0,0 +1,99 @@
#pragma once
#include <ostd/Utils.hpp>
#include <ostd/Color.hpp>
#include <ostd/IOHandlers.hpp>
#include <vector>
namespace dragon
{
struct tRichChar
{
unsigned char ascii { 0 };
ostd::Color foreground { 0, 0 };
ostd::Color background { 0, 0 };
};
class RichString //TODO: Legacy RichString should be replaced with new tStyledString
{
public:
inline RichString(void) { }
inline ostd::String getText(void) const { return m_text; }
tRichChar at(uint32_t index) const;
void add(tRichChar rchar);
void add(ostd::String str, ostd::Color fg, ostd::Color bg);
void add(ostd::String str);
void clear(void);
private:
ostd::String m_text { "" };
std::vector<ostd::Color> m_foreground;
std::vector<ostd::Color> m_background;
};
class VirtualConsoleOutputHandler : public ostd::legacy::IOutputHandler
{
public:
ostd::legacy::IOutputHandler& col(ostd::String color) override;
ostd::legacy::IOutputHandler& col(const ostd::Color& color) override;
ostd::legacy::IOutputHandler& p(char c) override;
ostd::legacy::IOutputHandler& p(const ostd::StringEditor& se) override;
ostd::legacy::IOutputHandler& pi(uint8_t i) override;
ostd::legacy::IOutputHandler& pi(int8_t i) override;
ostd::legacy::IOutputHandler& pi(uint16_t i) override;
ostd::legacy::IOutputHandler& pi(int16_t i) override;
ostd::legacy::IOutputHandler& pi(uint32_t i) override;
ostd::legacy::IOutputHandler& pi(int32_t i) override;
ostd::legacy::IOutputHandler& pi(uint64_t i) override;
ostd::legacy::IOutputHandler& pi(int64_t i) override;
ostd::legacy::IOutputHandler& pf(float f, uint8_t precision = 0) override;
ostd::legacy::IOutputHandler& pf(double f, uint8_t precision = 0) override;
ostd::legacy::IOutputHandler& nl(void) override;
inline ostd::legacy::IOutputHandler& pStyled(const ostd::StringEditor& styled) override { return *this; } //TODO: Implement
inline ostd::legacy::IOutputHandler& pStyled(const ostd::TextStyleBuilder::IRichStringBase& styled) override { return *this; } //TODO: Implement
inline ostd::legacy::IOutputHandler& pStyled(const ostd::TextStyleParser::tStyledString& styled) override { return *this; }; //TODO: Implement
ostd::legacy::IOutputHandler& flush(void) override;
ostd::legacy::IOutputHandler& reset(void) override;
ostd::legacy::IOutputHandler& clear(void) override;
ostd::legacy::IOutputHandler& bgcol(const ostd::Color& color) override;
ostd::legacy::IOutputHandler& bgcol(ostd::String color) override;
ostd::legacy::IOutputHandler& resetColors(void) override;
void init(uint32_t* screenPixels, int32_t screenWidth, int32_t screenHeight, uint32_t* fontPixels);
inline bool isInitialized(void) const { return m_initialized; }
inline bool isFixedScreen(void) const { return m_fixedScreen; }
inline void enableFixedScreen(bool fs = true) { m_fixedScreen = fs; }
void refreshScreen(void);
private:
void add_current_to_rich_buffer(void);
void check_if_new_line(void);
void draw_rich_String(const RichString& rstr, uint32_t row);
private:
std::vector<RichString> m_lines;
RichString m_currentBuffer;
ostd::StringEditor m_currentBufferEditor;
// std::vector<ostd::String> m_allLines;
// ostd::StringEditor m_buffer;
// int32_t m_currentRow { 0 };
// int32_t m_currentColumn { 0 };
ostd::Color m_currentForegroundColor { 255, 255, 255, 255 };
ostd::Color m_currentBackgroundColor { 255, 255, 255, 0 };
ostd::Color m_defaultForegroundColor { 255, 255, 255, 255 };
ostd::Color m_defaultBackgroundColor { 255, 255, 255, 0 };
uint32_t* m_screenPixels { nullptr };
uint32_t* m_fontPixels { nullptr };
int32_t m_screenWidth { 0 };
int32_t m_screenHeight { 0 };
bool m_initialized { false };
bool m_newLine { true };
bool m_fixedScreen { false };
};
}

98
src/gui/Window.cpp Normal file
View file

@ -0,0 +1,98 @@
#include "Window.hpp"
namespace dragon
{
void Window::initialize(int32_t width, int32_t height, const ostd::String& fontPath)
{
m_windowWidth = width;
m_windowHeight = height;
SDL_Init(SDL_INIT_VIDEO);
m_window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_windowWidth, m_windowHeight, SDL_WINDOW_RESIZABLE);
SDL_SetWindowResizable(m_window, SDL_FALSE);
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED);
SDL_SetWindowMinimumSize(m_window, m_windowWidth, m_windowHeight);
// m_screenTexture = SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, m_windowWidth, m_windowHeight);
m_fontSurface = SDL_LoadBMP(fontPath.c_str());
if (m_fontSurface == NULL)
out.bg(ostd::ConsoleColors::Red).p("Error loading font.").reset().nl();
m_fontPixels = (uint32_t*)m_fontSurface->pixels;
// m_screenPixels = (uint32_t*)malloc(m_windowWidth * m_windowHeight * 4);
m_initialized = true;
m_running = true;
}
void Window::update(void)
{
if (!m_initialized) return;
Uint64 start = SDL_GetPerformanceCounter();
handleEvents();
SDL_RenderClear(m_renderer);
for (int32_t i = 0; i < m_widgets.size(); i++)
{
if (m_widgets[i] == nullptr) continue;
m_widgets[i]->draw();
}
SDL_RenderPresent(m_renderer);
// finalizeRender();
Uint64 end = SDL_GetPerformanceCounter();
float elapsed = (end - start) / (float)SDL_GetPerformanceFrequency();
m_redrawAccumulator += elapsed;
if (m_redrawAccumulator >= 0.2f)
{
for (int32_t i = 0; i < m_widgets.size(); i++)
{
if (m_widgets[i] == nullptr) continue;
m_widgets[i]->fixedUpdate();
}
m_redrawAccumulator = 0.0f;
}
end = SDL_GetPerformanceCounter();
elapsed = (end - start) / (float)SDL_GetPerformanceFrequency();
m_timeAccumulator += elapsed;
if (m_timeAccumulator >= 0.5f)
{
for (int32_t i = 0; i < m_widgets.size(); i++)
{
if (m_widgets[i] == nullptr) continue;
m_widgets[i]->slowUpdate();
}
m_title.clr().add("FPS: ").addi((int)(1.0f / elapsed));
SDL_SetWindowTitle(m_window, m_title.c_str());
m_timeAccumulator = 0.0f;
}
}
bool Window::addWidget(Widget& widget)
{
widget.__init(*this);
m_widgets.push_back(&widget);
return true;
}
void Window::setSize(int32_t width, int32_t height)
{
if (!isInitialized()) return;
SDL_SetWindowSize(m_window, width, height);
}
void Window::handleEvents(void)
{
if (!m_initialized) return;
SDL_Event event;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
m_running = false;
}
}
}
void Window::finalizeRender(void)
{
// SDL_UpdateTexture(m_screenTexture, NULL, m_screenPixels, m_windowWidth * 4);
// SDL_RenderCopy(m_renderer, m_screenTexture, NULL, NULL);
}
}

57
src/gui/Window.hpp Normal file
View file

@ -0,0 +1,57 @@
#pragma once
#include "../tools/SDLInclude.hpp"
#include <ostd/Utils.hpp>
#include <ostd/Signals.hpp>
#include "VirtualConsoleOutputHandler.hpp"
#include "../tools/GlobalData.hpp"
#include "../gui/widgets/Widget.hpp"
namespace dragon
{
class Window
{
public:
inline Window(void) { }
inline bool isInitialized(void) const { return m_initialized; }
inline bool isRunning(void) const { return m_running; }
inline void hide(void) { SDL_HideWindow(m_window); }
inline void show(void) { SDL_ShowWindow(m_window); }
void initialize(int32_t width, int32_t height, const ostd::String& fontPath);
void update(void);
bool addWidget(Widget& widget);
void setSize(int32_t width, int32_t height);
private:
void handleEvents(void);
void finalizeRender(void);
private:
ostd::ConsoleOutputHandler out;
std::vector<Widget*> m_widgets;
SDL_Window* m_window { nullptr };
SDL_Renderer* m_renderer { nullptr };
// SDL_Texture* m_screenTexture { nullptr };
SDL_Surface* m_fontSurface { nullptr };
int32_t m_windowWidth { 0 };
int32_t m_windowHeight { 0 };
uint32_t* m_fontPixels { nullptr };
// uint32_t* m_screenPixels { nullptr };
float m_timeAccumulator { 0.0f };
bool m_running { false };
ostd::StringEditor m_title { "" };
float m_redrawAccumulator { 0.0f };
bool m_initialized { false };
//Signals
inline static const uint32_t Signal_OnMousePressed = ostd::SignalHandler::newCustomSignal(4096);
friend class VirtualConsoleWidtget;
};
}

View file

@ -0,0 +1,128 @@
#include "VirtualConsoleWidget.hpp"
#include "../../gui/RawTextRenderer.hpp"
#include "../../gui/Window.hpp"
#include "../../hardware/VirtualIODevices.hpp"
#include "../../hardware/VirtualCPU.hpp"
namespace dragon
{
void VirtualConsoleWidtget::init(void)
{
setw(RawTextRenderer::CONSOLE_CHARS_H * RawTextRenderer::FONT_CHAR_W); //60 * 16;
seth(RawTextRenderer::CONSOLE_CHARS_V * RawTextRenderer::FONT_CHAR_H); //60 * 9;
setPosition(0, 0);
dragon::RawTextRenderer::initialize();
m_pixels = new uint32_t[getw() * geth()]; //TODO: Delete (Memory Leak)
vout.init(m_pixels, getw(), geth(), m_parent->m_fontPixels);
vout.enableFixedScreen();
m_palettes.push_back(new data::BiosVideoDefaultPalette); //TODO: Possible Memory Leak, palettes are never destroyed
m_texture = SDL_CreateTexture(m_parent->m_renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, getw(), geth());
m_initialized = true;
validate();
}
void VirtualConsoleWidtget::draw(void)
{
if (isInvalid()) return;
SDL_Rect rect { (int)getx(), (int)gety(), (int)getw(), (int)geth() };
SDL_RenderCopy(m_parent->m_renderer, m_texture, NULL, &rect);
}
void VirtualConsoleWidtget::drawConsole(void)
{
if (isInvalid()) return;
const uint8_t TRANSPARENCY_COLOR_INDEX = 0x5;
uint8_t clearColor = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::ClearColor);
uint8_t palette = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::Palette);
bool useTransparency = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::UseTransparencyOn0x5) != 0;
if (palette >= m_palettes.size())
palette = 0; //TODO: Error
auto& pal = *m_palettes[palette];
ostd::Color clrCol = pal.getColor(clearColor);
if (m_clearMemory)
{
for (int y = 0; y < m_parent->m_windowHeight; ++y)
{
for (int x = 0; x < m_parent->m_windowWidth; ++x)
{
m_pixels[x + y * m_parent->m_windowWidth] = clrCol.asInteger();
}
}
m_clearMemory = false;
}
ostd::Color fgCol;
ostd::Color bgCol;
uint16_t index = hw::VirtualBIOSVideo::tRegisters::VideoMemoryStart;
// if (m_clearMemory)
// {
// for (int32_t i = index; i < (index + (RawTextRenderer::CONSOLE_CHARS_H * RawTextRenderer::CONSOLE_CHARS_V * 2)); i += 2)
// {
// m_biosVideo.write8((uint16_t)i, 0x00);
// m_biosVideo.write8((uint16_t)(i + 1), clearColor);
// }
// m_clearMemory = false;
// }
for (int32_t i = index; i < (index + (RawTextRenderer::CONSOLE_CHARS_H * RawTextRenderer::CONSOLE_CHARS_V * 2)); i += 2)
{
uint8_t character = m_biosVideo.read8((uint16_t)i);
uint8_t colors = m_biosVideo.read8((uint16_t)(i + 1));
uint8_t bgcol = (colors >> 4) & 0xF;
uint8_t fgcol = (colors & 0xF);
if (!isprint(character))
{
character = ' ';
fgCol = pal.getColor(fgcol);
bgCol = clrCol;
}
else
{
fgCol = pal.getColor(fgcol);
bgCol = pal.getColor(bgcol);
}
vout.bgcol(bgCol).col(fgCol).p((char)character).flush().bgcol(clrCol);
}
vout.refreshScreen();
m_cpu.handleInterrupt(data::InterruptCodes::BiosVideoScreenRefresh);
}
void VirtualConsoleWidtget::update(void)
{
}
void VirtualConsoleWidtget::fixedUpdate(void)
{
if (isInvalid()) return;
uint8_t signal = m_biosVideo.read8(hw::VirtualBIOSVideo::tRegisters::Signal);
switch (signal)
{
case tSignals::ClearScreen:
m_clearMemory = true;
break;
case tSignals::Continue: break;
default: break;
}
m_biosVideo.write8(hw::VirtualBIOSVideo::tRegisters::Signal, tSignals::Continue);
drawConsole();
SDL_UpdateTexture(m_texture, NULL, m_pixels, getw() * 4);
}
void VirtualConsoleWidtget::slowUpdate(void)
{
}
}

View file

@ -0,0 +1,67 @@
#pragma once
#include "Widget.hpp"
#include "../../tools/SDLInclude.hpp"
#include "../../gui/VirtualConsoleOutputHandler.hpp"
#include "../../tools/GlobalData.hpp"
namespace dragon
{
namespace hw
{
class VirtualBIOSVideo;
class VirtualCPU;
}
class VirtualConsoleWidtget : public Widget
{
public: struct tSignals
{
inline static constexpr uint8_t Continue = 0x00;
inline static constexpr uint8_t ClearScreen = 0x01;
};
public:
inline VirtualConsoleWidtget(hw::VirtualBIOSVideo& biosVideo, hw::VirtualCPU& cpu) : m_biosVideo(biosVideo), m_cpu(cpu) { invalidate(); }
void init(void) override;
void draw(void) override;
void update(void) override;
void fixedUpdate(void) override;
void slowUpdate(void) override;
inline bool isInitialized(void) const { return m_initialized; }
private:
void drawConsole(void);
private:
dragon::VirtualConsoleOutputHandler vout;
// SDL_Window* m_window { nullptr };
// SDL_Renderer* m_renderer { nullptr };
// SDL_Texture* m_screenTexture { nullptr };
// SDL_Surface* m_fontSurface { nullptr };
// int32_t m_windowWidth { 0 };
// int32_t m_windowHeight { 0 };
// uint32_t* m_fontPixels { nullptr };
// uint32_t* m_screenPixels { nullptr };
uint32_t* m_pixels { nullptr };
SDL_Texture* m_texture;
// bool m_redrawConsole { true };
// float m_timeAccumulator { 0.0f };
// bool m_running { false };
// ostd::StringEditor m_title { "" };
// float m_redrawAccumulator { 0.0f };
bool m_initialized { false };
bool m_clearMemory { false };
hw::VirtualBIOSVideo& m_biosVideo;
hw::VirtualCPU& m_cpu;
std::vector<data::IBiosVideoPalette*> m_palettes;
};
}

View file

@ -0,0 +1,25 @@
#pragma once
#include <cstdint>
#include <ostd/Geometry.hpp>
#include <ostd/BaseObject.hpp>
namespace dragon
{
class Window;
class Widget : public ostd::Rectangle, public ostd::BaseObject
{
public:
void __init(Window& parent) { m_parent = &parent; init(); }
virtual void draw(void) { }
virtual void update(void) { }
virtual void fixedUpdate(void) { }
virtual void slowUpdate(void) { }
protected:
virtual void init(void) { }
protected:
Window* m_parent { nullptr };
};
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <ostd/Types.hpp>
namespace dragon
{
namespace hw
{
class IMemoryDevice
{
public:
virtual int8_t read8(uint16_t addr) = 0;
virtual int16_t read16(uint16_t addr) = 0;
virtual int8_t write8(uint16_t addr, int8_t value) = 0;
virtual int16_t write16(uint16_t addr, int16_t value) = 0;
virtual inline ostd::ByteStream* getByteStream(void) = 0;
};
}
}

View file

@ -0,0 +1,75 @@
#include "MemoryMapper.hpp"
#include <ostd/Utils.hpp>
#include <iostream>
#include "../tools/GlobalData.hpp"
namespace dragon
{
namespace hw
{
MemoryMapper::MemoryMapper(void)
{
}
int8_t MemoryMapper::read8(uint16_t addr)
{
auto region = findRegion(addr);
if (region == nullptr) return 0x0000; //TODO: Error
uint16_t finalAddr = (region->remap ? addr - region->startAddress : addr);
return region->device->read8(finalAddr);
}
int16_t MemoryMapper::read16(uint16_t addr)
{
auto region = findRegion(addr);
if (region == nullptr) return 0x0000; //TODO: Error
uint16_t finalAddr = (region->remap ? addr - region->startAddress : addr);
return region->device->read16(finalAddr);
}
int8_t MemoryMapper::write8(uint16_t addr, int8_t value)
{
auto region = findRegion(addr);
if (region == nullptr) return 0x0000; //TODO: Error
uint16_t finalAddr = (region->remap ? addr - region->startAddress : addr);
return region->device->write8(finalAddr, value);
}
int16_t MemoryMapper::write16(uint16_t addr, int16_t value)
{
auto region = findRegion(addr);
if (region == nullptr) return 0x0000; //TODO: Error
uint16_t finalAddr = (region->remap ? addr - region->startAddress : addr);
return region->device->write16(finalAddr, value);
}
void MemoryMapper::mapDevice(IMemoryDevice& device, uint16_t startAddr, uint16_t endAddr, bool remap, ostd::String name)
{
m_regions.push_back({ &device, startAddr, endAddr, remap, name });
}
ostd::String MemoryMapper::getMemoryRegionName(uint16_t address)
{
tMemoryRegion* region = findRegion(address);
if (region == nullptr) return "Invalid";
return region->name;
}
ostd::ByteStream* MemoryMapper::getByteStream(void)
{
return nullptr;
}
MemoryMapper::tMemoryRegion* MemoryMapper::findRegion(uint16_t address)
{
for (auto& region : m_regions)
{
if (address >= region.startAddress && address <= region.endAddress)
return &region;
}
data::ErrorHandler::pushError(data::ErrorCodes::MM_RegionNotFound, ostd::StringEditor("Memory device not found for address: ").add(ostd::Utils::getHexStr(address, true, 2)).str());
return nullptr; //TODO: Error
}
}
}

View file

@ -0,0 +1,41 @@
#pragma once
#include "IMemoryDevice.hpp"
#include <vector>
namespace dragon
{
namespace hw
{
class MemoryMapper : public IMemoryDevice
{
private: struct tMemoryRegion
{
IMemoryDevice* device { nullptr };
uint16_t startAddress { 0x0000 };
uint16_t endAddress { 0x0000 };
bool remap { false };
ostd::String name { "" };
};
public:
MemoryMapper(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
void mapDevice(IMemoryDevice& device, uint16_t startAddr, uint16_t endAddr, bool remap = false, ostd::String name = "");
ostd::String getMemoryRegionName(uint16_t address);
ostd::ByteStream* getByteStream(void) override;
private:
tMemoryRegion* findRegion(uint16_t address);
private:
std::vector<tMemoryRegion> m_regions;
};
}
}

707
src/hardware/VirtualCPU.cpp Normal file
View file

@ -0,0 +1,707 @@
#include "VirtualCPU.hpp"
#include "../tools/GlobalData.hpp"
#include <ostd/Utils.hpp>
#include <iostream>
namespace dragon
{
namespace hw
{
VirtualCPU::VirtualCPU(IMemoryDevice& memory) : m_memory(memory)
{
writeRegister(data::Registers::SP, (uint16_t)(0xFFFF - 1 - 1));
writeRegister(data::Registers::FP, (uint16_t)(0xFFFF - 1 - 1));
}
int16_t VirtualCPU::readRegister(uint8_t reg)
{
if (reg >= data::Registers::Last) return 0x0000; //TODO: Error
return m_registers[reg];
}
int16_t VirtualCPU::writeRegister(uint8_t reg, int16_t value)
{
if (reg >= data::Registers::Last) return 0x0000; //TODO: Error
m_registers[reg] = value;
return value;
}
int8_t VirtualCPU::fetch8(void)
{
uint16_t nextInstAddr = readRegister(data::Registers::IP);
int8_t inst = m_memory.read8(nextInstAddr);
writeRegister(data::Registers::IP, nextInstAddr + 1);
return inst;
}
int16_t VirtualCPU::fetch16(void)
{
uint16_t nextInstAddr = readRegister(data::Registers::IP);
int16_t inst = m_memory.read16(nextInstAddr);
writeRegister(data::Registers::IP, nextInstAddr + 2);
return inst;
}
void VirtualCPU::pushToStack(int16_t value)
{
uint16_t stackAddr = readRegister(data::Registers::SP);
m_memory.write16(stackAddr, value);
writeRegister(data::Registers::SP, stackAddr - 2);
m_stackFrameSize += 2;
}
int16_t VirtualCPU::popFromStack(void)
{
uint16_t nextSP = readRegister(data::Registers::SP) + 2;
writeRegister(data::Registers::SP, nextSP);
int16_t value = m_memory.read16(nextSP);
m_stackFrameSize -= 2;
return value;
}
void VirtualCPU::pushStackFrame(void)
{
uint16_t argStartAddr = readRegister(data::Registers::SP) + 2;
uint16_t argCount = m_memory.read16(argStartAddr);
if (argCount == 0)
argStartAddr = 0;
else
argStartAddr += (argCount * 2);
pushToStack(readRegister(data::Registers::R1));
pushToStack(readRegister(data::Registers::R2));
pushToStack(readRegister(data::Registers::R3));
pushToStack(readRegister(data::Registers::R4));
pushToStack(readRegister(data::Registers::R5));
pushToStack(readRegister(data::Registers::R6));
pushToStack(readRegister(data::Registers::R7));
pushToStack(readRegister(data::Registers::R8));
pushToStack(readRegister(data::Registers::R9));
pushToStack(readRegister(data::Registers::R10));
pushToStack(readRegister(data::Registers::PP));
pushToStack(readRegister(data::Registers::ACC));
pushToStack(readRegister(data::Registers::IP));
pushToStack(m_stackFrameSize + 2);
writeRegister(data::Registers::PP, argStartAddr);
writeRegister(data::Registers::FP, readRegister(data::Registers::SP));
m_stackFrameSize = 0;
}
void VirtualCPU::popStackFrame(void)
{
uint16_t framePointerAddr = readRegister(data::Registers::FP);
writeRegister(data::Registers::SP, framePointerAddr);
m_stackFrameSize = popFromStack();
uint16_t tmpStackFrameSize = m_stackFrameSize;
writeRegister(data::Registers::IP, popFromStack());
writeRegister(data::Registers::ACC, popFromStack());
writeRegister(data::Registers::PP, popFromStack());
writeRegister(data::Registers::R10, popFromStack());
writeRegister(data::Registers::R9, popFromStack());
writeRegister(data::Registers::R8, popFromStack());
writeRegister(data::Registers::R7, popFromStack());
writeRegister(data::Registers::R6, popFromStack());
writeRegister(data::Registers::R5, popFromStack());
writeRegister(data::Registers::R4, popFromStack());
writeRegister(data::Registers::R3, popFromStack());
writeRegister(data::Registers::R2, popFromStack());
writeRegister(data::Registers::R1, popFromStack());
uint16_t nArgs = popFromStack();
for (int32_t i = 0; i < nArgs; i++)
popFromStack();
writeRegister(data::Registers::FP, framePointerAddr + tmpStackFrameSize);
}
bool VirtualCPU::readFlag(uint8_t flg)
{
if (flg >= 16) return false;
m_tempFlags.value = readRegister(data::Registers::FL);
return ostd::Bits::get(m_tempFlags, flg);
}
void VirtualCPU::setFlag(uint8_t flg, bool val)
{
if (flg >= 16) return;
m_tempFlags.value = readRegister(data::Registers::FL);
ostd::Bits::val(m_tempFlags, flg, val);
writeRegister(data::Registers::FL, m_tempFlags.value);
}
void VirtualCPU::handleInterrupt(uint8_t intValue)
{
// std::cout << "Interrupt: " << ostd::Utils::getHexStr(intValue, true, 1) << "\n";
uint16_t entryPointer = data::MemoryMapAddresses::IntVector_Start + (intValue * 3);
uint8_t interruptStatus = m_memory.read8(entryPointer);
if (interruptStatus != 0xFF) return;
uint16_t handlerAddress = m_memory.read16(entryPointer + 1);
if (!m_isInInterruptHandler)
{
pushToStack(0);
pushStackFrame();
}
m_isInInterruptHandler = true;
writeRegister(data::Registers::IP, handlerAddress);
}
bool VirtualCPU::execute(void)
{
if (m_halt) return false;
m_isDebugBreakPoint = false;
uint8_t inst = fetch8();
m_currentInst = inst;
switch (inst)
{
case data::OpCodes::NoOp:
{
}
break;
case data::OpCodes::DEBUG_Break:
{
m_isDebugBreakPoint = true;
}
break;
case data::OpCodes::MovImmReg:
{
uint8_t regAddr = fetch8();
int16_t literal = fetch16();
writeRegister(regAddr, literal);
}
break;
case data::OpCodes::MovImmMem:
{
uint16_t addr = fetch16();
int16_t literal = fetch16();
m_memory.write16(addr, literal);
}
break;
case data::OpCodes::MovRegReg:
{
uint8_t destRegAddr = fetch8();
uint8_t srcRegAddr = fetch8();
int16_t value = readRegister(srcRegAddr);
writeRegister(destRegAddr, value);
}
break;
case data::OpCodes::MovRegMem:
{
uint16_t addr = fetch16();
uint8_t srcRegAddr = fetch8();
int16_t value = readRegister(srcRegAddr);
m_memory.write16(addr, value);
}
break;
case data::OpCodes::MovMemReg:
{
uint8_t destRegAddr = fetch8();
uint16_t addr = fetch16();
int16_t value = m_memory.read16(addr);
writeRegister(destRegAddr, value);
}
break;
case data::OpCodes::MovDerefRegReg:
{
uint8_t destRegAddr = fetch8();
uint8_t srcRegAddr = fetch8();
uint16_t addr = readRegister(srcRegAddr);
int16_t value = m_memory.read16(addr);
writeRegister(destRegAddr, value);
}
break;
case data::OpCodes::MovDerefRegMem:
{
uint16_t destAddr = fetch16();
uint8_t srcRegAddr = fetch8();
uint16_t addr = readRegister(srcRegAddr);
int16_t value = m_memory.read16(addr);
m_memory.write16(destAddr, value);
}
break;
case data::OpCodes::MovImmRegOffReg:
{
uint8_t destRegAddr = fetch8();
uint16_t addr = fetch16();
uint8_t offRegAddr = fetch8();
int16_t offset = readRegister(offRegAddr);
int16_t value = m_memory.read16(addr + offset);
writeRegister(destRegAddr, value);
}
break;
case data::OpCodes::MovRegDerefReg:
{
uint8_t destRegAddr = fetch8();
uint8_t srcRegAddr = fetch8();
int16_t value = readRegister(srcRegAddr);
uint16_t addr = readRegister(destRegAddr);
m_memory.write16(addr, value);
}
break;
case data::OpCodes::MovMemDerefReg:
{
uint8_t destRegAddr = fetch8();
uint16_t srcAddr = fetch16();
int16_t value = m_memory.read16(srcAddr);
uint16_t addr = readRegister(destRegAddr);
m_memory.write16(addr, value);
}
break;
case data::OpCodes::MovImmDerefReg:
{
uint8_t destRegAddr = fetch8();
int16_t value = fetch16();
uint16_t addr = readRegister(destRegAddr);
m_memory.write16(addr, value);
}
break;
case data::OpCodes::MovDerefRegDerefReg:
{
uint8_t destRegAddr = fetch8();
uint8_t srcRegAddr = fetch8();
uint16_t srcAddr = readRegister(srcRegAddr);
uint16_t destAddr = readRegister(destRegAddr);
int16_t value = m_memory.read16(srcAddr);
m_memory.write16(destAddr, value);
}
break;
case data::OpCodes::MovByteImmMem:
{
uint16_t addr = fetch16();
int8_t literal = fetch8();
m_memory.write8(addr, literal);
}
break;
case data::OpCodes::MovByteDerefRegMem:
{
uint16_t destAddr = fetch16();
uint8_t srcRegAddr = fetch8();
uint16_t addr = readRegister(srcRegAddr);
int8_t value = m_memory.read8(addr);
m_memory.write8(destAddr, value);
}
break;
case data::OpCodes::MovByteRegDerefReg:
{
uint8_t destRegAddr = fetch8();
uint8_t srcRegAddr = fetch8();
int16_t value = readRegister(srcRegAddr);
uint16_t addr = readRegister(destRegAddr);
m_memory.write8(addr, (int8_t)value);
}
break;
case data::OpCodes::MovByteMemDerefReg:
{
uint8_t destRegAddr = fetch8();
uint16_t srcAddr = fetch16();
int8_t value = m_memory.read8(srcAddr);
uint16_t addr = readRegister(destRegAddr);
m_memory.write8(addr, value);
}
break;
case data::OpCodes::MovByteImmDerefReg:
{
uint8_t destRegAddr = fetch8();
int8_t value = fetch8();
uint16_t addr = readRegister(destRegAddr);
m_memory.write8(addr, value);
}
break;
case data::OpCodes::MovByteDerefRegDerefReg:
{
uint8_t destRegAddr = fetch8();
uint8_t srcRegAddr = fetch8();
uint16_t srcAddr = readRegister(srcRegAddr);
uint16_t destAddr = readRegister(destRegAddr);
int8_t value = m_memory.read8(srcAddr);
m_memory.write8(destAddr, value);
}
break;
case data::OpCodes::MovByteMemReg:
{
uint8_t destRegAddr = fetch8();
uint16_t srcAddr = fetch16();
int8_t value = m_memory.read8(srcAddr);
writeRegister(destRegAddr, value);
}
break;
case data::OpCodes::MovByteImmReg:
{
uint8_t destRegAddr = fetch8();
int8_t value = fetch8();
writeRegister(destRegAddr, value);
}
break;
case data::OpCodes::MovByteDerefRegReg:
{
uint8_t destRegAddr = fetch8();
uint8_t srcRegAddr = fetch8();
uint16_t srcAddr = readRegister(srcRegAddr);
int8_t value = m_memory.read8(srcAddr);
writeRegister(destRegAddr, value);
}
break;
case data::OpCodes::MovByteRegMem:
{
uint16_t addr = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
m_memory.write8(addr, (int8_t)(value & 0x00FF));
}
break;
case data::OpCodes::AddImmReg:
{
uint8_t regAddr = fetch8();
int16_t literal = fetch16();
int16_t regValue = readRegister(regAddr);
writeRegister(data::Registers::ACC, regValue + literal);
}
break;
case data::OpCodes::AddRegReg:
{
uint8_t regAddr1 = fetch8();
uint8_t regAddr2 = fetch8();
int16_t regValue1 = readRegister(regAddr1);
int16_t regValue2 = readRegister(regAddr2);
writeRegister(data::Registers::ACC, regValue1 + regValue2);
}
break;
case data::OpCodes::SubImmReg:
{
uint8_t regAddr = fetch8();
int16_t literal = fetch16();
int16_t regValue = readRegister(regAddr);
writeRegister(data::Registers::ACC, regValue - literal);
}
break;
case data::OpCodes::SubRegReg:
{
uint8_t regAddr1 = fetch8();
uint8_t regAddr2 = fetch8();
int16_t regValue1 = readRegister(regAddr1);
int16_t regValue2 = readRegister(regAddr2);
writeRegister(data::Registers::ACC, regValue1 - regValue2);
}
break;
case data::OpCodes::MulImmReg:
{
uint8_t regAddr = fetch8();
int16_t literal = fetch16();
int16_t regValue = readRegister(regAddr);
writeRegister(data::Registers::ACC, regValue * literal);
}
break;
case data::OpCodes::MulRegReg:
{
uint8_t regAddr1 = fetch8();
uint8_t regAddr2 = fetch8();
int16_t regValue1 = readRegister(regAddr1);
int16_t regValue2 = readRegister(regAddr2);
writeRegister(data::Registers::ACC, regValue1 * regValue2);
}
break;
case data::OpCodes::IncReg:
{
uint8_t regAddr = fetch8();
int16_t regValue = readRegister(regAddr);
writeRegister(regAddr, regValue + 1);
}
break;
case data::OpCodes::DecReg:
{
uint8_t regAddr = fetch8();
int16_t regValue = readRegister(regAddr);
writeRegister(regAddr, regValue - 1);
}
break;
case data::OpCodes::RShiftRegImm:
{
int16_t literal = fetch16();
uint8_t regAddr = fetch8();
int16_t regValue = readRegister(regAddr);
regValue = regValue >> literal;
writeRegister(regAddr, regValue);
}
break;
case data::OpCodes::RShiftRegReg:
{
uint8_t shiftRegAddr = fetch8();
uint8_t regAddr = fetch8();
int16_t shiftValue = readRegister(shiftRegAddr);
int16_t regValue = readRegister(regAddr);
regValue = regValue >> shiftValue;
writeRegister(regAddr, regValue);
}
break;
case data::OpCodes::LShiftRegImm:
{
int16_t literal = fetch16();
uint8_t regAddr = fetch8();
int16_t regValue = readRegister(regAddr);
regValue = regValue << literal;
writeRegister(regAddr, regValue);
}
break;
case data::OpCodes::LShiftRegReg:
{
uint8_t shiftRegAddr = fetch8();
uint8_t regAddr = fetch8();
int16_t shiftValue = readRegister(shiftRegAddr);
int16_t regValue = readRegister(regAddr);
regValue = regValue << shiftValue;
writeRegister(regAddr, regValue);
}
break;
case data::OpCodes::AndRegImm:
{
int16_t literal = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
writeRegister(data::Registers::ACC, value & literal);
}
break;
case data::OpCodes::AndRegReg:
{
uint8_t andRegAddr = fetch8();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t andValue = readRegister(andRegAddr);
writeRegister(data::Registers::ACC, value & andValue);
}
break;
case data::OpCodes::OrRegImm:
{
int16_t literal = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
writeRegister(data::Registers::ACC, value | literal);
}
break;
case data::OpCodes::OrRegReg:
{
uint8_t andRegAddr = fetch8();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t andValue = readRegister(andRegAddr);
writeRegister(data::Registers::ACC, value | andValue);
}
break;
case data::OpCodes::XorRegImm:
{
int16_t literal = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
writeRegister(data::Registers::ACC, value ^ literal);
}
break;
case data::OpCodes::XorRegReg:
{
uint8_t andRegAddr = fetch8();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t andValue = readRegister(andRegAddr);
writeRegister(data::Registers::ACC, value ^ andValue);
}
break;
case data::OpCodes::NotReg:
{
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
writeRegister(data::Registers::ACC, ~value);
}
break;
case data::OpCodes::JmpNotEqImm:
{
uint16_t addr = fetch16();
int16_t value = fetch16();
int16_t accValue = readRegister(data::Registers::ACC);
if (value != accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpNotEqReg:
{
uint16_t addr = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t accValue = readRegister(data::Registers::ACC);
if (value != accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpEqImm:
{
uint16_t addr = fetch16();
int16_t value = fetch16();
int16_t accValue = readRegister(data::Registers::ACC);
if (value == accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpEqReg:
{
uint16_t addr = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t accValue = readRegister(data::Registers::ACC);
if (value == accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpGrImm:
{
uint16_t addr = fetch16();
int16_t value = fetch16();
int16_t accValue = readRegister(data::Registers::ACC);
if (value > accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpGrReg:
{
uint16_t addr = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t accValue = readRegister(data::Registers::ACC);
if (value > accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpLessImm:
{
uint16_t addr = fetch16();
int16_t value = fetch16();
int16_t accValue = readRegister(data::Registers::ACC);
if (value < accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpLessReg:
{
uint16_t addr = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t accValue = readRegister(data::Registers::ACC);
if (value < accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpGeImm:
{
uint16_t addr = fetch16();
int16_t value = fetch16();
int16_t accValue = readRegister(data::Registers::ACC);
if (value >= accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpGeReg:
{
uint16_t addr = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t accValue = readRegister(data::Registers::ACC);
if (value >= accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpLeImm:
{
uint16_t addr = fetch16();
int16_t value = fetch16();
int16_t accValue = readRegister(data::Registers::ACC);
if (value <= accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::JmpLeReg:
{
uint16_t addr = fetch16();
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
int16_t accValue = readRegister(data::Registers::ACC);
if (value <= accValue)
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::Jmp:
{
uint16_t addr = fetch16();
writeRegister(data::Registers::IP, addr);
}
break;
case data::OpCodes::Halt:
{
m_halt = true;
return false;
}
break;
case data::OpCodes::PushImm:
{
int16_t value = fetch16();
pushToStack(value);
}
break;
case data::OpCodes::PushReg:
{
uint8_t regAddr = fetch8();
int16_t value = readRegister(regAddr);
pushToStack(value);
}
break;
case data::OpCodes::PopReg:
{
uint8_t regAddr = fetch8();
int16_t value = popFromStack();
writeRegister(regAddr, value);
}
break;
case data::OpCodes::CallImm:
{
uint16_t subroutineAddr = fetch16();
pushStackFrame();
writeRegister(data::Registers::IP, subroutineAddr);
}
break;
case data::OpCodes::CallReg:
{
uint8_t regAddr = fetch8();
uint16_t subroutineAddr = readRegister(regAddr);
pushStackFrame();
writeRegister(data::Registers::IP, subroutineAddr);
}
break;
case data::OpCodes::Ret:
{
popStackFrame();
}
break;
case data::OpCodes::RetInt:
{
m_isInInterruptHandler = false;
popStackFrame();
}
break;
case data::OpCodes::Int:
{
uint8_t intValue = fetch8();
if (!readFlag(data::Flags::InterruptsEnabled))
return true;
handleInterrupt(intValue);
}
break;
default:
{
data::ErrorHandler::pushError(data::ErrorCodes::CPU_UnknownInstruction, ostd::StringEditor("Unknown instruction: ").add(ostd::Utils::getHexStr(inst, true, 1)).str());
m_halt = true;
return false;
}
}
return true;
}
}
}

View file

@ -0,0 +1,54 @@
#pragma once
#include <ostd/Types.hpp>
#include <ostd/Utils.hpp>
#include <ostd/Bitfields.hpp>
#include "IMemoryDevice.hpp"
namespace dragon
{
class DragonRuntime;
namespace hw
{
class VirtualCPU
{
public:
VirtualCPU(IMemoryDevice& memory);
int16_t readRegister(uint8_t reg);
int16_t writeRegister(uint8_t reg, int16_t value);
int8_t fetch8(void);
int16_t fetch16(void);
void pushToStack(int16_t value);
int16_t popFromStack(void);
void pushStackFrame(void);
void popStackFrame(void);
bool readFlag(uint8_t flg);
void setFlag(uint8_t flg, bool val = true);
void handleInterrupt(uint8_t intValue);
bool execute(void);
inline bool isHalted(void) const { return m_halt; }
inline uint8_t getCurrentInstruction(void) const { return m_currentInst; }
inline bool isInDebugBreakPoint(void) const { return m_isDebugBreakPoint; }
private:
int16_t m_registers[20];
ostd::BitField_16 m_tempFlags;
IMemoryDevice& m_memory;
uint16_t m_stackFrameSize { 0 };
bool m_halt { false };
uint8_t m_currentInst { 0x00 };
bool m_biosMode { true };
bool m_isInInterruptHandler { false };
bool m_isDebugBreakPoint { false };
friend class dragon::DragonRuntime;
};
}
}

View file

@ -0,0 +1,106 @@
#include "VirtualHardDrive.hpp"
#include "../tools/GlobalData.hpp"
#include <iostream>
namespace dragon
{
namespace hw
{
void VirtualHardDrive::init(const ostd::String& dataFilePath)
{
m_dataFile.open(dataFilePath, std::ios::out | std::ios::in | std::ios::binary);
if(!m_dataFile)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_UnableToMount, ostd::StringEditor("Unable to mount virtual HardDrive: ").add(dataFilePath).str());
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 );
m_diskID = s_nextDiskID++;
m_initialized = true;
}
bool VirtualHardDrive::read(uint32_t addr, uint16_t size, ostd::ByteStream& outData)
{
if (!m_initialized)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_Uninitialized, "Attempt to read uninitialized drive.");
return false;
}
if (addr + size > m_fileSize)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_ReadOverflow, "Read Overflow on HardDrive.");
return false;
}
uint8_t cell = 0;
outData.clear();
m_dataFile.seekg(addr);
for (int32_t i = 0; i < size; i++)
{
m_dataFile.read((char*)&cell, sizeof(cell));
outData.push_back(cell);
}
return true;
}
bool VirtualHardDrive::write(uint32_t addr, int8_t value)
{
if (!m_initialized)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_Uninitialized, "Attempt to read uninitialized drive.");
return false;
}
if (addr >= m_fileSize)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_WriteOverflow, "Write Overflow on HardDrive.");
return false;
}
m_dataFile.seekp(addr);
m_dataFile.write((char*)(&value), sizeof(value));
return true;
}
void VirtualHardDrive::bufferedWrite(int8_t value)
{
if (!m_initialized)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_Uninitialized, "Attempt to read uninitialized drive.");
return;
}
m_writeBuffer.push_back(value);
}
bool VirtualHardDrive::writeBuffer(uint32_t addr)
{
if (!m_initialized)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_Uninitialized, "Attempt to read uninitialized drive.");
return false;
}
if (m_writeBuffer.size() == 0)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_EmptyBuffer, "Buffered Write empty buffer on HardDrive.");
return false;
}
if (addr + m_writeBuffer.size() > m_fileSize)
{
data::ErrorHandler::pushError(data::ErrorCodes::HardDrive_BuffWriteOverflow, "Buffered Write Overflow on HardDrive.");
return false;
}
m_dataFile.seekp(addr);
m_dataFile.write((char*)(&m_writeBuffer[0]), m_writeBuffer.size());
m_writeBuffer.clear();
return true;
}
void VirtualHardDrive::unmount(void)
{
if (!m_initialized) return;
m_dataFile.close();
m_initialized = false;
}
}
}

View file

@ -0,0 +1,38 @@
#pragma once
#include <fstream>
#include <ostd/Utils.hpp>
namespace dragon
{
namespace hw
{
class VirtualHardDrive
{
public:
inline VirtualHardDrive(void) { m_initialized = false; }
inline VirtualHardDrive(const ostd::String& dataFilePath) { init(dataFilePath); }
void init(const ostd::String& dataFilePath);
bool read(uint32_t addr, uint16_t size, ostd::ByteStream& outData);
bool write(uint32_t addr, int8_t value);
void bufferedWrite(int8_t value);
bool writeBuffer(uint32_t addr);
void unmount(void);
inline bool isInitialized(void) const { return m_initialized; }
inline uint64_t getSize(void) const { return m_fileSize; };
inline bool isSame(VirtualHardDrive& vhdd) { return m_diskID == vhdd.m_diskID; }
private:
std::fstream m_dataFile;
bool m_initialized { false };
uint64_t m_fileSize { 0 };
ostd::ByteStream m_writeBuffer;
uint32_t m_diskID { 0 };
inline static uint32_t s_nextDiskID = 0;
};
}
}

View file

@ -0,0 +1,655 @@
#include "VirtualIODevices.hpp"
#include <ostd/Utils.hpp>
#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() != 1024)
data::ErrorHandler::pushError(data::ErrorCodes::BIOS_InvalidSize, ostd::StringEditor("Invalid BIOS size: ").add(ostd::Utils::getHexStr(m_bios.size(), true, 2)).str());
m_initialized = true;
}
int8_t VirtualBIOS::read8(uint16_t addr)
{
if (addr >= m_bios.size())
data::ErrorHandler::pushError(data::ErrorCodes::BIOS_InvalidAddress, ostd::StringEditor("Invalid Byte BIOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word BIOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Byte IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Byte MBR location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word MBR location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word IntVector location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
m_mbr[addr + 0] = (value >> 8) & 0xFF;
m_mbr[addr + 1] = value & 0xFF;
return value;
}
ostd::ByteStream* VirtualBootloader::getByteStream(void)
{
return &m_mbr;
}
VirtualBIOSVideo::VirtualBIOSVideo(VirtualRAM& memory) : m_memory(memory)
{
intptr_t iMemPtr = reinterpret_cast<intptr_t>(memory.getByteStream()->data());
iMemPtr += data::MemoryMapAddresses::BIOSVideo_Start;
m_dataPtr = reinterpret_cast<ostd::Byte*>(iMemPtr);
}
int8_t VirtualBIOSVideo::read8(uint16_t addr)
{
if (addr >= m_size)
data::ErrorHandler::pushError(data::ErrorCodes::BIOSVideo_InvalidAddress, ostd::StringEditor("Invalid read Byte BiosVideo location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
return m_dataPtr[addr];
}
int16_t VirtualBIOSVideo::read16(uint16_t addr)
{
if (addr >= m_size - 1)
data::ErrorHandler::pushError(data::ErrorCodes::BIOSVideo_InvalidAddress, ostd::StringEditor("Invalid read Word BiosVideo location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
return ((m_dataPtr[addr + 0] << 8) & 0xFF00U)
| ( m_dataPtr[addr + 1] & 0x00FFU);
}
int8_t VirtualBIOSVideo::write8(uint16_t addr, int8_t value)
{
if (addr >= m_size)
data::ErrorHandler::pushError(data::ErrorCodes::BIOSVideo_InvalidAddress, ostd::StringEditor("Invalid read Byte BiosVideo location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
m_dataPtr[addr] = value;
return value;
}
int16_t VirtualBIOSVideo::write16(uint16_t addr, int16_t value)
{
if (addr >= m_size - 1)
data::ErrorHandler::pushError(data::ErrorCodes::BIOSVideo_InvalidAddress, ostd::StringEditor("Invalid read Word BiosVideo location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
m_dataPtr[addr + 0] = (value >> 8) & 0xFF;
m_dataPtr[addr + 1] = value & 0xFF;
return value;
}
ostd::ByteStream* VirtualBIOSVideo::getByteStream(void)
{
m_dataCopy.clear();
m_dataCopy.insert(m_dataCopy.end(), &m_dataPtr[0], &m_dataPtr[m_size]);
return &m_dataCopy;
}
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: <signal> register must be set to <ignore> 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: <status> register set to <free> 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: <mode> must be set to <read> or <write> 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, 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::StringEditor("Invalid virtual CMOS chhip size: ").addi(m_fileSize).str());
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::StringEditor("Invalid Byte CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Byte CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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::StringEditor("Invalid Word CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2)).str());
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;
}
}
}
}

View file

@ -0,0 +1,237 @@
#pragma once
#include "IMemoryDevice.hpp"
#include "../tools/GlobalData.hpp"
#include <ostd/Serial.hpp>
#include <fstream>
namespace dragon
{
namespace hw
{
class VirtualHardDrive;
class MemoryMapper;
class VirtualCPU;
class VirtualRAM;
class VirtualBIOS : public IMemoryDevice
{
public:
inline VirtualBIOS(void) { m_initialized = 0; }
inline VirtualBIOS(const ostd::String& biosFilePath) { init(biosFilePath); }
void init(const ostd::String& biosFilePath);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
ostd::ByteStream m_bios;
bool m_initialized { false };
};
class InterruptVector : public IMemoryDevice
{
public:
InterruptVector(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
ostd::ByteStream m_data;
};
class VirtualKeyboard : public IMemoryDevice
{
public:
VirtualKeyboard(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
};
class VirtualMouse : public IMemoryDevice
{
public:
VirtualMouse(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
};
class VirtualBootloader : public IMemoryDevice
{
public:
VirtualBootloader(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
ostd::ByteStream m_mbr;
};
class VirtualBIOSVideo : public IMemoryDevice
{
public: struct tRegisters
{
inline static constexpr uint16_t ClearColor = 0x00;
inline static constexpr uint16_t Palette = 0x01;
inline static constexpr uint16_t UseTransparencyOn0x5 = 0x02;
inline static constexpr uint16_t Signal = 0x03;
inline static constexpr uint16_t VideoMemoryStart = 0x003C;
};
public:
VirtualBIOSVideo(VirtualRAM& memory);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
uint16_t m_baseAddress { data::MemoryMapAddresses::BIOSVideo_Start };
uint16_t m_size { (data::MemoryMapAddresses::BIOSVideo_End + 1) - data::MemoryMapAddresses::BIOSVideo_Start };
ostd::Byte* m_dataPtr { nullptr };
ostd::ByteStream m_dataCopy;
VirtualRAM& m_memory;
};
namespace interface
{
class Disk : public IMemoryDevice
{
public: struct tRegisters
{
inline static constexpr uint16_t Signal = 0x00;
inline static constexpr uint16_t ModeSelector = 0x01;
inline static constexpr uint16_t DiskSelector = 0x02;
inline static constexpr uint16_t SectorSelector = 0x03;
inline static constexpr uint16_t AddressSelector = 0x05;
inline static constexpr uint16_t DataSize = 0x07;
inline static constexpr uint16_t DataSourceAddress = 0x09;
inline static constexpr uint16_t FirstReadOnly = 0x0B;
inline static constexpr uint16_t Status = 0x0B;
inline static constexpr uint16_t CurrentDisk = 0x0C;
inline static constexpr uint16_t CurrentSector = 0x0D;
inline static constexpr uint16_t CurrentAddress = 0x0F;
inline static constexpr uint16_t RestDataSize = 0x11;
inline static constexpr uint16_t SourceData = 0x13;
};
public: struct tSignalValues
{
inline static constexpr uint8_t Start = 0x00;
inline static constexpr uint8_t Cancel = 0x01;
inline static constexpr uint8_t Ignore = 0xFF;
};
public: struct tModeValues
{
inline static constexpr uint8_t Read = 0x00;
inline static constexpr uint8_t Write = 0x01;
};
public: struct tStatusValues
{
inline static constexpr uint8_t Free = 0x00;
inline static constexpr uint8_t Writing = 0x01;
inline static constexpr uint8_t Reading = 0x02;
};
public:
Disk(MemoryMapper& memory, VirtualCPU& cpu);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
void cycleStep(void);
data::VDiskID connectDisk(VirtualHardDrive& hdd);
bool disconnectDisk(data::VDiskID diskID);
inline bool isBusy(void) const { return m_busy; }
private:
ostd::serial::SerialIO m_data { data::MemoryMapAddresses::DiskInterface_End - data::MemoryMapAddresses::DiskInterface_Start };
bool m_busy { false };
std::unordered_map<data::VDiskID, VirtualHardDrive*> m_connectedDisks;
MemoryMapper& m_memory;
VirtualCPU& m_cpu;
inline static data::VDiskID s_nextDiskID = 0;
};
class Graphics : public IMemoryDevice
{
public:
Graphics(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
};
class SerialPort : public IMemoryDevice
{
public:
SerialPort(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
};
class CMOS : public IMemoryDevice
{
public:
inline CMOS(void) { m_initialized = false; }
inline CMOS(const ostd::String& cmosFilePath) { init(cmosFilePath); }
void init(const ostd::String& cmosFilePath);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
ostd::ByteStream m_data;
uint16_t m_size { 0 };
std::fstream m_dataFile;
bool m_initialized { false };
uint64_t m_fileSize { 0 };
};
}
}
}

View file

@ -0,0 +1,45 @@
#include "VirtualRAM.hpp"
#include "../tools/GlobalData.hpp"
namespace dragon
{
namespace hw
{
VirtualRAM::VirtualRAM(void)
{
m_memory.init(0xFFFF);
}
int8_t VirtualRAM::read8(uint16_t addr)
{
int8_t outVal = 0;
if (!m_memory.r_Byte(addr, outVal)) return 0x00; //TODO: Error
return outVal;
}
int16_t VirtualRAM::read16(uint16_t addr)
{
int16_t outVal = 0;
if (!m_memory.r_Word(addr, outVal)) return 0x00; //TODO: Error
return outVal;
}
int8_t VirtualRAM::write8(uint16_t addr, int8_t value)
{
if (!m_memory.w_Byte(addr, value)) return 0; //TODO: Error
return value;
}
int16_t VirtualRAM::write16(uint16_t addr, int16_t value)
{
if (!m_memory.w_Word(addr, value)) return 0; //TODO: Error
return value;
}
ostd::ByteStream* VirtualRAM::getByteStream(void)
{
return &m_memory.getData();
}
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include <ostd/Serial.hpp>
#include "IMemoryDevice.hpp"
namespace dragon
{
namespace hw
{
class VirtualRAM : public IMemoryDevice
{
public:
VirtualRAM(void);
int8_t read8(uint16_t addr) override;
int16_t read16(uint16_t addr) override;
int8_t write8(uint16_t addr, int8_t value) override;
int16_t write16(uint16_t addr, int16_t value) override;
ostd::ByteStream* getByteStream(void) override;
private:
ostd::serial::SerialIO m_memory;
};
}
}

View file

@ -0,0 +1,53 @@
#include "ConfigLoader.hpp"
#include <ostd/File.hpp>
#include <ostd/Utils.hpp>
namespace dragon
{
const tMachineConfig MachineConfigLoader::loadConfig(const ostd::String& configFilePath)
{
tMachineConfig config;
ostd::TextFileBuffer file(configFilePath);
if (!file.exists()) return config;
auto lines = file.getLines();
for (auto& line : lines)
{
ostd::StringEditor 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.str() == "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.str();
}
}
else if (lineEdit.str() == "bios")
{
lineEdit = tokens.next();
config.bios_path = lineEdit.str();
}
else if (lineEdit.str() == "cmos")
{
lineEdit = tokens.next();
config.cmos_path = lineEdit.str();
}
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,30 @@
#pragma once
#include <ostd/Types.hpp>
#include <map>
namespace dragon
{
struct tMachineConfig
{
std::map<int32_t, ostd::String> vdisk_paths;
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);
};
}

View file

@ -0,0 +1,382 @@
#include "DragonRuntime.hpp"
namespace dragon
{
void DragonRuntime::printRegisters(dragon::hw::VirtualCPU& cpu)
{
out.col("green").p("IP: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::IP), true, 2));
out.p(" ");
out.col("yellow").p("R1: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R1), true, 2));
out.p(" ");
out.col("yellow").p("R2: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R2), true, 2)).nl();
out.col("green").p("SP: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::SP), true, 2));
out.p(" ");
out.col("yellow").p("R3: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R3), true, 2));
out.p(" ");
out.col("yellow").p("R4: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R4), true, 2)).nl();
out.col("green").p("FP: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::FP), true, 2));
out.p(" ");
out.col("yellow").p("R5: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R5), true, 2));
out.p(" ");
out.col("yellow").p("R6: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R6), true, 2)).nl();
out.col("green").p("RV: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::RV), true, 2));
out.p(" ");
out.col("yellow").p("R7: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R7), true, 2));
out.p(" ");
out.col("yellow").p("R8: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R8), true, 2)).nl();
out.col("green").p("PP: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::PP), true, 2));
out.p(" ");
out.col("yellow").p("R9: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R9), true, 2));
out.p(" ");
out.col("yellow").p("R10: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::R10), true, 2)).nl();
out.col("green").p("ACC: ").col("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::ACC), true, 2));
out.p(" ");
out.col("yellow").p("FL: ").col("white").p(ostd::Utils::getBinStr(cpu.readRegister(dragon::data::Registers::FL), true, 2));
}
void DragonRuntime::processErrors(void)
{
while (dragon::data::ErrorHandler::hasError())
{
auto err = dragon::data::ErrorHandler::popError();
out.nl().col(ostd::legacy::ConsoleCol::Red).p("Error ").p(ostd::Utils::getHexStr(err.code, true, 8)).p(": ").p(err.text).nl();
}
}
std::vector<data::ErrorHandler::tError> DragonRuntime::getErrorList(void)
{
std::vector<data::ErrorHandler::tError> list;
if (!hasError())
return list;
while (dragon::data::ErrorHandler::hasError())
{
auto err = dragon::data::ErrorHandler::popError();
list.push_back(err);
}
return list;
}
int32_t DragonRuntime::initMachine(const ostd::String& configFilePath, bool verbose, bool trackMachineInfoDiff, bool hideVirtualDisplay)
{
if (verbose)
out.p("Loading machine config: ").p(configFilePath).nl();
machine_config = dragon::MachineConfigLoader::loadConfig(configFilePath);
if (!machine_config.isValid()) return 1; //TODO: Error
if (verbose)
out.p(" Initializing virtual display:").nl();
vDisplay.initialize(800, 600, "font.bmp");
vDisplay.addWidget(vConsWidg);
vDisplay.setSize(vConsWidg.getw(), vConsWidg.geth());
if (hideVirtualDisplay)
vDisplay.hide();
if (verbose)
{
out.p(" Done. (").pi((int)vConsWidg.getw()).p("x").pi((int)vConsWidg.geth()).p(")");
if (hideVirtualDisplay)
out.p(" - HIDDEN").nl();
else
out.nl();
}
if (machine_config.vdisk_paths.size() == 0) return 2; //TODO: Error
if (verbose)
out.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]);
if (verbose)
out.p(" Disk").pi(disk_path.first).p(" connected: ").p(disk_path.second).nl();
}
if (verbose)
out.p(" Loading vBIOS file: ").p(machine_config.bios_path).nl();
vBIOS.init(machine_config.bios_path);
if (verbose)
out.p(" Loading vCMOS file: ").p(machine_config.cmos_path).nl();
vCMOS.init(machine_config.cmos_path);
if (verbose)
{
out.p(" Initializing Memory Mapper:").nl();
out.p(" vBIOS: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::BIOS_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::BIOS_End, true, 2));
out.p(" (remap=false)").nl();
}
memMap.mapDevice(vBIOS, dragon::data::MemoryMapAddresses::BIOS_Start, dragon::data::MemoryMapAddresses::BIOS_End, false, "vBIOS");
if (verbose)
{
out.p(" vCMOS: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::CMOS_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::CMOS_End, true, 2));
out.p(" (remap=true)").nl();
}
memMap.mapDevice(vCMOS, dragon::data::MemoryMapAddresses::CMOS_Start, dragon::data::MemoryMapAddresses::CMOS_End, true, "vCMOS");
if (verbose)
{
out.p(" intVec: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::IntVector_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::IntVector_End, true, 2));
out.p(" (remap=true)").nl();
}
memMap.mapDevice(intVec, dragon::data::MemoryMapAddresses::IntVector_Start, dragon::data::MemoryMapAddresses::IntVector_End, true, "intVec");
if (verbose)
{
out.p(" vKeyboard: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Keyboard_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Keyboard_End, true, 2));
out.p(" (remap=false)").nl();
}
memMap.mapDevice(vKeyboard, dragon::data::MemoryMapAddresses::Keyboard_Start, dragon::data::MemoryMapAddresses::Keyboard_End, false, "vKeyboard");
if (verbose)
{
out.p(" vMouse: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Mouse_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Mouse_End, true, 2));
out.p(" (remap=false)").nl();
}
memMap.mapDevice(vMouse, dragon::data::MemoryMapAddresses::Mouse_Start, dragon::data::MemoryMapAddresses::Mouse_End, false, "vMouse");
if (verbose)
{
out.p(" vBIOSVideo: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::BIOSVideo_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::BIOSVideo_End, true, 2));
out.p(" (remap=true)").nl();
}
memMap.mapDevice(vBIOSVideo, dragon::data::MemoryMapAddresses::BIOSVideo_Start, dragon::data::MemoryMapAddresses::BIOSVideo_End, true, "vBIOSVideo");
if (verbose)
{
out.p(" vMBR: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::MBR_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::MBR_End, true, 2));
out.p(" (remap=true)").nl();
}
memMap.mapDevice(vMBR, dragon::data::MemoryMapAddresses::MBR_Start, dragon::data::MemoryMapAddresses::MBR_End, true, "vMBR");
if (verbose)
{
out.p(" vDiskInterface: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::DiskInterface_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::DiskInterface_End, true, 2));
out.p(" (remap=true)").nl();
}
memMap.mapDevice(vDiskInterface, dragon::data::MemoryMapAddresses::DiskInterface_Start, dragon::data::MemoryMapAddresses::DiskInterface_End, true, "vDiskInterface");
if (verbose)
{
out.p(" vGraphicsInterface: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::VideoCardInterface_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::VideoCardInterface_End, true, 2));
out.p(" (remap=true)").nl();
}
memMap.mapDevice(vGraphicsInterface, dragon::data::MemoryMapAddresses::VideoCardInterface_Start, dragon::data::MemoryMapAddresses::VideoCardInterface_End, true, "vGraphicsInterface");
if (verbose)
{
out.p(" vSerialInterface: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::SerialInterface_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::SerialInterface_End, true, 2));
out.p(" (remap=true)").nl();
}
memMap.mapDevice(vSerialInterface, dragon::data::MemoryMapAddresses::SerialInterface_Start, dragon::data::MemoryMapAddresses::SerialInterface_End, true, "vSerialInterface");
if (verbose)
{
out.p(" RAM: ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Memory_Start, true, 2));
out.p(" to ");
out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Memory_End, true, 2));
out.p(" (remap=false)").nl();
}
memMap.mapDevice(ram, dragon::data::MemoryMapAddresses::Memory_Start, dragon::data::MemoryMapAddresses::Memory_End, false, "RAM");
//Default VideoBios Colors
uint8_t default_bios_video_color = 0x4;
if (verbose)
{
out.p(" Initializing vCPU reset sequence:").nl();
out.p(" BIOSVideo default colors: ").p(ostd::Utils::getHexStr(default_bios_video_color)).nl();
}
memMap.write8(dragon::data::MemoryMapAddresses::BIOSVideo_Start, default_bios_video_color);
uint16_t reset_ip_addr = 0x0000;
if (verbose)
out.p(" Reset IP register: ").p(ostd::Utils::getHexStr(reset_ip_addr, true, 2)).nl();
cpu.writeRegister(dragon::data::Registers::IP, reset_ip_addr);
out.nl().nl();
s_trackMachineInfo = trackMachineInfoDiff;
return 0;
}
void DragonRuntime::runMachine(int32_t cycleLimit, bool basic_debug, bool step_exec)
{
int32_t cycleCounter = 0;
bool running = true;
uint8_t currentInst = 0x00;
uint32_t instSize = 0;
ostd::ByteStream& ramData = *ram.getByteStream();
while ((running || vDiskInterface.isBusy()) && cycleCounter++ < 2048)
{
out.clear();
uint16_t addr = cpu.readRegister(dragon::data::Registers::IP);
uint16_t spAddr = cpu.readRegister(dragon::data::Registers::SP);
running = cpu.execute() && vDisplay.isRunning();
vDisplay.update();
currentInst = cpu.getCurrentInstruction();
vDiskInterface.cycleStep();
ostd::ConsoleOutputHandler newOut; //TODO: This workaround is not good, this whole file needs to be updated to not use the legacy ConsoleOutputHandler
if ((cpu.isInDebugBreakPoint() || step_exec) && basic_debug)
{
instSize = dragon::data::OpCodes::getInstructionSIze(currentInst);
ostd::Utils::printByteStream(ramData, dragon::data::MemoryMapAddresses::Memory_Start, 16, 10, newOut, addr, instSize, " MEMORY");
ostd::Utils::printByteStream(*vBIOSVideo.getByteStream(), 0x0000, 16, 16, newOut, -1, 1, " Bios Video");
printRegisters(cpu);
out.nl().col("yellow").p("Current Instruction: ").col("white").p(ostd::Utils::getHexStr(currentInst, true, 1));
out.nl();
out.col(ostd::legacy::ConsoleCol::Magenta).p("######### Reached Break Point at ");
out.p(ostd::Utils::getHexStr(addr, true, 2));
out.nl().resetColors();
std::cin.get();
continue;
}
else if (dragon::data::ErrorHandler::hasError())
{
if (!basic_debug)
{
processErrors();
continue;
}
instSize = dragon::data::OpCodes::getInstructionSIze(currentInst);
ostd::Utils::printByteStream(ramData, dragon::data::MemoryMapAddresses::Memory_Start, 16, 10, newOut, addr, instSize, " MEMORY");
ostd::Utils::printByteStream(*vBIOSVideo.getByteStream(), 0x0000, 16, 16, newOut, -1, 1, " Bios Video");
printRegisters(cpu);
out.nl().col("yellow").p("Current Instruction: ").col("white").p(ostd::Utils::getHexStr(currentInst, true, 1));
out.nl();
out.col(ostd::legacy::ConsoleCol::Red).p("######### Error occurred at ");
out.p(ostd::Utils::getHexStr(addr, true, 2));
out.nl().resetColors();
processErrors();
std::cin.get();
continue;
}
}
}
bool DragonRuntime::runStep(std::vector<uint16_t> trackedAddresses)
{
__get_machine_footprint(&s_machineInfo, trackedAddresses, true);
bool running = cpu.execute() && vDisplay.isRunning();
vDisplay.update();
vDiskInterface.cycleStep();
__get_machine_footprint(&s_machineInfo, trackedAddresses, false);
return running || vDiskInterface.isBusy();
}
void DragonRuntime::forceLoad(const ostd::String& filePath, uint16_t loadAddress)
{
ostd::ByteStream code;
ostd::Utils::loadByteStreamFromFile(filePath, code);
int16_t index = 0;
for (auto& b : code)
{
ram.write8(dragon::data::MemoryMapAddresses::Memory_Start + loadAddress + index, b);
index++;
}
}
void DragonRuntime::__get_machine_footprint(DragonRuntime::tMachineDebugInfo* machineInfo, std::vector<uint16_t> trackedAddresses, bool previous)
{
if (!s_trackMachineInfo || machineInfo == nullptr) return;
auto& minfo = *machineInfo;
if (previous)
{
minfo.vCPUHalt = cpu.m_halt;
minfo.trackedAddresses.clear();
minfo.previousInstructionTrackedValues.clear();
minfo.currentInstructionTrackedValues.clear();
for (int32_t i = 0; i < 20; i++)
{
if (i < 5)
{
minfo.previousInstructionFootprint[i] = 0;
minfo.currentInstructionFootprint[i] = 0;
}
minfo.previousInstructionRegisters[i] = 0;
minfo.currentInstructionRegisters[i] = 0;
}
for (auto& addr : trackedAddresses)
minfo.trackedAddresses.push_back(addr);
}
uint16_t instAddr = cpu.readRegister(data::Registers::IP);
uint8_t instSize = data::OpCodes::getInstructionSIze(memMap.read8(instAddr));
ostd::String opCode = data::OpCodes::getOpCodeString(memMap.read8(instAddr));
uint16_t stackFrameSize = cpu.m_stackFrameSize;
bool debugBreak = cpu.m_isDebugBreakPoint;
bool intHandler = cpu.m_isInInterruptHandler;
bool biosMode = cpu.m_biosMode;
if (previous)
{
minfo.previousInstructionAddress = instAddr;
minfo.previousInstructionFootprintSize = instSize;
minfo.previousInstructionStackFrameSize = stackFrameSize;
minfo.previousInstructionOpCode = opCode;
for (int8_t i = 0; i < instSize; i++)
minfo.previousInstructionFootprint[i] = memMap.read8(instAddr + i);
for (int8_t i = 0; i < 20; i++)
minfo.previousInstructionRegisters[i] = cpu.readRegister(i);
for (auto& addr : minfo.trackedAddresses)
minfo.previousInstructionTrackedValues.push_back(memMap.read8(addr));
minfo.previousInstructionDebugBreak = debugBreak;
minfo.previousInstructionInterruptHandler = intHandler;
minfo.previousInstructionBiosMode = biosMode;
}
else
{
minfo.currentInstructionAddress = instAddr;
minfo.currentInstructionFootprintSize = instSize;
minfo.currentInstructionStackFrameSize = stackFrameSize;
minfo.currentInstructionOpCode = opCode;
for (int8_t i = 0; i < instSize; i++)
minfo.currentInstructionFootprint[i] = memMap.read8(instAddr + i);
for (int8_t i = 0; i < 20; i++)
minfo.currentInstructionRegisters[i] = cpu.readRegister(i);
for (auto& addr : minfo.trackedAddresses)
minfo.currentInstructionTrackedValues.push_back(memMap.read8(addr));
minfo.currentInstructionDebugBreak = debugBreak;
minfo.currentInstructionInterruptHandler = intHandler;
minfo.currentInstructionBiosMode = biosMode;
}
}
}

View file

@ -0,0 +1,94 @@
#pragma once
#include "../gui/Window.hpp"
#include "../gui/widgets/VirtualConsoleWidget.hpp"
#include "../hardware/VirtualCPU.hpp"
#include "../hardware/MemoryMapper.hpp"
#include "../hardware/VirtualRAM.hpp"
#include "../hardware/VirtualIODevices.hpp"
#include "../hardware/VirtualHardDrive.hpp"
#include "../tools/GlobalData.hpp"
#include "ConfigLoader.hpp"
namespace dragon
{
class DragonRuntime
{
public: struct tMachineDebugInfo {
inline tMachineDebugInfo(void) { }
uint16_t previousInstructionAddress { 0x0000 };
uint16_t currentInstructionAddress { 0x0000 };
int8_t previousInstructionFootprintSize { 0x00 };
int8_t currentInstructionFootprintSize { 0x00 };
uint16_t previousInstructionStackFrameSize { 0x00 };
uint16_t currentInstructionStackFrameSize { 0x00 };
ostd::String previousInstructionOpCode { "" };
ostd::String currentInstructionOpCode { "" };
int8_t previousInstructionFootprint[5] { 0x00, 0x00, 0x00, 0x00, 0x00 };
int8_t currentInstructionFootprint[5] { 0x00, 0x00, 0x00, 0x00, 0x00 };
int16_t previousInstructionRegisters[20] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
int16_t currentInstructionRegisters[20] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
std::vector<uint16_t> trackedAddresses;
std::vector<int8_t> previousInstructionTrackedValues;
std::vector<int8_t> currentInstructionTrackedValues;
bool previousInstructionDebugBreak { false };
bool currentInstructionDebugBreak { false };
bool previousInstructionInterruptHandler { false };
bool currentInstructionInterruptHandler { false };
bool previousInstructionBiosMode { false };
bool currentInstructionBiosMode { false };
bool vCPUHalt { false };
};
public:
static void printRegisters(dragon::hw::VirtualCPU& cpu);
static void processErrors(void);
static std::vector<data::ErrorHandler::tError> getErrorList(void);
static int32_t initMachine(const ostd::String& configFilePath, bool verbose = false, bool trackMachineInfoDiff = false, bool hideVirtualDisplay = false);
static void runMachine(int32_t cycleLimit, bool basic_debug, bool step_exec);
static bool runStep(std::vector<uint16_t> trackedAddresses = { });
static void forceLoad(const ostd::String& filePath, uint16_t loadAddress);
inline static const tMachineDebugInfo& getMachineInfoDiff(void) { return s_machineInfo; }
inline static bool hasError(void) { return data::ErrorHandler::hasError(); }
private:
static void __get_machine_footprint(tMachineDebugInfo* machineInfo, std::vector<uint16_t> trackedAddresses, bool previous);
public:
inline static ostd::legacy::ConsoleOutputHandler out;
inline static dragon::hw::MemoryMapper memMap;
inline static dragon::hw::VirtualCPU cpu { memMap };
inline static dragon::hw::VirtualRAM ram;
inline static dragon::hw::InterruptVector intVec;
inline static dragon::hw::VirtualBIOS vBIOS;
inline static dragon::hw::interface::CMOS vCMOS;
inline static dragon::hw::VirtualBIOSVideo vBIOSVideo { ram };
inline static dragon::hw::VirtualBootloader vMBR;
inline static dragon::hw::VirtualKeyboard vKeyboard;
inline static dragon::hw::VirtualMouse vMouse;
inline static dragon::hw::interface::Disk vDiskInterface { memMap, cpu };
inline static dragon::hw::interface::Graphics vGraphicsInterface;
inline static dragon::hw::interface::SerialPort vSerialInterface;
inline static std::unordered_map<int32_t, dragon::hw::VirtualHardDrive> vDisks;
inline static dragon::Window vDisplay;
inline static dragon::VirtualConsoleWidtget vConsWidg { vBIOSVideo, cpu };
inline static dragon::tMachineConfig machine_config;
private:
inline static tMachineDebugInfo s_machineInfo;
inline static bool s_trackMachineInfo { false };
};
}

View file

@ -0,0 +1,74 @@
#include <ostd/Utils.hpp>
#include "DragonRuntime.hpp"
ostd::ConsoleOutputHandler out;
struct tCommandLineArgs
{
ostd::String machine_config_path = "";
bool basic_debug = false;
bool step_exec = false;
bool verbose_load = false;
int32_t cycle_limit = 0;
bool force_load = false;
ostd::String force_load_file = "";
uint16_t force_load_mem_offset = 0x00;
};
int main(int argc, char** argv)
{
tCommandLineArgs args;
if (argc < 2)
{
out.fg(ostd::ConsoleColors::Red).p("Error, too few arguments.").reset().nl();
return 128;
}
else
{
args.machine_config_path = argv[1];
for (int32_t i = 2; i < argc; i++)
{
ostd::StringEditor edit(argv[i]);
if (edit.str() == "--debug")
args.basic_debug = true;
else if (edit.str() == "--step")
args.step_exec = true;
else if (edit.str() == "--verbose-load")
args.verbose_load = true;
else if (edit.str() == "--limit-cycles")
{
if (i == argc - 1)
break; //TODO: Warning
i++;
edit = argv[i];
if (!edit.isNumeric())
continue; //TODO: Error
args.cycle_limit = (int32_t)edit.toInt();
}
else if (edit.str() == "--force-load")
{
if ((argc - 1) - i < 2)
break; //TODO: Warning
i++;
args.force_load_file = argv[i];
i++;
edit = argv[i];
if (!edit.isNumeric())
continue; //TODO: Error
args.force_load_mem_offset = (uint16_t)edit.toInt();
args.force_load = true;
}
}
}
int32_t init_state = dragon::DragonRuntime::initMachine(args.machine_config_path, args.verbose_load);
if (init_state != 0) return init_state; //TODO: Error
if (args.force_load)
dragon::DragonRuntime::forceLoad(args.force_load_file, args.force_load_mem_offset);
dragon::DragonRuntime::runMachine(args.cycle_limit, args.basic_debug, args.step_exec);
return 0;
}

424
src/tools/GlobalData.hpp Normal file
View file

@ -0,0 +1,424 @@
#pragma once
#include <ostd/Types.hpp>
#include <ostd/Color.hpp>
namespace dragon
{
namespace data
{
typedef uint32_t VDiskID;
class ErrorCodes
{
public:
inline static constexpr uint64_t NoError = 0x0000000000000000;
inline static constexpr uint64_t MM_RegionNotFound = 0x1000000000000000;
inline static constexpr uint64_t CPU_UnknownInstruction = 0x2000000000000000;
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 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 IntVector_InvalidAddress = 0x7000000000000000;
};
class ErrorHandler
{
public: struct tError
{
uint64_t code;
ostd::String text;
};
public:
inline static void pushError(uint64_t code, ostd::String text) { m_errorStack.push_back({ code, text }); }
inline static bool hasError(void) { return m_errorStack.size() > 0; }
inline static tError popError(void)
{
if (m_errorStack.size() == 0)
return { ErrorCodes::NoError, "No Errors." };
tError err = m_errorStack[m_errorStack.size() - 1];
m_errorStack.pop_back();
return err;
}
private:
inline static std::vector<tError> m_errorStack;
};
class MemoryMapAddresses
{
public:
inline static constexpr uint16_t BIOS_Start = 0x0000;
inline static constexpr uint16_t BIOS_End = 0x03FF;
inline static constexpr uint16_t CMOS_Start = 0x0400;
inline static constexpr uint16_t CMOS_End = 0x047F;
inline static constexpr uint16_t IntVector_Start = 0x0480;
inline static constexpr uint16_t IntVector_End = 0x067F;
inline static constexpr uint16_t Keyboard_Start = 0x0680;
inline static constexpr uint16_t Keyboard_End = 0x075F;
inline static constexpr uint16_t Mouse_Start = 0x0760;
inline static constexpr uint16_t Mouse_End = 0x077F;
inline static constexpr uint16_t BIOSVideo_Start = 0x0780;
inline static constexpr uint16_t BIOSVideo_End = 0x167F;
inline static constexpr uint16_t MBR_Start = 0x1680;
inline static constexpr uint16_t MBR_End = 0x187F;
inline static constexpr uint16_t DiskInterface_Start = 0x1880;
inline static constexpr uint16_t DiskInterface_End = 0x18FF;
inline static constexpr uint16_t VideoCardInterface_Start = 0x1900;
inline static constexpr uint16_t VideoCardInterface_End = 0x19FF;
inline static constexpr uint16_t SerialInterface_Start = 0x1A00;
inline static constexpr uint16_t SerialInterface_End = 0x1A3F;
inline static constexpr uint16_t Memory_Start = 0x1A40;
inline static constexpr uint16_t Memory_End = 0xFFFF;
};
class IBiosVideoPalette
{
public:
virtual ostd::Color getColor(uint8_t col) = 0;
};
class BiosVideoDefaultPalette : public IBiosVideoPalette
{
public:
inline BiosVideoDefaultPalette(void)
{
m_colors.push_back({ 0, 0, 0 });
m_colors.push_back({ 157, 157, 157 });
m_colors.push_back({ 255, 255, 255 });
m_colors.push_back({ 190, 38, 51 });
m_colors.push_back({ 224, 111, 139 });
m_colors.push_back({ 73, 60, 43 });
m_colors.push_back({ 164, 100, 34 });
m_colors.push_back({ 235, 137, 49 });
m_colors.push_back({ 247, 226, 107 });
m_colors.push_back({ 47, 80, 42 });
m_colors.push_back({ 68, 137, 26 });
m_colors.push_back({ 163, 206, 39 });
m_colors.push_back({ 27, 38, 50 });
m_colors.push_back({ 0, 87, 132 });
m_colors.push_back({ 49, 162, 242 });
m_colors.push_back({ 178, 220, 239 });
}
inline ostd::Color getColor(uint8_t col) override
{
if (col >= m_colors.size()) return { 0, 0, 0 };
return m_colors[col];
}
private:
std::vector<ostd::Color> m_colors;
};
class Registers
{
public:
inline static constexpr uint8_t IP = 0x00;
inline static constexpr uint8_t SP = 0x01;
inline static constexpr uint8_t FP = 0x02;
inline static constexpr uint8_t RV = 0x03;
inline static constexpr uint8_t PP = 0x04;
inline static constexpr uint8_t FL = 0x05;
inline static constexpr uint8_t ACC = 0x06;
//TODO: Either add general purpose registers in this space or check when read/write registers
inline static constexpr uint8_t R1 = 0x0A;
inline static constexpr uint8_t R2 = 0x0B;
inline static constexpr uint8_t R3 = 0x0C;
inline static constexpr uint8_t R4 = 0x0D;
inline static constexpr uint8_t R5 = 0x0E;
inline static constexpr uint8_t R6 = 0x0F;
inline static constexpr uint8_t R7 = 0x10;
inline static constexpr uint8_t R8 = 0x11;
inline static constexpr uint8_t R9 = 0x12;
inline static constexpr uint8_t R10 = 0x13;
inline static constexpr uint8_t Last = 0x14;
};
class Flags
{
public:
inline static constexpr uint8_t InterruptsEnabled = 0;
};
class InterruptCodes
{
public:
inline static constexpr uint8_t Keyboard = 0x10;
inline static constexpr uint8_t Mouse = 0x11;
inline static constexpr uint8_t BiosInterrupt = 0x20;
inline static constexpr uint8_t DiskInterfaceFFinished = 0x80;
inline static constexpr uint8_t BiosVideoScreenRefresh = 0x81;
};
class OpCodes
{
public:
inline static constexpr uint8_t NoOp = 0x00;
inline static constexpr uint8_t DEBUG_Break = 0x01;
inline static constexpr uint8_t MovImmReg = 0x10;
inline static constexpr uint8_t MovRegReg = 0x11;
inline static constexpr uint8_t MovRegMem = 0x12;
inline static constexpr uint8_t MovMemReg = 0x13;
inline static constexpr uint8_t MovImmMem = 0x14;
inline static constexpr uint8_t MovDerefRegReg = 0x15;
inline static constexpr uint8_t MovImmRegOffReg = 0x16;
inline static constexpr uint8_t MovDerefRegMem = 0x17;
inline static constexpr uint8_t MovRegDerefReg = 0x18;
inline static constexpr uint8_t MovMemDerefReg = 0x19;
inline static constexpr uint8_t MovImmDerefReg = 0x1A;
inline static constexpr uint8_t MovDerefRegDerefReg = 0x1B;
inline static constexpr uint8_t MovByteImmMem = 0x20;
inline static constexpr uint8_t MovByteDerefRegMem = 0x21;
inline static constexpr uint8_t MovByteImmDerefReg = 0x22;
inline static constexpr uint8_t MovByteRegDerefReg = 0x23;
inline static constexpr uint8_t MovByteMemDerefReg = 0x24;
inline static constexpr uint8_t MovByteDerefRegDerefReg = 0x25;
inline static constexpr uint8_t MovByteMemReg = 0x26;
inline static constexpr uint8_t MovByteImmReg = 0x27;
inline static constexpr uint8_t MovByteDerefRegReg = 0x28;
inline static constexpr uint8_t MovByteRegMem = 0x29;
inline static constexpr uint8_t AddRegReg = 0x30;
inline static constexpr uint8_t AddImmReg = 0x31;
inline static constexpr uint8_t SubRegReg = 0x32;
inline static constexpr uint8_t SubImmReg = 0x33;
inline static constexpr uint8_t MulRegReg = 0x34;
inline static constexpr uint8_t MulImmReg = 0x35;
inline static constexpr uint8_t IncReg = 0x40;
inline static constexpr uint8_t DecReg = 0x41;
inline static constexpr uint8_t PushImm = 0x50;
inline static constexpr uint8_t PushReg = 0x51;
inline static constexpr uint8_t PopReg = 0x52;
inline static constexpr uint8_t CallImm = 0x53;
inline static constexpr uint8_t CallReg = 0x54;
inline static constexpr uint8_t Ret = 0x55;
inline static constexpr uint8_t LShiftRegImm = 0x60;
inline static constexpr uint8_t LShiftRegReg = 0x61;
inline static constexpr uint8_t RShiftRegImm = 0x62;
inline static constexpr uint8_t RShiftRegReg = 0x63;
inline static constexpr uint8_t AndRegImm = 0x64;
inline static constexpr uint8_t AndRegReg = 0x65;
inline static constexpr uint8_t OrRegImm = 0x66;
inline static constexpr uint8_t OrRegReg = 0x67;
inline static constexpr uint8_t XorRegImm = 0x68;
inline static constexpr uint8_t XorRegReg = 0x69;
inline static constexpr uint8_t NotReg = 0x6A;
inline static constexpr uint8_t JmpNotEqImm = 0x70;
inline static constexpr uint8_t JmpNotEqReg = 0x71;
inline static constexpr uint8_t JmpEqImm = 0x72;
inline static constexpr uint8_t JmpEqReg = 0x73;
inline static constexpr uint8_t JmpGrImm = 0x74;
inline static constexpr uint8_t JmpGrReg = 0x75;
inline static constexpr uint8_t JmpLessImm = 0x76;
inline static constexpr uint8_t JmpLessReg = 0x77;
inline static constexpr uint8_t JmpGeImm = 0x78;
inline static constexpr uint8_t JmpGeReg = 0x79;
inline static constexpr uint8_t JmpLeImm = 0x7A;
inline static constexpr uint8_t JmpLeReg = 0x7B;
inline static constexpr uint8_t Jmp = 0x7C;
inline static constexpr uint8_t RetInt = 0xFD;
inline static constexpr uint8_t Int = 0xFE;
inline static constexpr uint8_t Halt = 0xFF;
inline static ostd::String getOpCodeString(uint8_t opCode)
{
switch (opCode)
{
case data::OpCodes::NoOp: return "NoOp";
case data::OpCodes::DEBUG_Break: return "debug_break";
case data::OpCodes::MovImmReg: return "MovImmReg";
case data::OpCodes::MovImmMem: return "MovImmMem";
case data::OpCodes::MovRegReg: return "MovRegReg";
case data::OpCodes::MovRegMem: return "MovRegMem";
case data::OpCodes::MovMemReg: return "MovMemReg";
case data::OpCodes::MovDerefRegReg: return "MovDerefRegReg";
case data::OpCodes::MovDerefRegMem: return "MovDerefRegMem";
case data::OpCodes::MovImmRegOffReg: return "MovImmRegOffReg";
case data::OpCodes::MovRegDerefReg: return "MovRegDerefReg";
case data::OpCodes::MovMemDerefReg: return "MovMemDerefReg";
case data::OpCodes::MovImmDerefReg: return "MovImmDerefReg";
case data::OpCodes::MovDerefRegDerefReg: return "MovDerefRegDerefReg";
case data::OpCodes::MovByteImmMem: return "MovByteImmMem";
case data::OpCodes::MovByteRegMem: return "MovByteRegMem";
case data::OpCodes::MovByteDerefRegMem: return "MovByteDerefRegMem";
case data::OpCodes::MovByteImmDerefReg: return "MovByteImmDerefReg";
case data::OpCodes::MovByteRegDerefReg: return "MovByteRegDerefReg";
case data::OpCodes::MovByteMemDerefReg: return "MovByteMemDerefReg";
case data::OpCodes::MovByteDerefRegDerefReg: return "MovByteDerefRegDerefReg";
case data::OpCodes::MovByteMemReg: return "MovByteMemReg";
case data::OpCodes::MovByteImmReg: return "MovByteImmReg";
case data::OpCodes::MovByteDerefRegReg: return "MovByteDerefRegReg";
case data::OpCodes::AddImmReg: return "AddImmReg";
case data::OpCodes::AddRegReg: return "AddRegReg";
case data::OpCodes::SubImmReg: return "SubImmReg";
case data::OpCodes::SubRegReg: return "SubRegReg";
case data::OpCodes::MulImmReg: return "MulImmReg";
case data::OpCodes::MulRegReg: return "MulRegReg";
case data::OpCodes::IncReg: return "IncReg";
case data::OpCodes::DecReg: return "DecReg";
case data::OpCodes::RShiftRegImm: return "RShiftRegImm";
case data::OpCodes::RShiftRegReg: return "RShiftRegReg";
case data::OpCodes::LShiftRegImm: return "LShiftRegImm";
case data::OpCodes::LShiftRegReg: return "LShiftRegReg";
case data::OpCodes::AndRegImm: return "AndRegImm";
case data::OpCodes::AndRegReg: return "AndRegReg";
case data::OpCodes::OrRegImm: return "OrRegImm";
case data::OpCodes::OrRegReg: return "OrRegReg";
case data::OpCodes::XorRegImm: return "XorRegImm";
case data::OpCodes::XorRegReg: return "XorRegReg";
case data::OpCodes::NotReg: return "NotReg";
case data::OpCodes::JmpNotEqImm: return "JmpNotEqImm";
case data::OpCodes::JmpNotEqReg: return "JmpNotEqReg";
case data::OpCodes::JmpEqImm: return "JmpEqImm";
case data::OpCodes::JmpEqReg: return "JmpEqReg";
case data::OpCodes::JmpGrImm: return "JmpGrImm";
case data::OpCodes::JmpGrReg: return "JmpGrReg";
case data::OpCodes::JmpLessImm: return "JmpLessImm";
case data::OpCodes::JmpLessReg: return "JmpLessReg";
case data::OpCodes::JmpGeImm: return "JmpGeImm";
case data::OpCodes::JmpGeReg: return "JmpGeReg";
case data::OpCodes::JmpLeImm: return "JmpLeImm";
case data::OpCodes::JmpLeReg: return "JmpLeReg";
case data::OpCodes::Jmp: return "Jmp";
case data::OpCodes::Halt: return "Halt";
case data::OpCodes::PushImm: return "PushImm";
case data::OpCodes::PushReg: return "PushReg";
case data::OpCodes::PopReg: return "PopReg";
case data::OpCodes::CallImm: return "CallImm";
case data::OpCodes::CallReg: return "CallReg";
case data::OpCodes::Ret: return "Ret";
case data::OpCodes::RetInt: return "RetInt";
case data::OpCodes::Int: return "Int";
default: return "UNKNOWN_INST";
}
}
inline static uint8_t getInstructionSIze(uint8_t opCode)
{
switch (opCode)
{
case data::OpCodes::NoOp: return 1;
case data::OpCodes::DEBUG_Break: return 1;
case data::OpCodes::MovImmReg: return 4;
case data::OpCodes::MovImmMem: return 5;
case data::OpCodes::MovRegReg: return 3;
case data::OpCodes::MovRegMem: return 4;
case data::OpCodes::MovMemReg: return 4;
case data::OpCodes::MovDerefRegReg: return 3;
case data::OpCodes::MovDerefRegMem: return 4;
case data::OpCodes::MovImmRegOffReg: return 5;
case data::OpCodes::MovRegDerefReg: return 3;
case data::OpCodes::MovMemDerefReg: return 4;
case data::OpCodes::MovImmDerefReg: return 4;
case data::OpCodes::MovDerefRegDerefReg: return 3;
case data::OpCodes::MovByteImmMem: return 4;
case data::OpCodes::MovByteRegMem: return 4;
case data::OpCodes::MovByteDerefRegMem: return 4;
case data::OpCodes::MovByteImmDerefReg: return 3;
case data::OpCodes::MovByteRegDerefReg: return 3;
case data::OpCodes::MovByteMemDerefReg: return 4;
case data::OpCodes::MovByteDerefRegDerefReg: return 3;
case data::OpCodes::MovByteMemReg: return 4;
case data::OpCodes::MovByteImmReg: return 3;
case data::OpCodes::MovByteDerefRegReg: return 3;
case data::OpCodes::AddImmReg: return 4;
case data::OpCodes::AddRegReg: return 3;
case data::OpCodes::SubImmReg: return 4;
case data::OpCodes::SubRegReg: return 3;
case data::OpCodes::MulImmReg: return 4;
case data::OpCodes::MulRegReg: return 3;
case data::OpCodes::IncReg: return 2;
case data::OpCodes::DecReg: return 2;
case data::OpCodes::RShiftRegImm: return 4;
case data::OpCodes::RShiftRegReg: return 3;
case data::OpCodes::LShiftRegImm: return 4;
case data::OpCodes::LShiftRegReg: return 3;
case data::OpCodes::AndRegImm: return 4;
case data::OpCodes::AndRegReg: return 3;
case data::OpCodes::OrRegImm: return 4;
case data::OpCodes::OrRegReg: return 3;
case data::OpCodes::XorRegImm: return 4;
case data::OpCodes::XorRegReg: return 3;
case data::OpCodes::NotReg: return 2;
case data::OpCodes::JmpNotEqImm: return 5;
case data::OpCodes::JmpNotEqReg: return 4;
case data::OpCodes::JmpEqImm: return 5;
case data::OpCodes::JmpEqReg: return 4;
case data::OpCodes::JmpGrImm: return 5;
case data::OpCodes::JmpGrReg: return 4;
case data::OpCodes::JmpLessImm: return 5;
case data::OpCodes::JmpLessReg: return 4;
case data::OpCodes::JmpGeImm: return 5;
case data::OpCodes::JmpGeReg: return 4;
case data::OpCodes::JmpLeImm: return 5;
case data::OpCodes::JmpLeReg: return 4;
case data::OpCodes::Jmp: return 3;
case data::OpCodes::Halt: return 1;
case data::OpCodes::PushImm: return 3;
case data::OpCodes::PushReg: return 2;
case data::OpCodes::PopReg: return 2;
case data::OpCodes::CallImm: return 3;
case data::OpCodes::CallReg: return 2;
case data::OpCodes::Ret: return 1;
case data::OpCodes::RetInt: return 1;
case data::OpCodes::Int: return 2;
default: return 0;
}
}
};
}
}

7
src/tools/SDLInclude.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#ifdef _WIN32
#undef __linux__
#endif
#include <SDL2/SDL.h>

7
src/tools/Utils.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "Utils.hpp"
#include <ostd/Serial.hpp>
#include <ostd/Utils.hpp>
namespace dragon
{
}

11
src/tools/Utils.hpp Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include <ostd/Types.hpp>
namespace dragon
{
class Utils
{
public:
};
}

122
src/tools/tools_main.cpp Normal file
View file

@ -0,0 +1,122 @@
#include <ostd/Utils.hpp>
#include <fstream>
#include <ostd/IOHandlers.hpp>
#include "../hardware/VirtualHardDrive.hpp"
constexpr int ErrorTopLevelTooFewArgs = 1;
constexpr int ErrorTopLevelUnknownTool = 4;
constexpr int ErrorNewVDiskTooFewArgs = 2;
constexpr int ErrorNewVDiskNonIntSize = 3;
constexpr int ErrorNewVDiskUnable = 5;
constexpr int ErrorLoadProgTooFewArgs = 6;
constexpr int ErrorLoadProgNonIntAddr = 7;
constexpr int ErrorLoadProgUnableToLoadVDisk = 8;
constexpr int ErrorLoadProgUnableToLoadDataFile = 8;
constexpr int ErrorNoError = 0;
ostd::legacy::ConsoleOutputHandler out;
bool createVirtualHardDrive(uint32_t sizeInBytes, const ostd::String& dataFilePath)
{
std::ofstream rf(dataFilePath, std::ios::out | std::ios::binary);
if(!rf) return false;
ostd::ByteStream stream;
for (int32_t i = 0; i < sizeInBytes; i++)
stream.push_back(0x00);
rf.write((char*)(&stream[0]), stream.size());
rf.close();
return true;
}
int main(int argc, char** argv)
{
if (argc < 2)
{
out.col("red").p("Error: too few arguments.").nl();
out.col("red").p(" Usage: ./dtools <tool_name> [...arguments...]").resetColors().nl();
return ErrorTopLevelTooFewArgs;
}
ostd::StringEditor tool = argv[1];
tool = tool.trim().toLower();
//Nex Virtual Disk
if (tool.str() == "new-vdisk")
{
if (argc < 4)
{
out.col("red").p("Error: too few arguments.").nl();
out.col("red").p(" Usage: ./dtools new-vdisk <destination_file> <size_in_bytes>").resetColors().nl();
return ErrorNewVDiskTooFewArgs;
}
ostd::String dest = argv[2];
ostd::String str_size = argv[3];
if (!ostd::Utils::isInt(str_size))
{
out.col("red").p("Error: <size_in_bytes> parameter must be integer.").resetColors().nl();
return ErrorNewVDiskNonIntSize;
}
bool result = createVirtualHardDrive(ostd::Utils::strToInt(str_size), dest);
if (!result)
{
out.col("red").p("Error: Unable to create virtual disk.").resetColors().nl();
return ErrorNewVDiskUnable;
}
out.col("green").p("Success. Virtual disk created:").nl();
out.p(" Path: ").p(dest).nl();
out.p(" Size: ").p(str_size).resetColors().nl();
}
//Load Program
else if (tool.str() == "load-program")
{
if (argc < 5)
{
out.col("red").p("Error: too few arguments.").nl();
out.col("red").p(" Usage: ./dtools load-program <virtual_disk_file> <data_file> <destination_address>").resetColors().nl();
return ErrorLoadProgTooFewArgs;
}
ostd::String vdisk_file = argv[2];
ostd::String data_file = argv[3];
ostd::String str_addr = argv[4];
if (!ostd::Utils::isInt(str_addr))
{
out.col("red").p("Error: <destination_address> parameter must be integer.").resetColors().nl();
return ErrorLoadProgNonIntAddr;
}
dragon::hw::VirtualHardDrive vHDD(vdisk_file);
if (!vHDD.isInitialized())
{
out.col("red").p("Error: Unable to load virtual disk.").resetColors().nl();
return ErrorLoadProgUnableToLoadVDisk;
}
ostd::ByteStream code;
if (!ostd::Utils::loadByteStreamFromFile(data_file, code))
{
out.col("red").p("Error: Unable to load data file.").resetColors().nl();
return ErrorLoadProgUnableToLoadDataFile;
}
int16_t index = 0;
uint32_t addr = (uint32_t)ostd::Utils::strToInt(str_addr);
for (auto& b : code)
{
vHDD.write(addr + index, b);
index++;
}
vHDD.unmount();
out.col("green").p("Success. Data writte to Virtual Disk:").nl();
out.p(" Data Path: ").p(data_file).nl();
out.p(" Disk Path: ").p(vdisk_file).nl();
out.p(" Data Address: ").p(ostd::Utils::getHexStr(addr, true, 4)).nl();
out.p(" Size: ").pi(code.size()).resetColors().nl();
}
//Unknown
else
{
out.col("red").p("Error: Unknown tool.").resetColors().nl();
return ErrorTopLevelUnknownTool;
}
return ErrorNoError;
}

BIN
tools/CppResourceMaker Normal file

Binary file not shown.

1
tools/build.nr Normal file
View file

@ -0,0 +1 @@
1536

BIN
tools/inc_bnr Normal file

Binary file not shown.

BIN
tools/inc_bnr.exe Normal file

Binary file not shown.