[0.2.1575] - UI Greatly improved + Debugger functionality added

This commit is contained in:
OmniaX 2024-01-11 01:36:29 +01:00
parent 758b1e41b8
commit 42f25f4e0b
30 changed files with 1857 additions and 757 deletions

View file

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

View file

@ -1 +1 @@
1544
1576

View file

@ -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
@ -48,8 +50,3 @@ 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

Binary file not shown.

View file

@ -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 <Status> register into ACC
mov ACC, [{MemoryAddresses.DISK_INTERFACE + 0xB}] ## Moving <Status> register into ACC
jne $_load_mbr_data_block_wait_loop, 0x00 ## If <Status> register not Free, loop around and wait
ret
_calc_interrupt_vector_address:
mov R1, *PP
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

View file

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

View file

@ -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 <Rectangle> = (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

View file

@ -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
##=================================================================================================================================
## 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.

View file

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

View file

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

View file

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

View file

@ -1 +1,2 @@
ddb config/testMachine.dvm --force-load test.bin 0x00 --verbose-load --track-step-diff --hide-vdisplay
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

View file

@ -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 <destination-directory>";
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 <destination-binary-file>";
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 <source> -o <destination> [...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<ostd::String> 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<ostd::String> 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<uint8_t> 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<ostd::String> 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();
}
if (m_symbolTable.size() > 0)
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_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();
ostd::ConsoleOutputHandler out;
ostd::Utils::printByteStream(m_code, 0x0000, 16, 8, out);
std::cout << "\n\n\n\n";
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();
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();
}
}
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <ostd/Utils.hpp>
#include <ostd/IOHandlers.hpp>
#include <unordered_map>
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<uint16_t> references;
uint16_t address { 0 };
};
public: enum class eOperandType {
public: struct tStructMember
{
ostd::String name;
std::vector<uint8_t> 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<tStructMember> 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<tStructDefinition> m_structDefs;
inline static std::vector<tDisassemblyLine> m_disassembly;
inline static ostd::ConsoleOutputHandler out;
};
}
}

View file

@ -1,60 +1,17 @@
#include "Assembler.hpp"
#include <ostd/Utils.hpp>
#include <ostd/IOHandlers.hpp>
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 <source> -o <destination> [...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;
}

View file

