From 42f25f4e0b706f3b00588ec854635115b9c68753 Mon Sep 17 00:00:00 2001 From: OmniaX Date: Thu, 11 Jan 2024 01:36:29 +0100 Subject: [PATCH] [0.2.1575] - UI Greatly improved + Debugger functionality added --- CMakeLists.txt | 7 +- build.nr | 2 +- create_win_release.sh | 21 +- extra/dragon/bios.bin | Bin 4096 -> 4096 bytes extra/dss/bios.dss | 75 ++- extra/dss/handlerTest.dss | 92 ---- extra/dss/newTest.dss | 108 ++-- extra/dss/test.dss | 103 +++- extra/make_and_run | 5 +- extra/scripts/bios_flash | 2 +- extra/scripts/load_mbr | 4 +- run-test.bat | 3 +- src/assembler/Assembler.cpp | 471 +++++++++++++++-- src/assembler/Assembler.hpp | 54 +- src/assembler/assembler_main.cpp | 59 +-- src/debugger/Debugger.cpp | 799 +++++++++++++++++++++++++---- src/debugger/Debugger.hpp | 34 +- src/debugger/DisassemblyLoader.cpp | 4 + src/debugger/debugger_main.cpp | 137 +---- src/gui/Window.cpp | 22 +- src/gui/Window.hpp | 7 +- src/hardware/VirtualCPU.cpp | 10 + src/runtime/DragonRuntime.cpp | 170 ++++-- src/runtime/DragonRuntime.hpp | 32 +- src/runtime/runtime_main.cpp | 76 +-- src/tools/GlobalData.hpp | 3 + src/tools/SDLInclude.hpp | 3 +- src/tools/Tools.cpp | 156 ++++++ src/tools/Tools.hpp | 35 ++ src/tools/tools_main.cpp | 120 +---- 30 files changed, 1857 insertions(+), 757 deletions(-) delete mode 100644 extra/dss/handlerTest.dss create mode 100644 src/tools/Tools.cpp create mode 100644 src/tools/Tools.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d7d152f..79e4a8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ #----------------------------------------------------------------------------------------- set(PROJ_NAME DragonVM) set(MAJOR_VER 0) -set(MINOR_VER 1) +set(MINOR_VER 2) #----------------------------------------------------------------------------------------- #Setup @@ -72,6 +72,7 @@ list(APPEND ASSEMBLER_SOURCE_FILES 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/tools/Tools.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp ) @@ -115,8 +116,8 @@ if (WIN32) 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} SDL2main SDL2) +target_link_libraries(${DEBUGGER_TARGET} SDL2main SDL2) #----------------------------------------------------------------------------------------- #Linking diff --git a/build.nr b/build.nr index df9d396..340b351 100644 --- a/build.nr +++ b/build.nr @@ -1 +1 @@ -1544 +1576 diff --git a/create_win_release.sh b/create_win_release.sh index d9b31bb..fcd10a4 100644 --- a/create_win_release.sh +++ b/create_win_release.sh @@ -7,33 +7,35 @@ rm -r ./win-release cd bin printf "${green}Compiling vBIOS...\n${clear}" -./dasm dss/bios.dss -o dragon/bios.bin --save-disassembly disassembly/bios.dds +./dasm dss/bios.dss -o dragon/bios.bin --save-disassembly disassembly/bios.dds --verbose printf "\n${green}Creating Virtual Disk...\n${clear}" rm dragon/disk1.dr ./dtools new-vdisk dragon/disk1.dr 1048576 printf "\n${green}Compiling MBR Block...\n${clear}" -./dasm dss/mbr.dss -o dragon/mbr.bin --save-disassembly disassembly/mbr.dds +./dasm dss/mbr.dss -o dragon/mbr.bin --save-disassembly disassembly/mbr.dds --verbose printf "\n${green}Loading MBR Block...\n${clear}" -./dtools load-program dragon/disk1.dr dragon/mbr.bin 0x00000000 +./dtools load-binary dragon/disk1.dr dragon/mbr.bin 0x00000000 printf "${green}Compiling Test Program...\n" -./dasm dss/test.dss -o test.bin --save-disassembly disassembly/test.dds +./dasm dss/test.dss -o test.bin --save-disassembly disassembly/test.dds --verbose printf "${green}Creating Windows Release...\n" cd .. mkdir win-release +mkdir win-release/disassembly cp ./bin/dasm.exe ./win-release cp ./bin/ddb.exe ./win-release cp ./bin/dtools.exe ./win-release cp ./bin/dvm.exe ./win-release cp ./bin/font.bmp ./win-release -cp ./bin/test.bin ./win-release +# cp ./bin/test.bin ./win-release cp -r ./bin/config ./win-release -cp -r ./bin/disassembly ./win-release +cp -r ./bin/disassembly/bios.dds ./win-release/disassembly +cp -r ./bin/disassembly/mbr.dds ./win-release/disassembly cp -r ./bin/dragon ./win-release mkdir ./win-release/dss @@ -47,9 +49,4 @@ cp C:/msys64/ucrt64/bin/libwinpthread-1.dll ./win-release cp C:/msys64/ucrt64/bin/SDL2.dll ./win-release cp C:/msys64/ucrt64/bin/libostd.dll ./win-release -cp ./run-test.bat ./win-release - -typeset -i build_number=$(cat build.nr) -((build_number++)) -truncate -s 0 build.nr -echo $build_number >> build.nr \ No newline at end of file +cp ./run-test.bat ./win-release \ No newline at end of file diff --git a/extra/dragon/bios.bin b/extra/dragon/bios.bin index 6f9e6af6c20c9840f21d601e21bc02040580a143..82a7ed1fbdab1305680e16b028a97d25c3670deb 100644 GIT binary patch delta 211 zcmXAgu}T9$6h+Tw*(n|Y-(+_B!9uVR73}TO?y{IK2&rX@R0EC%5fKv7sz2c$So;e$ z`VU$r>n0ZWaPNVeLJH$ZL8g6zes!gObFL0_#7Zrv+c536MFkD7GqNJO)LG+N#%RkN zS{7uH<@)mEW?9>MR557y?9Hz7zc%~JGeln41b@qss2=+}Dg6RUf4{@SpvR+2!6%&Y nPuIM|)kH3Y+=@99O*S{ZsHGxn_^^^sOv^7EN*8(i86Nxr^o=h1 delta 178 zcmXAgp$@_@6h&`YS5d?3x?(UjC=vn!hu9Q$%ffd+rYq_OK`nscFOX=y0S}gcfU$u) z_v9XLy-gfK&L{-4^k`ywVoVyK2wPH^eryI5I5DSvql7MzfJ&@^5)RCvs+W^dm8(3; z&?|6nO^5ie_b)l1{T}P{aDg~BJMFFU(8BCDxB@F9`CKAUQp_ZI6^PE{G6O4cQ!+?- L%8lTc*P#Cef1)Jr diff --git a/extra/dss/bios.dss b/extra/dss/bios.dss index d150c9d..d666458 100644 --- a/extra/dss/bios.dss +++ b/extra/dss/bios.dss @@ -1,41 +1,42 @@ ## ============================= Memory Mapped Devices and Registers ============================= -@define MBR_START_ADDRESS 0x1380 -@define INT_VEC_START_ADDRESS 0x1080 -@define DISK_INTERFACE_START_ADDRESS 0x1580 -@define MEMORY_START_ADDRESS 0x1740 -@define CMOS_START_ADDRESS 0x1000 -## =============================================================================================== +@group MemoryAddresses + MBR 0x1380 + INT_VEC 0x1080 + DISK_INTERFACE 0x1580 + RAM 0x1740 + CMOS 0x1000 +@end -@define _CMOS_REG_BOOT_DISK {CMOS_START_ADDRESS + 0x0010} +@group CMOS_Settings + BOOT_DISK {MemoryAddresses.CMOS + 0x0010} +@end +## =============================================================================================== .load 0x0000 ## BIOS is mapped to address 0x0000 in memory .data - $bios_version_number 0x04, 0x01 ## BIOS Version stored in memory + $bios_version_number 0x00, 0x01 ## BIOS Version stored in memory ## ============================= BIOS Program ============================= .code ## mov FL, 0 ## jmp 0x1740 mov FL, 0b0000000000000001 ## Zero the FL Register and enable interrupts - movb [{INT_VEC_START_ADDRESS + (0x20 * 3)}], 0xFF ## Setting up int 0x20 handler - mov [{INT_VEC_START_ADDRESS + (0x20 * 3) + 1}], $_int_20_handler ## -- + movb [{MemoryAddresses.INT_VEC + (0x20 * 3)}], 0xFF ## Setting up int 0x20 handler + mov [{MemoryAddresses.INT_VEC + (0x20 * 3) + 1}], $_int_20_handler ## -- mov R10, 0x00 ## Setting up int 0x30 handler by using int 0x20 functionality mov R9, $_int_30_handler ## -Passing the handler's address mov R8, 0x30 ## -Passing the interrupt's code to setup int 0x20 ## -Calling int 0x20 with 0x00 as parameter, to set new handler up - ## TODO: Probably move this code before interrupts are enabled - ## to prevent an interrupt being fired while loading MBR - ## data from disk - ## ---- ## MBR Loading + and FL, 0b1111111111111110 ## Disable interrupts push 0 call $_load_mbr_data_block + or FL, 0b0000000000000001 ## Enable interrupts ## ---- - debug_break - jmp MBR_START_ADDRESS ## Jump to start of MBR in memory + jmp MemoryAddresses.MBR ## Jump to start of MBR in memory hlt ## Just in case somehow execution reaches this point ## ======================================================================== @@ -49,21 +50,21 @@ _int_20_handler: jeq $_int_20_clear_interrupt, 0x0001 jmp $_int_20_end _int_20_disk_interface: - movb [{DISK_INTERFACE_START_ADDRESS + 0x1}], *R9 ## Mode + movb [{MemoryAddresses.DISK_INTERFACE + 0x1}], *R9 ## Mode inc R9 - movb [{DISK_INTERFACE_START_ADDRESS + 0x2}], *R9 ## Disk + movb [{MemoryAddresses.DISK_INTERFACE + 0x2}], *R9 ## Disk inc R9 - mov [{DISK_INTERFACE_START_ADDRESS + 0x3}], *R9 ## Sector + mov [{MemoryAddresses.DISK_INTERFACE + 0x3}], *R9 ## Sector inc R9 inc R9 - mov [{DISK_INTERFACE_START_ADDRESS + 0x5}], *R9 ## Address + mov [{MemoryAddresses.DISK_INTERFACE + 0x5}], *R9 ## Address inc R9 inc R9 - mov [{DISK_INTERFACE_START_ADDRESS + 0x7}], *R9 ## Size + mov [{MemoryAddresses.DISK_INTERFACE + 0x7}], *R9 ## Size inc R9 inc R9 - mov [{DISK_INTERFACE_START_ADDRESS + 0x9}], *R9 ## Memory Address - movb [DISK_INTERFACE_START_ADDRESS], 0x00 ## Signal set to "Start Operation" + mov [{MemoryAddresses.DISK_INTERFACE + 0x9}], *R9 ## Memory Address + movb [MemoryAddresses.DISK_INTERFACE], 0x00 ## Signal set to "Start Operation" jmp $_int_20_end _int_20_set_new_interrupt_handler: push R8 ## Interrupt Code @@ -93,36 +94,34 @@ _int_30_end: _load_mbr_data_block: - movb [{DISK_INTERFACE_START_ADDRESS + 0x1}], 0x00 ## Mode = Read - movb R1, [_CMOS_REG_BOOT_DISK] - movb [{DISK_INTERFACE_START_ADDRESS + 0x2}], R1 ## Disk = Default - mov [{DISK_INTERFACE_START_ADDRESS + 0x3}],0x0000 ## Sector = 0x0000 - mov [{DISK_INTERFACE_START_ADDRESS + 0x5}],0x0000 ## Address = 0x0000 (Start of Disk) - mov [{DISK_INTERFACE_START_ADDRESS + 0x7}], 20 ## Size = 512 (Size of MBR is 512 bytes) - mov [{DISK_INTERFACE_START_ADDRESS + 0x9}], MBR_START_ADDRESS ## MemoryAddress = MBR Address in memory - movb [DISK_INTERFACE_START_ADDRESS], 0x00 ## Signal = Start + movb [{MemoryAddresses.DISK_INTERFACE + 0x1}], 0x00 ## Mode = Read + movb R1, [CMOS_Settings.BOOT_DISK] + movb [{MemoryAddresses.DISK_INTERFACE + 0x2}], R1 ## Disk = Default + mov [{MemoryAddresses.DISK_INTERFACE + 0x3}],0x0000 ## Sector = 0x0000 + mov [{MemoryAddresses.DISK_INTERFACE + 0x5}],0x0000 ## Address = 0x0000 (Start of Disk) + mov [{MemoryAddresses.DISK_INTERFACE + 0x7}], 512 ## Size = 512 (Size of MBR is 512 bytes) + mov [{MemoryAddresses.DISK_INTERFACE + 0x9}], MemoryAddresses.MBR ## MemoryAddress = MBR Address in memory + movb [MemoryAddresses.DISK_INTERFACE], 0x00 ## Signal = Start _load_mbr_data_block_wait_loop: - mov ACC, [{DISK_INTERFACE_START_ADDRESS + 0xB}] ## Moving register into ACC + mov ACC, [{MemoryAddresses.DISK_INTERFACE + 0xB}] ## Moving 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 + arg R1 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 + add R1, MemoryAddresses.INT_VEC ## Add Interrupt Vector base address to Interrupt code mov RV, ACC ret _set_interrupt_vector_entry: - mov R1, *PP + arg R1 push R1 push 1 call $_calc_interrupt_vector_address mov R1, RV - dec PP - dec PP - mov ACC, *PP + arg ACC jeq $_set_interrupt_vector_entry_disable, 0x0000 movb *R1, 0xFF jmp $_set_interrupt_vector_entry_end diff --git a/extra/dss/handlerTest.dss b/extra/dss/handlerTest.dss deleted file mode 100644 index b06d314..0000000 --- a/extra/dss/handlerTest.dss +++ /dev/null @@ -1,92 +0,0 @@ -.load 0x1740 - -##@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 -infinite_loop: - push 0 - call $clear_screen - - ## mov $rect.x, 200 - - - 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/newTest.dss b/extra/dss/newTest.dss index 8790c26..8e8b2ee 100644 --- a/extra/dss/newTest.dss +++ b/extra/dss/newTest.dss @@ -1,74 +1,80 @@ .load 0x1740 -.data - $cursor_pos 0x00, 0x00 - $color 0x02 - $current_video_addr 0x07, 0xBC +@struct Rectangle + width:2 > 0x11, 0x22 + height:2 > 0x99 + x:2 + y:2 > 0xAA +@end +.data $string "Hello World!!" + $rect = (0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88) + $test_byte 0xAF + $super_long_variable_name 0x01, 0x02, 0x03, 0x04 .code - debug_break -infinite_loop: +begin: + mov R10, Rectangle.SIZE + mov R9, [$rect.height] + mov R8, [$rect.width] + mov R7, [$rect.x] + mov R5, $strlen + push $string + push 1 + call R5 push 0 - call $clear_screen - - jmp $infinite_loop + call $testinttest + debug_break + mov [$rect.y], 0x0000 + ## debug_break + jmp $begin + push $string + push 1 + call $strlen + mov R1, RV + mov IP, 0x0001 + mov R1, RV hlt -print_string: - mov R1, *PP - dec PP - dec PP +testinttest: + push 0 + call $inttest + ret - push R1 - push 1 - call $strlen - mov R2, RV +inttest: + push 0 + call $testint + ret - push 65 - push 1 - call $print_char +testint: + push 0 + call $test_int + ret + +test_int: + push 0 + call $int_test + ret + +int_test: + nop ret strlen: - mov R1, *PP - dec PP - dec PP + arg R1 mov R2, 0 -_strlen_loop: +loop: movb ACC, *R1 - jeq $_strlen_end_loop, 0 - inc R2 inc R1 - jmp $_strlen_loop -_strlen_end_loop: + jeq $loop_end, 0 +not_zero: + inc R2 + jmp $loop +loop_end: 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/test.dss b/extra/dss/test.dss index d460bb0..204e9fb 100644 --- a/extra/dss/test.dss +++ b/extra/dss/test.dss @@ -1,8 +1,101 @@ -.load 0x1740 +##================================================================================================================================= +## This is the test program for the Dragon Virtual Machine. The example has two subroutines: One is the $strlen subroutine, +## which calculates the length of the string passed as a parameter; The other calculates the fibonacci sequence up to the +## Nth number (N being the parameter passed to the subroutine). +## In the main part of the .code section, debug_break instructions are used in order to pause the debugger, so that the +## results can be seen, since there is no Virtual Display implemented yet in the Dragon Runtime (that's the next thing to come). +##================================================================================================================================= + +.load 0x1740 ## The program is loaded at address 0x1740, which is the first address of normal RAM + +##================================================================================================================================= +## This is the data section, used to declare variables that will be stored inside the application space +##================================================================================================================================= +.data + $hello_world_str "Hello World!!" ## In the data section only, you can declare a stream of bytes as a + ## string literal between double-quotes. The assembler will convert + ## it into the corresponding bytes, and will add a 0 byte at the end + ## as the null termination. + $n1 0x00, 0x00 ## Other data declared in the data section must be represented as a + $n2 0x00, 0x01 ## comma-separated series of bytes (except for string literals - see + $n3 0x00, 0x01 ## above). No size data is stored for byte streams. +##================================================================================================================================= + + + +##================================================================================================================================= +## This is the start of the .code section, and it acts as the entry point for the program +##================================================================================================================================= .code - mov R1, 0xABCD - debug_break - hlt + push $hello_world_str ## Push the address of the first character of the $hello_world_str string to the stack + push 1 ## Push the number 1 to the stack, as the number of arguments for the next call instruction + call $strlen ## Call the $strlen subroutine + mov R1, RV ## Load the return value into the R1 register + debug_break ## This debug break is here for the debugger to pause, in order to see the results of + ## the function call in the debugger, since the Virtual Display is not implemented yet. + push 25 ## Push the number 25 as a parameter to the $fibonacci subroutine, so that it will + ## calculate up to the 25th fibonacci value, which is the maximum for 16-bits. + push 1 ## Push the number 1 to the stack, as the number of arguments for the next call instruction + call $fibonacci ## Call the $fibonacci subroutine + mov R2, RV ## Load the return value into the R2 register + debug_break ## This debug break is here for the debugger to pause, in order to see the results of + ## the function call in the debugger, since the Virtual Display is not implemented yet. + hlt ## This is the halt instruction, and it stops the CPU of the Virtual Machine. +##================================================================================================================================= -.fixed 512, 0x00 \ No newline at end of file + + +##================================================================================================================================= +## Subroutine to calculate the length of a null-terminated string +## It expects the address of the first character of the +## string, pushed onto the stack +##================================================================================================================================= +strlen: + arg R1 ## Store the first argument (address of the string) into the R1 register + mov R2, 0 ## Zero the R2 register, which will be used to store the length of the string +_strlen_loop: + movb ACC, *R1 ## Dereference the R1 register (pointer to the string), to get the first character of the string + inc R1 ## Increment the R1 register (pointer to the string) so that it points to the next byte + jeq $_streln_loop_end, 0 ## If the character ascii value is 0, we are done counting and we jump to the $_streln_loop_end label + inc R2 ## Else, increment the R2 register, because the character is not a zero so we add 1 to the count + jmp $_strlen_loop ## Jump to the $_strlen_loop label (beginning of the loop) +_streln_loop_end: + mov RV, R2 ## Copy the value of the R2 register (length of the string) to the RV register (Return Value) + ret ## Return to the calling code +##================================================================================================================================= + + + +##================================================================================================================================= +## Subroutine to calculate the fibonacci sequence +## It expects Nth number (starting at 1) up to which +## to calculate, pushed onto the stack +##================================================================================================================================= +fibonacci: + mov R1, 0 ## R1, R2, R3 Registers will be used as variables for the calculation + mov R2, 1 ## -- + mov R3, 1 ## -- + arg R4 ## Store the first argument (Nth number of the fibonacci sequence) into the R4 register + mov ACC, 3 ## -- + jle $_fibonacci_loop_end, R4 ## Compare the value passed as parameter and if less or equals to 3 (ACC Register) jump to the end + dec R4 ## Decrement the R4 register by 1, to convert from index-1 to index-0 + mov R5, 2 ## Load the numebr 2 into the R5 register, this will be used as the loop counter +_fibonacci_loop: + add R1, R2 ## Add R1 and R2 registers + mov R3, ACC ## andstore the result into the R3 register + ##debug_break ## This commented debug_break can be uncommented in order to analyze every iteration of the loop in the debugger + mov R1, R2 ## Swap the numbers around + mov R2, R3 ## -- + mov ACC, R5 ## Load the value of the R5 register (used as the loop counter) into the ACC register + jeq $_fibonacci_loop_end, R4 ## If the counter is equals to the R4 register (parameter passed from calling code) jump to the end of the loop + inc R5 ## else increment the counter + jmp $_fibonacci_loop ## and jump to the beginning of the loop +_fibonacci_loop_end: + mov Rv, R3 ## Load the final result (stored in the R3 register) into the RV (Return Value) register + ret ## Return to the calling code +##================================================================================================================================= + + +.fixed 512, 0x00 ## This directive is used to fill (with 0x00 bytes) the binary output file from the assembler, + ## exactly to 512 bytes. \ No newline at end of file diff --git a/extra/make_and_run b/extra/make_and_run index 7cacc5a..4157d6a 100644 --- a/extra/make_and_run +++ b/extra/make_and_run @@ -1,5 +1,8 @@ #!/bin/bash +green='\033[0;32m' +clear='\033[0m' + printf "${green}\n============================================[ Building Application ]============================================\n\n${clear}" \cp -r ../extra/* ./ if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then @@ -14,7 +17,7 @@ if [ $? -eq 0 ]; then /bin/bash ./load_mbr cd .. printf "${green}Compiling Test Program...\n" - ./dasm dss/newTest.dss -o newTest.bin --save-disassembly disassembly/newTest.dds + ./dasm dss/newTest.dss -o newTest.bin --save-disassembly disassembly/newTest.dds --verbose printf "\n${green}Running Application...\n\n${clear}" ./ddb config/testMachine.dvm --force-load newTest.bin 0x00 --verbose-load --hide-vdisplay # ./dvm config/testMachine.dvm --debug --force-load newTest.bin 0x00 --verbose-load diff --git a/extra/scripts/bios_flash b/extra/scripts/bios_flash index 69b345d..c7a66ce 100644 --- a/extra/scripts/bios_flash +++ b/extra/scripts/bios_flash @@ -6,5 +6,5 @@ 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 +./dasm dss/bios.dss -o dragon/bios.bin --save-disassembly disassembly/bios.dds --verbose cp dragon/bios.bin ../extra/dragon/bios.bin diff --git a/extra/scripts/load_mbr b/extra/scripts/load_mbr index 321574d..ff0b049 100644 --- a/extra/scripts/load_mbr +++ b/extra/scripts/load_mbr @@ -6,8 +6,8 @@ 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 +./dasm dss/mbr.dss -o dragon/mbr.bin --save-disassembly disassembly/mbr.dds --verbose +./dtools load-binary dragon/disk1.dr dragon/mbr.bin 0x00000000 printf "\n" cp dragon/disk1.dr ../extra/dragon/disk1.dr rm dragon/mbr.bin diff --git a/run-test.bat b/run-test.bat index 7251887..589665b 100644 --- a/run-test.bat +++ b/run-test.bat @@ -1 +1,2 @@ -ddb config/testMachine.dvm --force-load test.bin 0x00 --verbose-load --track-step-diff --hide-vdisplay \ No newline at end of file +dasm dss/test.dss -o test.bin --save-disassembly disassembly/test.dds --verbose +ddb config/testMachine.dvm --force-load test.bin 0x00 --verbose-load --hide-vdisplay \ No newline at end of file diff --git a/src/assembler/Assembler.cpp b/src/assembler/Assembler.cpp index 6888a87..e0a7f82 100644 --- a/src/assembler/Assembler.cpp +++ b/src/assembler/Assembler.cpp @@ -14,6 +14,77 @@ namespace dragon { namespace code { + int32_t Assembler::Application::loadArguments(int argc, char** argv) + { + if (argc < 2) + { + out.fg(ostd::ConsoleColors::Red).p("Error: too few arguments.").nl(); + out.fg(ostd::ConsoleColors::Red).p("Use the --help option for more info.").reset().nl(); + return RETURN_VAL_TOO_FEW_ARGUMENTS; + } + else + { + args.source_file_path = argv[1]; + if (args.source_file_path == "--help") + { + print_application_help(); + return RETURN_VAL_CLOSE_PROGRAM; + } + for (int32_t i = 2; i < argc; i++) + { + ostd::String edit(argv[i]); + if (edit == "-o") + { + if (i == argc - 1) + return RETURN_VAL_MISSING_PARAM; + i++; + args.dest_file_path = argv[i]; + } + else if (edit == "--save-disassembly") + { + if (i == argc - 1) + return RETURN_VAL_MISSING_PARAM; + i++; + args.disassembly_file_path = argv[i]; + args.save_disassembly = true; + } + else if (edit == "--help") + { + print_application_help(); + return RETURN_VAL_CLOSE_PROGRAM; + } + else if (edit == "--verbose") + args.verbose = true; + } + } + return RETURN_VAL_EXIT_SUCCESS; + } + + void Assembler::Application::print_application_help(void) + { + int32_t commandLength = 46; + + out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available parameters:").reset().nl(); + ostd::String tmpCommand = "--save-disassembly "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Saves debug information in the destination directory.").reset().nl(); + tmpCommand = "--verbose"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Shows more information about the assembled program.").reset().nl(); + tmpCommand = "-o "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to specify the output binary file.").reset().nl(); + tmpCommand = "--help"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Displays this help message.").reset().nl(); + + out.nl().fg(ostd::ConsoleColors::Magenta).p("Usage: ./dasm -o [...options...]").reset().nl(); + out.nl(); + } + + + + ostd::ByteStream Assembler::assembleFromFile(ostd::String fileName) { m_rawSource = ""; @@ -24,6 +95,7 @@ namespace dragon m_symbolTable.clear(); m_labelTable.clear(); m_disassembly.clear(); + m_structDefs.clear(); m_fixedSize = 0; m_fixedFillValue = 0x00; m_loadAddress = 0x0000; @@ -32,8 +104,12 @@ namespace dragon ostd::TextFileBuffer file(fileName); loadSource(file.rawContent()); removeComments(); + replaceGroupDefines(); + replaceDefines(); + parseStructures(); replaceDefines(); parseSections(); + parseStructInstances(); parseDataSection(); parseCodeSection(); replaceLabelRefs(); @@ -79,6 +155,7 @@ namespace dragon uint64_t da_size = 0; da_size += (header_string.len() + 1) * ostd::tTypeSize::BYTE; da_size += m_disassembly.size() * ostd::tTypeSize::DWORD; //Addresses + da_size += m_disassembly.size() * ostd::tTypeSize::WORD; //Data Size da_size += m_disassembly.size() * ostd::tTypeSize::BYTE; //Null Termination for (auto& da : m_disassembly) da_size += da.code.len() * ostd::tTypeSize::BYTE; //Code Strings @@ -90,16 +167,10 @@ namespace dragon { serializer.w_DWord(stream_addr, da.addr); stream_addr += ostd::tTypeSize::DWORD; + serializer.w_Word(stream_addr, (int16_t)da.size); + stream_addr += ostd::tTypeSize::WORD; serializer.w_String(stream_addr, da.code); stream_addr += (da.code.len() + 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); } @@ -166,7 +237,6 @@ namespace dragon return; } defines.push_back({ define_name, define_value }); - // std::cout << define_name << ": " << define_value << "\n"; continue; } newLines.push_back(lineEdit); @@ -182,6 +252,61 @@ namespace dragon m_lines = newLines; } + void Assembler::replaceGroupDefines(void) + { + std::vector newLines; + ostd::String lineEdit; + bool in_group = false; + ostd::String group_name = ""; + for (auto& line : m_lines) + { + lineEdit = line; + lineEdit.trim(); + if (lineEdit.new_toLower().startsWith("@group ")) + { + if (in_group) + { + std::cout << "Nested define groups not allowed: " << line << "\n"; + return; + } + lineEdit.substr(6).trim(); + if (lineEdit == "") + { + std::cout << "Group name cannot be empty: " << line << "\n"; + return; + } + group_name = lineEdit; + in_group = true; + continue; + } + else if (in_group && lineEdit.new_toLower() == "@end") + { + if (!in_group) + { + std::cout << "Missing group declaration for @end directive: " << line << "\n"; + return; + } + in_group = false; + continue; + } + if (!in_group) + { + newLines.push_back(lineEdit); + continue; + } + if (!lineEdit.contains(" ")) + { + std::cout << "Invalid definition inside group: " << line << "\n"; + return; + } + ostd::String newLine = "@define "; + newLine.add(group_name).add(".").add(lineEdit); + newLines.push_back(newLine); + } + m_lines.clear(); + m_lines = newLines; + } + void Assembler::parseSections(void) { constexpr uint8_t DATA_SECTION = 0x01; @@ -274,6 +399,258 @@ namespace dragon } } + void Assembler::parseStructInstances(void) + { + std::vector newLines; + ostd::String lineEdit; + bool in_group = false; + ostd::String group_name = ""; + for (auto& line : m_rawDataSection) + { + lineEdit = line; + lineEdit.trim(); + if (!lineEdit.startsWith("$") || lineEdit.indexOf(" ") < 2) + { + std::cout << "Invalid data entry: " << lineEdit << "\n"; + return; + } + ostd::String symbolName = lineEdit.new_substr(0, lineEdit.indexOf(" ")); + symbolName.trim(); + lineEdit.substr(lineEdit.indexOf(" ") + 1); + lineEdit.trim(); + ostd::String initialization_data = ""; + bool has_init_data = false; + if (lineEdit.startsWith("<") && lineEdit.contains("=")) + { + initialization_data = lineEdit.new_substr(lineEdit.indexOf("=") + 1).trim(); + if (initialization_data.startsWith("(") && initialization_data.endsWith(")")) + { + initialization_data.substr(1, initialization_data.len() - 1).trim(); + has_init_data = initialization_data != ""; + } + else + { + std::cout << "Invalid initialization data: " << lineEdit << "\n"; + return; + } + lineEdit.substr(0, lineEdit.indexOf("=")).trim(); + } + if (lineEdit.startsWith("<") && lineEdit.endsWith(">") && lineEdit.len() > 2) + { + lineEdit = lineEdit.substr(1, lineEdit.len() - 1); + std::vector init_data; + if (has_init_data) + { + auto tokens = initialization_data.tokenize(","); + while (tokens.hasNext()) + { + ostd::String tok = tokens.next(); + if (!tok.isNumeric()) + { + std::cout << "Invalid initialization data: " << lineEdit << "\n"; + return; + } + init_data.push_back((uint8_t)tok.toInt()); + } + } + tStructDefinition struct_def; + bool found = false; + for (auto& _struct : m_structDefs) + { + if (_struct.name == lineEdit) + { + struct_def = _struct; + found = true; + break; + } + } + if (!found) + { + std::cout << "Unknown Structure name: " << lineEdit << "\n"; + return; + } + if (has_init_data && struct_def.size != init_data.size()) + { + std::cout << "Structure size must match initialization data size: " << lineEdit << "\n"; + return; + } + int32_t data_index = 0; + for (auto& member : struct_def.members) + { + ostd::String newLine = symbolName; + newLine.add(".").add(member.name).add(" "); + for (int32_t i = 0; i < member.data.size(); i++, data_index++) + { + if (has_init_data) + newLine.add(ostd::Utils::getHexStr(init_data[data_index], true, 2)); + else + newLine.add(ostd::Utils::getHexStr(member.data[i], true, 2)); + newLine.add(","); + } + newLine.substr(0, newLine.len() - 1); + newLines.push_back(newLine); + } + continue; + } + newLines.push_back(line); + } + m_rawDataSection.clear(); + m_rawDataSection = newLines; + } + + void Assembler::parseStructures(void) + { + std::vector newLines; + ostd::String lineEdit; + bool in_struct = false; + ostd::String struct_name = ""; + tStructDefinition struct_def; + int32_t member_index = 0; + for (auto& line : m_lines) + { + lineEdit = line; + lineEdit.trim(); + if (lineEdit.new_toLower().startsWith("@struct ")) + { + if (in_struct) + { + std::cout << "Nested structs not allowed: " << line << "\n"; + return; + } + lineEdit.substr(7).trim(); + if (lineEdit == "") + { + std::cout << "Struct name cannot be empty: " << line << "\n"; + return; + } + struct_name = lineEdit; + struct_def.members.clear(); + struct_def.name = struct_name; + member_index = 0; + in_struct = true; + continue; + } + else if (in_struct && lineEdit.new_toLower() == "@end") + { + if (!in_struct) + { + std::cout << "Missing struct declaration for @end directive: " << line << "\n"; + return; + } + struct_def.size = 0; + for (auto& data : struct_def.members) + struct_def.size += data.data.size(); + std::sort(struct_def.members.begin(), struct_def.members.end()); + m_structDefs.push_back(struct_def); + ostd::String size_def = "@define "; + size_def.add(struct_def.name).add(".").add("SIZE").add(" "); + size_def.add(ostd::Utils::getHexStr(struct_def.size, true, 2)); + newLines.push_back(size_def); + in_struct = false; + continue; + } + if (!in_struct) + { + newLines.push_back(lineEdit); + continue; + } + if (!lineEdit.contains(":")) + { + std::cout << "Invalid definition inside struct. Size specification missing: " << line << "\n"; + return; + } + ostd::String member_name = lineEdit.new_substr(0, lineEdit.indexOf(":")).trim(); + ostd::String member_data = ""; + lineEdit.substr(lineEdit.indexOf(":") + 1).trim(); + if (member_name.contains(" ")) + { + std::cout << "Invalid definition inside struct. Member name cannot contain spaces: " << line << "\n"; + return; + } + if (lineEdit.contains(">")) + { + member_data = lineEdit.new_substr(lineEdit.indexOf(">") + 1).trim(); + lineEdit.substr(0, lineEdit.indexOf(">")).trim(); + } + if (!lineEdit.isNumeric()) + { + std::cout << "Invalid definition inside struct. Member size must be numeric: " << line << "\n"; + return; + } + int32_t bytes = lineEdit.toInt(); + if (bytes < 1) + { + std::cout << "Invalid definition inside struct. Member must be at least 1 Byte: " << line << "\n"; + return; + } + tStructMember member; + member.position = member_index++; + member.name = member_name; + if (member_data == "") + { + for (int32_t i = 0; i < bytes; i++) + member.data.push_back(0x00); + } + else + { + auto tokens = member_data.tokenize(","); + if (tokens.count() == 1) + { + ostd::String tok = tokens.next(); + if (tok.isNumeric()) + { + uint8_t data = (uint8_t)tok.toInt(); + for (int32_t i = 0; i < bytes; i++) + member.data.push_back(data); + } + else + { + std::cout << "Invalid definition inside struct. Member data must be numeric: " << line << "\n"; + return; + } + } + else + { + if (tokens.count() != bytes) + { + std::cout << "Invalid definition inside struct. Member data count and size mismatch: " << line << "\n"; + return; + } + while (tokens.hasNext()) + { + ostd::String tok = tokens.next(); + if (tok.isNumeric()) + { + uint8_t data = (uint8_t)tok.toInt(); + member.data.push_back(data); + } + else + { + std::cout << "Invalid definition inside struct. Member data must be numeric: " << line << "\n"; + return; + } + } + } + } + struct_def.members.push_back(member); + } + m_lines.clear(); + m_lines = newLines; + + // for (auto& str : m_structDefs) + // { + // std::cout << str.name << "\n"; + // for(auto& d : str.memberData) + // { + // std::cout << " " << d.first << " "; + // for (auto& b : d.second) + // std::cout << ostd::Utils::getHexStr(b) << " "; + // } + // std::cout << "\n"; + // } + // std::cin.get(); + } + void Assembler::parseDataSection(void) { for (auto& line : m_rawDataSection) @@ -287,7 +664,7 @@ namespace dragon } ostd::String symbolName = lineEdit.new_substr(0, lineEdit.indexOf(" ")); symbolName.trim(); - lineEdit = lineEdit.substr(lineEdit.indexOf(" ") + 1); + lineEdit.substr(lineEdit.indexOf(" ") + 1); lineEdit.trim(); if (lineEdit.isNumeric()) { @@ -314,7 +691,7 @@ namespace dragon 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_currentDataAddr += symbol.bytes.size(); m_symbolTable[symbolName] = symbol; continue; } @@ -500,6 +877,19 @@ namespace dragon m_code.push_back((uint8_t)word); return; } + else if (instEdit == "arg") + { + eOperandType opType = parseOperand(opEdit, word); + if (opType != eOperandType::Register) + { + std::cout << "Invalid operand type; " << line << " (" << opEdit << ") -> Register required\n"; + exit(0); + return; + } + m_code.push_back(data::OpCodes::ArgReg); + m_code.push_back((uint8_t)word); + return; + } else if (instEdit == "push") { eOperandType opType = parseOperand(opEdit, word); @@ -998,7 +1388,7 @@ namespace dragon m_disassembly.push_back({ 0xFFFFFF00, "{ DATA }" }); for (auto& d : m_symbolTable) { - m_disassembly.push_back({ d.second.address, d.first }); + m_disassembly.push_back({ d.second.address, d.first, (uint16_t)d.second.bytes.size() }); } m_disassembly.push_back({ 0xFFFFFF01, "{ LABELS }" }); for (auto& l : m_labelTable) @@ -1121,32 +1511,55 @@ namespace dragon return data::Registers::Last; } - void Assembler::tempPrint(void) + void Assembler::printProgramInfo(void) { + int32_t symbol_len = 30; + out.nl(); + + if (m_symbolTable.size() > 0) + out.fg(ostd::ConsoleColors::Yellow).p("Symbols:").nl(); for (auto& symbol : m_symbolTable) { - std::cout << symbol.first << "\n"; - std::cout << ostd::Utils::getHexStr(symbol.second.address, true, 2) << "\n"; + out.fg(ostd::ConsoleColors::Red).p(ostd::Utils::getHexStr(symbol.second.address, true, 2)); + out.fg(ostd::ConsoleColors::Magenta).p(" ").p(symbol.first); + out.fg(ostd::ConsoleColors::Blue).nl().p(" "); for (auto& b : symbol.second.bytes) - std::cout << ostd::Utils::getHexStr(b, true, 1) << " "; - std::cout << "\n\n"; + out.p(ostd::Utils::getHexStr(b, false, 1)).p("."); + out.nl(); } - - 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"; - + if (m_symbolTable.size() > 0) + out.nl(); + + if (m_labelTable.size() > 0) + out.fg(ostd::ConsoleColors::Yellow).p("Labels:").nl(); for (auto& label : m_labelTable) { - std::cout << label.first << " " << ostd::Utils::getHexStr(label.second.address, true, 2) << "\n"; + out.fg(ostd::ConsoleColors::Magenta).p(label.first.new_fixedLength(symbol_len)); + out.fg(ostd::ConsoleColors::Red).p(ostd::Utils::getHexStr(label.second.address, true, 2)).nl(); } + if (m_labelTable.size() > 0) + out.nl(); + + if (m_structDefs.size() > 0) + out.fg(ostd::ConsoleColors::Yellow).p("Structures:").nl(); + for (auto& str : m_structDefs) + { + out.fg(ostd::ConsoleColors::Magenta).p(str.name.new_fixedLength(symbol_len)); + out.fg(ostd::ConsoleColors::Red).p(str.size).p(" bytes").nl(); + } + if (m_structDefs.size() > 0) + out.nl(); - ostd::ConsoleOutputHandler out; - ostd::Utils::printByteStream(m_code, 0x0000, 16, 8, out); - std::cout << "\n\n\n\n"; + + out.fg(ostd::ConsoleColors::Yellow).p("Program data:").nl(); + out.fg(ostd::ConsoleColors::Cyan).p(ostd::String("Fixed Size: ").new_fixedLength(symbol_len)).fg(ostd::ConsoleColors::BrightRed).p((int)m_fixedSize).p(" bytes").nl(); + out.fg(ostd::ConsoleColors::Cyan).p(ostd::String("Program Size:").new_fixedLength(symbol_len)).fg(ostd::ConsoleColors::BrightRed).p((int)m_programSize).p(" bytes").nl(); + out.fg(ostd::ConsoleColors::Cyan).p(ostd::String("Data Size: ").new_fixedLength(symbol_len)).fg(ostd::ConsoleColors::BrightRed).p((int)m_dataSize).p(" bytes").nl(); + out.fg(ostd::ConsoleColors::Cyan).p(ostd::String("Fixed Fill: ").new_fixedLength(symbol_len)).fg(ostd::ConsoleColors::BrightRed).p(ostd::Utils::getHexStr(m_fixedFillValue, true, 1)).nl(); + out.fg(ostd::ConsoleColors::Cyan).p(ostd::String("Load Address:").new_fixedLength(symbol_len)).fg(ostd::ConsoleColors::BrightRed).p(ostd::Utils::getHexStr(m_loadAddress, true, 2)).nl(); + out.fg(ostd::ConsoleColors::Cyan).p(ostd::String("Entry Point: ").new_fixedLength(symbol_len)).fg(ostd::ConsoleColors::BrightRed).p(ostd::Utils::getHexStr(m_dataSize + m_loadAddress + 3, true, 2)).nl(); + + out.nl(); } } } \ No newline at end of file diff --git a/src/assembler/Assembler.hpp b/src/assembler/Assembler.hpp index ffbc2c3..b2c53ef 100644 --- a/src/assembler/Assembler.hpp +++ b/src/assembler/Assembler.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace dragon @@ -13,9 +14,11 @@ namespace dragon { class Assembler { - public: struct tDisassemblyLine { + public: struct tDisassemblyLine + { uint32_t addr = 0; ostd::String code = ""; + uint16_t size = 1; inline bool operator<(const tDisassemblyLine& second) const { return addr < second.addr; } inline bool operator>(const tDisassemblyLine& second) const { return addr > second.addr; } }; @@ -29,7 +32,22 @@ namespace dragon std::vector references; uint16_t address { 0 }; }; - public: enum class eOperandType { + public: struct tStructMember + { + ostd::String name; + std::vector data; + int32_t position; + inline bool operator<(const tStructMember& second) const { return position < second.position; } + inline bool operator>(const tStructMember& second) const { return position > second.position; } + }; + public: struct tStructDefinition + { + ostd::String name; + std::vector members; + int32_t size; + }; + public: enum class eOperandType + { Register = 0, Immediate, DerefMemory, @@ -39,6 +57,30 @@ namespace dragon Error }; + public: class Application + { + public: struct tCommandLineArgs + { + inline tCommandLineArgs(void) { } + ostd::String source_file_path { "" }; + ostd::String dest_file_path = { "" }; + bool save_disassembly = { false }; + bool verbose = { false }; + ostd::String disassembly_file_path = { "" }; + }; + + public: + static int32_t loadArguments(int argc, char** argv); + static void print_application_help(void); + + public: + inline static tCommandLineArgs args; + inline static const int32_t RETURN_VAL_EXIT_SUCCESS = 0; + inline static const int32_t RETURN_VAL_CLOSE_PROGRAM = 512; + inline static const int32_t RETURN_VAL_TOO_FEW_ARGUMENTS = 1; + inline static const int32_t RETURN_VAL_MISSING_PARAM = 2; + }; + public: static ostd::ByteStream assembleFromFile(ostd::String fileName); static void loadSource(ostd::String source); @@ -46,12 +88,15 @@ namespace dragon static ostd::ByteStream assembleToVirtualDisk(ostd::String fileName, hw::VirtualHardDrive& vhdd, uint32_t address); static bool saveDisassemblyToFile(ostd::String fileName); - static void tempPrint(void); + static void printProgramInfo(void); private: static void removeComments(void); static void replaceDefines(void); + static void replaceGroupDefines(void); static void parseSections(void); + static void parseStructInstances(void); + static void parseStructures(void); static void parseDataSection(void); static void parseCodeSection(void); static void parseDebugOperands(ostd::String line); @@ -80,8 +125,11 @@ namespace dragon 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_structDefs; inline static std::vector m_disassembly; + + inline static ostd::ConsoleOutputHandler out; }; } } \ No newline at end of file diff --git a/src/assembler/assembler_main.cpp b/src/assembler/assembler_main.cpp index d2d360c..358f107 100644 --- a/src/assembler/assembler_main.cpp +++ b/src/assembler/assembler_main.cpp @@ -1,60 +1,17 @@ #include "Assembler.hpp" -#include -#include - -ostd::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.fg("red").p("Error: too few arguments.").nl(); - out.fg("red").p(" Usage: ./dasm -o [...options...]").reset().nl(); - return 1; - } - else - { - args.source_file_path = argv[1]; - for (int32_t i = 2; i < argc; i++) - { - ostd::String edit(argv[i]); - if (edit == "-o") - { - if (i == argc - 1) - break; //TODO: Warning - i++; - args.dest_file_path = argv[i]; - } - else if (edit == "--save-disassembly") - { - if (i == argc - 1) - break; //TODO: Warning - i++; - args.disassembly_file_path = argv[i]; - args.save_disassembly = true; - } - else if (edit == "--verbose") - args.verbose = true; - } - } - + int32_t rValue = dragon::code::Assembler::Application::loadArguments(argc, argv); + if (rValue == dragon::code::Assembler::Application::RETURN_VAL_CLOSE_PROGRAM) + return dragon::code::Assembler::Application::RETURN_VAL_EXIT_SUCCESS; + if (rValue != dragon::code::Assembler::Application::RETURN_VAL_EXIT_SUCCESS) + return rValue; + auto& args = dragon::code::Assembler::Application::args; dragon::code::Assembler::assembleToFile(args.source_file_path, args.dest_file_path); if (args.verbose) - dragon::code::Assembler::tempPrint(); - + dragon::code::Assembler::printProgramInfo(); if (args.save_disassembly) dragon::code::Assembler::saveDisassemblyToFile(args.disassembly_file_path); - - return 0; + return dragon::code::Assembler::Application::RETURN_VAL_EXIT_SUCCESS; } \ No newline at end of file diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp index d3b3e99..36873f7 100644 --- a/src/debugger/Debugger.cpp +++ b/src/debugger/Debugger.cpp @@ -6,6 +6,24 @@ namespace dragon { + //Close Event Listener + Debugger::tCloseEventListener Debugger::closeEventListener; + + void Debugger::tCloseEventListener::init(void) + { + validate(); + enableSignals(); + connectSignal(dragon::Window::Signal_OnWindowClosed); + } + + void Debugger::tCloseEventListener::handleSignal(ostd::tSignal& signal) + { + m_mainWindowClosed = signal.ID == dragon::Window::Signal_OnWindowClosed; + } + + + + //Debugger::Utils DisassemblyList Debugger::Utils::findCodeRegion(const DisassemblyList& code, uint16_t address, uint16_t codeRegionMargin) { @@ -32,22 +50,30 @@ namespace dragon return codeRegion; } - ostd::String Debugger::Utils::findSymbol(const DisassemblyList& labels, uint16_t address) + ostd::String Debugger::Utils::findSymbol(const DisassemblyList& list, uint16_t address, uint16_t* outSize) { - for (auto& label : labels) + for (auto& line : list) { - if (label.addr == address) - return label.code; + if (line.addr == address) + { + if (outSize != nullptr) + *outSize = line.size; + return line.code; + } } return ""; } - uint16_t Debugger::Utils::findSymbol(const DisassemblyList& labels, const ostd::String& symbol) + uint16_t Debugger::Utils::findSymbol(const DisassemblyList& list, const ostd::String& symbol, uint16_t* outSize) { - for (auto& label : labels) + for (auto& line : list) { - if (label.code == symbol) - return label.addr; + if (line.code == symbol) + { + if (outSize != nullptr) + *outSize = line.size; + return line.addr; + } } return 0x0000; } @@ -67,6 +93,39 @@ namespace dragon out.p("\b"); } + bool Debugger::Utils::isEscapeKeyPressed(bool blocking) + { + ostd::KeyboardController kbc; + kbc.disableCommandBuffer(); + kbc.disableOutput(); + if (blocking) + { + ostd::eKeys key = ostd::eKeys::NoKeyPressed; + while ((key = kbc.waitForKeyPress()) != ostd::eKeys::Escape) + { + ostd::Utils::sleep(120); + } + return true; + } + return kbc.getPressedKey() == ostd::eKeys::Escape; + } + + ostd::ConsoleOutputHandler& Debugger::Utils::printFullLine(char c, const ostd::ConsoleColors::tConsoleColor& foreground) + { + int32_t cw = ostd::Utils::getConsoleWidth(); + ostd::String str = ostd::Utils::duplicateChar(c, cw); + out.fg(foreground).p(str).reset().nl(); + return out; + } + + ostd::ConsoleOutputHandler& Debugger::Utils::printFullLine(char c, const ostd::ConsoleColors::tConsoleColor& foreground, const ostd::ConsoleColors::tConsoleColor& background) + { + int32_t cw = ostd::Utils::getConsoleWidth(); + ostd::String str = ostd::Utils::duplicateChar(c, cw); + out.bg(background).fg(foreground).p(str).reset().nl(); + return out; + } + @@ -131,7 +190,7 @@ namespace dragon void Debugger::Display::printPrompt(void) { - out.fg(ostd::ConsoleColors::Magenta).p(" #/> ").fg(ostd::ConsoleColors::White); + out.fg(ostd::ConsoleColors::Magenta).p(" (ddb) #/> ").fg(ostd::ConsoleColors::White); } void Debugger::Display::printStep(void) @@ -166,9 +225,7 @@ namespace dragon colorCodeInstructions(_da.code, currentLine, debugger.labels); out.reset(); } - int32_t cw = ostd::Utils::getConsoleWidth(); - ostd::String str = ostd::Utils::duplicateChar('#', cw); - out.bg(ostd::ConsoleColors::Yellow).fg(ostd::ConsoleColors::Black).p(str.cpp_str()).reset().nl(); + Utils::printFullLine('#', ostd::ConsoleColors::Black, ostd::ConsoleColors::Yellow); } void Debugger::Display::printDiff(void) @@ -176,7 +233,7 @@ namespace dragon out.clear(); ostd::String str; - str.add("|===============|================PREV================|================CURR================|=====|====REG====|====REG====|"); + str.add("|===============|================PREV================|================CURR================|=====|===PREV====|===CURR====|"); str.add("\n"); str.add("| InstAddr: |*%PREV_INST_ADDR%*******************|*%CURR_INST_ADDR%*******************| R1 |*%PREV_R1%*|*%CURR_R1%*|"); str.add("\n"); @@ -202,19 +259,19 @@ namespace dragon str.add("\n"); str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|"); str.add("\n"); - str.add("| SubRoutine: |*%PREV_SUB_ROUTINE%*****************|**%CURR_SUB_ROUTINE%****************| R6 |*%PREV_R7%*|*%CURR_R7%*|"); + str.add("| SubRoutine: |*%PREV_SUB_ROUTINE%*****************|**%CURR_SUB_ROUTINE%****************| R7 |*%PREV_R7%*|*%CURR_R7%*|"); str.add("\n"); str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|"); str.add("\n"); - str.add("| IP: |*%PREV_IP%**************************|**%CURR_IP%*************************| R7 |*%PREV_R8%*|*%CURR_R8%*|"); + str.add("| IP: |*%PREV_IP%**************************|**%CURR_IP%*************************| R8 |*%PREV_R8%*|*%CURR_R8%*|"); str.add("\n"); str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|"); str.add("\n"); - str.add("| SP: |*%PREV_SP%**************************|**%CURR_SP%*************************| R8 |*%PREV_R9%*|*%CURR_R9%*|"); + str.add("| SP: |*%PREV_SP%**************************|**%CURR_SP%*************************| R9 |*%PREV_R9%*|*%CURR_R9%*|"); str.add("\n"); str.add("|---------------|------------------------------------|------------------------------------|-----|-----------|-----------|"); str.add("\n"); - str.add("| FP: |*%PREV_FP%**************************|**%CURR_FP%*************************| R9 |*%PREV_R10%|*%CURR_R10%|"); + str.add("| FP: |*%PREV_FP%**************************|**%CURR_FP%*************************| R10 |*%PREV_R10%|*%CURR_R10%|"); str.add("\n"); str.add("|---------------|------------------------------------|------------------------------------|=====|===========|===========|"); str.add("\n"); @@ -741,45 +798,153 @@ namespace dragon rgxstr.fg("CURR", "Green"); out.pStyled(rgxstr); - out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to continue execution...").nl().reset(); - std::cin.get(); + out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to go back...").nl().reset(); + Utils::isEscapeKeyPressed(true); } void Debugger::Display::printTrackedAddresses(void) { out.clear(); - + out.fg(ostd::ConsoleColors::Yellow).p("Tracked Data: ").reset().nl(); + const int32_t symbol_len = 24; + const int32_t size_len = 8; + const int32_t addr_len = 10; + const int32_t map_len = 8; + const int32_t prev_len = 35; const dragon::DragonRuntime::tMachineDebugInfo& minfo = dragon::DragonRuntime::getMachineInfoDiff(); if (minfo.trackedAddresses.size() == 0) return; + int32_t cw = ostd::Utils::getConsoleWidth(); + ostd::String header = "|Symbol"; + header.addRightPadding(symbol_len); + header.add("|Bytes"); + header.addRightPadding(header.len() + size_len - 6); + header.add("|Addr"); + header.addRightPadding(header.len() + addr_len - 5); + header.add("|Map"); + header.addRightPadding(header.len() + map_len - 4); + header.add("|Previous"); + header.addRightPadding(header.len() + prev_len - 9); + header.add("|Current"); + header.fixedLength(cw - 1); + header.addChar('|'); + ostd::String border = ostd::Utils::duplicateChar('=', cw); + out.fg(ostd::ConsoleColors::Blue).p(border).nl(); + ostd::RegexRichString rgx(header); + rgx.fg("\\|", "blue"); + rgx.fg("Symbol|Bytes|Addr|Map|Previous|Current", "yellow"); + out.pStyled(rgx).reset().nl(); + ostd::String h_sep = ostd::Utils::duplicateChar('=', cw); + h_sep.put(0, '|'); + h_sep.put(symbol_len, '|'); + h_sep.put(symbol_len + size_len, '|'); + h_sep.put(symbol_len + size_len + addr_len, '|'); + h_sep.put(symbol_len + size_len + addr_len + map_len, '|'); + h_sep.put(symbol_len + size_len + addr_len + map_len + prev_len, '|'); + h_sep.put(cw - 1, '|'); + out.fg(ostd::ConsoleColors::Blue).p(h_sep).reset().nl(); for (int32_t i = 0; i < minfo.trackedAddresses.size(); i++) { + uint16_t data_size = 1; auto addr = minfo.trackedAddresses[i]; - ostd::String symbol = Utils::findSymbol(debugger.data, addr); + ostd::String symbol = Utils::findSymbol(debugger.data, addr, &data_size); if (symbol == "") - symbol = Utils::findSymbol(debugger.labels, addr); - symbol.fixedLength(30); - out.p(symbol); - out.p(ostd::Utils::getHexStr(addr, true, 2)).p(" "); - out.p(ostd::Utils::getHexStr(minfo.previousInstructionTrackedValues[i], true, 1)).p(" "); - out.p(ostd::Utils::getHexStr(minfo.currentInstructionTrackedValues[i], true, 1)).p(" "); - out.nl(); - } + symbol = Utils::findSymbol(debugger.labels, addr, &data_size); + bool no_symbol = false; + if (symbol == "") + { + symbol = ""; + no_symbol = true; + } + symbol.fixedLength(symbol_len - 1); + if (no_symbol) + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::Gray).p(symbol).reset(); + else + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::Magenta).p(symbol).reset(); - out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to continue execution...").nl().reset(); - std::cin.get(); + ostd::String tmp = ""; + tmp.add(data_size); + tmp.fixedLength(size_len - 1); + if (no_symbol) + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::Cyan).p(tmp).reset(); + else + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::BrightCyan).p(tmp).reset(); + + tmp = ""; + tmp.add(ostd::Utils::getHexStr(addr, true, 2)); + tmp.fixedLength(addr_len - 1); + if (no_symbol) + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::Gray).p(tmp).reset(); + else + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::BrightGray).p(tmp).reset(); + + tmp = ""; + if (addr >= (uint16_t)DragonRuntime::cpu.readRegister(data::Registers::SP)) + tmp.add("STACK"); + else + tmp.add(DragonRuntime::memMap.getMemoryRegionName(addr)); + tmp.fixedLength(map_len - 1); + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::BrightYellow).p(tmp).reset(); + + tmp = ""; + tmp.add(ostd::Utils::getHexStr(minfo.previousInstructionTrackedValues[i], false, 1)); + for (int32_t j = 1; j < data_size; j++) + tmp.add(".").add(ostd::Utils::getHexStr(minfo.previousInstructionTrackedValues[i + j], false, 1)); + tmp.fixedLength(prev_len - 1); + if (no_symbol) + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::Red).p(tmp).reset(); + else + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::BrightRed).p(tmp).reset(); + + tmp = ""; + tmp.add(ostd::Utils::getHexStr(minfo.currentInstructionTrackedValues[i], false, 1)); + uint32_t tmp_i = i; + for (int32_t j = 1; j < data_size; j++, i++) + tmp.add(".").add(ostd::Utils::getHexStr(minfo.currentInstructionTrackedValues[i + 1], false, 1)); + tmp.fixedLength(prev_len - 1); + bool data_different = false; + for (int32_t j = 0; j < data_size; j++) + { + if (minfo.currentInstructionTrackedValues[tmp_i + j] != minfo.previousInstructionTrackedValues[tmp_i + j]) + { + data_different = true; + break; + } + } + if (no_symbol) + { + if (data_different) + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::BrightGray).p(tmp).reset(); + else + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::Green).p(tmp).reset(); + } + else + { + if (data_different) + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::White).p(tmp).reset(); + else + out.fg(ostd::ConsoleColors::Blue).p("|").fg(ostd::ConsoleColors::BrightGreen).p(tmp).reset(); + } + tmp = "|"; + tmp.addLeftPadding(cw - symbol_len - size_len - addr_len - map_len - prev_len - prev_len); + out.fg(ostd::ConsoleColors::Blue).p(tmp).reset(); + } + out.nl(); + out.fg(ostd::ConsoleColors::Blue).p(border).reset().nl(); + + out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to go back...").nl().reset(); + Utils::isEscapeKeyPressed(true); } - void Debugger::Display::printStack(void) + void Debugger::Display::printStack(uint16_t nrows) { + if (nrows == 0) nrows = 8; + uint16_t ncols = 16; + uint16_t startAddr = (0xFFFF - (ncols * nrows) + 1); out.clear(); - const dragon::DragonRuntime::tMachineDebugInfo& minfo = dragon::DragonRuntime::getMachineInfoDiff(); - uint16_t stack_ptr = DragonRuntime::cpu.readRegister(data::Registers::SP); - std::cout << " " << ostd::Utils::getHexStr(stack_ptr, true, 2) << " \n"; - ostd::Utils::printByteStream(*DragonRuntime::ram.getByteStream(), 0xFF80, 16, 8, out, stack_ptr, 1, "Stack"); - out.nl(); - + out.fg(ostd::ConsoleColors::Yellow).p("Stack frames: ").reset().nl(); + Utils::printFullLine('=', ostd::ConsoleColors::BrightBlue); for (auto& frame : dragon::DragonRuntime::cpu.m_debug_stackFrameStrings) { ostd::RegexRichString rgxrstr(frame); @@ -787,73 +952,186 @@ namespace dragon rgxrstr.fg("(? to continue execution...").nl().reset(); - std::cin.get(); + out.fg(ostd::ConsoleColors::Yellow).p("Stack view: ").reset().nl(); + uint16_t stack_ptr = DragonRuntime::cpu.readRegister(data::Registers::SP); + ostd::Utils::printByteStream(*DragonRuntime::ram.getByteStream(), startAddr, ncols, nrows, out, stack_ptr, 2, "Stack"); + + out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to go back...").nl().reset(); + Utils::isEscapeKeyPressed(true); } void Debugger::Display::printCallStack(void) { out.clear(); + out.fg(ostd::ConsoleColors::Yellow).p("Subroutine/Interrupt call stack: ").reset().nl(); + Utils::printFullLine('=', ostd::ConsoleColors::Yellow); const dragon::DragonRuntime::tMachineDebugInfo& minfo = dragon::DragonRuntime::getMachineInfoDiff(); auto& callStack = minfo.callStack; - int32_t depth = -1; + int32_t level = -1; + ostd::String ch_ang_u = ""; + ch_ang_u.addChar((unsigned char)218); + ostd::String ch_ang_l = "|"; + ostd::String ch_ang_d = ""; + ch_ang_d.addChar((unsigned char)192); for (auto& call : callStack) { - if (call.info.new_trim().toLower().startsWith("ret")) - depth--; - else - depth++; - if (depth < 0) depth = 0; - out.p(ostd::String().addLeftPadding(depth * 4)); - out.p(call.info.new_fixedLength(10)).p(" "); + ostd::String call_info = call.info.new_trim().toLower(); + ostd::String subroutine_addr = ostd::Utils::getHexStr(call.addr, true, 2); ostd::String subroutine_name = Utils::findSymbol(debugger.labels, call.addr); - out.p(ostd::Utils::getHexStr(call.addr, true, 2)); - if (call.info.new_trim().toLower() == "call imm" && subroutine_name != "") - out.p(" (").p(subroutine_name).p(")"); - out.nl(); + ostd::String call_str = ""; + bool dec_lvl = false; + if (call_info == "int") + { + call_str = ch_ang_u + "int " + ostd::Utils::getHexStr(call.addr, true, 1); + level++; + } + else if (call_info == "ret") + { + call_str = ch_ang_d + "ret"; + dec_lvl = true; + } + else if (call_info == "ret int") + { + call_str = ch_ang_d + "rti"; + dec_lvl = true; + } + else + { + if (call_info == "call reg") + subroutine_addr = "*" + subroutine_addr; + if (subroutine_name != "") + call_str = ch_ang_u + subroutine_name + " (" + subroutine_addr + ")"; + else + call_str = ch_ang_u + subroutine_addr; + level++; + } + + ostd::String line_str = ""; + for (int32_t i = 0; i < level; i++) + line_str.add("| "); + line_str.add(call_str); + + ostd::RegexRichString rgx(line_str); + rgx.fg("\\(|\\)|\\*", "Cyan"); //Operators + rgx.fg("(? ").reset(); + out.pStyled(rgx).nl().reset(); + + if (dec_lvl) + level--; } - out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to continue execution...").nl().reset(); - std::cin.get(); + Utils::printFullLine('=', ostd::ConsoleColors::Yellow); + out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to go back...").nl().reset(); + Utils::isEscapeKeyPressed(true); + } + + void Debugger::Display::printHelp(void) + { + out.clear(); + int32_t commandLength = 34; + Utils::printFullLine('#', ostd::ConsoleColors::Black, ostd::ConsoleColors::Red); + + out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available commands:").reset().nl(); + ostd::String tmpCommand = "(d)iff-view"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show the state of the machine, comparing the current cycle with the last.").reset().nl(); + tmpCommand = "(q)uit"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to close the program and exit the debugger. ( key can also be used.)").reset().nl(); + tmpCommand = "(h)elp"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show this dialog.").reset().nl(); + tmpCommand = "(t)racker"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show manually tracked values.").reset().nl(); + tmpCommand = "(s)tack [rows=8]"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show the stack and a description of every stack-frame saved.").reset().nl(); + tmpCommand = "(ca)ll-stack"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show information about all subroutine/interrupt calls.").reset().nl(); + tmpCommand = "(c)ontinue"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used exit step-execution mode and resume the normal execution of the program.").reset().nl(); + tmpCommand = "(p)rint [word] "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to print values from memory. Valid [size] values are: byte/word/dword/qword.").reset().nl(); + + out.nl().fg(ostd::ConsoleColors::Cyan).p("The letters in parenthesis can be used as the short version of the command.").nl(); + out.p("Square brackets represent optional parameters. If they have an '=' and a value, that is the default if not specified.").reset().nl(); + + out.nl(); + Utils::printFullLine('#', ostd::ConsoleColors::Black, ostd::ConsoleColors::Red); + + out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres to go back...").nl().reset(); + Utils::isEscapeKeyPressed(true); } ostd::String Debugger::Display::changeScreen(void) { - if (debugger.command == "diff") + if (debugger.command == "diff-view" || debugger.command == "d") { printDiff(); printStep(); printPrompt(); return getCommandInput(); } - else if (debugger.command == "tracker") + else if (debugger.command == "tracker" || debugger.command == "t") { printTrackedAddresses(); printStep(); printPrompt(); return getCommandInput(); } - else if (debugger.command == "stack") + else if (debugger.command.startsWith("stack") || debugger.command.startsWith("s")) { - printStack(); + uint16_t default_stack_rows = 8; + ostd::String params = debugger.command.new_trim(); + if (params == "stack" || params == "s") + printStack(default_stack_rows); + else if (params.contains(" ")) + { + params.substr(params.indexOf(" ") + 1).trim(); + if (!params.isNumeric()) return ""; + default_stack_rows = (uint16_t)params.toInt(); + if (default_stack_rows > 0xFFFF / 16) default_stack_rows = 8; + printStack(default_stack_rows); + } + else return ""; printStep(); printPrompt(); return getCommandInput(); } - else if (debugger.command == "call") + else if (debugger.command == "call-stack" || debugger.command == "ca") { printCallStack(); printStep(); printPrompt(); return getCommandInput(); } + else if (debugger.command == "help" || debugger.command == "h") + { + printHelp(); + printStep(); + printPrompt(); + return getCommandInput(); + } return ""; } @@ -876,12 +1154,18 @@ namespace dragon { if (argc < 2) { - out.fg("red").p("Error, too few arguments.").reset().nl(); - return 128; + out.fg(ostd::ConsoleColors::Red).p("Error: too few arguments.").nl(); + out.fg(ostd::ConsoleColors::Red).p("Use the --help option for more info.").reset().nl(); + return DragonRuntime::RETURN_VAL_TOO_FEW_ARGUMENTS; } else { debugger.args.machine_config_path = argv[1]; + if (debugger.args.machine_config_path == "--help") + { + print_application_help(); + return DragonRuntime::RETURN_VAL_CLOSE_DEBUGGER; + } for (int32_t i = 2; i < argc; i++) { ostd::String edit(argv[i]); @@ -891,6 +1175,8 @@ namespace dragon debugger.args.step_exec = true; else if (edit == "--track-step-diff-off") debugger.args.track_step_diff = false; + else if (edit == "--auto-track-data-off") + debugger.args.auto_track_all_data_symbols = false; else if (edit == "--hide-vdisplay") debugger.args.hide_virtual_display = true; else if (edit == "--auto-start") @@ -898,19 +1184,24 @@ namespace dragon else if (edit == "--force-load") { if ((argc - 1) - i < 2) - break; //TODO: Warning + return DragonRuntime::RETURN_VAL_MISSING_PARAM; i++; debugger.args.force_load_file = argv[i]; i++; edit = argv[i]; if (!edit.isNumeric()) - continue; //TODO: Error + return DragonRuntime::RETURN_VAL_PARAMETER_NOT_NUMERIC; debugger.args.force_load_mem_offset = (uint16_t)edit.toInt(); debugger.args.force_load = true; } + else if (edit == "--help") + { + print_application_help(); + return DragonRuntime::RETURN_VAL_CLOSE_DEBUGGER; + } } } - return 0; + return DragonRuntime::RETURN_VAL_EXIT_SUCCESS; } int32_t Debugger::initRuntime(void) @@ -921,6 +1212,7 @@ namespace dragon debugger.args.hide_virtual_display, debugger.args.track_call_stack, true); //CPU Debug Mode Enabled + closeEventListener.init(); if (init_state != 0) return init_state; //TODO: Error if (debugger.args.force_load) @@ -931,49 +1223,348 @@ namespace dragon debugger.labels = dragon::DisassemblyLoader::getLabelTable(); debugger.data = dragon::DisassemblyLoader::getDataTable(); - return 0; + return DragonRuntime::RETURN_VAL_EXIT_SUCCESS; } ostd::String Debugger::getCommandInput(void) { - // ostd::String cmd; - // ostd::KeyboardController keyboard; - - // ostd::eKeys key = ostd::eKeys::NoKeyPressed; - // int32_t commandHistoryIndex = ZERO(commandHistory.size() - 1); - // while (key != ostd::eKeys::Enter) - // { - // key = keyboard.waitForKeyPress(); - // if (key == ostd::eKeys::Up) - // { - // if (commandHistory.size() == 0) - // continue; - // Utils::clearConsoleLine(); - // Display::printPrompt(); - // out.p(commandHistory[commandHistoryIndex]); - // commandHistoryIndex = ZERO(commandHistoryIndex - 1); - // } - // else if (key == ostd::eKeys::Down) - // { - // if (commandHistory.size() == 0) - // continue; - // Utils::clearConsoleLine(); - // Display::printPrompt(); - // out.p(commandHistory[commandHistoryIndex]); - // commandHistoryIndex = CAP(commandHistoryIndex + 1, commandHistory.size() - 1); - // } - // else if (key == ostd::eKeys::Enter) - // { - // cmd = keyboard.getInputString(); - // commandHistory.push_back(cmd); - // } - // } - - // return cmd; - ostd::String cmd; - std::getline(std::cin, cmd.cpp_str_ref()); - return cmd; + ostd::KeyboardController kbc; + ostd::eKeys key = ostd::eKeys::NoKeyPressed; + + while ((key = kbc.getPressedKey()) != ostd::eKeys::Enter) + { + if (key == ostd::eKeys::Escape) + return InputCommandQuit; + } + return kbc.getInputString(); + + // ostd::String cmd; + // std::getline(std::cin, cmd.cpp_str_ref()); + // return cmd; } + int32_t Debugger::topLevelPrompt(void) + { + if (!data().args.auto_start_debug) + { + bool exit_loop = false; + bool done_with_auto_data_track = false; + DragonRuntime::vDisplay.hide(); + while (!exit_loop) + { + if (!done_with_auto_data_track) + { + data().command = "track "; + for (auto& sym : debugger.data) + data().command.add(sym.code).add(" "); + done_with_auto_data_track = true; + } + else + { + Display::printPrompt(); + data().command = getCommandInput(); + } + data().command.trim().toLower(); + if (data().command == "r" || data().command == "run") + { + output().p("Starting program...").nl(); + exit_loop = true; + } + else if (data().command == "q" || data().command == "quit" || data().command == InputCommandQuit) + { + output().nl().p("Exiting debugger...").nl(); + return DragonRuntime::RETURN_VAL_CLOSE_DEBUGGER; + } + else if (data().command.startsWith("t ") || data().command.startsWith("track ")) + { + data().command.substr(data().command.indexOf(" ") + 1).trim(); + exec_watch_command(); + } + else if (data().command == "h" || data().command == "help") + { + print_top_level_prompt_help(); + } + else if (data().command == "s" || data().command == "step") + { + data().args.step_exec = !data().args.step_exec; + output().p("Step execution = ").p(STR_BOOL(data().args.step_exec)).nl(); + } + } + } + + if (!data().args.hide_virtual_display) + DragonRuntime::vDisplay.show(); + + data().currentAddress = DragonRuntime::cpu.readRegister(data::Registers::IP); + return DragonRuntime::RETURN_VAL_EXIT_SUCCESS; + } + + int32_t Debugger::executeRuntime(void) + { + int32_t rValue = 0; + bool userQuit = false; + output().clear().fg(ostd::ConsoleColors::Green).p("Program running...").nl(); + if (!data().args.step_exec) + output().fg(ostd::ConsoleColors::Yellow).p("Press to enter in step-execution mode...").reset().nl(); + while (!userQuit) + { + ostd::SignalHandler::refresh(); + if (closeEventListener.hasHappened()) + userQuit = true; + data().command.clr(); + if (!userQuit && data().args.step_exec) + rValue = step_execution(userQuit, true); + else if (!userQuit) + rValue = normal_runtime(userQuit); + data().currentAddress = DragonRuntime::cpu.readRegister(data::Registers::IP); + } + output().nl().fg(ostd::ConsoleColors::Yellow).p("Execution terminated.").nl().nl().reset(); + return rValue; + } + + int32_t Debugger::step_execution(bool& outUserQuit, bool exec_first_step) + { + if (exec_first_step && !DragonRuntime::cpu.isHalted()) + DragonRuntime::runStep(data().trackedAddresses); + Display::printStep(); + processErrors(); + if (DragonRuntime::cpu.isInDebugBreakPoint()) + output().fg(ostd::ConsoleColors::Red).p("Reached Debug Break Point.").reset().nl(); + Display::printPrompt(); + data().command = getCommandInput(); + data().command.trim().toLower(); + while (data().command != "") + { + if (data().command == "q" || data().command == "quit" || data().command == InputCommandQuit) + { + output().nl(); + outUserQuit = true; + data().command = ""; + } + else if (data().command == "c" || data().command == "continue") + { + data().args.step_exec = false; + data().command = ""; + output().clear().fg(ostd::ConsoleColors::Green).p("Program running...").nl(); + output().fg(ostd::ConsoleColors::Yellow).p("Press to enter in step-execution mode...").reset().nl(); + } + else if (data().command.startsWith("p ") || data().command.startsWith("print ")) + { + data().command.substr(data().command.indexOf(" ") + 1).trim(); + const uint8_t TYPE_BYTE = 1; + const uint8_t TYPE_WORD = 2; + const uint8_t TYPE_DWORD = 4; + const uint8_t TYPE_QWORD = 8; + uint8_t type = TYPE_WORD; + if (data().command.contains(" ")) + { + ostd::String size_str = data().command.new_substr(0, data().command.indexOf(" ")).trim(); + data().command.substr(data().command.indexOf(" ") + 1).trim(); + if (size_str == "byte") type = TYPE_BYTE; + else if (size_str == "word") type = TYPE_WORD; + else if (size_str == "dword") type = TYPE_DWORD; + else if (size_str == "qword") type = TYPE_QWORD; + } + if (data().command.isNumeric()) + { + uint16_t addr = data().command.toInt(); + uint16_t end_addr = addr; + ostd::String tmp = ""; + tmp.add("*(").add(ostd::Utils::getHexStr(addr, true, 2)); + if (type != TYPE_BYTE) + { + end_addr = addr + type - 1; + if (end_addr < addr) + end_addr = addr; + else + tmp.add("-").add(ostd::Utils::getHexStr(end_addr, true, 2)); + } + tmp.add(")"); + ostd::RegexRichString rgx(tmp); + rgx.fg("\\(|\\)|-", "darkgray"); + rgx.fg("\\*", "red"); + rgx.fg("0x[0-9A-Fa-f]+|0b[0-1]+|(? command.").reset().nl(); + else + { + ostd::String tmp = ""; + tmp.add("*(").add(ostd::Utils::getHexStr(addr, true, 2)); + if (type != TYPE_BYTE) + if (size > 1) + tmp.add("-").add(ostd::Utils::getHexStr((uint16_t)(addr + size - 1), true, 2)); + tmp.add(")"); + ostd::RegexRichString rgx(tmp); + rgx.fg("\\(|\\)|-", "darkgray"); + rgx.fg("\\*", "red"); + rgx.fg("0x[0-9A-Fa-f]+|0b[0-1]+|(? command.").reset().nl(); + } + Display::printPrompt(); + data().command = getCommandInput(); + } + else + data().command = Display::changeScreen(); + } + return DragonRuntime::RETURN_VAL_EXIT_SUCCESS; + } + + int32_t Debugger::normal_runtime(bool& outUserQuit) + { + bool result = DragonRuntime::runStep(data().trackedAddresses); + bool hasError = DragonRuntime::hasError(); + bool enableStepExec = !result || hasError || Utils::isEscapeKeyPressed() || DragonRuntime::cpu.isInDebugBreakPoint(); + data().args.step_exec = enableStepExec; + if (enableStepExec) + return step_execution(outUserQuit, false); + return DragonRuntime::RETURN_VAL_EXIT_SUCCESS; + } + + void Debugger::exec_watch_command(void) + { + auto tokens = data().command.tokenize(); + for (auto& addr : tokens) + { + data().command = addr; + if (data().command.isNumeric()) + { + data().trackedAddresses.push_back((uint16_t)data().command.toInt()); + } + else if (data().command.startsWith("$")) + { + uint16_t nbytes = 1; + uint16_t addr = Utils::findSymbol(data().data, data().command, &nbytes); + if (addr > 0) + { + for (int32_t i = 0; i < nbytes; i++) + data().trackedAddresses.push_back(addr + i); + } + else + { + addr = Utils::findSymbol(data().labels, data().command, &nbytes); + if (addr > 0) + { + for (int32_t i = 0; i < nbytes; i++) + data().trackedAddresses.push_back(addr + i); + } + } + } + else if (data().command.contains("[") && data().command.endsWith("]")) + { + uint16_t nbytes = 1; + ostd::String str_nbytes = data().command.new_substr(data().command.indexOf("[") + 1).trim(); + str_nbytes.substr(0, str_nbytes.len() - 1); + data().command.substr(0, data().command.indexOf("[")).trim(); + if (str_nbytes.isNumeric()) + nbytes = str_nbytes.toInt(); + if (nbytes < 1) nbytes = 1; + if (data().command.isNumeric()) + { + uint16_t addr = data().command.toInt(); + for (int32_t i = 0; i < nbytes; i++) + data().trackedAddresses.push_back(addr + i); + } + } + } + output().p("Tracking ").p((int)data().trackedAddresses.size()).p(" addresses.").nl(); + } + + void Debugger::print_top_level_prompt_help(void) + { + int32_t commandLength = 40; + Utils::printFullLine('#', ostd::ConsoleColors::Black, ostd::ConsoleColors::Red); + + out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available commands:").reset().nl(); + ostd::String tmpCommand = "(r)un"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to start the runtime.").reset().nl(); + tmpCommand = "(q)uit"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to exit the debugger. ( key can also be used here.)").reset().nl(); + tmpCommand = "(h)elp"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show this dialog.").reset().nl(); + tmpCommand = "(s)tep"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to enable/disable step-execution for the runtime.").reset().nl(); + tmpCommand = "(t)rack "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to track specific addresses during execution.").reset().nl(); + + out.nl().fg(ostd::ConsoleColors::Cyan).p("The letters in parenthesis can be used as the short version of the command.").nl(); + out.p("Square brackets represent optional parameters. If they have an '=' and a value, that is the default if not specified.").reset().nl(); + + out.nl(); + Utils::printFullLine('#', ostd::ConsoleColors::Black, ostd::ConsoleColors::Red); + } + + void Debugger::print_application_help(void) + { + int32_t commandLength = 46; + + out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available parameters:").reset().nl(); + ostd::String tmpCommand = "--verbose-load"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show more information while loading the virtual machine.").reset().nl(); + tmpCommand = "--step-exec"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Launches the debugger directly in step-execution mode.").reset().nl(); + tmpCommand = "--track-step-diff-off"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Disables the tracking of most machine data used for differential-view.").reset().nl(); + tmpCommand = "--auto-track-data-off"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Disables the automatic tracking of all data-symbols.").reset().nl(); + tmpCommand = "--hide-vdisplay"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Hides the Virtual Display during the execution of the program.").reset().nl(); + tmpCommand = "--auto-start"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Launches the runtime directly, skipping the top-level prompt of the debugger.").reset().nl(); + tmpCommand = "--force-load "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Injects the specified binary into RAM at the specified offset.").reset().nl(); + tmpCommand = "--help"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Displays this help message.").reset().nl(); + + out.nl().fg(ostd::ConsoleColors::Magenta).p("Usage: ./ddb [...options...]").reset().nl(); + out.nl(); + } } \ No newline at end of file diff --git a/src/debugger/Debugger.hpp b/src/debugger/Debugger.hpp index 2b39272..7c5fd04 100644 --- a/src/debugger/Debugger.hpp +++ b/src/debugger/Debugger.hpp @@ -21,6 +21,7 @@ namespace dragon bool auto_start_debug = false; bool hide_virtual_display = false; bool track_call_stack = true; + bool auto_track_all_data_symbols = true; ostd::String force_load_file = ""; uint16_t force_load_mem_offset = 0x00; }; @@ -38,14 +39,26 @@ namespace dragon bool userQuit { false }; ostd::String disassemblyDirectory { "disassembly" }; }; + struct tCloseEventListener : public ostd::BaseObject + { + void init(void); + void handleSignal(ostd::tSignal& signal); + inline bool hasHappened(void) const { return m_mainWindowClosed; } + + private: + bool m_mainWindowClosed { false }; + }; 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::String& symbol); + static ostd::String findSymbol(const DisassemblyList& labels, uint16_t address, uint16_t* outSize = nullptr); + static uint16_t findSymbol(const DisassemblyList& labels, const ostd::String& symbol, uint16_t* outSize = nullptr); static bool isValidLabelNameChar(char c); static void clearConsoleLine(void); + static bool isEscapeKeyPressed(bool blocking = false); + static ostd::ConsoleOutputHandler& printFullLine(char c, const ostd::ConsoleColors::tConsoleColor& foreground); + static ostd::ConsoleOutputHandler& printFullLine(char c, const ostd::ConsoleColors::tConsoleColor& foreground, const ostd::ConsoleColors::tConsoleColor& background); }; public: class Display { @@ -56,8 +69,9 @@ namespace dragon static void printStep(void); static void printDiff(void); static void printTrackedAddresses(void); - static void printStack(void); + static void printStack(uint16_t nrows); static void printCallStack(void); + static void printHelp(void); static ostd::String changeScreen(void); }; public: @@ -67,10 +81,22 @@ namespace dragon static ostd::String getCommandInput(void); static inline tDebuggerData& data(void) { return debugger; } static inline ostd::ConsoleOutputHandler& output(void) { return out; } + static int32_t topLevelPrompt(void); + static int32_t executeRuntime(void); + + private: + static int32_t step_execution(bool& outUserQuit, bool exec_first_step = true); + static int32_t normal_runtime(bool& outUserQuit); + static void exec_watch_command(void); + static void print_top_level_prompt_help(void); + static void print_application_help(void); private: inline static tDebuggerData debugger; inline static ostd::ConsoleOutputHandler out; - inline static std::vector commandHistory; + static tCloseEventListener closeEventListener; + + public: + inline static const ostd::String InputCommandQuit = "//quit//"; }; } \ No newline at end of file diff --git a/src/debugger/DisassemblyLoader.cpp b/src/debugger/DisassemblyLoader.cpp index ae0e97f..206d042 100644 --- a/src/debugger/DisassemblyLoader.cpp +++ b/src/debugger/DisassemblyLoader.cpp @@ -29,6 +29,7 @@ namespace dragon ostd::serial::SerialIO serializer(stream, ostd::serial::SerialIO::tEndianness::BigEndian); ostd::StreamIndex addr = 0; int32_t line_addr = 0; + int16_t data_size = 1; int8_t line_code_char = 0; ostd::String header_string = ""; serializer.r_NullTerminatedString(0, header_string); @@ -38,6 +39,8 @@ namespace dragon { serializer.r_DWord(addr, line_addr); addr += ostd::tTypeSize::DWORD; + serializer.r_Word(addr, data_size); + addr += ostd::tTypeSize::WORD; ostd::String code_line = ""; serializer.r_NullTerminatedString(addr, code_line); addr += (code_line.len() + 1) * ostd::tTypeSize::BYTE; @@ -59,6 +62,7 @@ namespace dragon code::Assembler::tDisassemblyLine line; line.addr = line_addr; line.code = code_line; + line.size = data_size; if (mode == MODE_CODE) { ostd::String codeEdit(line.code); diff --git a/src/debugger/debugger_main.cpp b/src/debugger/debugger_main.cpp index 78635e9..2ff62bf 100644 --- a/src/debugger/debugger_main.cpp +++ b/src/debugger/debugger_main.cpp @@ -1,131 +1,26 @@ #include "Debugger.hpp" #include "../runtime/DragonRuntime.hpp" -void exec_watch_command(void) -{ - using namespace dragon; - - 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("$")) - { - uint8_t nbytes = 1; - if (Debugger::data().command.contains("[") && Debugger::data().command.endsWith("]")) - { - ostd::String str_nbytes = Debugger::data().command.new_substr(Debugger::data().command.indexOf("[") + 1).trim(); - str_nbytes.substr(0, str_nbytes.len() - 1); - std::cout << str_nbytes << "\n"; - std::cout << Debugger::data().command << "\n"; - Debugger::data().command.substr(0, Debugger::data().command.indexOf("[")).trim(); - std::cout << Debugger::data().command << "\n"; - if (str_nbytes.isNumeric()) - nbytes = str_nbytes.toInt(); - if (nbytes < 1) nbytes = 1; - } - uint16_t addr = Debugger::Utils::findSymbol(Debugger::data().data, Debugger::data().command); - if (addr > 0) - { - for (int32_t i = 0; i < nbytes; i++) - Debugger::data().trackedAddresses.push_back(addr + i); - } - else - { - addr = Debugger::Utils::findSymbol(Debugger::data().labels, Debugger::data().command); - if (addr > 0) - { - for (int32_t i = 0; i < nbytes; i++) - Debugger::data().trackedAddresses.push_back(addr + i); - } - } - } - } - std::cout << (int)Debugger::data().trackedAddresses.size() << "\n"; -} - int main(int argc, char** argv) { - using namespace dragon; - int32_t rValue = Debugger::loadArguments(argc, argv); - if (rValue != 0) return rValue; - rValue = Debugger::initRuntime(); + //Loading commandline arguments + int32_t rValue = dragon::Debugger::loadArguments(argc, argv); + if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_DEBUGGER) + return 0; if (rValue != 0) return rValue; - + //Initializing the runtime + rValue = dragon::Debugger::initRuntime(); + if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_DEBUGGER) + return 0; + 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 == "r" || Debugger::data().command == "run") - break; - else if (Debugger::data().command == "q" || Debugger::data().command == "quit") - return rValue; - else if (Debugger::data().command.startsWith("watch ")) - { - Debugger::data().command.substr(6).trim(); - exec_watch_command(); - } - else if (Debugger::data().command == "step") - { - Debugger::data().args.step_exec = true; - } - } - } + //Running top-level prompt + rValue = dragon::Debugger::topLevelPrompt(); + if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_DEBUGGER) + return 0; + if (rValue != 0) return rValue; - 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().fg(ostd::ConsoleColors::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 != "") - { - if (Debugger::data().command == "q" || Debugger::data().command == "quit") - { - userQuit = true; - Debugger::data().command = ""; - } - else if (Debugger::data().command == "c" || Debugger::data().command == "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; + //Executing the runtime + return dragon::Debugger::executeRuntime(); } \ No newline at end of file diff --git a/src/gui/Window.cpp b/src/gui/Window.cpp index f7329ce..fa67852 100644 --- a/src/gui/Window.cpp +++ b/src/gui/Window.cpp @@ -8,7 +8,7 @@ namespace dragon SDL_FreeSurface(m_fontSurface); SDL_DestroyRenderer(m_renderer); SDL_DestroyWindow(m_window); - IMG_Quit(); + // IMG_Quit(); SDL_Quit(); } @@ -23,12 +23,12 @@ namespace dragon printf( "SDL could not initialize! Error: %s\n", SDL_GetError() ); exit(1); } - int imgFlags = IMG_INIT_PNG; - if (!(IMG_Init(imgFlags) & imgFlags)) - { - printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() ); - exit(2); - } + // int imgFlags = IMG_INIT_PNG; + // if (!(IMG_Init(imgFlags) & imgFlags)) + // { + // printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() ); + // exit(2); + // } 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); @@ -122,7 +122,7 @@ namespace dragon SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight); wrd.new_width = m_windowWidth; wrd.new_height = m_windowHeight; - ostd::SignalHandler::emitSignal(Signal_OnWindowResized, ostd::tSignalPriority::RealTime, wrd); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowResized, ostd::tSignalPriority::RealTime, wrd); } } else if (event.type == SDL_MOUSEMOTION) @@ -131,17 +131,17 @@ namespace dragon if (isMouseDragEventEnabled() && mmd.button != MouseEventData::eButton::None) ostd::SignalHandler::emitSignal(Signal_OnMouseDragged, ostd::tSignalPriority::RealTime, mmd); else - ostd::SignalHandler::emitSignal(Signal_OnMouseMoved, ostd::tSignalPriority::RealTime, mmd); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::MouseMoved, ostd::tSignalPriority::RealTime, mmd); } else if (event.type == SDL_MOUSEBUTTONDOWN) { MouseEventData mmd = l_getMouseState(); - ostd::SignalHandler::emitSignal(Signal_OnMousePressed, ostd::tSignalPriority::RealTime, mmd); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::MousePressed, ostd::tSignalPriority::RealTime, mmd); } else if (event.type == SDL_MOUSEBUTTONUP) { MouseEventData mmd = l_getMouseState(); - ostd::SignalHandler::emitSignal(Signal_OnMouseReleased, ostd::tSignalPriority::RealTime, mmd); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::MouseReleased, ostd::tSignalPriority::RealTime, mmd); } } } diff --git a/src/gui/Window.hpp b/src/gui/Window.hpp index 765a642..db6ac8a 100644 --- a/src/gui/Window.hpp +++ b/src/gui/Window.hpp @@ -63,14 +63,9 @@ namespace dragon bool m_initialized { false }; public: - //Signals - inline static const uint32_t Signal_OnMousePressed = ostd::SignalHandler::newCustomSignal(1000); - inline static const uint32_t Signal_OnMouseReleased = ostd::SignalHandler::newCustomSignal(1001); - inline static const uint32_t Signal_OnMouseMoved = ostd::SignalHandler::newCustomSignal(1002); + //Signals //TODO: Add theese to the builtin signals in ostd::SignalHandler inline static const uint32_t Signal_OnMouseDragged = ostd::SignalHandler::newCustomSignal(1003); - inline static const uint32_t Signal_OnWindowClosed = ostd::SignalHandler::newCustomSignal(2000); - inline static const uint32_t Signal_OnWindowResized = ostd::SignalHandler::newCustomSignal(2001); }; class WindowResizedData : public ostd::BaseObject { diff --git a/src/hardware/VirtualCPU.cpp b/src/hardware/VirtualCPU.cpp index 411fafe..e784936 100644 --- a/src/hardware/VirtualCPU.cpp +++ b/src/hardware/VirtualCPU.cpp @@ -693,6 +693,16 @@ namespace dragon m_subroutineCounter--; } break; + case data::OpCodes::ArgReg: + { + uint8_t regAddr = fetch8(); + if (!isInSubRoutine()) break; + int16_t pp_val = readRegister(data::Registers::PP); + int16_t arg_data = m_memory.read16(pp_val); + writeRegister(data::Registers::PP, pp_val - 2); + writeRegister(regAddr, arg_data); + } + break; case data::OpCodes::RetInt: { m_isInInterruptHandler = false; diff --git a/src/runtime/DragonRuntime.cpp b/src/runtime/DragonRuntime.cpp index dcf1526..acb302c 100644 --- a/src/runtime/DragonRuntime.cpp +++ b/src/runtime/DragonRuntime.cpp @@ -63,6 +63,64 @@ namespace dragon return list; } + int32_t DragonRuntime::loadArguments(int argc, char** argv, tCommandLineArgs& args) + { + if (argc < 2) + { + out.fg(ostd::ConsoleColors::Red).p("Error: too few arguments.").nl(); + out.fg(ostd::ConsoleColors::Red).p("Use the --help option for more info.").reset().nl(); + return dragon::DragonRuntime::RETURN_VAL_TOO_FEW_ARGUMENTS; + } + else + { + args.machine_config_path = argv[1]; + if (args.machine_config_path == "--help") + { + __print_application_help(); + return DragonRuntime::RETURN_VAL_CLOSE_RUNTIME; + } + for (int32_t i = 2; i < argc; i++) + { + ostd::String edit(argv[i]); + if (edit == "--debug") + args.basic_debug = true; + else if (edit == "--step") + args.step_exec = true; + else if (edit == "--verbose-load") + args.verbose_load = true; + else if (edit == "--limit-cycles") + { + if (i == argc - 1) + return RETURN_VAL_MISSING_PARAM; + i++; + edit = argv[i]; + if (!edit.isNumeric()) + return RETURN_VAL_PARAMETER_NOT_NUMERIC; + args.cycle_limit = (int32_t)edit.toInt(); + } + else if (edit == "--force-load") + { + if ((argc - 1) - i < 2) + return RETURN_VAL_MISSING_PARAM; + i++; + args.force_load_file = argv[i]; + i++; + edit = argv[i]; + if (!edit.isNumeric()) + return RETURN_VAL_PARAMETER_NOT_NUMERIC; + args.force_load_mem_offset = (uint16_t)edit.toInt(); + args.force_load = true; + } + else if (edit == "--help") + { + __print_application_help(); + return RETURN_VAL_CLOSE_RUNTIME; + } + } + } + return RETURN_VAL_EXIT_SUCCESS; + } + int32_t DragonRuntime::initMachine(const ostd::String& configFilePath, bool verbose, bool trackMachineInfoDiff, @@ -70,13 +128,14 @@ namespace dragon bool trackCallStack, bool debugModeEnabled) { + ostd::SignalHandler::init(); if (verbose) - out.p("Loading machine config: ").p(configFilePath.cpp_str()).nl(); + out.fg(ostd::ConsoleColors::Magenta).p("Loading machine config: ").fg(ostd::ConsoleColors::BrightYellow).p(configFilePath.cpp_str()).nl(); machine_config = dragon::MachineConfigLoader::loadConfig(configFilePath); - if (!machine_config.isValid()) return 1; //TODO: Error + if (!machine_config.isValid()) return RETURN_VAL_INVALID_MACHINE_CONFIG; //TODO: Error if (verbose) - out.p(" Initializing virtual display:").nl(); + out.fg(ostd::ConsoleColors::Magenta).p(" Initializing virtual display:").nl(); vDisplay.initialize(800, 600, "DragonVM", "font.bmp"); int32_t w = RawTextRenderer::CONSOLE_CHARS_H * RawTextRenderer::FONT_CHAR_W; //60 * 16; int32_t h = RawTextRenderer::CONSOLE_CHARS_V * RawTextRenderer::FONT_CHAR_H; //60 * 9; @@ -85,6 +144,7 @@ namespace dragon vDisplay.hide(); if (verbose) { + out.fg(ostd::ConsoleColors::BrightYellow); out.p(" Done. (").p(w).p("x").p(h).p(")"); if (hideVirtualDisplay) out.p(" - HIDDEN").nl(); @@ -92,47 +152,50 @@ namespace dragon out.nl(); } - if (machine_config.vdisk_paths.size() == 0) return 2; //TODO: Error + if (machine_config.vdisk_paths.size() == 0) return RETURN_VAL_NO_DISK; //TODO: Error if (verbose) - out.p(" Initializing virtual disks:").nl(); + out.fg(ostd::ConsoleColors::Magenta).p(" Initializing virtual disks:").nl(); for (auto const& disk_path : machine_config.vdisk_paths) { vDisks[disk_path.first] = dragon::hw::VirtualHardDrive(disk_path.second); vDiskInterface.connectDisk(vDisks[disk_path.first]); if (verbose) - out.p(" Disk").p(disk_path.first).p(" connected: ").p(disk_path.second.cpp_str()).nl(); + out.fg(ostd::ConsoleColors::BrightYellow).p(" Disk").p(disk_path.first).p(" connected: ").p(disk_path.second.cpp_str()).nl(); } if (verbose) - out.p(" Loading vBIOS file: ").p(machine_config.bios_path.cpp_str()).nl(); + out.fg(ostd::ConsoleColors::Magenta).p(" Loading vBIOS file: ").fg(ostd::ConsoleColors::BrightYellow).p(machine_config.bios_path.cpp_str()).nl(); vBIOS.init(machine_config.bios_path); if (verbose) - out.p(" Loading vCMOS file: ").p(machine_config.cmos_path.cpp_str()).nl(); + out.fg(ostd::ConsoleColors::Magenta).p(" Loading vCMOS file: ").fg(ostd::ConsoleColors::BrightYellow).p(machine_config.cmos_path.cpp_str()).nl(); vCMOS.init(machine_config.cmos_path); if (verbose) { - out.p(" Initializing Memory Mapper:").nl(); + out.fg(ostd::ConsoleColors::Magenta).p(" Initializing Memory Mapper:").nl(); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(" vBIOS: "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::BIOS_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::BIOS_End, true, 2).cpp_str()); out.p(" (remap=false)").nl(); } - memMap.mapDevice(vBIOS, dragon::data::MemoryMapAddresses::BIOS_Start, dragon::data::MemoryMapAddresses::BIOS_End, false, "vBIOS"); + memMap.mapDevice(vBIOS, dragon::data::MemoryMapAddresses::BIOS_Start, dragon::data::MemoryMapAddresses::BIOS_End, false, "BIOS"); if (verbose) { - out.p(" vCMOS: "); + out.fg(ostd::ConsoleColors::Magenta).fg(ostd::ConsoleColors::Magenta).p(" vCMOS: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::CMOS_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::CMOS_End, true, 2).cpp_str()); out.p(" (remap=true)").nl(); } - memMap.mapDevice(vCMOS, dragon::data::MemoryMapAddresses::CMOS_Start, dragon::data::MemoryMapAddresses::CMOS_End, true, "vCMOS"); + memMap.mapDevice(vCMOS, dragon::data::MemoryMapAddresses::CMOS_Start, dragon::data::MemoryMapAddresses::CMOS_End, true, "CMOS"); if (verbose) { - out.p(" intVec: "); + out.fg(ostd::ConsoleColors::Magenta).p(" intVec: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::IntVector_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::IntVector_End, true, 2).cpp_str()); @@ -141,61 +204,68 @@ namespace dragon memMap.mapDevice(intVec, dragon::data::MemoryMapAddresses::IntVector_Start, dragon::data::MemoryMapAddresses::IntVector_End, true, "intVec"); if (verbose) { - out.p(" vKeyboard: "); + out.fg(ostd::ConsoleColors::Magenta).p(" vKeyboard: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Keyboard_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Keyboard_End, true, 2).cpp_str()); out.p(" (remap=false)").nl(); } - memMap.mapDevice(vKeyboard, dragon::data::MemoryMapAddresses::Keyboard_Start, dragon::data::MemoryMapAddresses::Keyboard_End, false, "vKeyboard"); + memMap.mapDevice(vKeyboard, dragon::data::MemoryMapAddresses::Keyboard_Start, dragon::data::MemoryMapAddresses::Keyboard_End, false, "Keyb."); if (verbose) { - out.p(" vMouse: "); + out.fg(ostd::ConsoleColors::Magenta).p(" vMouse: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Mouse_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Mouse_End, true, 2).cpp_str()); out.p(" (remap=false)").nl(); } - memMap.mapDevice(vMouse, dragon::data::MemoryMapAddresses::Mouse_Start, dragon::data::MemoryMapAddresses::Mouse_End, false, "vMouse"); + memMap.mapDevice(vMouse, dragon::data::MemoryMapAddresses::Mouse_Start, dragon::data::MemoryMapAddresses::Mouse_End, false, "Mouse"); if (verbose) { - out.p(" vMBR: "); + out.fg(ostd::ConsoleColors::Magenta).p(" vMBR: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::MBR_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::MBR_End, true, 2).cpp_str()); out.p(" (remap=true)").nl(); } - memMap.mapDevice(vMBR, dragon::data::MemoryMapAddresses::MBR_Start, dragon::data::MemoryMapAddresses::MBR_End, true, "vMBR"); + memMap.mapDevice(vMBR, dragon::data::MemoryMapAddresses::MBR_Start, dragon::data::MemoryMapAddresses::MBR_End, true, "MBR"); if (verbose) { - out.p(" vDiskInterface: "); + out.fg(ostd::ConsoleColors::Magenta).p(" vDiskInterface: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::DiskInterface_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::DiskInterface_End, true, 2).cpp_str()); out.p(" (remap=true)").nl(); } - memMap.mapDevice(vDiskInterface, dragon::data::MemoryMapAddresses::DiskInterface_Start, dragon::data::MemoryMapAddresses::DiskInterface_End, true, "vDiskInterface"); + memMap.mapDevice(vDiskInterface, dragon::data::MemoryMapAddresses::DiskInterface_Start, dragon::data::MemoryMapAddresses::DiskInterface_End, true, "Disk"); if (verbose) { - out.p(" vGraphicsInterface: "); + out.fg(ostd::ConsoleColors::Magenta).p(" vGraphicsInterface: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::VideoCardInterface_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::VideoCardInterface_End, true, 2).cpp_str()); out.p(" (remap=true)").nl(); } - memMap.mapDevice(vGraphicsInterface, dragon::data::MemoryMapAddresses::VideoCardInterface_Start, dragon::data::MemoryMapAddresses::VideoCardInterface_End, true, "vGraphicsInterface"); + memMap.mapDevice(vGraphicsInterface, dragon::data::MemoryMapAddresses::VideoCardInterface_Start, dragon::data::MemoryMapAddresses::VideoCardInterface_End, true, "VGA"); if (verbose) { - out.p(" vSerialInterface: "); + out.fg(ostd::ConsoleColors::Magenta).p(" vSerialInterface: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::SerialInterface_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::SerialInterface_End, true, 2).cpp_str()); out.p(" (remap=true)").nl(); } - memMap.mapDevice(vSerialInterface, dragon::data::MemoryMapAddresses::SerialInterface_Start, dragon::data::MemoryMapAddresses::SerialInterface_End, true, "vSerialInterface"); + memMap.mapDevice(vSerialInterface, dragon::data::MemoryMapAddresses::SerialInterface_Start, dragon::data::MemoryMapAddresses::SerialInterface_End, true, "serial"); if (verbose) { - out.p(" RAM: "); + out.fg(ostd::ConsoleColors::Magenta).p(" RAM: "); + out.fg(ostd::ConsoleColors::BrightYellow); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Memory_Start, true, 2).cpp_str()); out.p(" to "); out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Memory_End, true, 2).cpp_str()); @@ -207,24 +277,24 @@ namespace dragon uint8_t default_bios_video_color = 0x4; if (verbose) { - out.p(" Initializing vCPU reset sequence:").nl(); + out.fg(ostd::ConsoleColors::Magenta).p(" Initializing vCPU reset sequence:").nl(); // out.p(" BIOSVideo default colors: ").p(ostd::Utils::getHexStr(default_bios_video_color).cpp_str()).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).cpp_str()).nl(); + out.fg(ostd::ConsoleColors::BrightYellow).p(" Reset IP register: ").p(ostd::Utils::getHexStr(reset_ip_addr, true, 2).cpp_str()).nl(); cpu.writeRegister(dragon::data::Registers::IP, reset_ip_addr); if (verbose) - out.p(" Debug mode enabled: ").p(STR_BOOL(debugModeEnabled)).nl(); + out.fg(ostd::ConsoleColors::BrightYellow).p(" Debug mode enabled: ").p(STR_BOOL(debugModeEnabled)).nl(); cpu.m_debugModeEnabled = debugModeEnabled; out.nl().nl(); s_trackMachineInfo = trackMachineInfoDiff; s_trackCallStack = trackCallStack; - return 0; + return RETURN_VAL_EXIT_SUCCESS; } void DragonRuntime::runMachine(int32_t cycleLimit, bool basic_debug, bool step_exec) @@ -239,6 +309,7 @@ namespace dragon { if (basic_debug) out.clear(); + ostd::SignalHandler::refresh(); uint16_t addr = cpu.readRegister(dragon::data::Registers::IP); uint16_t spAddr = cpu.readRegister(dragon::data::Registers::SP); running = cpu.execute() && vDisplay.isRunning(); @@ -283,6 +354,7 @@ namespace dragon bool DragonRuntime::runStep(std::vector trackedAddresses) { + std::sort(trackedAddresses.begin(), trackedAddresses.end()); __get_machine_footprint(&s_machineInfo, trackedAddresses, true); __track_call_stack(&s_machineInfo); bool running = cpu.execute() && vDisplay.isRunning(); @@ -404,26 +476,54 @@ namespace dragon if (inst == data::OpCodes::CallImm) { uint16_t call_addr = memMap.read16(instAddr + 1); - minfo.callStack.push_back({ "CALL IMM", call_addr }); + minfo.callStack.push_back({ "CALL IMM", call_addr, instAddr }); } else if (inst == data::OpCodes::CallReg) { uint8_t reg_addr = memMap.read8(instAddr + 1); uint16_t call_addr = cpu.readRegister(reg_addr); - minfo.callStack.push_back({ "CALL REG", call_addr }); + minfo.callStack.push_back({ "CALL REG", call_addr, instAddr }); } else if (interrupts_enabled && inst == data::OpCodes::Int) { uint8_t int_num = memMap.read8(instAddr + 1); - minfo.callStack.push_back({ "INT", int_num }); + minfo.callStack.push_back({ "INT", int_num, instAddr }); } else if (inst == data::OpCodes::Ret) { - minfo.callStack.push_back({ "RET", 0x0000 }); + minfo.callStack.push_back({ "RET", 0x0000, instAddr }); } else if (interrupts_enabled && inst == data::OpCodes::RetInt) { - minfo.callStack.push_back({ "RET INT", 0x0000 }); + minfo.callStack.push_back({ "RET INT", 0x0000, instAddr }); } } + + void DragonRuntime::__print_application_help(void) + { + int32_t commandLength = 46; + + out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available parameters:").reset().nl(); + ostd::String tmpCommand = "--verbose-load"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to show more information while loading the virtual machine.").reset().nl(); + tmpCommand = "--step"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Launches the runtime in basic step-esecution mode.").reset().nl(); + tmpCommand = "--debug"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Launches the runtime in basic debug mode.").reset().nl(); + tmpCommand = "--limit-cycles "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Sets the maximum amount of clock cycles the runtime can run.").reset().nl(); + tmpCommand = "--force-load "; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Injects the specified binary into RAM at the specified offset.").reset().nl(); + tmpCommand = "--help"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Displays this help message.").reset().nl(); + + out.nl().fg(ostd::ConsoleColors::Magenta).p("Usage: ./dvm [...options...]").reset().nl(); + out.nl(); + } } \ No newline at end of file diff --git a/src/runtime/DragonRuntime.hpp b/src/runtime/DragonRuntime.hpp index de93e04..81faa0e 100644 --- a/src/runtime/DragonRuntime.hpp +++ b/src/runtime/DragonRuntime.hpp @@ -16,12 +16,25 @@ namespace dragon { class DragonRuntime { - public: struct tCallInfo { + public: struct tCallInfo + { ostd::String info; uint16_t addr; + uint16_t inst_addr; }; - - public: struct tMachineDebugInfo { + public: 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; + }; + public: struct tMachineDebugInfo + { inline tMachineDebugInfo(void) { } uint16_t previousInstructionAddress { 0x0000 }; @@ -63,6 +76,7 @@ namespace dragon static void printRegisters(dragon::hw::VirtualCPU& cpu); static void processErrors(void); static std::vector getErrorList(void); + static int32_t loadArguments(int argc, char** argv, tCommandLineArgs& args); static int32_t initMachine(const ostd::String& configFilePath, bool verbose = false, bool trackMachineInfoDiff = false, @@ -75,10 +89,12 @@ namespace dragon inline static const tMachineDebugInfo& getMachineInfoDiff(void) { return s_machineInfo; } inline static bool hasError(void) { return data::ErrorHandler::hasError(); } + inline static ostd::ConsoleOutputHandler& output(void) { return out; } private: static void __get_machine_footprint(tMachineDebugInfo* machineInfo, std::vector trackedAddresses, bool previous); static void __track_call_stack(tMachineDebugInfo* machineInfo); + static void __print_application_help(void); public: inline static ostd::ConsoleOutputHandler out; @@ -106,5 +122,15 @@ namespace dragon inline static tMachineDebugInfo s_machineInfo; inline static bool s_trackMachineInfo { false }; inline static bool s_trackCallStack { false }; + + public: + inline static const int32_t RETURN_VAL_CLOSE_DEBUGGER = 128; + inline static const int32_t RETURN_VAL_CLOSE_RUNTIME = 256; + inline static const int32_t RETURN_VAL_INVALID_MACHINE_CONFIG = 1; + inline static const int32_t RETURN_VAL_NO_DISK = 2; + inline static const int32_t RETURN_VAL_TOO_FEW_ARGUMENTS = 3; + inline static const int32_t RETURN_VAL_MISSING_PARAM = 4; + inline static const int32_t RETURN_VAL_PARAMETER_NOT_NUMERIC = 5; + inline static const int32_t RETURN_VAL_EXIT_SUCCESS = 0; }; } \ No newline at end of file diff --git a/src/runtime/runtime_main.cpp b/src/runtime/runtime_main.cpp index bf6a2bf..3a12ac1 100644 --- a/src/runtime/runtime_main.cpp +++ b/src/runtime/runtime_main.cpp @@ -1,74 +1,24 @@ #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::String edit(argv[i]); - if (edit == "--debug") - args.basic_debug = true; - else if (edit == "--step") - args.step_exec = true; - else if (edit == "--verbose-load") - args.verbose_load = true; - else if (edit == "--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 == "--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; - } - } - } + //Loading commandline arguments + dragon::DragonRuntime::tCommandLineArgs args; + int32_t rValue = dragon::DragonRuntime::loadArguments(argc, argv, args); + if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_RUNTIME) + return 0; + if (rValue != 0) return rValue; - int32_t init_state = dragon::DragonRuntime::initMachine(args.machine_config_path, args.verbose_load); - if (init_state != 0) return init_state; //TODO: Error + //Initializing the runtime + rValue = dragon::DragonRuntime::initMachine(args.machine_config_path, args.verbose_load); + if (rValue == dragon::DragonRuntime::RETURN_VAL_CLOSE_RUNTIME) + return 0; + if (rValue != 0) return rValue; + //Executing the runtime if (args.force_load) dragon::DragonRuntime::forceLoad(args.force_load_file, args.force_load_mem_offset); - dragon::DragonRuntime::runMachine(args.cycle_limit, args.basic_debug, args.step_exec); - - return 0; + return dragon::DragonRuntime::RETURN_VAL_EXIT_SUCCESS; } diff --git a/src/tools/GlobalData.hpp b/src/tools/GlobalData.hpp index 3a9797c..841a80f 100644 --- a/src/tools/GlobalData.hpp +++ b/src/tools/GlobalData.hpp @@ -239,6 +239,7 @@ namespace dragon 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 ArgReg = 0x56; inline static constexpr uint8_t LShiftRegImm = 0x60; inline static constexpr uint8_t LShiftRegReg = 0x61; @@ -337,6 +338,7 @@ namespace dragon case data::OpCodes::CallImm: return "CallImm"; case data::OpCodes::CallReg: return "CallReg"; case data::OpCodes::Ret: return "Ret"; + case data::OpCodes::ArgReg: return "ArgReg"; case data::OpCodes::RetInt: return "RetInt"; case data::OpCodes::Int: return "Int"; default: return "UNKNOWN_INST"; @@ -410,6 +412,7 @@ namespace dragon case data::OpCodes::CallImm: return 3; case data::OpCodes::CallReg: return 2; case data::OpCodes::Ret: return 1; + case data::OpCodes::ArgReg: return 2; case data::OpCodes::RetInt: return 1; case data::OpCodes::Int: return 2; default: return 0; diff --git a/src/tools/SDLInclude.hpp b/src/tools/SDLInclude.hpp index 06b6acb..56c17b3 100644 --- a/src/tools/SDLInclude.hpp +++ b/src/tools/SDLInclude.hpp @@ -4,5 +4,4 @@ #undef __linux__ #endif -#include -#include \ No newline at end of file +#include \ No newline at end of file diff --git a/src/tools/Tools.cpp b/src/tools/Tools.cpp new file mode 100644 index 0000000..1eb6f8c --- /dev/null +++ b/src/tools/Tools.cpp @@ -0,0 +1,156 @@ +#include "Tools.hpp" + +#include +#include +#include "../hardware/VirtualHardDrive.hpp" + +namespace dragon +{ + bool Tools::createVirtualHardDrive(uint32_t sizeInBytes, const ostd::String& dataFilePath) + { + std::ofstream rf(dataFilePath.cpp_str(), 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; + } + + int32_t Tools::execute(int argc, char** argv) + { + ostd::String tool = ""; + int32_t rValue = get_tool(argc, argv, tool); + if (rValue != ErrorNoError) + return rValue; + + if (tool == "new-vdisk") + { + rValue = tool_new_virtual_disk(argc, argv); + if (rValue != ErrorNoError) + return rValue; + } + else if (tool == "load-binary") + { + rValue = tool_load_binary(argc, argv); + if (rValue != ErrorNoError) + return rValue; + } + else if (tool == "--help") + { + print_application_help(); + return ErrorNoError; + } + else + { + out.fg(ostd::ConsoleColors::Red).p("Error: Unknown tool.").reset().nl(); + out.fg(ostd::ConsoleColors::Red).p("Use the --help option for more info.").reset().nl(); + return ErrorTopLevelUnknownTool; + } + return ErrorNoError; + } + + int32_t Tools::tool_new_virtual_disk(int argc, char** argv) + { + if (argc < 4) + { + out.fg(ostd::ConsoleColors::Red).p("Error: too few arguments.").nl(); + out.fg(ostd::ConsoleColors::Red).p(" Usage: ./dtools new-vdisk ").reset().nl(); + return ErrorNewVDiskTooFewArgs; + } + ostd::String dest = argv[2]; + ostd::String str_size = argv[3]; + if (!ostd::Utils::isInt(str_size)) + { + out.fg(ostd::ConsoleColors::Red).p("Error: parameter must be integer.").reset().nl(); + return ErrorNewVDiskNonIntSize; + } + bool result = createVirtualHardDrive(ostd::Utils::strToInt(str_size), dest); + if (!result) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Unable to create virtual disk.").reset().nl(); + return ErrorNewVDiskUnable; + } + out.nl().fg(ostd::ConsoleColors::Green).p("Success. Virtual disk created:").nl(); + out.p(" Path: ").p(dest.cpp_str()).nl(); + out.p(" Size: ").p(str_size.cpp_str()).reset().nl(); + return ErrorNoError; + } + + int32_t Tools::tool_load_binary(int argc, char** argv) + { + if (argc < 5) + { + out.fg(ostd::ConsoleColors::Red).p("Error: too few arguments.").nl(); + out.fg(ostd::ConsoleColors::Red).p(" Usage: ./dtools load-binary ").reset().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.fg(ostd::ConsoleColors::Red).p("Error: parameter must be integer.").reset().nl(); + return ErrorLoadProgNonIntAddr; + } + dragon::hw::VirtualHardDrive vHDD(vdisk_file); + if (!vHDD.isInitialized()) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Unable to load virtual disk.").reset().nl(); + return ErrorLoadProgUnableToLoadVDisk; + } + ostd::ByteStream code; + if (!ostd::Utils::loadByteStreamFromFile(data_file, code)) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Unable to load data file.").reset().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.nl().fg(ostd::ConsoleColors::Green).p("Success. Data writte to Virtual Disk:").nl(); + out.p(" Data Path: ").p(data_file.cpp_str()).nl(); + out.p(" Disk Path: ").p(vdisk_file.cpp_str()).nl(); + out.p(" Data Address: ").p(ostd::Utils::getHexStr(addr, true, 4).cpp_str()).nl(); + out.p(" Size: ").p(code.size()).reset().nl(); + return ErrorNoError; + } + + void Tools::print_application_help(void) + { + out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available tools:").nl().nl(); + + out.fg(ostd::ConsoleColors::Blue).p("load-binary ").nl(); + out.fg(ostd::ConsoleColors::Green).p("The tool is used to load a binary file directly into a Virtual Disk.").nl(); + out.p(" Path to the destination Virtual Disk file.").nl(); + out.p(" Path to the source binary file.").nl(); + out.p(" Destination address on the Virtual Disk.").nl().nl(); + + out.fg(ostd::ConsoleColors::Blue).p("new-vdisk ").nl(); + out.fg(ostd::ConsoleColors::Green).p("The tool is used to create a new Virtual Disk File.").nl(); + out.p(" Path of the destination Virtual Disk file to be created.").nl(); + out.p(" Size of the new Virtual Disk file, in bytes .").nl().nl(); + + out.fg(ostd::ConsoleColors::Magenta).p("Usage: ./dtools [...arguments...]").nl().nl().reset(); + } + + int32_t Tools::get_tool(int argc, char** argv, ostd::String& outTool) + { + if (argc < 2) + { + out.fg(ostd::ConsoleColors::Red).p("Error: too few arguments.").nl(); + out.fg(ostd::ConsoleColors::Red).p("Use the --help option for more info.").reset().nl(); + return ErrorTopLevelTooFewArgs; + } + ostd::String tool = argv[1]; + tool = tool.trim().toLower(); + outTool = tool; + return ErrorNoError; + } +} \ No newline at end of file diff --git a/src/tools/Tools.hpp b/src/tools/Tools.hpp new file mode 100644 index 0000000..84c014d --- /dev/null +++ b/src/tools/Tools.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace dragon +{ + class Tools + { + public: + static inline ostd::ConsoleOutputHandler& output(void) { return out; } + static bool createVirtualHardDrive(uint32_t sizeInBytes, const ostd::String& dataFilePath); + static int32_t execute(int argc, char** argv); + + private: + static int32_t tool_new_virtual_disk(int argc, char** argv); + static int32_t tool_load_binary(int argc, char** argv); + static void print_application_help(void); + static int32_t get_tool(int argc, char** argv, ostd::String& outTool); + + private: + inline static ostd::ConsoleOutputHandler out; + + public: + inline static constexpr int32_t ErrorNoError = 0; + inline static constexpr int32_t ErrorTopLevelTooFewArgs = 1; + inline static constexpr int32_t ErrorTopLevelUnknownTool = 2; + inline static constexpr int32_t ErrorNewVDiskTooFewArgs = 3; + inline static constexpr int32_t ErrorNewVDiskNonIntSize = 4; + inline static constexpr int32_t ErrorNewVDiskUnable = 5; + inline static constexpr int32_t ErrorLoadProgTooFewArgs = 6; + inline static constexpr int32_t ErrorLoadProgNonIntAddr = 7; + inline static constexpr int32_t ErrorLoadProgUnableToLoadVDisk = 8; + inline static constexpr int32_t ErrorLoadProgUnableToLoadDataFile = 9; + }; +} \ No newline at end of file diff --git a/src/tools/tools_main.cpp b/src/tools/tools_main.cpp index f9a7d5c..18d5fef 100644 --- a/src/tools/tools_main.cpp +++ b/src/tools/tools_main.cpp @@ -1,122 +1,6 @@ -#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::ConsoleOutputHandler out; - -bool createVirtualHardDrive(uint32_t sizeInBytes, const ostd::String& dataFilePath) -{ - std::ofstream rf(dataFilePath.cpp_str(), 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; -} +#include "Tools.hpp" int main(int argc, char** argv) { - if (argc < 2) - { - out.fg("red").p("Error: too few arguments.").nl(); - out.fg("red").p(" Usage: ./dtools [...arguments...]").reset().nl(); - return ErrorTopLevelTooFewArgs; - } - ostd::String tool = argv[1]; - tool = tool.trim().toLower(); - - //Nex Virtual Disk - if (tool == "new-vdisk") - { - if (argc < 4) - { - out.fg("red").p("Error: too few arguments.").nl(); - out.fg("red").p(" Usage: ./dtools new-vdisk ").reset().nl(); - return ErrorNewVDiskTooFewArgs; - } - ostd::String dest = argv[2]; - ostd::String str_size = argv[3]; - if (!ostd::Utils::isInt(str_size)) - { - out.fg("red").p("Error: parameter must be integer.").reset().nl(); - return ErrorNewVDiskNonIntSize; - } - bool result = createVirtualHardDrive(ostd::Utils::strToInt(str_size), dest); - if (!result) - { - out.fg("red").p("Error: Unable to create virtual disk.").reset().nl(); - return ErrorNewVDiskUnable; - } - out.fg("green").p("Success. Virtual disk created:").nl(); - out.p(" Path: ").p(dest.cpp_str()).nl(); - out.p(" Size: ").p(str_size.cpp_str()).reset().nl(); - } - //Load Program - else if (tool == "load-program") - { - if (argc < 5) - { - out.fg("red").p("Error: too few arguments.").nl(); - out.fg("red").p(" Usage: ./dtools load-program ").reset().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.fg("red").p("Error: parameter must be integer.").reset().nl(); - return ErrorLoadProgNonIntAddr; - } - dragon::hw::VirtualHardDrive vHDD(vdisk_file); - if (!vHDD.isInitialized()) - { - out.fg("red").p("Error: Unable to load virtual disk.").reset().nl(); - return ErrorLoadProgUnableToLoadVDisk; - } - ostd::ByteStream code; - if (!ostd::Utils::loadByteStreamFromFile(data_file, code)) - { - out.fg("red").p("Error: Unable to load data file.").reset().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.fg("green").p("Success. Data writte to Virtual Disk:").nl(); - out.p(" Data Path: ").p(data_file.cpp_str()).nl(); - out.p(" Disk Path: ").p(vdisk_file.cpp_str()).nl(); - out.p(" Data Address: ").p(ostd::Utils::getHexStr(addr, true, 4).cpp_str()).nl(); - out.p(" Size: ").p(code.size()).reset().nl(); - } - //Unknown - else - { - out.fg("red").p("Error: Unknown tool.").reset().nl(); - return ErrorTopLevelUnknownTool; - } - return ErrorNoError; + return dragon::Tools::execute(argc, argv); } \ No newline at end of file