diff --git a/CMakeLists.txt b/CMakeLists.txt index 79e4a8c..f2969fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,12 +31,14 @@ list(APPEND RUNTIME_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/gui/Window.cpp ${CMAKE_CURRENT_LIST_DIR}/src/gui/RawTextRenderer.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/gui/Renderer.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualCPU.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualRAM.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/MemoryMapper.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualIODevices.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualDisplay.cpp ${CMAKE_CURRENT_LIST_DIR}/src/tools/Utils.cpp ) @@ -47,12 +49,14 @@ list(APPEND DEBUGGER_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/gui/Window.cpp ${CMAKE_CURRENT_LIST_DIR}/src/gui/RawTextRenderer.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/gui/Renderer.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualCPU.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualRAM.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/MemoryMapper.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualIODevices.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualDisplay.cpp ${CMAKE_CURRENT_LIST_DIR}/src/runtime/DragonRuntime.cpp ${CMAKE_CURRENT_LIST_DIR}/src/runtime/ConfigLoader.cpp diff --git a/build.nr b/build.nr index 340b351..7bb9a89 100644 --- a/build.nr +++ b/build.nr @@ -1 +1 @@ -1576 +1578 diff --git a/count_lines.sh b/count_lines.sh new file mode 100644 index 0000000..2839182 --- /dev/null +++ b/count_lines.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +green='\033[0;32m' +red='\033[0;31m' +clear='\033[0m' + +printf "Counting lines of code...\n" + +(find ./src/ -type f \( -iname \*.hpp -o -iname \*.cpp \) | xargs -n1 cpp -fpreprocessed -P | awk '!/^[{[:space:]}]*$/' | wc -l) > _lines.tmp 2> /dev/null +typeset -i netto_lines=$(cat _lines.tmp) +rm _lines.tmp +total_lines=$(find ./src/ -type f \( -iname \*.hpp -o -iname \*.cpp \) | xargs wc -l | sort -n | tail -1 | tr -d -c 0-9) +printf "Total: ${red}${total_lines}\n${clear}" +printf "Netto: ${green}${netto_lines}${clear} (No braces, comments or empty lines.)\n" \ No newline at end of file diff --git a/create_win_release.sh b/create_win_release.sh index fcd10a4..291b80c 100644 --- a/create_win_release.sh +++ b/create_win_release.sh @@ -7,7 +7,7 @@ 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 --verbose +./dasm dss/bios/bios.dss -o dragon/bios.bin --save-disassembly disassembly/bios.dds --verbose printf "\n${green}Creating Virtual Disk...\n${clear}" rm dragon/disk1.dr @@ -19,8 +19,10 @@ printf "\n${green}Compiling MBR Block...\n${clear}" printf "\n${green}Loading MBR Block...\n${clear}" ./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 --verbose +printf "${green}Compiling Test Program 1...\n" +./dasm dss/test1.dss -o test1.bin --save-disassembly disassembly/test1.dds --verbose +printf "${green}Compiling Test Program 2...\n" +./dasm dss/test2.dss -o test2.bin --save-disassembly disassembly/test2.dds --verbose printf "${green}Creating Windows Release...\n" cd .. @@ -31,17 +33,23 @@ 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 -r ./bin/config ./win-release cp -r ./bin/disassembly/bios.dds ./win-release/disassembly cp -r ./bin/disassembly/mbr.dds ./win-release/disassembly +cp -r ./bin/disassembly/test1.dds ./win-release/disassembly +cp -r ./bin/disassembly/test2.dds ./win-release/disassembly cp -r ./bin/dragon ./win-release +rm ./bin/disassembly/test1.dds +rm ./bin/disassembly/test2.dds + mkdir ./win-release/dss -cp ./bin/dss/bios.dss ./win-release/dss +mkdir ./win-release/dss/bios +cp ./bin/dss/bios/bios.dss ./win-release/dss/bios cp ./bin/dss/mbr.dss ./win-release/dss -cp ./bin/dss/test.dss ./win-release/dss +cp ./bin/dss/test1.dss ./win-release/dss +cp ./bin/dss/test2.dss ./win-release/dss cp C:/msys64/ucrt64/bin/libgcc_s_seh-1.dll ./win-release cp C:/msys64/ucrt64/bin/libstdc++-6.dll ./win-release @@ -49,4 +57,5 @@ cp C:/msys64/ucrt64/bin/libwinpthread-1.dll ./win-release cp C:/msys64/ucrt64/bin/SDL2.dll ./win-release cp C:/msys64/ucrt64/bin/libostd.dll ./win-release -cp ./run-test.bat ./win-release \ No newline at end of file +cp ./extra/scripts/run-test-1.bat ./win-release +cp ./extra/scripts/run-test-2.bat ./win-release \ No newline at end of file diff --git a/extra/config/testMachine.dvm b/extra/config/testMachine.dvm index c6c902d..90cb646 100644 --- a/extra/config/testMachine.dvm +++ b/extra/config/testMachine.dvm @@ -1,3 +1,5 @@ Disks = dragon/disk1.dr Bios = dragon/bios.bin -CMOS = dragon/cmos.dr \ No newline at end of file +CMOS = dragon/cmos.dr +SingleColor_foreground = #009900FF +SingleColor_background = #111111FF \ No newline at end of file diff --git a/extra/dragon info/DragonVM Information Sheet b/extra/dragon info/DragonVM Information Sheet new file mode 100644 index 0000000..eaa340f --- /dev/null +++ b/extra/dragon info/DragonVM Information Sheet @@ -0,0 +1,122 @@ +#========================================================================================================================================== +# Memory Map +#========================================================================================================================================== + + +0x0000 BIOS (4096 Bytes) +0x0FFF +------- +0x1000 CMOS (128 Bytes) + BootDisk: 0x0010 +0x107F +------- +0x1080 INTERRUPT VECTOR (512 Bytes) + Status: 1 Byte + 0xFF: Enabled + 0x00: Disabled + Handler Address: 2 Byte + ...Repeat... +0x127F +------- +0x1280 KEYBOARD MAPPING (224 Bytes) +0x135F +------- +0x1360 MOUSE MAPPING (32 Bytes) +0x137F +------- +0x1380 BOOTLOADER (512 Bytes) +0x157F +------- +0x1580 DISK INTERFACE (128 Bytes) + Read/Write: + Signal: 1 Byte + 0x00: Start Operation + 0x01: Cancel Current Operation + 0xFF: Ignore + Mode: 1 Byte + 0x00: Read + 0x01: Write + Disk: 1 Byte + Sector: 2 Bytes + Address: 2 Bytes + DataSize: 2 Bytes + DataAddress: 2 Byte + ReadOnly: + Status: 1 Byte + 0x00: Free + 0x01: Writing + 0x02: Reading + CurrentDisk: 1 Byte + CurrentSector: 2 Bytes + CurrentAddress: 2 Bytes + RestDataSize: 2 Bytes + SourceData: 2 Bytes + Free: 107 Byte +0x15FF +------- +0x1600 VIDEO CARD INTERFACE (256 Bytes) + 0x00: Video Mode (1 Byte) + 0x00: Text Single Color + # 0x01: Text 16-color + # 0x02: Text 256-color + # 0x03: Text True Color Mode + # 0x04: GFX Single Color + # 0x05: GFX 16-color + # 0x06: GFX 256-color + # 0x07: GFX True Color Mode + # 0x01: Clear Color (1 Byte) + # 0x02: Palette (1 Byte) + 0x03: Signal (1 Byte) + 0x00: Continue + + 0x02: Text Single Color - Direct Print Character + 0x03: Text Single Color - Store character in buffer + 0x04: Text SIngle Color - Direct Print buffer & Flush Buffer + 0x05: Text Single Color - Flush Buffer + 0x06: Text Single Color - Print Buffer Without Flushing + + 0xE0: Refresh Screen + 0xE1: Clear Screen + 0x04: Text Single Color Character +0x16FF +------- +0x1700 GENERIC SERIAL INTERFACE (64 Bytes) +0x173F +------- +0x1740 RAM (59583 Bytes) +0xFFFF + + + +#========================================================================================================================================== +# INTERRUPTS +#========================================================================================================================================== + + + + +BIOS Specific - Software Interrupts: +==================================== +0x20: BIOS Interrupt (Uses R10 register for specific functionality) + 0x00: Set Interrupt Handler + 0x01: Clear Interrupt Handler + 0x10: Disk Interface +0x30: BIOS Video Interrupt (Uses R10 register for specific functionality) + 0x01: Print Char in Text Single Mode (char stored in R9 register) + 0x02: New Line in Text Single Mode + 0x03: Print Null-Terminated String in Text Single Mode (string address stored in R9 register) + 0x04: Print Integer in Text Single Mode (integer stored in R9 register) + 0x05: Store Integer in buffer in Text Single Mode (integer stored in R9 register) + 0x06: Store character in buffer in Text Single Mode (integer stored in R9 register) + 0x07: Print buffer no flush in Text Single Mode + 0x08: Print buffer and flush in Text Single Mode + 0x09: Flush buffer in Text Single Mode + 0x0A: Store Null-Terminated String in buffer in Text Single Mode (string address stored in R9 register) + + 0xE0: Refresh Screen + 0xE1: Clear Screen + + +Machine Specific - Hardware Interrupts: +======================================= +0x80: Disk Interface Finished \ No newline at end of file diff --git a/extra/dragon info/DragonVM Memory Structure b/extra/dragon info/DragonVM Memory Structure deleted file mode 100644 index 2499593..0000000 --- a/extra/dragon info/DragonVM Memory Structure +++ /dev/null @@ -1,69 +0,0 @@ -0x0000 BIOS (4096 Bytes) -0x0FFF -------- -0x1000 CMOS (128 Bytes) - BootDisk: 0x0010 -0x107F -------- -0x1080 INTERRUPT VECTOR (512 Bytes) - Status: 1 Byte - 0xFF: Enabled - 0x00: Disabled - Handler Address: 2 Byte - ...Repeat... -0x127F -------- -0x1280 KEYBOARD MAPPING (224 Bytes) -0x135F -------- -0x1360 MOUSE MAPPING (32 Bytes) -0x137F -------- -0x1380 BOOTLOADER (512 Bytes) -0x157F -------- -0x1580 DISK INTERFACE (128 Bytes) - Read/Write: - Signal: 1 Byte - 0x00: Start Operation - 0x01: Cancel Current Operation - 0xFF: Ignore - Mode: 1 Byte - 0x00: Read - 0x01: Write - Disk: 1 Byte - Sector: 2 Bytes - Address: 2 Bytes - DataSize: 2 Bytes - DataAddress: 2 Byte - ReadOnly: - Status: 1 Byte - 0x00: Free - 0x01: Writing - 0x02: Reading - CurrentDisk: 1 Byte - CurrentSector: 2 Bytes - CurrentAddress: 2 Bytes - RestDataSize: 2 Bytes - SourceData: 2 Bytes - Free: 107 Byte -0x15FF -------- -0x1600 VIDEO CARD INTERFACE (256 Bytes) - 0x00: Video Mode (1 Byte) - 0x00: Single Color - 0x01: 16-color - 0x02: 256-color - 0x03: True Color Mode - 0x01: Clear Color (1 Byte) - 0x02: Palette (1 Byte) - 0x03: Signal (1 Byte) - 0x00: Continue - 0x01: Clear Screen -0x16FF -------- -0x1700 GENERIC SERIAL INTERFACE (64 Bytes) -0x173F -------- -0x1740 RAM (59583 Bytes) -0xFFFF diff --git a/extra/dragon info/Interrupts b/extra/dragon info/Interrupts deleted file mode 100644 index ecf25f3..0000000 --- a/extra/dragon info/Interrupts +++ /dev/null @@ -1,13 +0,0 @@ -Software Interrupts: -==================== -0x20: BIOS Interrupt (Uses R10 register for specific functionality) - 0x00: Set Interrupt Handler - 0x01: Clear Interrupt Handler - 0x10: Disk Interface -0x30: BIOS Video Interrupt (Uses R10 register for specific functionality) - 0x00: Clear Screen - - -Hardware Interrupts: -==================== -0x80: Disk Interface Finished \ No newline at end of file diff --git a/extra/dragon info/Memory Map.png b/extra/dragon info/Memory Map.png deleted file mode 100644 index 1ac7c13..0000000 Binary files a/extra/dragon info/Memory Map.png and /dev/null differ diff --git a/extra/dragon/bios.bin b/extra/dragon/bios.bin index 82a7ed1..7d25a2b 100644 Binary files a/extra/dragon/bios.bin and b/extra/dragon/bios.bin differ diff --git a/extra/dss/bios.dss b/extra/dss/bios/bios.dss similarity index 51% rename from extra/dss/bios.dss rename to extra/dss/bios/bios.dss index d666458..0bf18c8 100644 --- a/extra/dss/bios.dss +++ b/extra/dss/bios/bios.dss @@ -1,25 +1,55 @@ ## ============================= Memory Mapped Devices and Registers ============================= @group MemoryAddresses - MBR 0x1380 - INT_VEC 0x1080 - DISK_INTERFACE 0x1580 - RAM 0x1740 - CMOS 0x1000 + MBR 0x1380 + INT_VEC 0x1080 + DISK_INTERFACE 0x1580 + RAM 0x1740 + CMOS 0x1000 + VGA 0x1600 @end @group CMOS_Settings - BOOT_DISK {MemoryAddresses.CMOS + 0x0010} + BOOT_DISK { MemoryAddresses.CMOS + 0x0010 } @end + +@group VGA_Registers + VIDEO_MODE { MemoryAddresses.VGA + 0x0000 } + SIGNAL { MemoryAddresses.VGA + 0x0003 } + TEXT_SINGLE_CHAR { MemoryAddresses.VGA + 0x0004 } + + BUFF_START { MemoryAddresses.VGA + 0x00E0 } +@end + +@group VGA_VideoModes + TEXT_SINGLE_COLOR 0x00 +@end + +@group Sig_VGA_Text_Single_Color + CONTINUE 0x00 + PRINT_CHAR 0x02 + STORE_CHAR 0x03 + PRINT_FLUSH_BUFF 0x04 + FLUSH_BUFF 0x05 + PRINT_BUFF 0x06 + + REFRESH_SCREEN 0xE0 + CLEAR_SCREEN 0xE1 +@end + +@define S_REG_1 0x07 +@define S_REG_2 0x08 +@define S_REG_3 0x09 ## =============================================================================================== .load 0x0000 ## BIOS is mapped to address 0x0000 in memory .data - $bios_version_number 0x00, 0x01 ## BIOS Version stored in memory + $_bios_name "DragonBIOS" + $_bios_version_number_maj 0x00, 0x00 + $_bios_version_number_min 0x00, 0x03 ## ============================= BIOS Program ============================= .code - ## mov FL, 0 - ## jmp 0x1740 + movb [VGA_Registers.VIDEO_MODE], VGA_VideoModes.TEXT_SINGLE_COLOR ## Enable Text_Single_Color graphics mode mov FL, 0b0000000000000001 ## Zero the FL Register and enable interrupts movb [{MemoryAddresses.INT_VEC + (0x20 * 3)}], 0xFF ## Setting up int 0x20 handler mov [{MemoryAddresses.INT_VEC + (0x20 * 3) + 1}], $_int_20_handler ## -- @@ -29,12 +59,30 @@ mov R8, 0x30 ## -Passing the interrupt's code to setup int 0x20 ## -Calling int 0x20 with 0x00 as parameter, to set new handler up - ## MBR Loading + mov R10, 0x03 + mov R9, $_bios_name + int 0x30 + mov R10, 0x01 + mov R9, 32 + int 0x30 + mov R10, 0x04 + mov R9, [$_bios_version_number_maj] + int 0x30 + mov R10, 0x01 + mov R9, 46 + int 0x30 + mov R10, 0x04 + mov R9, [$_bios_version_number_min] + int 0x30 + mov R10, 0x02 + int 0x30 + + ## MBR Loading //TODO: Figure out why and is not working and FL, 0b1111111111111110 ## Disable interrupts push 0 call $_load_mbr_data_block or FL, 0b0000000000000001 ## Enable interrupts - ## ---- + ## ---- //TODO: Figure out why or is not working jmp MemoryAddresses.MBR ## Jump to start of MBR in memory hlt ## Just in case somehow execution reaches this point @@ -84,10 +132,78 @@ _int_20_end: ## ========================== BIOS Video Interrupt handler ========================= _int_30_handler: mov ACC, R10 - jeq $_int_30_clear_screen, 0x0000 + jeq $_int_30_direct_print_char_text_single, 0x0001 + jeq $_int_30_direct_new_line_text_single, 0x0002 + jeq $_int_30_direct_print_string_text_single, 0x0003 + jeq $_int_30_direct_print_integer_text_single, 0x0004 + jeq $_int_30_store_integer_text_single, 0x0005 + jeq $_int_30_store_char_text_single, 0x0006 + jeq $_int_30_print_buffer_no_flush_text_single, 0x0007 + jeq $_int_30_print_buffer_and_flush_text_single, 0x0008 + jeq $_int_30_flush_buffer_text_single, 0x0009 + jeq $_int_30_store_string_text_single, 0x000A + jeq $_int_30_clear_screen, 0x00E0 + jeq $_int_30_refresh_screen, 0x00E1 jmp $_int_30_end + + +_int_30_direct_print_char_text_single: + movb [VGA_Registers.TEXT_SINGLE_CHAR], R9 + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_CHAR + jmp $_int_30_end +_int_30_direct_new_line_text_single: + movb [VGA_Registers.TEXT_SINGLE_CHAR], 10 ## New line char code + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.STORE_CHAR + jmp $_int_30_end +_int_30_direct_print_string_text_single: + movb R1, *R9 + movb [VGA_Registers.TEXT_SINGLE_CHAR], R1 + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_CHAR + inc R9 + movb ACC, *R9 + jne $_int_30_direct_print_string_text_single, 0 + jmp $_int_30_end +_int_30_direct_print_integer_text_single: + mov reg(S_REG_1), Sig_VGA_Text_Single_Color.PRINT_CHAR + push R9 + push 1 + call $_print_integer + jmp $_int_30_end +_int_30_store_integer_text_single: + mov reg(S_REG_1), Sig_VGA_Text_Single_Color.STORE_CHAR + push R9 + push 1 + call $_print_integer + jmp $_int_30_end +_int_30_store_char_text_single: + movb [VGA_Registers.TEXT_SINGLE_CHAR], R9 + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.STORE_CHAR + jmp $_int_30_end +_int_30_print_buffer_no_flush_text_single: + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_BUFF +_int_30_print_buffer_and_flush_text_single: + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.PRINT_FLUSH_BUFF + jmp $_int_30_end +_int_30_flush_buffer_text_single: + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.FLUSH_BUFF + jmp $_int_30_end +_int_30_store_string_text_single: + movb R1, *R9 + movb [VGA_Registers.TEXT_SINGLE_CHAR], R1 + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.STORE_CHAR + inc R9 + movb ACC, *R9 + jne $_int_30_store_string_text_single, 0 + debug_break + jmp $_int_30_end + + _int_30_clear_screen: - nop + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.CLEAR_SCREEN + jmp $_int_30_end +_int_30_refresh_screen: + movb [VGA_Registers.SIGNAL], Sig_VGA_Text_Single_Color.REFRESH_SCREEN + jmp $_int_30_end _int_30_end: rti ## ================================================================================== @@ -132,4 +248,44 @@ _set_interrupt_vector_entry_end: mov *R1, ACC ret + +_print_integer: + mov R1, 0 + arg ACC + jne $_print_integer_loop, 0 + push 48 ## '0' ASCII is 48 + inc R1 + jmp $_print_integer_loop_end +_print_integer_loop: + jeq $_print_integer_loop_end, 0 + div ACC, 10 + mov R2, ACC + add RV, 48 ## '0' ASCII is 48 + push ACC + inc R1 + mov ACC, R2 + jmp $_print_integer_loop +_print_integer_loop_end: + pop R9 + dec R1 + movb [VGA_Registers.TEXT_SINGLE_CHAR], R9 + movb [VGA_Registers.SIGNAL], reg(S_REG_1) + mov ACC, R1 + jne $_print_integer_loop_end, 0 + ret + + +_strlen: + arg R1 + mov R2, 0 +_strlen_loop: + movb ACC, *R1 + inc R1 + jeq $_strlen_loop_end, 0 + inc R2 + jmp $_strlen_loop +_strlen_loop_end: + mov RV, R2 + ret + .fixed 4096, 0x00 ## BIOS Needs to be 4096 Bytes exactly diff --git a/extra/dss/newTest.dss b/extra/dss/newTest.dss index 8e8b2ee..ad0b86b 100644 --- a/extra/dss/newTest.dss +++ b/extra/dss/newTest.dss @@ -1,80 +1,48 @@ -.load 0x1740 +##================================================================================================================================= +## This is the test2 program for the Dragon Virtual Machine. This example simply demonstrates the Virtual Display in Text-Single-Color +## mode, by printing an increasing counter followed by whatever is in the $string variable. The program also clears the screen +## every 10 lines. +##================================================================================================================================= -@struct Rectangle - width:2 > 0x11, 0x22 - height:2 > 0x99 - x:2 - y:2 > 0xAA -@end +.load 0x1740 .data $string "Hello World!!" - $rect = (0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88) - $test_byte 0xAF - $super_long_variable_name 0x01, 0x02, 0x03, 0x04 .code -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 $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 + mov R1, 99 ## Zero the counter +infinite_loop: + inc R1 ## Increment the counter + div R1, 10 ## Divide the counter by 10 + mov ACC, RV + jne $no_clear_screen, 0 ## If reminder not zero, keep going + mov R10, 0xE0 ## Else clear the screen (0x0E is for 'Clear Screen' functionality in int 0x30) + int 0x30 ## BIOS Video Interrupt +no_clear_screen: + + ## Print counter + mov R9, R1 ## Pass the counter as parameter to int 0x30 + mov R10, 0x05 ## 0x05 is for 'Store Integer in buffer' functionality in int 0x30 + int 0x30 ## BIOS Video Interrupt + + ## Print a space + mov R9, 32 ## Pass 32 (space character in ASCII) as parameter to int 0x30 + mov R10, 0x06 ## 0x06 is for 'Store Character in buffer' functionality in int 0x30 + int 0x30 ## BIOS Video Interrupt + + ## Print the string + mov R9, $string ## Pass the address of the string as parameter to int 0x30 + mov R10, 0x0A ## 0x0A is for 'Store String in buffer' functionality in int 0x30 + int 0x30 ## BIOS Video Interrupt + + ## Print a new-line character + mov R10, 0x02 ## 0x02 is for 'Print New-Line' functionality in int 0x30 + ## Printing a new line when in Text-Single-Color Mode also + ## prints and flushes the buffer + int 0x30 ## BIOS Video Interrupt + + jmp $infinite_loop ## jump to the beginning of infinite loop hlt -testinttest: - push 0 - call $inttest - ret - -inttest: - push 0 - call $testint - ret - -testint: - push 0 - call $test_int - ret - -test_int: - push 0 - call $int_test - ret - -int_test: - nop - ret - - -strlen: - arg R1 - mov R2, 0 -loop: - movb ACC, *R1 - inc R1 - jeq $loop_end, 0 -not_zero: - inc R2 - jmp $loop -loop_end: - mov RV, R2 - ret - .fixed 512, 0x00 \ No newline at end of file diff --git a/extra/dss/test.dss b/extra/dss/test1.dss similarity index 98% rename from extra/dss/test.dss rename to extra/dss/test1.dss index 204e9fb..7c3d69c 100644 --- a/extra/dss/test.dss +++ b/extra/dss/test1.dss @@ -1,5 +1,5 @@ ##================================================================================================================================= -## This is the test program for the Dragon Virtual Machine. The example has two subroutines: One is the $strlen subroutine, +## This is the test1 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 diff --git a/extra/dss/test2.dss b/extra/dss/test2.dss new file mode 100644 index 0000000..ad0b86b --- /dev/null +++ b/extra/dss/test2.dss @@ -0,0 +1,48 @@ +##================================================================================================================================= +## This is the test2 program for the Dragon Virtual Machine. This example simply demonstrates the Virtual Display in Text-Single-Color +## mode, by printing an increasing counter followed by whatever is in the $string variable. The program also clears the screen +## every 10 lines. +##================================================================================================================================= + +.load 0x1740 + +.data + $string "Hello World!!" + +.code + mov R1, 99 ## Zero the counter +infinite_loop: + inc R1 ## Increment the counter + div R1, 10 ## Divide the counter by 10 + mov ACC, RV + jne $no_clear_screen, 0 ## If reminder not zero, keep going + mov R10, 0xE0 ## Else clear the screen (0x0E is for 'Clear Screen' functionality in int 0x30) + int 0x30 ## BIOS Video Interrupt +no_clear_screen: + + ## Print counter + mov R9, R1 ## Pass the counter as parameter to int 0x30 + mov R10, 0x05 ## 0x05 is for 'Store Integer in buffer' functionality in int 0x30 + int 0x30 ## BIOS Video Interrupt + + ## Print a space + mov R9, 32 ## Pass 32 (space character in ASCII) as parameter to int 0x30 + mov R10, 0x06 ## 0x06 is for 'Store Character in buffer' functionality in int 0x30 + int 0x30 ## BIOS Video Interrupt + + ## Print the string + mov R9, $string ## Pass the address of the string as parameter to int 0x30 + mov R10, 0x0A ## 0x0A is for 'Store String in buffer' functionality in int 0x30 + int 0x30 ## BIOS Video Interrupt + + ## Print a new-line character + mov R10, 0x02 ## 0x02 is for 'Print New-Line' functionality in int 0x30 + ## Printing a new line when in Text-Single-Color Mode also + ## prints and flushes the buffer + int 0x30 ## BIOS Video Interrupt + + jmp $infinite_loop ## jump to the beginning of infinite loop + hlt + + +.fixed 512, 0x00 \ No newline at end of file diff --git a/extra/font_big.bmp b/extra/font_big.bmp new file mode 100644 index 0000000..133414b Binary files /dev/null and b/extra/font_big.bmp differ diff --git a/extra/make b/extra/make new file mode 100644 index 0000000..8f22b0a --- /dev/null +++ b/extra/make @@ -0,0 +1,13 @@ +#!/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 + make +elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then + mingw32-make.exe +fi +printf "${green}\n=================================================================================================================\n\n" \ No newline at end of file diff --git a/extra/make_and_debug b/extra/make_and_debug new file mode 100644 index 0000000..c5cb565 --- /dev/null +++ b/extra/make_and_debug @@ -0,0 +1,25 @@ +#!/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 + make +elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then + mingw32-make.exe +fi +printf "${green}\n=================================================================================================================\n\n" +if [ $? -eq 0 ]; then + cd scripts + /bin/bash ./bios_flash + /bin/bash ./load_mbr + cd .. + printf "${green}Compiling Test Program...\n" + ./dasm dss/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 + cp dragon/disk1.dr ../extra/dragon/disk1.dr + cp dragon/cmos.dr ../extra/dragon/cmos.dr +fi diff --git a/extra/make_and_run b/extra/make_and_run index 4157d6a..948a57b 100644 --- a/extra/make_and_run +++ b/extra/make_and_run @@ -19,8 +19,7 @@ if [ $? -eq 0 ]; then printf "${green}Compiling Test Program...\n" ./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 + ./dvm config/testMachine.dvm --force-load newTest.bin 0x00 cp dragon/disk1.dr ../extra/dragon/disk1.dr cp dragon/cmos.dr ../extra/dragon/cmos.dr fi diff --git a/extra/scripts/bios_flash b/extra/scripts/bios_flash index c7a66ce..7f5af9d 100644 --- a/extra/scripts/bios_flash +++ b/extra/scripts/bios_flash @@ -5,6 +5,6 @@ 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 --verbose +cp ../extra/dss/bios/bios.dss ./dss/bios/bios.dss +./dasm dss/bios/bios.dss -o dragon/bios.bin --save-disassembly disassembly/bios.dds --verbose cp dragon/bios.bin ../extra/dragon/bios.bin diff --git a/extra/scripts/run-test-1.bat b/extra/scripts/run-test-1.bat new file mode 100644 index 0000000..2b89fff --- /dev/null +++ b/extra/scripts/run-test-1.bat @@ -0,0 +1,2 @@ +dasm dss/test1.dss -o test1.bin --save-disassembly disassembly/test1.dds --verbose +ddb config/testMachine.dvm --force-load test1.bin 0x00 --verbose-load --hide-vdisplay \ No newline at end of file diff --git a/extra/scripts/run-test-2.bat b/extra/scripts/run-test-2.bat new file mode 100644 index 0000000..7080b39 --- /dev/null +++ b/extra/scripts/run-test-2.bat @@ -0,0 +1,2 @@ +dasm dss/test2.dss -o test2.bin --save-disassembly disassembly/test2.dds --verbose +ddb config/testMachine.dvm --force-load test2.bin 0x00 --verbose-load --hide-vdisplay \ No newline at end of file diff --git a/run-test.bat b/run-test.bat deleted file mode 100644 index 589665b..0000000 --- a/run-test.bat +++ /dev/null @@ -1,2 +0,0 @@ -dasm dss/test.dss -o test.bin --save-disassembly disassembly/test.dds --verbose -ddb config/testMachine.dvm --force-load test.bin 0x00 --verbose-load --hide-vdisplay \ No newline at end of file diff --git a/src/assembler/Assembler.cpp b/src/assembler/Assembler.cpp index e0a7f82..2a8df79 100644 --- a/src/assembler/Assembler.cpp +++ b/src/assembler/Assembler.cpp @@ -128,7 +128,7 @@ namespace dragon void Assembler::loadSource(ostd::String source) { m_rawSource = source; - m_lines = ostd::String(source).tokenize("\n").getRawData(); + m_lines = ostd::String(source.replaceAll("\t", " ")).tokenize("\n").getRawData(); } ostd::ByteStream Assembler::assembleToFile(ostd::String sourceFileName, ostd::String binaryFileName) @@ -245,7 +245,7 @@ namespace dragon { ostd::String lineEdit(line); for (int32_t i = defines.size() - 1; i >= 0; i--) - lineEdit.replaceAll(defines[i].name, defines[i].value); + lineEdit.replaceAll(defines[i].name, defines[i].value.new_trim()); line = lineEdit; } m_lines.clear(); @@ -1190,7 +1190,7 @@ namespace dragon } return; } - else if (instEdit == "add" || instEdit == "sub" || instEdit == "mul") + else if (instEdit == "add" || instEdit == "sub" || instEdit == "mul" || instEdit == "div") { m_code.push_back(0x00); eOperandType opType = parseOperand(st.next(), word); @@ -1208,6 +1208,7 @@ namespace dragon if (instEdit == "add") m_code[m_code.size() - 2] = data::OpCodes::AddImmReg; else if (instEdit == "sub") m_code[m_code.size() - 2] = data::OpCodes::SubImmReg; else if (instEdit == "mul") m_code[m_code.size() - 2] = data::OpCodes::MulImmReg; + else if (instEdit == "div") m_code[m_code.size() - 2] = data::OpCodes::DivImmReg; m_code.push_back((uint8_t)((word & 0xFF00) >> 8)); m_code.push_back((uint8_t)(word & 0x00FF)); } @@ -1216,6 +1217,7 @@ namespace dragon if (instEdit == "add") m_code[m_code.size() - 2] = data::OpCodes::AddRegReg; else if (instEdit == "sub") m_code[m_code.size() - 2] = data::OpCodes::SubRegReg; else if (instEdit == "mul") m_code[m_code.size() - 2] = data::OpCodes::MulRegReg; + else if (instEdit == "div") m_code[m_code.size() - 2] = data::OpCodes::DivRegReg; m_code.push_back((uint8_t)word); } else @@ -1435,6 +1437,26 @@ namespace dragon return eOperandType::DerefRegister; return eOperandType::Register; } + else if (opEdit.startsWith("reg(") && opEdit.endsWith(")")) + { + opEdit.substr(4, opEdit.len() - 1); + opEdit.trim(); + if (!opEdit.isNumeric()) + { + std::cout << "Invalid numeric value: " << opEdit << "\n"; + return eOperandType::Error; + } + uint8_t _reg = opEdit.toInt(); + if (_reg >= data::Registers::Last) + { + std::cout << "Invalid Register: " << opEdit << "\n"; + return eOperandType::Error; + } + outOp = (int16_t)_reg; + if (derefReg) + return eOperandType::DerefRegister; + return eOperandType::Register; + } if (derefReg) { std::cout << "Invalid Register Dereferencing: " << op << "\n"; diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp index 36873f7..55b8255 100644 --- a/src/debugger/Debugger.cpp +++ b/src/debugger/Debugger.cpp @@ -13,12 +13,12 @@ namespace dragon { validate(); enableSignals(); - connectSignal(dragon::Window::Signal_OnWindowClosed); + connectSignal(ostd::tBuiltinSignals::WindowClosed); } void Debugger::tCloseEventListener::handleSignal(ostd::tSignal& signal) { - m_mainWindowClosed = signal.ID == dragon::Window::Signal_OnWindowClosed; + m_mainWindowClosed = signal.ID == ostd::tBuiltinSignals::WindowClosed; } @@ -1290,6 +1290,11 @@ namespace dragon data().args.step_exec = !data().args.step_exec; output().p("Step execution = ").p(STR_BOOL(data().args.step_exec)).nl(); } + else if (data().command == "display") + { + data().args.hide_virtual_display = !data().args.hide_virtual_display; + output().p("Virtual Display Hidden = ").p(STR_BOOL(data().args.hide_virtual_display)).nl(); + } } } @@ -1526,6 +1531,9 @@ namespace dragon tmpCommand = "(t)rack "; tmpCommand.addRightPadding(commandLength); out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to track specific addresses during execution.").reset().nl(); + tmpCommand = "display"; + tmpCommand.addRightPadding(commandLength); + out.fg(ostd::ConsoleColors::Blue).p(tmpCommand).fg(ostd::ConsoleColors::Green).p("Used to enable/disable the Virtual Display for the runtime.").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(); diff --git a/src/debugger/Debugger.hpp b/src/debugger/Debugger.hpp index 7c5fd04..d28455c 100644 --- a/src/debugger/Debugger.hpp +++ b/src/debugger/Debugger.hpp @@ -19,7 +19,7 @@ namespace dragon bool step_exec = false; bool track_step_diff = true; bool auto_start_debug = false; - bool hide_virtual_display = false; + bool hide_virtual_display = true; bool track_call_stack = true; bool auto_track_all_data_symbols = true; ostd::String force_load_file = ""; diff --git a/src/gui/Renderer.cpp b/src/gui/Renderer.cpp new file mode 100644 index 0000000..ebc33fe --- /dev/null +++ b/src/gui/Renderer.cpp @@ -0,0 +1,70 @@ +#include "Renderer.hpp" +#include "Window.hpp" +#include + +namespace dragon +{ + Renderer::~Renderer(void) + { + if (isInvalid()) return; + ostd::Utils::destroyArray(m_pixels); + SDL_DestroyTexture(m_texture); + } + + void Renderer::initialize(Window& parent) + { + if (isValid()) return; //TODO: Error + if (!parent.isValid() || !parent.isInitialized()) + return; //TODO: Error + m_parent = &parent; + m_pixels = ostd::Utils::createArray(parent.getWindowWidth() * parent.getWindowHeight()); + m_texture = SDL_CreateTexture(parent.getSDLRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, parent.getWindowWidth(), parent.getWindowHeight()); + m_windowWidth = parent.getWindowWidth(); + m_windowHeight = parent.getWindowHeight(); + setTypeName("lspp::gfx::Renderer"); + enableSignals(); + connectSignal(ostd::tBuiltinSignals::WindowResized); + validate(); + } + + void Renderer::handleSignal(ostd::tSignal& signal) + { + if (isInvalid()) return; + if (signal.ID == ostd::tBuiltinSignals::WindowResized) + { + m_pixels = ostd::Utils::resizeArray(m_pixels, m_parent->getWindowWidth() * m_parent->getWindowHeight()); + SDL_DestroyTexture(m_texture); + m_texture = SDL_CreateTexture(m_parent->getSDLRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, m_parent->getWindowWidth(), m_parent->getWindowHeight()); + m_windowWidth = m_parent->getWindowWidth(); + m_windowHeight = m_parent->getWindowHeight(); + updateBuffer(); + displayBuffer(); + } + } + + void Renderer::updateBuffer(void) + { + if (isInvalid()) return; + SDL_UpdateTexture(m_texture, NULL, m_pixels, m_windowWidth * 4); + } + + void Renderer::displayBuffer(void) + { + if (isInvalid()) return; + SDL_Rect rect { 0, 0, m_windowWidth, m_windowHeight }; + SDL_RenderCopy(m_parent->getSDLRenderer(), m_texture, NULL, &rect); + } + + void Renderer::clear(const ostd::Color& color) + { + if (isInvalid()) return; + for (int32_t y = 0; y < m_windowHeight; y++) + { + for (uint32_t x = 0; x < m_windowWidth; x++) + { + int32_t index = CONVERT_2D_1D(x, y, m_windowWidth); + m_pixels[index] = color.asInteger(); + } + } + } +} \ No newline at end of file diff --git a/src/gui/Renderer.hpp b/src/gui/Renderer.hpp new file mode 100644 index 0000000..654e7bf --- /dev/null +++ b/src/gui/Renderer.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include "../tools/SDLInclude.hpp" + +namespace dragon +{ + class Window; + class Renderer : public ostd::BaseObject + { + public: + inline Renderer(void) { invalidate(); } + ~Renderer(void); + void initialize(Window& parent); + void handleSignal(ostd::tSignal& signal) override; + void updateBuffer(void); + void displayBuffer(void); + inline uint32_t* getScreenPixels(void) { return m_pixels; } + + void clear(const ostd::Color& color); + + private: + uint32_t* m_pixels { nullptr }; + SDL_Texture* m_texture { nullptr }; + Window* m_parent { nullptr }; + int32_t m_windowWidth { 0 }; + int32_t m_windowHeight { 0 }; + }; +} \ No newline at end of file diff --git a/src/gui/Window.cpp b/src/gui/Window.cpp index fa67852..eca0648 100644 --- a/src/gui/Window.cpp +++ b/src/gui/Window.cpp @@ -54,8 +54,9 @@ namespace dragon if (!m_initialized) return; Uint64 start = SDL_GetPerformanceCounter(); handleEvents(); + if (m_refreshScreen) + SDL_RenderClear(m_renderer); onUpdate(); - SDL_RenderClear(m_renderer); onRender(); SDL_RenderPresent(m_renderer); Uint64 end = SDL_GetPerformanceCounter(); @@ -81,6 +82,7 @@ namespace dragon { if (!isInitialized()) return; SDL_SetWindowSize(m_window, width, height); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowResized, ostd::tSignalPriority::RealTime); } void Window::setTitle(const ostd::String& title) @@ -113,7 +115,7 @@ namespace dragon if (event.type == SDL_QUIT) { m_running = false; - ostd::SignalHandler::emitSignal(Signal_OnWindowClosed, ostd::tSignalPriority::Normal, *this); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowClosed, ostd::tSignalPriority::Normal, *this); } else if (event.type == SDL_WINDOWEVENT) { @@ -129,7 +131,7 @@ namespace dragon { MouseEventData mmd = l_getMouseState(); if (isMouseDragEventEnabled() && mmd.button != MouseEventData::eButton::None) - ostd::SignalHandler::emitSignal(Signal_OnMouseDragged, ostd::tSignalPriority::RealTime, mmd); + ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::WindowClosed, ostd::tSignalPriority::RealTime, mmd); else ostd::SignalHandler::emitSignal(ostd::tBuiltinSignals::MouseMoved, ostd::tSignalPriority::RealTime, mmd); } diff --git a/src/gui/Window.hpp b/src/gui/Window.hpp index db6ac8a..0c31f39 100644 --- a/src/gui/Window.hpp +++ b/src/gui/Window.hpp @@ -49,6 +49,8 @@ namespace dragon SDL_Surface* m_fontSurface { nullptr }; uint32_t* m_fontPixels { nullptr }; + bool m_refreshScreen { true }; + private: int32_t m_windowWidth { 0 }; int32_t m_windowHeight { 0 }; @@ -61,11 +63,6 @@ namespace dragon bool m_deagEventEnabled { false }; bool m_running { false }; bool m_initialized { false }; - - public: - //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); }; class WindowResizedData : public ostd::BaseObject { diff --git a/src/hardware/VirtualCPU.cpp b/src/hardware/VirtualCPU.cpp index e784936..7f7a530 100644 --- a/src/hardware/VirtualCPU.cpp +++ b/src/hardware/VirtualCPU.cpp @@ -413,6 +413,29 @@ namespace dragon writeRegister(data::Registers::ACC, regValue1 * regValue2); } break; + case data::OpCodes::DivImmReg: //TODO: Division by zero is unhandled + { + uint8_t regAddr = fetch8(); + int16_t literal = fetch16(); + int16_t regValue = readRegister(regAddr); + int16_t quotient = regValue / literal; + int16_t reminder = regValue % literal; + writeRegister(data::Registers::ACC, quotient); + writeRegister(data::Registers::RV, reminder); + } + break; + case data::OpCodes::DivRegReg: //TODO: Division by zero is unhandled + { + uint8_t regAddr1 = fetch8(); + uint8_t regAddr2 = fetch8(); + int16_t regValue1 = readRegister(regAddr1); + int16_t regValue2 = readRegister(regAddr2); + int16_t quotient = regValue1 / regValue2; + int16_t reminder = regValue1 % regValue2; + writeRegister(data::Registers::ACC, quotient); + writeRegister(data::Registers::RV, reminder); + } + break; case data::OpCodes::IncReg: { uint8_t regAddr = fetch8(); diff --git a/src/hardware/VirtualDisplay.cpp b/src/hardware/VirtualDisplay.cpp new file mode 100644 index 0000000..31bee4f --- /dev/null +++ b/src/hardware/VirtualDisplay.cpp @@ -0,0 +1,158 @@ +#include "VirtualDisplay.hpp" +#include "../gui/RawTextRenderer.hpp" +#include "../runtime/DragonRuntime.hpp" + +namespace dragon +{ + namespace hw + { + void VirtualDisplay::onInitialize(void) + { + m_renderer.initialize(*this); + RawTextRenderer::initialize(); + } + + void VirtualDisplay::onDestroy(void) { } + + void VirtualDisplay::onRender(void) + { + auto& config = DragonRuntime::machine_config; + if (m_refreshScreen) + { + m_renderer.clear(config.singleColor_background); + for (int32_t i = 0; i < m_singleTextLines.size(); i++) + { + auto& line = m_singleTextLines[i]; + RawTextRenderer::drawString(line, 0, i, m_renderer.getScreenPixels(), getWindowWidth(), getWindowHeight(), m_fontPixels, config.singleColor_foreground); + } + m_refreshScreen = false; + } + m_renderer.updateBuffer(); + m_renderer.displayBuffer(); + } + + void VirtualDisplay::onUpdate(void) + { + auto& mem = DragonRuntime::memMap; + uint16_t vga_addr = data::MemoryMapAddresses::VideoCardInterface_Start; + uint8_t video_mode = mem.read8(vga_addr + tRegisters::VideoMode); + uint8_t signal = mem.read8(vga_addr + tRegisters::Signal); + if (signal == tSignalValues::Continue) return; + if (video_mode == tVideoModeValues::TextSingleColor) + { + if (signal == tSignalValues::TextSingleColor_DirectPrintChar) + { + char c = (char)mem.read8(vga_addr + tRegisters::TextSingleCharacter); + single_text_add_char_to_line(c); + } + else if (signal == tSignalValues::TextSingleColor_StoreChar) + { + char c = (char)mem.read8(vga_addr + tRegisters::TextSingleCharacter); + single_text_add_char_to_buffer(c); + } + else if (signal == tSignalValues::TextSingleColor_DirectPrintBuffNoFlush) + { + single_text_print_buffer_no_flush(); + } + else if (signal == tSignalValues::TextSingleColor_DirectPrintBuffAndFlush) + { + single_text_print_buffer_and_flush(); + } + else if (signal == tSignalValues::TextSingleColor_FlushBuffer) + { + single_text_flush_buffer(); + } + else if (signal == tSignalValues::ClearSCreen) + { + single_text_clear_screen(); + } + else if (signal == tSignalValues::RefreshScreen) + { + single_text_refresh_screen(); + } + } + else return; + mem.write8(vga_addr + tRegisters::Signal, tSignalValues::Continue); + } + + void VirtualDisplay::onFixedUpdate(void) { } + + void VirtualDisplay::onSlowUpdate(void) { } + + void VirtualDisplay::single_text_add_char_to_line(char c) + { + auto& config = DragonRuntime::machine_config; + if (m_singleTextLines.size() == 0) + { + m_singleTextLines.push_back(ostd::String().addChar(c)); + RawTextRenderer::drawString(ostd::String().addChar(c), 0, 0, m_renderer.getScreenPixels(), getWindowWidth(), getWindowHeight(), m_fontPixels, config.singleColor_foreground); + return; + } + auto& line = m_singleTextLines[m_singleTextLines.size() - 1]; + if (c == '\n') + m_singleTextLines.push_back(""); + else if (isprint(c)) + { + if (line.len() == RawTextRenderer::CONSOLE_CHARS_H) + { + m_singleTextLines.push_back(ostd::String().addChar(c)); + auto& line = m_singleTextLines[m_singleTextLines.size() - 1]; + RawTextRenderer::drawString(ostd::String().addChar(c), line.len() - 1, m_singleTextLines.size() - 1, m_renderer.getScreenPixels(), getWindowWidth(), getWindowHeight(), m_fontPixels, config.singleColor_foreground); + } + else + { + line.addChar(c); + RawTextRenderer::drawString(ostd::String().addChar(c), line.len() - 1, m_singleTextLines.size() - 1, m_renderer.getScreenPixels(), getWindowWidth(), getWindowHeight(), m_fontPixels, config.singleColor_foreground); + } + } + else return; + if (m_singleTextLines.size() == RawTextRenderer::CONSOLE_CHARS_V + 1) + { + m_refreshScreen = true; + m_singleTextLines.erase(m_singleTextLines.begin()); + } + } + + void VirtualDisplay::single_text_add_char_to_buffer(char c) + { + if (c == '\n' || m_singleTextBuffer.len() == RawTextRenderer::CONSOLE_CHARS_H) + { + single_text_print_buffer_and_flush(); + single_text_add_char_to_line(c); + } + else + m_singleTextBuffer.addChar(c); + } + + void VirtualDisplay::single_text_flush_buffer(void) + { + m_singleTextBuffer = ""; + } + + void VirtualDisplay::single_text_print_buffer_and_flush(void) + { + for (auto& ch : m_singleTextBuffer) + single_text_add_char_to_line(ch); + single_text_flush_buffer(); + } + + void VirtualDisplay::single_text_print_buffer_no_flush(void) + { + for (auto& ch : m_singleTextBuffer) + single_text_add_char_to_line(ch); + } + + + void VirtualDisplay::single_text_clear_screen(void) + { + m_singleTextLines.clear(); + m_refreshScreen = true; + } + + void VirtualDisplay::single_text_refresh_screen(void) + { + m_refreshScreen = true; + } + + } +} \ No newline at end of file diff --git a/src/hardware/VirtualDisplay.hpp b/src/hardware/VirtualDisplay.hpp new file mode 100644 index 0000000..c9599b6 --- /dev/null +++ b/src/hardware/VirtualDisplay.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "../gui/Window.hpp" +#include "../gui/Renderer.hpp" + +namespace dragon +{ + namespace hw + { + class VirtualDisplay : public Window + { + public: struct tRegisters + { + inline static constexpr uint8_t VideoMode = 0x00; + inline static constexpr uint8_t Signal = 0x03; + inline static constexpr uint8_t TextSingleCharacter = 0x04; + }; + public: struct tVideoModeValues + { + inline static constexpr uint8_t TextSingleColor = 0x00; + }; + public: struct tSignalValues + { + inline static constexpr uint8_t Continue = 0x00; + + inline static constexpr uint8_t TextSingleColor_DirectPrintChar = 0x02; + inline static constexpr uint8_t TextSingleColor_StoreChar = 0x03; + inline static constexpr uint8_t TextSingleColor_DirectPrintBuffAndFlush = 0x04; + inline static constexpr uint8_t TextSingleColor_FlushBuffer = 0x05; + inline static constexpr uint8_t TextSingleColor_DirectPrintBuffNoFlush = 0x06; + + inline static constexpr uint8_t RefreshScreen = 0xE0; + inline static constexpr uint8_t ClearSCreen = 0xE1; + }; + public: + void onInitialize(void) override; + void onDestroy(void) override; + void onRender(void) override; + void onUpdate(void) override; + void onFixedUpdate(void) override; + void onSlowUpdate(void) override; + + private: + void single_text_add_char_to_line(char c); + void single_text_add_char_to_buffer(char c); + void single_text_flush_buffer(void); + void single_text_print_buffer_and_flush(void); + void single_text_print_buffer_no_flush(void); + + void single_text_clear_screen(void); + void single_text_refresh_screen(void); + + private: + Renderer m_renderer; + + std::vector m_singleTextLines; + ostd::String m_singleTextBuffer { "" }; + }; + } +} \ No newline at end of file diff --git a/src/hardware/VirtualIODevices.cpp b/src/hardware/VirtualIODevices.cpp index 290f3e9..3bd3e8e 100644 --- a/src/hardware/VirtualIODevices.cpp +++ b/src/hardware/VirtualIODevices.cpp @@ -438,31 +438,54 @@ namespace dragon Graphics::Graphics(void) { + m_videoMemory.init(0xFFFF); } int8_t Graphics::read8(uint16_t addr) { - return 0x00; + int8_t outVal = 0; + if (!m_videoMemory.r_Byte(addr, outVal)) + { + data::ErrorHandler::pushError(data::ErrorCodes::Graphics_MemoryReadFailed, "Failed to read byte from Graphics Memory"); + return 0; + } + return outVal; } int16_t Graphics::read16(uint16_t addr) { - return 0x0000; + int16_t outVal = 0; + if (!m_videoMemory.r_Word(addr, outVal)) + { + data::ErrorHandler::pushError(data::ErrorCodes::Graphics_MemoryReadFailed, "Failed to read word from Graphics Memory"); + return 0; + } + return outVal; } int8_t Graphics::write8(uint16_t addr, int8_t value) { - return 0x00; + if (!m_videoMemory.w_Byte(addr, value)) + { + data::ErrorHandler::pushError(data::ErrorCodes::Graphics_MemoryWriteFailed, "Failed to write byte to Graphics Memory"); + return 0; + } + return value; } int16_t Graphics::write16(uint16_t addr, int16_t value) { - return 0x0000; + if (!m_videoMemory.w_Word(addr, value)) + { + data::ErrorHandler::pushError(data::ErrorCodes::Graphics_MemoryWriteFailed, "Failed to word byte to Graphics Memory"); + return 0; + } + return value; } ostd::ByteStream* Graphics::getByteStream(void) { - return nullptr; + return &m_videoMemory.getData(); } diff --git a/src/hardware/VirtualIODevices.hpp b/src/hardware/VirtualIODevices.hpp index e853aea..1418da9 100644 --- a/src/hardware/VirtualIODevices.hpp +++ b/src/hardware/VirtualIODevices.hpp @@ -170,6 +170,7 @@ namespace dragon ostd::ByteStream* getByteStream(void) override; private: + ostd::serial::SerialIO m_videoMemory; }; class SerialPort : public IMemoryDevice { diff --git a/src/runtime/ConfigLoader.cpp b/src/runtime/ConfigLoader.cpp index 1b9c8ae..2e21432 100644 --- a/src/runtime/ConfigLoader.cpp +++ b/src/runtime/ConfigLoader.cpp @@ -40,6 +40,16 @@ namespace dragon lineEdit = tokens.next(); config.cmos_path = lineEdit; } + else if (lineEdit == "singlecolor_foreground") + { + lineEdit = tokens.next(); + config.singleColor_foreground.set(lineEdit); + } + else if (lineEdit == "singlecolor_background") + { + lineEdit = tokens.next(); + config.singleColor_background.set(lineEdit); + } else continue; //TODO: Warning } return validate_machine_config(config); diff --git a/src/runtime/ConfigLoader.hpp b/src/runtime/ConfigLoader.hpp index 1d54ab2..5b550af 100644 --- a/src/runtime/ConfigLoader.hpp +++ b/src/runtime/ConfigLoader.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include namespace dragon @@ -10,6 +10,8 @@ namespace dragon std::map vdisk_paths; ostd::String bios_path; ostd::String cmos_path; + ostd::Color singleColor_background; + ostd::Color singleColor_foreground; inline bool isValid(void) const { return m_valid; } diff --git a/src/runtime/DragonRuntime.cpp b/src/runtime/DragonRuntime.cpp index acb302c..313039f 100644 --- a/src/runtime/DragonRuntime.cpp +++ b/src/runtime/DragonRuntime.cpp @@ -136,10 +136,9 @@ namespace dragon if (verbose) 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; - vDisplay.setSize(w, h); + vDisplay.initialize(w, h, "DragonVM", "font.bmp"); if (hideVirtualDisplay) vDisplay.hide(); if (verbose) diff --git a/src/runtime/DragonRuntime.hpp b/src/runtime/DragonRuntime.hpp index 81faa0e..2bffa0b 100644 --- a/src/runtime/DragonRuntime.hpp +++ b/src/runtime/DragonRuntime.hpp @@ -7,6 +7,7 @@ #include "../hardware/VirtualRAM.hpp" #include "../hardware/VirtualIODevices.hpp" #include "../hardware/VirtualHardDrive.hpp" +#include "../hardware/VirtualDisplay.hpp" #include "../tools/GlobalData.hpp" @@ -73,7 +74,7 @@ namespace dragon std::vector callStack; }; public: - static void printRegisters(dragon::hw::VirtualCPU& cpu); + static void printRegisters(hw::VirtualCPU& cpu); static void processErrors(void); static std::vector getErrorList(void); static int32_t loadArguments(int argc, char** argv, tCommandLineArgs& args); @@ -99,24 +100,24 @@ namespace dragon public: inline static ostd::ConsoleOutputHandler out; - inline static dragon::hw::MemoryMapper memMap; - inline static dragon::hw::VirtualCPU cpu { memMap }; - inline static dragon::hw::VirtualRAM ram; - inline static dragon::hw::InterruptVector intVec; - inline static dragon::hw::VirtualBIOS vBIOS; - inline static dragon::hw::interface::CMOS vCMOS; - inline static dragon::hw::VirtualBootloader vMBR; - inline static dragon::hw::VirtualKeyboard vKeyboard; - inline static dragon::hw::VirtualMouse vMouse; - inline static dragon::hw::interface::Disk vDiskInterface { memMap, cpu }; - inline static dragon::hw::interface::Graphics vGraphicsInterface; - inline static dragon::hw::interface::SerialPort vSerialInterface; + inline static hw::MemoryMapper memMap; + inline static hw::VirtualCPU cpu { memMap }; + inline static hw::VirtualRAM ram; + inline static hw::InterruptVector intVec; + inline static hw::VirtualBIOS vBIOS; + inline static hw::interface::CMOS vCMOS; + inline static hw::VirtualBootloader vMBR; + inline static hw::VirtualKeyboard vKeyboard; + inline static hw::VirtualMouse vMouse; + inline static hw::interface::Disk vDiskInterface { memMap, cpu }; + inline static hw::interface::Graphics vGraphicsInterface; + inline static hw::interface::SerialPort vSerialInterface; - inline static std::unordered_map vDisks; + inline static std::unordered_map vDisks; - inline static dragon::Window vDisplay; + inline static hw::VirtualDisplay vDisplay; - inline static dragon::tMachineConfig machine_config; + inline static tMachineConfig machine_config; private: inline static tMachineDebugInfo s_machineInfo; diff --git a/src/tools/GlobalData.hpp b/src/tools/GlobalData.hpp index 841a80f..bea96f2 100644 --- a/src/tools/GlobalData.hpp +++ b/src/tools/GlobalData.hpp @@ -49,6 +49,9 @@ namespace dragon inline static constexpr uint64_t IntVector_InvalidAddress = 0x7000000000000000; + inline static constexpr uint64_t Graphics_MemoryReadFailed = 0x8000000000000000; + inline static constexpr uint64_t Graphics_MemoryWriteFailed = 0x8000000000000001; + }; class ErrorHandler @@ -158,9 +161,7 @@ namespace dragon inline static constexpr uint8_t PP = 0x04; inline static constexpr uint8_t FL = 0x05; inline static constexpr uint8_t ACC = 0x06; - - //TODO: Either add general purpose registers in this space or check when read/write registers - + //0x07, 0x08, 0x09 Are hidden registers used internally, but can be normally accessed by address inline static constexpr uint8_t R1 = 0x0A; inline static constexpr uint8_t R2 = 0x0B; inline static constexpr uint8_t R3 = 0x0C; @@ -184,11 +185,6 @@ namespace dragon class InterruptCodes { public: - inline static constexpr uint8_t Keyboard = 0x10; - inline static constexpr uint8_t Mouse = 0x11; - inline static constexpr uint8_t BiosInterrupt = 0x20; - inline static constexpr uint8_t BiosVideoInterrupt = 0x30; - inline static constexpr uint8_t DiskInterfaceFFinished = 0x80; }; @@ -229,6 +225,8 @@ namespace dragon inline static constexpr uint8_t SubImmReg = 0x33; inline static constexpr uint8_t MulRegReg = 0x34; inline static constexpr uint8_t MulImmReg = 0x35; + inline static constexpr uint8_t DivRegReg = 0x36; + inline static constexpr uint8_t DivImmReg = 0x37; inline static constexpr uint8_t IncReg = 0x40; inline static constexpr uint8_t DecReg = 0x41; @@ -305,6 +303,8 @@ namespace dragon case data::OpCodes::SubRegReg: return "SubRegReg"; case data::OpCodes::MulImmReg: return "MulImmReg"; case data::OpCodes::MulRegReg: return "MulRegReg"; + case data::OpCodes::DivImmReg: return "DivImmReg"; + case data::OpCodes::DivRegReg: return "DivRegReg"; case data::OpCodes::IncReg: return "IncReg"; case data::OpCodes::DecReg: return "DecReg"; case data::OpCodes::RShiftRegImm: return "RShiftRegImm"; @@ -379,6 +379,8 @@ namespace dragon case data::OpCodes::SubRegReg: return 3; case data::OpCodes::MulImmReg: return 4; case data::OpCodes::MulRegReg: return 3; + case data::OpCodes::DivImmReg: return 4; + case data::OpCodes::DivRegReg: return 3; case data::OpCodes::IncReg: return 2; case data::OpCodes::DecReg: return 2; case data::OpCodes::RShiftRegImm: return 4;