@ -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 <Enter> to continue execution...").nl().reset();
std::cin.get();
out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres <Escape> 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, &data_size);
bool no_symbol = false;
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();
}
out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres <Enter> to continue execution...").nl().reset();
std::cin.get();
}
void Debugger::Display::printStack(void)
{
symbol = "<no-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();
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 <Escape> to go back...").nl().reset();
Utils::isEscapeKeyPressed(true);
}
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("(?<!\\w)(r[1-9]|r10|fl|pp|rv|fp|sp|ip|acc)(?!\\w)", "BrightGreen", true); //Registers
rgxrstr.fg("(?<!\\w)(args|argc|StackFrame|New FP)(?!\\w)", "Magenta", true); //Other
out.reset().nl().fg(ostd::ConsoleColors::BrightBlue).p("=======================").nl().reset();
out.pStyled(rgxrstr);
out.nl();
Utils::printFullLine('=', ostd::ConsoleColors::BrightBlue);
}
out.reset().nl().fg(ostd::ConsoleColors::BrightBlue).p("=======================").nl().reset();
out.nl();
out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres <Enter> 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 <Escape> 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++;
}
out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres <Enter> to continue execution...").nl().reset();
std::cin.get();
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("(?<![a-zA-Z\\_\\$\\.])int|rti(?! [a-zA-Z\\_\\$\\.])", "Blue");
rgx.fg("(?<![a-zA-Z\\_\\$\\.])ret(?! [a-zA-Z\\_\\$\\.])", "Red");
rgx.fg("0x[0-9A-Fa-f]+|0b[0-1]+|(?<!\\w)[0-9]+(?!\\w)", "BrightYellow"); //Number Constants
rgx.fg(ostd::String("[\\").add(ch_ang_u).add("\\|\\").add(ch_ang_d).add("]"), "darkgray");
rgx.fg("\\$\\w+", "Green"); //Labels
out.fg(ostd::ConsoleColors::BrightGray).p("|").reset();
out.fg(ostd::ConsoleColors::Magenta).p(ostd::Utils::getHexStr(call.inst_addr, true, 2));
out.fg(ostd::ConsoleColors::BrightGray).p("| > ").reset();
out.pStyled(rgx).nl().reset();
if (dec_lvl)
level--;
}
Utils::printFullLine('=', ostd::ConsoleColors::Yellow);
out.nl().nl().fg(ostd::ConsoleColors::Yellow).p("Pres <Escape> 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. (<Escape> 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] <addr/symbol>";
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 <Escape> 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 <Escape> 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 <Escape> 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]+|(?<!\\w)[0-9]+(?!\\w)", "cyan"); //Number Constants
output().pStyled(rgx);
output().fg(ostd::ConsoleColors::White).p(" = ");
output().fg(ostd::ConsoleColors::Gray).p("[");
for (uint16_t a = addr; a <= end_addr; a++)
{
uint8_t value = DragonRuntime::memMap.read8(a);
output().fg(ostd::ConsoleColors::BrightRed).p(ostd::Utils::getHexStr(value, true, 1));
if (a < end_addr)
output().p(" ");
}
output().fg(ostd::ConsoleColors::Gray).p("]");
output().reset().nl();
}
else if (data().command.startsWith("$"))
{
uint16_t size = 0;
uint16_t addr = Utils::findSymbol(debugger.data, data().command, &size);
if (addr == 0)
addr = Utils::findSymbol(debugger.labels, data().command, &size);
if (addr == 0)
output().fg(ostd::ConsoleColors::Red).p("Unknown symbol for <print> 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]+|(?<!\\w)[0-9]+(?!\\w)", "cyan"); //Number Constants
output().pStyled(rgx);
output().fg(ostd::ConsoleColors::White).p(" = ");
output().fg(ostd::ConsoleColors::Gray).p("[");
for (uint16_t a = addr; a < addr + size; a++)
{
uint8_t value = DragonRuntime::memMap.read8(a);
output().fg(ostd::ConsoleColors::BrightRed).p(ostd::Utils::getHexStr(value, true, 1));
if (a < addr + size - 1)
output().p(" ");
}
output().fg(ostd::ConsoleColors::Gray).p("]");
output().reset().nl();
}
}
else
{
output().fg(ostd::ConsoleColors::Red).p("Invalid value for <print> 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. (<Escape> 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 <address[bytes=1]/symbol>";
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 <binary-file> <ram-offset>";
tmpCommand.addRightPadding(commandLength);
out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Injects the specified binary into RAM at the specified offset.").reset().nl();
tmpCommand = "--help";
tmpCommand.addRightPadding(commandLength);
out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Displays this help message.").reset().nl();
out.nl().fg(ostd::ConsoleColors::Magenta).p("Usage: ./ddb <machine-config-file> [...options...]").reset().nl();
out.nl();
}
}

View file

@ -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<ostd::String> commandHistory;
static tCloseEventListener closeEventListener;
public:
inline static const ostd::String InputCommandQuit = "//quit//";
};
}

View file

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

View file

@ -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;
//Running top-level prompt
rValue = dragon::Debugger::topLevelPrompt();
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;
}
}
}
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 <Enter> to exit...").nl().reset();
std::cin.get();
break;
}
Debugger::Display::printPrompt();
if (Debugger::data().args.step_exec || dragon::DragonRuntime::cpu.isInDebugBreakPoint())
{
Debugger::data().command = Debugger::getCommandInput();
if (dragon::DragonRuntime::cpu.isInDebugBreakPoint())
Debugger::data().args.step_exec = true;
}
Debugger::data().command.trim().toLower();
while (Debugger::data().command != "")
{
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();
}

View file

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

View file

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

View file

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

View file

