First Commit
This commit is contained in:
parent
7be98df33e
commit
43e9e82264
69 changed files with 7707 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
bin
|
||||
tools/win-release-tools
|
||||
win-release
|
||||
31
.vscode/c_cpp_properties.json
vendored
Normal file
31
.vscode/c_cpp_properties.json
vendored
Normal 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
143
.vscode/settings.json
vendored
Normal 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
181
CMakeLists.txt
Normal 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
22
README.md
Normal 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
16
compile
Normal 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
|
||||
3
extra/config/testMachine.dvm
Normal file
3
extra/config/testMachine.dvm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Disks = dragon/disk1.dr
|
||||
Bios = dragon/bios.bin
|
||||
CMOS = dragon/cmos.dr
|
||||
79
extra/dragon info/DragonVM Memory Structure
Normal file
79
extra/dragon info/DragonVM Memory Structure
Normal 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
|
||||
70
extra/dragon info/Interrupts
Normal file
70
extra/dragon info/Interrupts
Normal 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
|
||||
BIN
extra/dragon info/Memory Map.png
Normal file
BIN
extra/dragon info/Memory Map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
10
extra/dragon info/regular_expressions.txt
Normal file
10
extra/dragon info/regular_expressions.txt
Normal 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
BIN
extra/dragon/bios.bin
Normal file
Binary file not shown.
BIN
extra/dragon/cmos.dr
Normal file
BIN
extra/dragon/cmos.dr
Normal file
Binary file not shown.
BIN
extra/dragon/disk1.dr
Normal file
BIN
extra/dragon/disk1.dr
Normal file
Binary file not shown.
140
extra/dss/bios.dss
Normal file
140
extra/dss/bios.dss
Normal 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
94
extra/dss/handlerTest.dss
Normal 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
7
extra/dss/mbr.dss
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.load 0x1680
|
||||
|
||||
.code
|
||||
jmp 0x1A40
|
||||
hlt
|
||||
|
||||
.fixed 512, 0x00
|
||||
15
extra/dss/newTest.dss
Normal file
15
extra/dss/newTest.dss
Normal 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
8
extra/dss/test.dss
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.load 0x1A40
|
||||
|
||||
.code
|
||||
mov R1, 0xABCD
|
||||
debug_break
|
||||
hlt
|
||||
|
||||
.fixed 512, 0x00
|
||||
BIN
extra/font.bmp
Normal file
BIN
extra/font.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
25
extra/make_and_run
Normal file
25
extra/make_and_run
Normal 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
10
extra/scripts/bios_flash
Normal 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
13
extra/scripts/load_mbr
Normal 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
1152
src/assembler/Assembler.cpp
Normal file
File diff suppressed because it is too large
Load diff
85
src/assembler/Assembler.hpp
Normal file
85
src/assembler/Assembler.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
60
src/assembler/assembler_main.cpp
Normal file
60
src/assembler/assembler_main.cpp
Normal 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
829
src/debugger/Debugger.cpp
Normal 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
72
src/debugger/Debugger.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
137
src/debugger/DisassemblyLoader.cpp
Normal file
137
src/debugger/DisassemblyLoader.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
49
src/debugger/DisassemblyLoader.hpp
Normal file
49
src/debugger/DisassemblyLoader.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
100
src/debugger/debugger_main.cpp
Normal file
100
src/debugger/debugger_main.cpp
Normal 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;
|
||||
}
|
||||
6
src/gui/DebuggerWindow.cpp
Normal file
6
src/gui/DebuggerWindow.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#include "DebuggerWindow.hpp"
|
||||
|
||||
namespace dragon
|
||||
{
|
||||
|
||||
}
|
||||
9
src/gui/DebuggerWindow.hpp
Normal file
9
src/gui/DebuggerWindow.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
namespace dragon
|
||||
{
|
||||
class DebuggerWindow
|
||||
{
|
||||
|
||||
};
|
||||
} // namespace dragon
|
||||
101
src/gui/RawTextRenderer.cpp
Normal file
101
src/gui/RawTextRenderer.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/gui/RawTextRenderer.hpp
Normal file
30
src/gui/RawTextRenderer.hpp
Normal 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
158
src/gui/VirtualConsole.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
64
src/gui/VirtualConsole.hpp
Normal file
64
src/gui/VirtualConsole.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
303
src/gui/VirtualConsoleOutputHandler.cpp
Normal file
303
src/gui/VirtualConsoleOutputHandler.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
99
src/gui/VirtualConsoleOutputHandler.hpp
Normal file
99
src/gui/VirtualConsoleOutputHandler.hpp
Normal 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
98
src/gui/Window.cpp
Normal 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
57
src/gui/Window.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
128
src/gui/widgets/VirtualConsoleWidget.cpp
Normal file
128
src/gui/widgets/VirtualConsoleWidget.cpp
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
67
src/gui/widgets/VirtualConsoleWidget.hpp
Normal file
67
src/gui/widgets/VirtualConsoleWidget.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
25
src/gui/widgets/Widget.hpp
Normal file
25
src/gui/widgets/Widget.hpp
Normal 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 };
|
||||
};
|
||||
}
|
||||
19
src/hardware/IMemoryDevice.hpp
Normal file
19
src/hardware/IMemoryDevice.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
75
src/hardware/MemoryMapper.cpp
Normal file
75
src/hardware/MemoryMapper.cpp
Normal 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 ®ion;
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/hardware/MemoryMapper.hpp
Normal file
41
src/hardware/MemoryMapper.hpp
Normal 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
707
src/hardware/VirtualCPU.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/hardware/VirtualCPU.hpp
Normal file
54
src/hardware/VirtualCPU.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
106
src/hardware/VirtualHardDrive.cpp
Normal file
106
src/hardware/VirtualHardDrive.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/hardware/VirtualHardDrive.hpp
Normal file
38
src/hardware/VirtualHardDrive.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
655
src/hardware/VirtualIODevices.cpp
Normal file
655
src/hardware/VirtualIODevices.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
237
src/hardware/VirtualIODevices.hpp
Normal file
237
src/hardware/VirtualIODevices.hpp
Normal 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 };
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/hardware/VirtualRAM.cpp
Normal file
45
src/hardware/VirtualRAM.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/hardware/VirtualRAM.hpp
Normal file
25
src/hardware/VirtualRAM.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
53
src/runtime/ConfigLoader.cpp
Normal file
53
src/runtime/ConfigLoader.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
30
src/runtime/ConfigLoader.hpp
Normal file
30
src/runtime/ConfigLoader.hpp
Normal 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);
|
||||
};
|
||||
}
|
||||
382
src/runtime/DragonRuntime.cpp
Normal file
382
src/runtime/DragonRuntime.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
94
src/runtime/DragonRuntime.hpp
Normal file
94
src/runtime/DragonRuntime.hpp
Normal 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 };
|
||||
};
|
||||
}
|
||||
74
src/runtime/runtime_main.cpp
Normal file
74
src/runtime/runtime_main.cpp
Normal 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
424
src/tools/GlobalData.hpp
Normal 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
7
src/tools/SDLInclude.hpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef __linux__
|
||||
#endif
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
7
src/tools/Utils.cpp
Normal file
7
src/tools/Utils.cpp
Normal 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
11
src/tools/Utils.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <ostd/Types.hpp>
|
||||
|
||||
namespace dragon
|
||||
{
|
||||
class Utils
|
||||
{
|
||||
public:
|
||||
};
|
||||
}
|
||||
122
src/tools/tools_main.cpp
Normal file
122
src/tools/tools_main.cpp
Normal 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
BIN
tools/CppResourceMaker
Normal file
Binary file not shown.
1
tools/build.nr
Normal file
1
tools/build.nr
Normal file
|
|
@ -0,0 +1 @@
|
|||
1536
|
||||
BIN
tools/inc_bnr
Normal file
BIN
tools/inc_bnr
Normal file
Binary file not shown.
BIN
tools/inc_bnr.exe
Normal file
BIN
tools/inc_bnr.exe
Normal file
Binary file not shown.
Loading…
Reference in a new issue