diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ea94eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +bin +tools/win-release-tools +win-release \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..006aa2e --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -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 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..61116ba --- /dev/null +++ b/.vscode/settings.json @@ -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" +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b31c020 --- /dev/null +++ b/CMakeLists.txt @@ -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/ $ + VERBATIM +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..e7a94b5 --- /dev/null +++ b/README.md @@ -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 +``` diff --git a/compile b/compile new file mode 100644 index 0000000..405fcd6 --- /dev/null +++ b/compile @@ -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 \ No newline at end of file diff --git a/extra/config/testMachine.dvm b/extra/config/testMachine.dvm new file mode 100644 index 0000000..c6c902d --- /dev/null +++ b/extra/config/testMachine.dvm @@ -0,0 +1,3 @@ +Disks = dragon/disk1.dr +Bios = dragon/bios.bin +CMOS = dragon/cmos.dr \ No newline at end of file diff --git a/extra/dragon info/DragonVM Memory Structure b/extra/dragon info/DragonVM Memory Structure new file mode 100644 index 0000000..4b39e89 --- /dev/null +++ b/extra/dragon info/DragonVM Memory Structure @@ -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 diff --git a/extra/dragon info/Interrupts b/extra/dragon info/Interrupts new file mode 100644 index 0000000..0fdc97e --- /dev/null +++ b/extra/dragon info/Interrupts @@ -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 \ No newline at end of file diff --git a/extra/dragon info/Memory Map.png b/extra/dragon info/Memory Map.png new file mode 100644 index 0000000..1ac7c13 Binary files /dev/null and b/extra/dragon info/Memory Map.png differ diff --git a/extra/dragon info/regular_expressions.txt b/extra/dragon info/regular_expressions.txt new file mode 100644 index 0000000..51f085c --- /dev/null +++ b/extra/dragon info/regular_expressions.txt @@ -0,0 +1,10 @@ +Regex: Javascript + +Number Constants +0x[0-9A-Fa-f]+|0b[0-1]+|(? register into ACC + jne $_load_mbr_data_block_wait_loop, 0x00 ## If 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 diff --git a/extra/dss/handlerTest.dss b/extra/dss/handlerTest.dss new file mode 100644 index 0000000..4e8e5e2 --- /dev/null +++ b/extra/dss/handlerTest.dss @@ -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 \ No newline at end of file diff --git a/extra/dss/mbr.dss b/extra/dss/mbr.dss new file mode 100644 index 0000000..c8b869f --- /dev/null +++ b/extra/dss/mbr.dss @@ -0,0 +1,7 @@ +.load 0x1680 + +.code + jmp 0x1A40 + hlt + +.fixed 512, 0x00 diff --git a/extra/dss/newTest.dss b/extra/dss/newTest.dss new file mode 100644 index 0000000..52c1b39 --- /dev/null +++ b/extra/dss/newTest.dss @@ -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 \ No newline at end of file diff --git a/extra/dss/test.dss b/extra/dss/test.dss new file mode 100644 index 0000000..6f8b098 --- /dev/null +++ b/extra/dss/test.dss @@ -0,0 +1,8 @@ +.load 0x1A40 + +.code + mov R1, 0xABCD + debug_break + hlt + +.fixed 512, 0x00 \ No newline at end of file diff --git a/extra/font.bmp b/extra/font.bmp new file mode 100644 index 0000000..65ab7c5 Binary files /dev/null and b/extra/font.bmp differ diff --git a/extra/make_and_run b/extra/make_and_run new file mode 100644 index 0000000..38e8578 --- /dev/null +++ b/extra/make_and_run @@ -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 diff --git a/extra/scripts/bios_flash b/extra/scripts/bios_flash new file mode 100644 index 0000000..69b345d --- /dev/null +++ b/extra/scripts/bios_flash @@ -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 diff --git a/extra/scripts/load_mbr b/extra/scripts/load_mbr new file mode 100644 index 0000000..321574d --- /dev/null +++ b/extra/scripts/load_mbr @@ -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 diff --git a/src/assembler/Assembler.cpp b/src/assembler/Assembler.cpp new file mode 100644 index 0000000..da38221 --- /dev/null +++ b/src/assembler/Assembler.cpp @@ -0,0 +1,1152 @@ +#include "Assembler.hpp" +#include +#include +#include +#include +#include +#include + +#include "../tools/GlobalData.hpp" +#include "../hardware/VirtualHardDrive.hpp" +#include "../tools/Utils.hpp" + +namespace dragon +{ + namespace code + { + ostd::ByteStream Assembler::assembleFromFile(ostd::String fileName) + { + m_rawSource = ""; + m_code.clear(); + m_lines.clear(); + m_rawDataSection.clear(); + m_rawCodeSection.clear(); + m_symbolTable.clear(); + m_labelTable.clear(); + m_disassembly.clear(); + m_fixedSize = 0; + m_fixedFillValue = 0x00; + m_loadAddress = 0x0000; + m_currentDataAddr = 0x0000; + m_dataSize = 0x0000; + ostd::TextFileBuffer file(fileName); + loadSource(file.rawContent()); + removeComments(); + replaceDefines(); + parseSections(); + parseDataSection(); + parseCodeSection(); + replaceLabelRefs(); + combineDataAndCode(); + m_programSize = m_code.size(); + if (m_fixedSize > 0 && m_code.size() > m_fixedSize) + std::cout << "Warning: Fixed size specified but exceeded: (" << (int)m_code.size() << "/" << (int)m_fixedSize << " bytes)\n"; + else if (m_fixedSize > 0 && m_code.size() < m_fixedSize) + { + for (int16_t i = m_code.size(); i < m_fixedSize; i++) + m_code.push_back(m_fixedFillValue); + } + return m_code; + } + + void Assembler::loadSource(ostd::String source) + { + m_rawSource = source; + m_lines = ostd::StringEditor(source).tokenize("\n").getRawData(); + } + + ostd::ByteStream Assembler::assembleToFile(ostd::String sourceFileName, ostd::String binaryFileName) + { + assembleFromFile(sourceFileName); + if (m_code.size() == 0) return { }; + ostd::Utils::saveByteStreamToFile(m_code, binaryFileName); + return m_code; + } + + ostd::ByteStream Assembler::assembleToVirtualDisk(ostd::String fileName, hw::VirtualHardDrive& vhdd, uint32_t address) + { + assembleFromFile(fileName); + if (m_code.size() == 0) return { }; + for (int32_t i = 0; i < m_code.size(); i++) + vhdd.write(address + i, m_code[i]); + return m_code; + } + + bool Assembler::saveDisassemblyToFile(ostd::String fileName) + { + if (m_code.size() == 0 || m_disassembly.size() == 0) return false; + ostd::String header_string = "{ DRAGON_DEBUG_DISASSEMBLY }"; + uint64_t da_size = 0; + da_size += (header_string.length() + 1) * ostd::tTypeSize::BYTE; + da_size += m_disassembly.size() * ostd::tTypeSize::DWORD; //Addresses + da_size += m_disassembly.size() * ostd::tTypeSize::BYTE; //Null Termination + for (auto& da : m_disassembly) + da_size += da.code.length() * ostd::tTypeSize::BYTE; //Code Strings + ostd::serial::SerialIO serializer(da_size, ostd::serial::SerialIO::tEndianness::BigEndian); + ostd::StreamIndex stream_addr = 0; + serializer.w_String(stream_addr, header_string); + stream_addr += (header_string.length() + 1) * ostd::tTypeSize::BYTE; + for (auto& da : m_disassembly) + { + serializer.w_DWord(stream_addr, da.addr); + stream_addr += ostd::tTypeSize::DWORD; + serializer.w_String(stream_addr, da.code); + stream_addr += (da.code.length() + 1) * ostd::tTypeSize::BYTE; + // ostd::ByteStream code_stream = ostd::Utils::stringToByteStream(da.code); + // for (int32_t i = 0; i < code_stream.size(); i++) + // { + // serializer.w_Byte(stream_addr, code_stream[i]); + // stream_addr += ostd::tTypeSize::BYTE; + // } + // serializer.w_Byte(stream_addr, 0x00); //NULL Termination + // stream_addr += ostd::tTypeSize::BYTE; + } + return serializer.saveToFile(fileName); + } + + void Assembler::removeComments(void) + { + std::vector newLines; + ostd::StringEditor lineEdit; + for (auto& line : m_lines) + { + lineEdit = line; + lineEdit.trim(); + if (lineEdit.startsWith("##")) + continue; + if (lineEdit.contains("##")) + { + lineEdit = lineEdit.substr(0, lineEdit.indexOf("##")); + lineEdit.trim(); + } + newLines.push_back(lineEdit.str()); + } + m_lines.clear(); + m_lines = newLines; + } + + void Assembler::replaceDefines(void) + { + struct tDefine { + ostd::String name; + ostd::String value; + }; + auto listContainsDefine = [](const std::vector& list, const ostd::String& name) -> bool { + for (auto& def : list) + { + if (def.name == name) + return true; + } + return false; + }; + std::vector defines; + std::vector newLines; + ostd::StringEditor lineEdit; + ostd::StringEditor tmpLineEdit; + for (auto& line : m_lines) + { + lineEdit = line; + lineEdit.trim(); + tmpLineEdit = lineEdit; + if (tmpLineEdit.toLower().startsWith("@define ")) + { + lineEdit = lineEdit.substr(8); + lineEdit.trim(); + if (!lineEdit.contains(" ")) + { + std::cout << "Invalid @define directive: " << line << "\n"; + return; + } + ostd::String define_name = lineEdit.substr(0, lineEdit.indexOf(" ")); + define_name = ostd::StringEditor(define_name).trim().str(); + ostd::String define_value = lineEdit.substr(lineEdit.indexOf(" ") + 1); + if (listContainsDefine(defines, define_name)) + { + std::cout << "Redefinition of @define value: " << line << "\n"; + return; + } + defines.push_back({ define_name, define_value }); + // std::cout << define_name << ": " << define_value << "\n"; + continue; + } + newLines.push_back(lineEdit.str()); + } + for (auto& line : newLines) + { + ostd::StringEditor lineEdit(line); + for (int32_t i = defines.size() - 1; i >= 0; i--) + lineEdit.replaceAll(defines[i].name, defines[i].value); + line = lineEdit.str(); + } + m_lines.clear(); + m_lines = newLines; + } + + void Assembler::parseSections(void) + { + constexpr uint8_t DATA_SECTION = 0x01; + constexpr uint8_t CODE_SECTION = 0x02; + + uint8_t currentSection = 0x00; + + for (auto& line : m_lines) + { + ostd::StringEditor lineEdit(line); + if (lineEdit.startsWith(".data")) + currentSection = DATA_SECTION; + else if (lineEdit.startsWith(".code")) + currentSection = CODE_SECTION; + else if (lineEdit.startsWith(".fixed ")) + { + if (lineEdit.len() < 8) + { + //TODO: Error + std::cout << "Invalid .fixed value: " << lineEdit << "\n"; + return; + } + lineEdit = lineEdit.substr(7); + lineEdit.trim(); + if (!lineEdit.contains(",")) + { + //TODO: Error + std::cout << "Invalid .fixed value: " << lineEdit << "\n"; + return; + } + ostd::StringEditor fixedSizeEdit = lineEdit.substr(0, lineEdit.indexOf(",")); + fixedSizeEdit.trim(); + lineEdit = lineEdit.substr(lineEdit.indexOf(",") + 1); + lineEdit.trim(); + if (!lineEdit.isNumeric()) + { + //TODO: Error + std::cout << "Invalid .fixed size value: " << lineEdit << "\n"; + return; + } + m_fixedFillValue = lineEdit.toInt(); + if (!fixedSizeEdit.isNumeric()) + { + //TODO: Error + std::cout << "Invalid .fixed fill value: " << lineEdit << "\n"; + return; + } + m_fixedSize = fixedSizeEdit.toInt(); + continue; + } + else if (lineEdit.startsWith(".load ")) + { + if (lineEdit.len() < 7) + { + //TODO: Error + std::cout << "Invalid .load value: " << lineEdit << "\n"; + return; + } + lineEdit = lineEdit.substr(6); + lineEdit.trim(); + if (!lineEdit.isNumeric()) + { + //TODO: Error + std::cout << "Invalid .load value: " << lineEdit << "\n"; + return; + } + m_loadAddress = lineEdit.toInt(); + m_currentDataAddr = m_loadAddress + 3; + continue; + } + else if (lineEdit.startsWith(".")) + { + //TODO: Error + std::cout << "Invalid section: " << lineEdit << "\n"; + return; + } + else + { + if (currentSection == DATA_SECTION) + m_rawDataSection.push_back(line); + else if (currentSection == CODE_SECTION) + m_rawCodeSection.push_back(line); + else + { + //TODO: Error + std::cout << "Invalid section: " << lineEdit << "\n"; + return; + } + } + } + } + + void Assembler::parseDataSection(void) + { + for (auto& line : m_rawDataSection) + { + ostd::StringEditor lineEdit(line); + tSymbol symbol; + if (!lineEdit.startsWith("$") || lineEdit.indexOf(" ") < 2) + { + std::cout << "Invalid data entry: " << lineEdit << "\n"; + continue; + } + ostd::StringEditor symbolName = lineEdit.substr(0, lineEdit.indexOf(" ")); + symbolName.trim(); + lineEdit = lineEdit.substr(lineEdit.indexOf(" ") + 1); + lineEdit.trim(); + if (lineEdit.isNumeric()) + { + // union valueSplit { + // uint16_t value; + // uint8_t bytes[2]; + // } split; + // split.value = lineEdit.toInt(); + // symbol.bytes.push_back(split.bytes[0]); + // symbol.bytes.push_back(split.bytes[1]); + // symbol.address = m_currentDataAddr; + // m_currentDataAddr += 2; + int8_t value = lineEdit.toInt(); + symbol.bytes.push_back(value); + symbol.address = m_currentDataAddr; + m_currentDataAddr += 1; + m_symbolTable[symbolName.str()] = symbol; + continue; + } + else if (lineEdit.startsWith("\"") && lineEdit.endsWith("\"") && lineEdit.len() > 2) + { + lineEdit = lineEdit.substr(1, lineEdit.len() - 1); + for (auto c : lineEdit.str()) + symbol.bytes.push_back((uint8_t)c); + symbol.bytes.push_back((uint8_t)0); //NULL Termination + symbol.address = m_currentDataAddr; + m_currentDataAddr += lineEdit.len(); + m_symbolTable[symbolName.str()] = symbol; + continue; + } + else if (lineEdit.contains(",")) + { + auto tokens = lineEdit.tokenize(","); + if (tokens.count() < 2) + { + std::cout << "Invalid data entry: " << lineEdit << "\n"; + return; + } + for (auto& token : tokens) + { + lineEdit = token; + if (!lineEdit.isNumeric()) + { + std::cout << "Invalid byte in data entry: " << lineEdit << "\n"; + return; + } + symbol.bytes.push_back((uint8_t)lineEdit.toInt()); + } + symbol.address = m_currentDataAddr; + m_currentDataAddr += symbol.bytes.size(); + m_symbolTable[symbolName.str()] = symbol; + continue; + } + else + { + std::cout << "Invalid data entry: " << lineEdit << "\n"; + continue; + } + } + + for (auto& symbol : m_symbolTable) + { + m_dataSize += symbol.second.bytes.size(); + } + } + + void Assembler::parseCodeSection(void) + { + for (auto& line : m_rawCodeSection) + { + ostd::StringEditor lineEdit(line); + uint32_t commaCount = lineEdit.count(","); + uint32_t spaceCount = lineEdit.count(" "); + if (lineEdit.endsWith(":") && commaCount == 0 && spaceCount == 0) //Labels + { + lineEdit = lineEdit.substr(0, lineEdit.len() - 1); + lineEdit.trim(); + m_labelTable["$" + lineEdit.str()] = { { }, 0x0000 }; + continue; + } + } + + tDisassemblyLine _disassembly_line; + for (auto& line : m_rawCodeSection) + { + ostd::StringEditor lineEdit(line); + uint32_t commaCount = lineEdit.count(","); + uint32_t spaceCount = lineEdit.count(" "); + if (lineEdit.endsWith(":") && commaCount == 0 && spaceCount == 0) //Labels + { + // _disassembly_line.addr = m_dataSize + m_loadAddress + m_code.size() + 3; + // _disassembly_line.code = lineEdit.str(); + // m_disassembly.push_back(_disassembly_line); + lineEdit = lineEdit.substr(0, lineEdit.len() - 1); + lineEdit.trim(); + m_labelTable["$" + lineEdit.str()].address = m_dataSize + m_loadAddress + m_code.size() + 3; + continue; + } + else if (commaCount == 0 && spaceCount <= 0) //0 Operands + { + _disassembly_line.addr = m_dataSize + m_loadAddress + m_code.size() + 3; + ostd::StringEditor _tmp_edit(lineEdit.str()); + if (_tmp_edit.toLower().trim().startsWith("debug_")) + parseDebugOperands(lineEdit.str()); + else + parse0Operand(lineEdit.str()); + _disassembly_line.code = lineEdit.str(); + m_disassembly.push_back(_disassembly_line); + continue; + } + else if (commaCount == 0) //1 Operands + { + _disassembly_line.addr = m_dataSize + m_loadAddress + m_code.size() + 3; + lineEdit = replaceSymbols(lineEdit.str()); + parse1Operand(lineEdit.str()); + _disassembly_line.code = lineEdit.str(); + m_disassembly.push_back(_disassembly_line); + continue; + } + else if (commaCount == 1) //2 Operands + { + _disassembly_line.addr = m_dataSize + m_loadAddress + m_code.size() + 3; + lineEdit = replaceSymbols(lineEdit.str()); + parse2Operand(lineEdit.str()); + _disassembly_line.code = lineEdit.str(); + m_disassembly.push_back(_disassembly_line); + continue; + } + else if (commaCount == 2) // 3 Operands + { + _disassembly_line.addr = m_dataSize + m_loadAddress + m_code.size() + 3; + lineEdit = replaceSymbols(lineEdit.str()); + parse3Operand(lineEdit.str()); + _disassembly_line.code = lineEdit.str(); + m_disassembly.push_back(_disassembly_line); + continue; + } + else + { + std::cout << "Invalid syntax: " << lineEdit << "\n"; + return; + } + } + } + + void Assembler::parse0Operand(ostd::String line) + { + if (ostd::StringEditor(line).toLower().startsWith("nop")) + { + m_code.push_back(data::OpCodes::NoOp); + return; + } + else if (ostd::StringEditor(line).toLower().startsWith("ret")) + { + m_code.push_back(data::OpCodes::Ret); + return; + } + else if (ostd::StringEditor(line).toLower().startsWith("rti")) + { + m_code.push_back(data::OpCodes::RetInt); + return; + } + else if (ostd::StringEditor(line).toLower().startsWith("hlt")) + { + m_code.push_back(data::OpCodes::Halt); + return; + } + } + + void Assembler::parseDebugOperands(ostd::String line) + { + if (ostd::StringEditor(line).toLower().startsWith("debug_break")) + { + m_code.push_back(data::OpCodes::DEBUG_Break); + return; + } + } + + void Assembler::parse1Operand(ostd::String line) + { + ostd::StringEditor lineEdit(line); + ostd::StringEditor instEdit(lineEdit.substr(0, lineEdit.indexOf(" "))); + instEdit.trim().toLower(); + ostd::StringEditor opEdit(lineEdit.substr(lineEdit.indexOf(" ") + 1)); + opEdit.trim(); + int16_t word = 0x0000; + if (instEdit.str() == "inc") + { + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back(data::OpCodes::IncReg); + m_code.push_back((uint8_t)word); + return; + } + else if (instEdit.str() == "dec") + { + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back(data::OpCodes::DecReg); + m_code.push_back((uint8_t)word); + return; + } + else if (instEdit.str() == "push") + { + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType == eOperandType::Immediate || opType == eOperandType::Label) + { + m_code.push_back(data::OpCodes::PushImm); + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + } + else if (opType == eOperandType::Register) + { + m_code.push_back(data::OpCodes::PushReg); + m_code.push_back((uint8_t)word); + } + else + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register or Immediate required\n"; + exit(0); + } + return; + } + else if (instEdit.str() == "pop") + { + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back(data::OpCodes::PopReg); + m_code.push_back((uint8_t)word); + return; + } + else if (instEdit.str() == "call") + { + m_code.push_back(0x00); + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType == eOperandType::Immediate || opType == eOperandType::Label) + { + m_code[m_code.size() - 1] = data::OpCodes::CallImm; + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + } + else if (opType == eOperandType::Register) + { + m_code[m_code.size() - 1] = data::OpCodes::CallReg; + m_code.push_back((uint8_t)word); + } + else + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register or Immediate (Label) required\n"; + exit(0); + } + return; + } + else if (instEdit.str() == "not") + { + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back(data::OpCodes::NotReg); + m_code.push_back((uint8_t)word); + return; + } + else if (instEdit.str() == "jmp") + { + m_code.push_back(data::OpCodes::Jmp); + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType != eOperandType::Label && opType != eOperandType::Immediate) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Immediate (or label) required\n"; + exit(0); + return; + } + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + return; + } + else if (instEdit.str() == "int") + { + eOperandType opType = parseOperand(opEdit.str(), word); + if (opType != eOperandType::Immediate) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Immediate required\n"; + exit(0); + return; + } + m_code.push_back(data::OpCodes::Int); + m_code.push_back((uint8_t)word); + return; + } + else + { + std::cout << "Unknown instruction; " << line << " (" << instEdit << ")\n"; + exit(0); + } + } + + void Assembler::parse2Operand(ostd::String line) + { + ostd::StringEditor lineEdit(line); + ostd::StringEditor instEdit(lineEdit.substr(0, lineEdit.indexOf(" "))); + instEdit.trim().toLower(); + ostd::StringEditor opEdit(lineEdit.substr(lineEdit.indexOf(" ") + 1)); + opEdit.trim(); + int16_t word = 0x0000; + auto st = opEdit.tokenize(","); + if (instEdit.str() == "mov") + { + m_code.push_back(0x00); + int16_t word1 = 0x0000; + int16_t word2 = 0x0000; + eOperandType opType1 = parseOperand(st.next(), word1); + if (opType1 == eOperandType::Register) + { + m_code.push_back((uint8_t)word1); + eOperandType opType2 = parseOperand(st.next(), word2); + switch (opType2) + { + case eOperandType::Immediate: + case eOperandType::Label: + m_code[m_code.size() - 2] = data::OpCodes::MovImmReg; + m_code.push_back((uint8_t)((word2 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word2 & 0x00FF)); + break; + case eOperandType::Register: + m_code[m_code.size() - 2] = data::OpCodes::MovRegReg; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::DerefMemory: + m_code[m_code.size() - 2] = data::OpCodes::MovMemReg; + m_code.push_back((uint8_t)((word2 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word2 & 0x00FF)); + break; + case eOperandType::DerefRegister: + m_code[m_code.size() - 2] = data::OpCodes::MovDerefRegReg; + m_code.push_back((uint8_t)word2); + return; + break; + default: + std::cout << "Invalid operand type; " << line << " (" << opEdit << ")\n"; + exit(0); + break; + } + } + else if (opType1 == eOperandType::DerefMemory) + { + m_code.push_back((uint8_t)((word1 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word1 & 0x00FF)); + eOperandType opType2 = parseOperand(st.next(), word2); + switch (opType2) + { + case eOperandType::Register: + m_code[m_code.size() - 3] = data::OpCodes::MovRegMem; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::DerefRegister: + m_code[m_code.size() - 3] = data::OpCodes::MovDerefRegMem; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::Immediate: + case eOperandType::Label: + m_code[m_code.size() - 3] = data::OpCodes::MovImmMem; + m_code.push_back((uint8_t)((word2 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word2 & 0x00FF)); + break; + default: + std::cout << "Invalid operand type; " << line << " (" << opEdit << ")\n"; + exit(0); + break; + } + } + else if (opType1 == eOperandType::DerefRegister) + { + m_code.push_back((uint8_t)word1); + eOperandType opType2 = parseOperand(st.next(), word2); + switch (opType2) + { + case eOperandType::Register: + m_code[m_code.size() - 2] = data::OpCodes::MovRegDerefReg; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::DerefMemory: + m_code[m_code.size() - 2] = data::OpCodes::MovMemDerefReg; + m_code.push_back((uint8_t)((word2 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word2 & 0x00FF)); + break; + case eOperandType::Immediate: + case eOperandType::Label: + m_code[m_code.size() - 2] = data::OpCodes::MovImmDerefReg; + m_code.push_back((uint8_t)((word2 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word2 & 0x00FF)); + break; + case eOperandType::DerefRegister: + m_code[m_code.size() - 2] = data::OpCodes::MovDerefRegDerefReg; + m_code.push_back((uint8_t)word2); + return; + break; + default: + std::cout << "Invalid operand type; " << line << " (" << opEdit << ")\n"; + exit(0); + break; + } + } + return; + } + else if (instEdit.str() == "movb") + { + m_code.push_back(0x00); + int16_t word1 = 0x0000; + int16_t word2 = 0x0000; + eOperandType opType1 = parseOperand(st.next(), word1); + if (opType1 == eOperandType::DerefMemory) + { + m_code.push_back((uint8_t)((word1 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word1 & 0x00FF)); + eOperandType opType2 = parseOperand(st.next(), word2); + switch (opType2) + { + case eOperandType::Immediate: + m_code[m_code.size() - 3] = data::OpCodes::MovByteImmMem; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::DerefRegister: + m_code[m_code.size() - 3] = data::OpCodes::MovByteDerefRegMem; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::Register: + m_code[m_code.size() - 3] = data::OpCodes::MovByteRegMem; + m_code.push_back((uint8_t)word2); + break; + default: + std::cout << "Invalid operand type; " << line << " (" << opEdit << ")\n"; + exit(0); + break; + } + } + else if (opType1 == eOperandType::DerefRegister) + { + m_code.push_back((uint8_t)word1); + eOperandType opType2 = parseOperand(st.next(), word2); + switch (opType2) + { + case eOperandType::Register: + m_code[m_code.size() - 2] = data::OpCodes::MovByteRegDerefReg; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::DerefMemory: + m_code[m_code.size() - 2] = data::OpCodes::MovByteMemDerefReg; + m_code.push_back((uint8_t)((word2 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word2 & 0x00FF)); + break; + case eOperandType::Immediate: + m_code[m_code.size() - 2] = data::OpCodes::MovByteImmDerefReg; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::DerefRegister: + m_code[m_code.size() - 2] = data::OpCodes::MovByteDerefRegDerefReg; + m_code.push_back((uint8_t)word2); + return; + break; + default: + std::cout << "Invalid operand type; " << line << " (" << opEdit << ")\n"; + exit(0); + break; + } + } + else if (opType1 == eOperandType::Register) + { + m_code.push_back((uint8_t)word1); + eOperandType opType2 = parseOperand(st.next(), word2); + switch (opType2) + { + case eOperandType::Immediate: + m_code[m_code.size() - 2] = data::OpCodes::MovByteImmReg; + m_code.push_back((uint8_t)word2); + break; + case eOperandType::DerefMemory: + m_code[m_code.size() - 2] = data::OpCodes::MovByteMemReg; + m_code.push_back((uint8_t)((word2 & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word2 & 0x00FF)); + break; + case eOperandType::DerefRegister: + m_code[m_code.size() - 2] = data::OpCodes::MovByteDerefRegReg; + m_code.push_back((uint8_t)word2); + return; + break; + default: + std::cout << "Invalid operand type; " << line << " (" << opEdit << ")\n"; + exit(0); + break; + } + } + return; + } + else if (instEdit.str() == "add" || instEdit.str() == "sub" || instEdit.str() == "mul") + { + m_code.push_back(0x00); + eOperandType opType = parseOperand(st.next(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back((uint8_t)word); + opType = parseOperand(st.next(), word); + + if (opType == eOperandType::Immediate) + { + if (instEdit.str() == "add") m_code[m_code.size() - 2] = data::OpCodes::AddImmReg; + else if (instEdit.str() == "sub") m_code[m_code.size() - 2] = data::OpCodes::SubImmReg; + else if (instEdit.str() == "mul") m_code[m_code.size() - 2] = data::OpCodes::MulImmReg; + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + } + else if (opType == eOperandType::Register) + { + if (instEdit.str() == "add") m_code[m_code.size() - 2] = data::OpCodes::AddRegReg; + else if (instEdit.str() == "sub") m_code[m_code.size() - 2] = data::OpCodes::SubRegReg; + else if (instEdit.str() == "mul") m_code[m_code.size() - 2] = data::OpCodes::MulRegReg; + m_code.push_back((uint8_t)word); + } + else + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Immediate or register required\n"; + exit(0); + return; + } + return; + } + else if (instEdit.str() == "lsh" || instEdit.str() == "rsh" || instEdit.str() == "and" || instEdit.str() == "or" || instEdit.str() == "xor") + { + eOperandType opType = parseOperand(st.next(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + uint8_t regAddr = (uint8_t)word; + opType = parseOperand(st.next(), word); + if (opType == eOperandType::Immediate) + { + if (instEdit.str() == "lsh") m_code.push_back(data::OpCodes::LShiftRegImm); + else if (instEdit.str() == "rsh") m_code.push_back(data::OpCodes::RShiftRegImm); + else if (instEdit.str() == "and") m_code.push_back(data::OpCodes::AndRegImm); + else if (instEdit.str() == "or") m_code.push_back(data::OpCodes::OrRegImm); + else if (instEdit.str() == "xor") m_code.push_back(data::OpCodes::XorRegImm); + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + m_code.push_back((uint8_t)regAddr); + } + else if (opType == eOperandType::Register) + { + if (instEdit.str() == "lsh") m_code.push_back(data::OpCodes::LShiftRegReg); + else if (instEdit.str() == "rsh") m_code.push_back(data::OpCodes::RShiftRegReg); + else if (instEdit.str() == "and") m_code.push_back(data::OpCodes::AndRegReg); + else if (instEdit.str() == "or") m_code.push_back(data::OpCodes::OrRegReg); + else if (instEdit.str() == "xor") m_code.push_back(data::OpCodes::XorRegReg); + m_code.push_back((uint8_t)word); + m_code.push_back((uint8_t)regAddr); + } + else + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register or immediate required\n"; + exit(0); + return; + } + return; + } + else if (instEdit.str() == "jne" || instEdit.str() == "jeq" || instEdit.str() == "jgr" || instEdit.str() == "jls" || instEdit.str() == "jge" || instEdit.str() == "jle") + { + m_code.push_back(0x00); + eOperandType opType = parseOperand(st.next(), word); + if (opType != eOperandType::Immediate && opType != eOperandType::Label) + { + std::cout << (int)opType << "\n"; + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Immediate (or label) required\n"; + exit(0); + return; + } + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + opType = parseOperand(st.next(), word); + + if (opType == eOperandType::Immediate) + { + if (instEdit.str() == "jeq") m_code[m_code.size() - 3] = data::OpCodes::JmpEqImm; + else if (instEdit.str() == "jne") m_code[m_code.size() - 3] = data::OpCodes::JmpNotEqImm; + else if (instEdit.str() == "jgr") m_code[m_code.size() - 3] = data::OpCodes::JmpGrImm; + else if (instEdit.str() == "jls") m_code[m_code.size() - 3] = data::OpCodes::JmpLessImm; + else if (instEdit.str() == "jge") m_code[m_code.size() - 3] = data::OpCodes::JmpGeImm; + else if (instEdit.str() == "jle") m_code[m_code.size() - 3] = data::OpCodes::JmpLeImm; + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + } + else if (opType == eOperandType::Register) + { + if (instEdit.str() == "jeq") m_code[m_code.size() - 3] = data::OpCodes::JmpEqReg; + else if (instEdit.str() == "jne") m_code[m_code.size() - 3] = data::OpCodes::JmpNotEqReg; + else if (instEdit.str() == "jgr") m_code[m_code.size() - 3] = data::OpCodes::JmpGrReg; + else if (instEdit.str() == "jls") m_code[m_code.size() - 3] = data::OpCodes::JmpLessReg; + else if (instEdit.str() == "jge") m_code[m_code.size() - 3] = data::OpCodes::JmpGeReg; + else if (instEdit.str() == "jle") m_code[m_code.size() - 3] = data::OpCodes::JmpLeReg; + m_code.push_back((uint8_t)word); + } + else + { + std::cout << "Invalid operand type: " << line << " (" << opEdit << ") -> Immediate or register required\n"; + exit(0); + return; + } + return; + } + else + { + std::cout << "Unknown instruction: " << line << " (" << instEdit << ")\n"; + exit(0); + } + } + + void Assembler::parse3Operand(ostd::String line) + { + ostd::StringEditor lineEdit(line); + ostd::StringEditor instEdit(lineEdit.substr(0, lineEdit.indexOf(" "))); + instEdit.trim().toLower(); + ostd::StringEditor opEdit(lineEdit.substr(lineEdit.indexOf(" ") + 1)); + opEdit.trim(); + int16_t word = 0x0000; + if (instEdit.str() == "mov") + { + auto st = opEdit.tokenize(","); + eOperandType opType = parseOperand(st.next(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back(data::OpCodes::MovImmRegOffReg); + m_code.push_back((uint8_t)word); + opType = parseOperand(st.next(), word); + if (opType != eOperandType::DerefMemory) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Pointer required\n"; + exit(0); + return; + } + m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); + m_code.push_back((uint8_t)(word & 0x00FF)); + opType = parseOperand(st.next(), word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back((uint8_t)word); + return; + } + else + { + std::cout << "Unknown instruction; " << line << " (" << instEdit << ")\n"; + exit(0); + } + } + + void Assembler::combineDataAndCode(void) + { + uint16_t entryAddr = m_dataSize + m_loadAddress + 3; + std::vector symbols; + ostd::ByteStream newCode; + newCode.push_back(data::OpCodes::Jmp); + newCode.push_back((uint8_t)((entryAddr & 0xFF00) >> 8)); + newCode.push_back((uint8_t)(entryAddr & 0x00FF)); + if (m_dataSize > 0) + m_disassembly.insert(m_disassembly.begin(), { (uint16_t)(m_loadAddress + 3), "[----------DATA_SECTION----------]" }); + m_disassembly.insert(m_disassembly.begin(), { m_loadAddress, ostd::StringEditor("jmp ").add(ostd::Utils::getHexStr(entryAddr, true, 2)).str() }); + for (auto& d : m_symbolTable) + symbols.push_back(d.second); + for (int32_t i = symbols.size() - 1; i >= 0; i--) + { + for (auto& dd : symbols[i].bytes) + newCode.push_back(dd); + } + for (auto& b : m_code) + newCode.push_back(b); + m_code = newCode; + + m_disassembly.push_back({ 0xFFFFFF00, "{ DATA }" }); + for (auto& d : m_symbolTable) + { + m_disassembly.push_back({ d.second.address, d.first }); + } + m_disassembly.push_back({ 0xFFFFFF01, "{ LABELS }" }); + for (auto& l : m_labelTable) + { + m_disassembly.push_back({ l.second.address, l.first }); + } + } + + ostd::String Assembler::replaceSymbols(ostd::String line) + { + ostd::StringEditor lineEdit(line); + for (auto& symbol : m_symbolTable) + lineEdit.replaceAll(symbol.first, ostd::Utils::getHexStr(symbol.second.address, true, 2)); + return lineEdit.str(); + } + + void Assembler::replaceLabelRefs(void) + { + for (auto& label : m_labelTable) + { + for (auto& addr : label.second.references) + { + m_code[addr] = (uint8_t)((label.second.address & 0xFF00) >> 8); + m_code[addr + 1] = (uint8_t)(label.second.address & 0x00FF); + } + } + } + + Assembler::eOperandType Assembler::parseOperand(ostd::String op, int16_t& outOp) + { + ostd::StringEditor opEdit(op); + bool derefReg = false; + if (opEdit.startsWith("*")) + { + opEdit = opEdit.substr(1); + opEdit.trim(); + derefReg = true; + } + int8_t reg = parseRegister(opEdit.str()); + if (reg != data::Registers::Last) + { + outOp = (int16_t)reg; + if (derefReg) + return eOperandType::DerefRegister; + return eOperandType::Register; + } + if (derefReg) + { + std::cout << "Invalid Register Dereferencing: " << op << "\n"; + return eOperandType::Error; + } + if (opEdit.isNumeric()) + { + outOp = (int16_t)opEdit.toInt(); + return eOperandType::Immediate; + } + if (opEdit.startsWith("$")) + { + if (m_labelTable.count(opEdit.str()) == 0) + { + std::cout << "Unknown symbol: " << opEdit << "\n"; + return eOperandType::Error; + } + uint16_t labelAddr = m_labelTable[opEdit.str()].address; + if (labelAddr == 0x0000) + m_labelTable[opEdit.str()].references.push_back(m_code.size()); + outOp = (int16_t)labelAddr; + return eOperandType::Label; + } + if (opEdit.startsWith("{") && opEdit.endsWith("}")) + { + opEdit = opEdit.substr(1, opEdit.len() - 1); + opEdit.trim(); + outOp = (int16_t)ostd::Utils::solveIntegerExpression(opEdit.str()); + return eOperandType::Immediate; + } + if (opEdit.startsWith("[") && opEdit.endsWith("]")) + { + opEdit = opEdit.substr(1, opEdit.len() - 1); + opEdit.trim(); + if (opEdit.startsWith("{") && opEdit.endsWith("}")) + { + opEdit = opEdit.substr(1, opEdit.len() - 1); + opEdit.trim(); + outOp = (int16_t)ostd::Utils::solveIntegerExpression(opEdit.str()); + return eOperandType::DerefMemory; + } + if (!opEdit.isNumeric()) + { + std::cout << "Invalid numeric value: " << opEdit << "\n"; + return eOperandType::Error; + } + outOp = (int16_t)opEdit.toInt(); + return eOperandType::DerefMemory; + } + return eOperandType::Error; + } + + uint8_t Assembler::parseRegister(ostd::String op) + { + ostd::StringEditor opEdit(op); + opEdit.trim().toLower(); + if (opEdit.str() == "r1") return data::Registers::R1; + if (opEdit.str() == "r2") return data::Registers::R2; + if (opEdit.str() == "r3") return data::Registers::R3; + if (opEdit.str() == "r4") return data::Registers::R4; + if (opEdit.str() == "r5") return data::Registers::R5; + if (opEdit.str() == "r6") return data::Registers::R6; + if (opEdit.str() == "r7") return data::Registers::R7; + if (opEdit.str() == "r8") return data::Registers::R8; + if (opEdit.str() == "r9") return data::Registers::R9; + if (opEdit.str() == "r10") return data::Registers::R10; + if (opEdit.str() == "acc") return data::Registers::ACC; + if (opEdit.str() == "fp") return data::Registers::FP; + if (opEdit.str() == "sp") return data::Registers::SP; + if (opEdit.str() == "ip") return data::Registers::IP; + if (opEdit.str() == "pp") return data::Registers::PP; + if (opEdit.str() == "rv") return data::Registers::RV; + if (opEdit.str() == "fl") return data::Registers::FL; + return data::Registers::Last; + } + + void Assembler::tempPrint(void) + { + for (auto& symbol : m_symbolTable) + { + std::cout << symbol.first << "\n"; + std::cout << ostd::Utils::getHexStr(symbol.second.address, true, 2) << "\n"; + for (auto& b : symbol.second.bytes) + std::cout << ostd::Utils::getHexStr(b, true, 1) << " "; + std::cout << "\n\n"; + } + + std::cout << "Fixed Size: " << (int)m_fixedSize << "\n"; + std::cout << "Fixed Fill: " << ostd::Utils::getHexStr(m_fixedFillValue, true, 1) << "\n"; + std::cout << "Load Address: " << ostd::Utils::getHexStr(m_loadAddress, true, 2) << "\n"; + std::cout << "Data Size: " << (int)m_dataSize << "\n"; + std::cout << "Code Start Address: " << ostd::Utils::getHexStr(m_dataSize + m_loadAddress + 3, true, 2) << "\n"; + std::cout << "Program Size: " << (int)m_programSize << "\n"; + + for (auto& label : m_labelTable) + { + std::cout << label.first << " " << ostd::Utils::getHexStr(label.second.address, true, 2) << "\n"; + } + + ostd::ConsoleOutputHandler out; + ostd::Utils::printByteStream(m_code, 0x0000, 16, 8, out); + std::cout << "\n\n\n\n"; + } + } +} \ No newline at end of file diff --git a/src/assembler/Assembler.hpp b/src/assembler/Assembler.hpp new file mode 100644 index 0000000..4cbb58b --- /dev/null +++ b/src/assembler/Assembler.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +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 bytes; + uint16_t address { 0 }; + }; + public: struct tLabel + { + std::vector 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 m_lines; + inline static std::vector m_rawDataSection; + inline static std::vector m_rawCodeSection; + inline static std::unordered_map m_symbolTable; + inline static std::unordered_map 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 m_disassembly; + }; + } +} \ No newline at end of file diff --git a/src/assembler/assembler_main.cpp b/src/assembler/assembler_main.cpp new file mode 100644 index 0000000..48cd970 --- /dev/null +++ b/src/assembler/assembler_main.cpp @@ -0,0 +1,60 @@ +#include "Assembler.hpp" +#include +#include + +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 -o [...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; +} \ No newline at end of file diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp new file mode 100644 index 0000000..cfaa7a2 --- /dev/null +++ b/src/debugger/Debugger.cpp @@ -0,0 +1,829 @@ +#include "Debugger.hpp" +#include "../runtime/DragonRuntime.hpp" +#include "DisassemblyLoader.hpp" +#include + +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 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]+|(? ").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 to continue execution...").nl().reset(); + std::cin.get(); + } + + void Debugger::Display::printTrackedAddresses(const std::vector& 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; + } + +} \ No newline at end of file diff --git a/src/debugger/Debugger.hpp b/src/debugger/Debugger.hpp new file mode 100644 index 0000000..24acb47 --- /dev/null +++ b/src/debugger/Debugger.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include "../assembler/Assembler.hpp" + +namespace dragon +{ + typedef std::vector 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 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& 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; + }; +} \ No newline at end of file diff --git a/src/debugger/DisassemblyLoader.cpp b/src/debugger/DisassemblyLoader.cpp new file mode 100644 index 0000000..f0f444f --- /dev/null +++ b/src/debugger/DisassemblyLoader.cpp @@ -0,0 +1,137 @@ +#include "DisassemblyLoader.hpp" + +#include +#include + +#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 DisassemblyLoader::getCodeTable(void) + { + std::vector fullTable; + for (auto& table : m_tables) + fullTable.insert(fullTable.end(), table.getCodeTable().begin(), table.getCodeTable().end()); + return fullTable; + } + + std::vector DisassemblyLoader::getDataTable(void) + { + std::vector fullTable; + for (auto& table : m_tables) + fullTable.insert(fullTable.end(), table.getDataTable().begin(), table.getDataTable().end()); + return fullTable; + } + + std::vector DisassemblyLoader::getLabelTable(void) + { + std::vector fullTable; + for (auto& table : m_tables) + fullTable.insert(fullTable.end(), table.getLabelTable().begin(), table.getLabelTable().end()); + return fullTable; + } +} \ No newline at end of file diff --git a/src/debugger/DisassemblyLoader.hpp b/src/debugger/DisassemblyLoader.hpp new file mode 100644 index 0000000..a959020 --- /dev/null +++ b/src/debugger/DisassemblyLoader.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "../assembler/Assembler.hpp" + +#include + +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& getCodeTable(void) const { return m_code; } + inline const std::vector& getDataTable(void) const { return m_data; } + inline const std::vector& getLabelTable(void) const { return m_labels; } + + inline bool isInitialized(void) const { return m_initialized; } + + private: + void load_data(ostd::ByteStream& stream); + + private: + std::vector m_code; + std::vector m_labels; + std::vector 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 getCodeTable(void); + static std::vector getDataTable(void); + static std::vector getLabelTable(void); + + private: + inline static std::vector m_tables; + }; +} \ No newline at end of file diff --git a/src/debugger/debugger_main.cpp b/src/debugger/debugger_main.cpp new file mode 100644 index 0000000..e942adf --- /dev/null +++ b/src/debugger/debugger_main.cpp @@ -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 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; +} \ No newline at end of file diff --git a/src/gui/DebuggerWindow.cpp b/src/gui/DebuggerWindow.cpp new file mode 100644 index 0000000..792d429 --- /dev/null +++ b/src/gui/DebuggerWindow.cpp @@ -0,0 +1,6 @@ +#include "DebuggerWindow.hpp" + +namespace dragon +{ + +} \ No newline at end of file diff --git a/src/gui/DebuggerWindow.hpp b/src/gui/DebuggerWindow.hpp new file mode 100644 index 0000000..7666a76 --- /dev/null +++ b/src/gui/DebuggerWindow.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace dragon +{ + class DebuggerWindow + { + + }; +} // namespace dragon diff --git a/src/gui/RawTextRenderer.cpp b/src/gui/RawTextRenderer.cpp new file mode 100644 index 0000000..36d3aaf --- /dev/null +++ b/src/gui/RawTextRenderer.cpp @@ -0,0 +1,101 @@ +#include "RawTextRenderer.hpp" +#include +#include +#include + +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; + } + } +} \ No newline at end of file diff --git a/src/gui/RawTextRenderer.hpp b/src/gui/RawTextRenderer.hpp new file mode 100644 index 0000000..9b0fc7d --- /dev/null +++ b/src/gui/RawTextRenderer.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +namespace dragon +{ + class RawTextRenderer + { + private: + inline static std::unordered_map 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; + }; +} \ No newline at end of file diff --git a/src/gui/VirtualConsole.cpp b/src/gui/VirtualConsole.cpp new file mode 100644 index 0000000..e2db9b9 --- /dev/null +++ b/src/gui/VirtualConsole.cpp @@ -0,0 +1,158 @@ +#include "VirtualConsole.hpp" +#include "RawTextRenderer.hpp" +#include + +#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); + } +} \ No newline at end of file diff --git a/src/gui/VirtualConsole.hpp b/src/gui/VirtualConsole.hpp new file mode 100644 index 0000000..0ae5b03 --- /dev/null +++ b/src/gui/VirtualConsole.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "../tools/SDLInclude.hpp" + +#include +#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 m_palettes; + }; +} \ No newline at end of file diff --git a/src/gui/VirtualConsoleOutputHandler.cpp b/src/gui/VirtualConsoleOutputHandler.cpp new file mode 100644 index 0000000..5e738b6 --- /dev/null +++ b/src/gui/VirtualConsoleOutputHandler.cpp @@ -0,0 +1,303 @@ +#include "VirtualConsoleOutputHandler.hpp" +#include "RawTextRenderer.hpp" + +#include +#include + +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(); + } +} \ No newline at end of file diff --git a/src/gui/VirtualConsoleOutputHandler.hpp b/src/gui/VirtualConsoleOutputHandler.hpp new file mode 100644 index 0000000..666b114 --- /dev/null +++ b/src/gui/VirtualConsoleOutputHandler.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +#include + +#include + +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 m_foreground; + std::vector 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 m_lines; + RichString m_currentBuffer; + ostd::StringEditor m_currentBufferEditor; + + // std::vector 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 }; + }; +} \ No newline at end of file diff --git a/src/gui/Window.cpp b/src/gui/Window.cpp new file mode 100644 index 0000000..dd624e9 --- /dev/null +++ b/src/gui/Window.cpp @@ -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); + } +} \ No newline at end of file diff --git a/src/gui/Window.hpp b/src/gui/Window.hpp new file mode 100644 index 0000000..2823d5f --- /dev/null +++ b/src/gui/Window.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "../tools/SDLInclude.hpp" +#include +#include +#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 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; + }; +} \ No newline at end of file diff --git a/src/gui/widgets/VirtualConsoleWidget.cpp b/src/gui/widgets/VirtualConsoleWidget.cpp new file mode 100644 index 0000000..6445c96 --- /dev/null +++ b/src/gui/widgets/VirtualConsoleWidget.cpp @@ -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) + { + + } + +} \ No newline at end of file diff --git a/src/gui/widgets/VirtualConsoleWidget.hpp b/src/gui/widgets/VirtualConsoleWidget.hpp new file mode 100644 index 0000000..8258e51 --- /dev/null +++ b/src/gui/widgets/VirtualConsoleWidget.hpp @@ -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 m_palettes; + }; +} \ No newline at end of file diff --git a/src/gui/widgets/Widget.hpp b/src/gui/widgets/Widget.hpp new file mode 100644 index 0000000..cdcc23b --- /dev/null +++ b/src/gui/widgets/Widget.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +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 }; + }; +} \ No newline at end of file diff --git a/src/hardware/IMemoryDevice.hpp b/src/hardware/IMemoryDevice.hpp new file mode 100644 index 0000000..47d1a77 --- /dev/null +++ b/src/hardware/IMemoryDevice.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +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; + }; + } +} \ No newline at end of file diff --git a/src/hardware/MemoryMapper.cpp b/src/hardware/MemoryMapper.cpp new file mode 100644 index 0000000..518d8bd --- /dev/null +++ b/src/hardware/MemoryMapper.cpp @@ -0,0 +1,75 @@ +#include "MemoryMapper.hpp" +#include +#include + +#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 + } + } +} \ No newline at end of file diff --git a/src/hardware/MemoryMapper.hpp b/src/hardware/MemoryMapper.hpp new file mode 100644 index 0000000..525deec --- /dev/null +++ b/src/hardware/MemoryMapper.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "IMemoryDevice.hpp" +#include + +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 m_regions; + }; + } +} \ No newline at end of file diff --git a/src/hardware/VirtualCPU.cpp b/src/hardware/VirtualCPU.cpp new file mode 100644 index 0000000..8a98026 --- /dev/null +++ b/src/hardware/VirtualCPU.cpp @@ -0,0 +1,707 @@ +#include "VirtualCPU.hpp" +#include "../tools/GlobalData.hpp" + +#include +#include + +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; + } + } +} \ No newline at end of file diff --git a/src/hardware/VirtualCPU.hpp b/src/hardware/VirtualCPU.hpp new file mode 100644 index 0000000..4ffdf89 --- /dev/null +++ b/src/hardware/VirtualCPU.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#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; + }; + } +} \ No newline at end of file diff --git a/src/hardware/VirtualHardDrive.cpp b/src/hardware/VirtualHardDrive.cpp new file mode 100644 index 0000000..704abd9 --- /dev/null +++ b/src/hardware/VirtualHardDrive.cpp @@ -0,0 +1,106 @@ +#include "VirtualHardDrive.hpp" + +#include "../tools/GlobalData.hpp" +#include + +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; + } + } +} \ No newline at end of file diff --git a/src/hardware/VirtualHardDrive.hpp b/src/hardware/VirtualHardDrive.hpp new file mode 100644 index 0000000..c08027c --- /dev/null +++ b/src/hardware/VirtualHardDrive.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +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; + }; + } +} \ No newline at end of file diff --git a/src/hardware/VirtualIODevices.cpp b/src/hardware/VirtualIODevices.cpp new file mode 100644 index 0000000..4d2cef4 --- /dev/null +++ b/src/hardware/VirtualIODevices.cpp @@ -0,0 +1,655 @@ +#include "VirtualIODevices.hpp" +#include + +#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(memory.getByteStream()->data()); + iMemPtr += data::MemoryMapAddresses::BIOSVideo_Start; + m_dataPtr = reinterpret_cast(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: register must be set to 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: register set to 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: must be set to or 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; + } + } + } +} \ No newline at end of file diff --git a/src/hardware/VirtualIODevices.hpp b/src/hardware/VirtualIODevices.hpp new file mode 100644 index 0000000..c67ea57 --- /dev/null +++ b/src/hardware/VirtualIODevices.hpp @@ -0,0 +1,237 @@ +#pragma once + +#include "IMemoryDevice.hpp" +#include "../tools/GlobalData.hpp" +#include +#include + +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 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 }; + }; + } + } +} \ No newline at end of file diff --git a/src/hardware/VirtualRAM.cpp b/src/hardware/VirtualRAM.cpp new file mode 100644 index 0000000..4bb212f --- /dev/null +++ b/src/hardware/VirtualRAM.cpp @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/hardware/VirtualRAM.hpp b/src/hardware/VirtualRAM.hpp new file mode 100644 index 0000000..472e6b0 --- /dev/null +++ b/src/hardware/VirtualRAM.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#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; + }; + } +} \ No newline at end of file diff --git a/src/runtime/ConfigLoader.cpp b/src/runtime/ConfigLoader.cpp new file mode 100644 index 0000000..fc5b342 --- /dev/null +++ b/src/runtime/ConfigLoader.cpp @@ -0,0 +1,53 @@ +#include "ConfigLoader.hpp" +#include +#include + +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; + } +} \ No newline at end of file diff --git a/src/runtime/ConfigLoader.hpp b/src/runtime/ConfigLoader.hpp new file mode 100644 index 0000000..89ac58f --- /dev/null +++ b/src/runtime/ConfigLoader.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +namespace dragon +{ + struct tMachineConfig + { + std::map 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); + }; +} \ No newline at end of file diff --git a/src/runtime/DragonRuntime.cpp b/src/runtime/DragonRuntime.cpp new file mode 100644 index 0000000..ce04a64 --- /dev/null +++ b/src/runtime/DragonRuntime.cpp @@ -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 DragonRuntime::getErrorList(void) + { + std::vector 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 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 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; + } + } +} \ No newline at end of file diff --git a/src/runtime/DragonRuntime.hpp b/src/runtime/DragonRuntime.hpp new file mode 100644 index 0000000..3acf05d --- /dev/null +++ b/src/runtime/DragonRuntime.hpp @@ -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 trackedAddresses; + std::vector previousInstructionTrackedValues; + std::vector 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 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 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 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 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 }; + }; +} \ No newline at end of file diff --git a/src/runtime/runtime_main.cpp b/src/runtime/runtime_main.cpp new file mode 100644 index 0000000..4243f96 --- /dev/null +++ b/src/runtime/runtime_main.cpp @@ -0,0 +1,74 @@ +#include + +#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; +} diff --git a/src/tools/GlobalData.hpp b/src/tools/GlobalData.hpp new file mode 100644 index 0000000..bb94e4b --- /dev/null +++ b/src/tools/GlobalData.hpp @@ -0,0 +1,424 @@ +#pragma once + +#include +#include + +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 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 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; + } + } + + }; + } +} \ No newline at end of file diff --git a/src/tools/SDLInclude.hpp b/src/tools/SDLInclude.hpp new file mode 100644 index 0000000..56c17b3 --- /dev/null +++ b/src/tools/SDLInclude.hpp @@ -0,0 +1,7 @@ +#pragma once + +#ifdef _WIN32 +#undef __linux__ +#endif + +#include \ No newline at end of file diff --git a/src/tools/Utils.cpp b/src/tools/Utils.cpp new file mode 100644 index 0000000..6f0e150 --- /dev/null +++ b/src/tools/Utils.cpp @@ -0,0 +1,7 @@ +#include "Utils.hpp" +#include +#include + +namespace dragon +{ +} \ No newline at end of file diff --git a/src/tools/Utils.hpp b/src/tools/Utils.hpp new file mode 100644 index 0000000..ac76547 --- /dev/null +++ b/src/tools/Utils.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace dragon +{ + class Utils + { + public: + }; +} \ No newline at end of file diff --git a/src/tools/tools_main.cpp b/src/tools/tools_main.cpp new file mode 100644 index 0000000..b9dc196 --- /dev/null +++ b/src/tools/tools_main.cpp @@ -0,0 +1,122 @@ +#include +#include +#include + +#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 [...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 ").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: 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 ").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: 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; +} \ No newline at end of file diff --git a/tools/CppResourceMaker b/tools/CppResourceMaker new file mode 100644 index 0000000..6d78724 Binary files /dev/null and b/tools/CppResourceMaker differ diff --git a/tools/build.nr b/tools/build.nr new file mode 100644 index 0000000..fa94e88 --- /dev/null +++ b/tools/build.nr @@ -0,0 +1 @@ +1536 diff --git a/tools/inc_bnr b/tools/inc_bnr new file mode 100644 index 0000000..4a0e37b Binary files /dev/null and b/tools/inc_bnr differ diff --git a/tools/inc_bnr.exe b/tools/inc_bnr.exe new file mode 100644 index 0000000..ba3b943 Binary files /dev/null and b/tools/inc_bnr.exe differ