@ -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<uint16_t> 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 <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 <binary-file> <ram-offset>";
tmpCommand.addRightPadding(commandLength);
out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Injects the specified binary into RAM at the specified offset.").reset().nl();
tmpCommand = "--help";
tmpCommand.addRightPadding(commandLength);
out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Displays this help message.").reset().nl();
out.nl().fg(ostd::ConsoleColors::Magenta).p("Usage: ./dvm <machine-config-file> [...options...]").reset().nl();
out.nl();
}
}

View file

@ -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<data::ErrorHandler::tError> 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<uint16_t> 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;
};
}

View file

@ -1,74 +1,24 @@
#include <ostd/Utils.hpp>
#include "DragonRuntime.hpp"
ostd::ConsoleOutputHandler out;
struct tCommandLineArgs
{
ostd::String machine_config_path = "";
bool basic_debug = false;
bool step_exec = false;
bool verbose_load = false;
int32_t cycle_limit = 0;
bool force_load = false;
ostd::String force_load_file = "";
uint16_t force_load_mem_offset = 0x00;
};
int main(int argc, char** argv)
{
tCommandLineArgs args;
if (argc < 2)
{
out.fg(ostd::ConsoleColors::Red).p("Error, too few arguments.").reset().nl();
return 128;
}
else
{
args.machine_config_path = argv[1];
for (int32_t i = 2; i < argc; i++)
{
ostd::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;
}

View file

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

View file

@ -5,4 +5,3 @@
#endif
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

156
src/tools/Tools.cpp Normal file
View file

@ -0,0 +1,156 @@
#include "Tools.hpp"
#include <ostd/Utils.hpp>
#include <fstream>
#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 <destination_file> <size_in_bytes>").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: <size_in_bytes> 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 <virtual_disk_file> <data_file> <destination_address>").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: <destination_address> 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 <virtual_disk_file> <data_file> <destination_address>").nl();
out.fg(ostd::ConsoleColors::Green).p("The <load-binary> tool is used to load a binary file directly into a Virtual Disk.").nl();
out.p(" <virtual_disk_file> Path to the destination Virtual Disk file.").nl();
out.p(" <data_file> Path to the source binary file.").nl();
out.p(" <destination_address> Destination address on the Virtual Disk.").nl().nl();
out.fg(ostd::ConsoleColors::Blue).p("new-vdisk <destination_file> <size_in_bytes>").nl();
out.fg(ostd::ConsoleColors::Green).p("The <new-vdisk> tool is used to create a new Virtual Disk File.").nl();
out.p(" <destination_file> Path of the destination Virtual Disk file to be created.").nl();
out.p(" <size_in_bytes> Size of the new Virtual Disk file, in bytes .").nl().nl();
out.fg(ostd::ConsoleColors::Magenta).p("Usage: ./dtools <tool_name> [...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;
}
}

35
src/tools/Tools.hpp Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <ostd/IOHandlers.hpp>
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;
};
}

View file

@ -1,122 +1,6 @@
#include <ostd/Utils.hpp>
#include <fstream>
#include <ostd/IOHandlers.hpp>
#include "../hardware/VirtualHardDrive.hpp"
constexpr int ErrorTopLevelTooFewArgs = 1;
constexpr int ErrorTopLevelUnknownTool = 4;
constexpr int ErrorNewVDiskTooFewArgs = 2;
constexpr int ErrorNewVDiskNonIntSize = 3;
constexpr int ErrorNewVDiskUnable = 5;
constexpr int ErrorLoadProgTooFewArgs = 6;
constexpr int ErrorLoadProgNonIntAddr = 7;
constexpr int ErrorLoadProgUnableToLoadVDisk = 8;
constexpr int ErrorLoadProgUnableToLoadDataFile = 8;
constexpr int ErrorNoError = 0;
ostd::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 <tool_name> [...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 <destination_file> <size_in_bytes>").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: <size_in_bytes> 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 <virtual_disk_file> <data_file> <destination_address>").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: <destination_address> 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);
}