From 2cbc8847b2b7c2a43b48911678dfd2396299c2a8 Mon Sep 17 00:00:00 2001 From: OmniaX Date: Sat, 13 Jul 2024 17:30:20 +0200 Subject: [PATCH] Preparing boot routine for Drake --- CMakeLists.txt | 2 + create_win_release.sh | 62 ----- extra/config/testMachine.dvm | 1 + .../dpt_4_partitions_on_4_mb_disk.bin | Bin 512 -> 0 bytes .../dpt_4_partitions_on_4_mb_disk.txt | 42 ---- extra/dragon info/dscript_mockup_1.dsc | 57 ----- extra/dragon info/dscript_mockup_2.dsc | 102 -------- extra/dragon info/font_big.bmp | Bin 439434 -> 0 bytes extra/dragon info/info | 168 -------------- extra/dragon info/regular_expressions.txt | 10 - extra/dragon info/todo.txt | 8 - extra/dragon/bios.bin | Bin 4096 -> 4096 bytes extra/dragon/disk1.dr | Bin 4194304 -> 4194304 bytes extra/dss/activeTest.dss | 17 -- extra/dss/bios/entry.dss | 5 +- extra/dss/bios_api.dss | 2 +- extra/dss/drake/bootsector.dss | 37 ++- extra/dss/drake/disk.dss | 2 +- extra/dss/drake/dpt.dss | 2 +- extra/dss/drake/loader.dss | 45 ++-- extra/info/info | 219 ++++++++++++++++++ extra/info/todo.txt | 26 +++ extra/make_and_debug | 6 +- extra/make_and_run | 6 +- extra/scripts/build_drake | 23 ++ extra/scripts/load_mbr | 13 -- extra/scripts/run-test-1.bat | 2 - extra/scripts/run-test-2.bat | 2 - src/hardware/VirtualIODevices.cpp | 4 +- src/hardware/VirtualMMU.cpp | 14 ++ src/hardware/VirtualMMU.hpp | 23 ++ src/hardware/VirtualRAM.cpp | 1 + src/runtime/ConfigLoader.cpp | 13 ++ src/runtime/ConfigLoader.hpp | 1 + src/tools/GlobalData.hpp | 19 +- src/tools/Tools.cpp | 219 +++++++++++++++++- src/tools/Tools.hpp | 13 ++ 37 files changed, 626 insertions(+), 540 deletions(-) delete mode 100644 create_win_release.sh delete mode 100644 extra/dragon info/dpt_4_partitions_on_4_mb_disk.bin delete mode 100644 extra/dragon info/dpt_4_partitions_on_4_mb_disk.txt delete mode 100644 extra/dragon info/dscript_mockup_1.dsc delete mode 100644 extra/dragon info/dscript_mockup_2.dsc delete mode 100644 extra/dragon info/font_big.bmp delete mode 100644 extra/dragon info/info delete mode 100644 extra/dragon info/regular_expressions.txt delete mode 100644 extra/dragon info/todo.txt delete mode 100644 extra/dss/activeTest.dss create mode 100644 extra/info/info create mode 100644 extra/info/todo.txt create mode 100644 extra/scripts/build_drake delete mode 100644 extra/scripts/load_mbr delete mode 100644 extra/scripts/run-test-1.bat delete mode 100644 extra/scripts/run-test-2.bat create mode 100644 src/hardware/VirtualMMU.cpp create mode 100644 src/hardware/VirtualMMU.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d183a0..2cbaf79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ list(APPEND RUNTIME_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualDisplay.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/CPUExtensions.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualMMU.cpp ${CMAKE_CURRENT_LIST_DIR}/src/tools/Utils.cpp ${CMAKE_CURRENT_LIST_DIR}/src/tools/GlobalData.cpp @@ -60,6 +61,7 @@ list(APPEND DEBUGGER_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualHardDrive.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualDisplay.cpp ${CMAKE_CURRENT_LIST_DIR}/src/hardware/CPUExtensions.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/hardware/VirtualMMU.cpp ${CMAKE_CURRENT_LIST_DIR}/src/runtime/DragonRuntime.cpp ${CMAKE_CURRENT_LIST_DIR}/src/runtime/ConfigLoader.cpp diff --git a/create_win_release.sh b/create_win_release.sh deleted file mode 100644 index 40e793f..0000000 --- a/create_win_release.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash - -rm -r ./win-release - -./compile - -cd bin - -printf "${green}Compiling vBIOS...\n${clear}" -./dasm dss/bios/entry.dss -o dragon/bios.bin --save-disassembly disassembly/bios.dds --verbose --save-exports - -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/drake/entry.dss -o dragon/drake.bin --save-disassembly disassembly/drake.dds --verbose - -printf "\n${green}Loading MBR Block...\n${clear}" -./dtools load-binary dragon/disk1.dr dragon/drake.bin 0x00000000 - -printf "${green}Compiling Test Program 1...\n" -./dasm dss/tests/test1.dss -o test1.bin --save-disassembly disassembly/test1.dds --verbose -printf "${green}Compiling Test Program 2...\n" -./dasm dss/tests/test2.dss -o test2.bin --save-disassembly disassembly/test2.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 -r ./bin/config ./win-release -cp -r ./bin/disassembly/bios.dds ./win-release/disassembly -cp -r ./bin/disassembly/drake.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/dss4 -mkdir ./win-release/dss/bios -mkdir ./win-release/dss/drake -cp ./bin/dss/bios/* ./win-release/dss/bios -cp ./bin/dss/drake/* ./win-release/dss/drake -cp ./bin/dss/tests/test1.dss ./win-release/dss -cp ./bin/dss/tests/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 -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 ./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 2b46009..d023769 100644 --- a/extra/config/testMachine.dvm +++ b/extra/config/testMachine.dvm @@ -4,6 +4,7 @@ CMOS = dragon/cmos.dr cpuext = extmov, extalu fixed_clock = true clock_rate_sec = 644 +memory_extension_pages = 16 SingleColor_foreground = #009900FF SingleColor_background = #111111FF \ No newline at end of file diff --git a/extra/dragon info/dpt_4_partitions_on_4_mb_disk.bin b/extra/dragon info/dpt_4_partitions_on_4_mb_disk.bin deleted file mode 100644 index 807d1e4382fbf1d538ee375ba13c94a8b006a125..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 512 zcmex(ih+scKLju^urM$%hyXEE01M^h?;jHE>Kx)91XM;AkYHe7%mC`dVmMTYEMt*1 zF*YzT$U6Y-!EJ|2QDS<2o_{d1Hj>y12OxIf@kdB%afw1eVo^zENoIZ?Nv1;-6JZA# IrlM#80N#+9tN;K2 diff --git a/extra/dragon info/dpt_4_partitions_on_4_mb_disk.txt b/extra/dragon info/dpt_4_partitions_on_4_mb_disk.txt deleted file mode 100644 index b539018..0000000 --- a/extra/dragon info/dpt_4_partitions_on_4_mb_disk.txt +++ /dev/null @@ -1,42 +0,0 @@ -F1 CA 00 02 04 FF FF FF FF FF FF FF - -00 00 04 00 -00 00 14 00 -00 00 -FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF -42 4F 4F 54 53 45 43 54 4F 52 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -00 00 18 00 -00 01 68 00 -00 00 -FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -00 01 80 00 -00 1F 40 00 -00 01 -FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF -44 72 61 67 6F 6E 4F 53 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -00 20 C0 00 -00 1F 40 00 -00 00 -FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF -54 65 73 74 20 50 61 72 74 69 74 69 6F 6E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -00 20 C0 00 -00 1F 40 00 -00 00 -FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - - -Disk: 4096 kb -============= -boot + dpt 1 kb 1024 bytes (9x400) 0x0000.0000 -> 0x0000.03FF -------------- -boot-ssector 5 kb 5120 bytes (0x1400) 0x0000.0400 -> 0x0000.17FF -Unallocated 90 kb 92160 bytes (0x16800) 0x0000.1800 -> 0x0001.7FFF -DragonOS 2000 kb 2048000 bytes (0x1F4000) 0x0001.8000 -> 0x0020.BFFF -Test Partition 2000 kb 2048000 bytes (0x1F4000) 0x0020.C000 -> 0x0040.0000 diff --git a/extra/dragon info/dscript_mockup_1.dsc b/extra/dragon info/dscript_mockup_1.dsc deleted file mode 100644 index dccdebc..0000000 --- a/extra/dragon info/dscript_mockup_1.dsc +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __SOME_DEFINE__ -#define __SOME_DEFINE__ 0 - -#include - - -struct SomeStruct { - //var data1; - int data2; - int data3 = 0; - string data4 = "Hello"; //string type is basically a byte array with a 0 at the end - int[10] data6; //Array - int[] data7 = { 2, 5, 7 }; //Array - int* data8; //Pointer - SomeStruct* data9 = null; -}; - -fn main(void) -> int { - let myVar: SomeStruct; - (*myVar.data8) = 12; - let myVar2: int* = myVar.data8; - myFunction(myVar2, 10); - dstd::print("The result is %d!", (*myVar2)); -} - -fn myFunction(int* num1_ptr, int num2) -> void { - (*num1_ptr) = (*num1_ptr) + num2; -} - -namespace dstd { - fn print(string fmt, var args@) -> int { - let _args_len: int = args_count(args); - let _args_counter: int = 0; - let _ctrl_char: bool = false; - let _ret_val: int = 0; - for (byte c in fmt) { - if (!_ctrl_char) { - _ret_val = __internal_dstd::__print_char_in_display_mode(__internal_dstd::__display_modes::__text_single_color, c); - continue; - } - if (c == '%') { _ctrl_char = true; continue; } - if (_ctrl_char) { - if (c == 'd') { - _ctrl_char = false; - if (_args_len < 1) { - continue; - } - let _integer: int = args_get_int(_args_counter); - _args_counter++; - _ret_val = __internal_dstd::__print_integer_in_display_mode(__internal_dstd::__display_modes::__text_single_color, _integer); - } - } - } - } -} - -#endif \ No newline at end of file diff --git a/extra/dragon info/dscript_mockup_2.dsc b/extra/dragon info/dscript_mockup_2.dsc deleted file mode 100644 index d7b37e4..0000000 --- a/extra/dragon info/dscript_mockup_2.dsc +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef __SOME_DEFINE__ -#define __SOME_DEFINE__ - -#include - -#data_alloc(128) // Total data memory allocated - -//Types: byte, word, word, string, bool - -struct SomeStruct { - var data1: byte = 0; - var data2: word; - var data3: string = "Hello"; //Basically equivalent to var data3: byte[] = { 'H', 'e', 'l', 'l', 'o', 0 } - var data4: bool = false; // and are respectively replaced with <1> and <0>, and - // is actually equivalent to - var data5: byte[] = { 4, 1, 4, 2 }; - var data6: word[10]; -} - -/** DASM Translation - - @struct SomeStruct - data1:1 > 0x00 - data2:4; - data3:6 > 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00 - data4:1 > 0x00 - data5:4 > 0x04, 0x01, 0x04, 0x02 - data6:20 - @end -**/ - -fn testFunction(var param1:word, var param2:string) -> word { - if (length == 0) { - return 0; - } else { - return param1; - } -} - -/** DASM Translation - - __fn_testFunction_2_word_byteptr__: - arg R1 ## param1 (word) - arg R2 ## param2 (string) - mov ACC, 6 - ret -**/ - -fn strlen(var str:string) -> word { - if (str == null) { return 0; } - var c:byte = str[0]; - var counter:word = 0; - while (c != 0) { - counter++; - c = str[counter]; - } - return counter; -} - -fn strlen(var str:string) -> word { - dasm { - 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 - } -} - -/** DASM Translation - - __fn_strlen_1_byteptr__: - arg R1 ## param2 (string) - mov ACC, R1 - jne $__fn_strlen_1_byteptr__branch_1_end__, 0 - __fn_strlen_1_byteptr__branch_1__: - mov RV, 0 - jmp $__fn_strlen_1_byteptr__end__ - __fn_strlen_1_byteptr__branch_1_end__: - omovb *FP, *R1, -1 ## c - omov *FP, 0x0000, -3 ## counter - __fn_strlen_1_byteptr__loop_1__: - movbo ACC, *FP, -1 - jeq, $__fn_strlen_1_byteptr__loop_1_end__, 0 - movo ACC, *FP, -3 - inc ACC - omov *FP, ACC, -3 - omovb *FP, *ACC, -1 - jmp $__fn_strlen_1_byteptr__loop_1__ - __fn_strlen_1_byteptr__loop_1_end__: - movo RV, *FP, -3 - __fn_strlen_1_byteptr__end__ - ret -**/ - -#endif \ No newline at end of file diff --git a/extra/dragon info/font_big.bmp b/extra/dragon info/font_big.bmp deleted file mode 100644 index 133414b50921e22b75a788e00b76260b2efbe90a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 439434 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 diff --git a/extra/dragon info/info b/extra/dragon info/info deleted file mode 100644 index 7a72510..0000000 --- a/extra/dragon info/info +++ /dev/null @@ -1,168 +0,0 @@ -#========================================================================================================================================== -# Memory Map -#========================================================================================================================================== - - -0x0000 BIOS (4096 Bytes) (write to this section is disabled) -0x0FFF -------- -0x1000 CMOS (128 Bytes) (write to this section is only allowed in BIOS Mode) - MemStart: (2 bytes) 0x0000 - MemSize: (2 bytes) 0x0002 - ClockSpeed: (2 bytes) 0x0004 - BootDisk: 0x0010 - - DiskList: (2 bytes) 0x007E -0x107F -------- -0x1080 INTERRUPT VECTOR (512 Bytes) - Status: 1 Byte - 0xFF: Enabled - 0x00: Disabled - Handler Address: 2 Byte - ...Repeat... -0x127F -------- -0x1280 KEYBOARD MAPPING (224 Bytes) (write to this section is only allowed in BIOS Mode) - 0x00: Modifiers (2 bytes) - 0b00000000.00000001: Left Shift - 0b00000000.00000010: Left Ctrl - 0b00000000.00000100: Left Alt - 0b00000000.00001000: Left Super - 0b00000000.00010000: Right Shift - 0b00000000.00100000: Right Control - 0b00000000.01000000: Right Alt - 0b00000000.10000000: Right Super - 0b00000001.00000000: Caps Lock - 0b00000010.00000000: Num Lock - 0b00000100.00000000: Scroll Lock - 0x02: KeyCode (2 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 - 0x07: Text Single Color - Print Buffered String - - 0xE0: Refresh Screen - 0xE1: Clear Screen - 0x04: Text Single Color Character (1 Byte) - 0x05: Text Single Color Inverted colors (1 Byte) - 0x06: (2 bytes) Text Single Color Buffered String Address -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) - 0x0B: Toggles inverted colors in Text Single Mode - - 0xE0: Refresh Screen - 0xE1: Clear Screen - - -Machine Specific - Hardware Interrupts: -======================================= -MAX INTERRUPT 0xAA -0x80: Disk Interface Finished -0xA0: Keyboard Interface - Key Pressed -0xA1: Keyboard Interface - Key Released -0xA2: Keyboard Interface - Text Entered - - - -#========================================================================================================================================== -# DPT - Partition Table -#========================================================================================================================================== - -Disk Address: 0x200 - 0x400 (512 bytes) -Header: (0x200 - 0x20B) (12 bytes) - 0x000: (2 bytes) - 0xF1CA - This number identifies this block as a DPT partition table - 0x002: (2 bytes) - DPT version - 0x004: (1 byte) - Partition count (max 5) - 0x005: (7 bytes) - Reserved -Partition Table: (0x20C - 0x3FF) (500 bytes) - Entry: (100 bytes) (x5) - 0x00: (4 bytes) - Start Address - 0x04: (4 bytes) - Size - 0x08: (2 bytes) - Flags - 0x08: (26 bytes) - Reserved - 0x24: (64 bytes) - Partition label diff --git a/extra/dragon info/regular_expressions.txt b/extra/dragon info/regular_expressions.txt deleted file mode 100644 index 51f085c..0000000 --- a/extra/dragon info/regular_expressions.txt +++ /dev/null @@ -1,10 +0,0 @@ -Regex: Javascript - -Number Constants -0x[0-9A-Fa-f]+|0b[0-1]+|(?4e-6vzL&Gw)3@r!cc8Yl;+05OV) z(y_4-g2ak0Vl8bW5qg4D=qZ#?1ub*{5_$<1dJQA=25UlZu`V=+75a#t&?)W+eZfNL zJC+Uo*vWNPUea0Y1{B^jH?D^$1e#wPA6=JnB^QNGb@KUYoJ94?Qi;<9Jk;2QSzs~;LR!j z^H$X?<^ix}yT)v9S(J9ea^);g^Td>KdBf5+rWR8J%lizw^a{g~im&DK?3s<@?}Uz? k?Ib2uwRJsUz_zqvWQ@6vn?h``PfqU|-O+Lx&SX71SyU5)4(Bt5k`V5RnNs#KIE2si3QJOI3A4 z%8)+;B(^TykQmzmu{GDfpi{RJ>;$PDtoQxivp;`!UDUA?C3L1IaN<5}t=5qg7E=slFsA+*pbNa!3!=o75a7pw?fU{z=e7P>@B=mu*- z-!T)qM^n+y&0J?iuF@cjTyu~r$Z?0WFo~F!S;u6fa0|k~S)?&aGO*iARR7_VS#$^| zO;~ydA7tuzYE98qqhP$SGNDC%gzX%S}vXmku#&pjjxBd-|Pxo!pw&<3GT` zkarl$v)N)-du2kS&x?fctcFJHD`11?mF;dYN`B7fz%ej1BfA^_v{!OvkHj7U%`*dT ryh5+9Vt02xd&lurn<0odJc^sb1+E2j46l_e_A^knD*NHOwr4AkIf!w5 diff --git a/extra/dragon/disk1.dr b/extra/dragon/disk1.dr index 41d685e1c8e7b0c75bc2a528f6d2425fee509325..38f951e2ca179d0f9ba5ee311803a7a4829e6d47 100644 GIT binary patch delta 2751 zcmeH_O-~b16o&8Iet-kW3%%p*079oAf<>jE@+nLpB%bp#JxygBQ&hE+0<9VL+{E1Xl`Y>{|ByJxP;{0kj zFGN9oQYtBgQg(oj09{cZ-O5KwyX&>9SX(TfSIN9E3hH=M7{Oi9ZbfY#o@HjXTUSb^ zV|_DSpWl6IYuCJROd++!`m$GF_RW;#iA3Bw*ixrLtW>kck6+S|%3fRCD3?E{=5o1W zVPU?QTXvk2Y_v&!y>a!~jCMVLV{LyiD)vrI>5?z3-vw9=@Lu}(5a6R;tE=l$pl<*C zuYO0}mb$O~H}JiXx_T)a!wLT&Y#?kPY#?kPY#?ml9yJiF{)xCJZuIOQVGxpt5K$sV z#EAx?k$6CuL=(|Wv=9%8RsuvD(M}`?MOefmLK8{CCOU{tqKkM;bQ3*9ig1WD(M$9Z zPl$fvDKS6{63>VXF+@BkUJx&dVIoV65TnE^;x+Mx7$e4s31X6%BBqJA#LREZUe>Mw D?~Bpb delta 373 zcmXAjO^*w40EhoGePKpdpKj-G#=bD6U8-oYY`rWaE-u1G#K}QYPEFc4Ph^l^M4Xz& z7w`>s8{fu(+q(G#t*Jfyp5!?^$uG+?H_KN@gJ@Ft)~9ZM&3n&`c^>Qy7V}cARQ@mT z-x_Brcwv%w*7ttLz887^LoW@JGZnB(&jU&17| znQ`i6`*j?ApFVZ=r(>se;5u$I+S) zqMvo)2v-zDQItek923WdCwy^2RK$unDXIcd6RRQ*17XD}5sFBh7HeW%Y=}*9Mw}I~ zXoz#-yl9G+*b*1SMbQ?QL`Pf}SHxA(6+LlHTo*UQwzw&7i5<}wx5XWCSKJf3;{FdF H{9XJ5r$Spw diff --git a/extra/dss/activeTest.dss b/extra/dss/activeTest.dss deleted file mode 100644 index b148ff9..0000000 --- a/extra/dss/activeTest.dss +++ /dev/null @@ -1,17 +0,0 @@ -.load 0x1740 - -.data - $string "Hello World!!" - -.code - mov R10, 0xE0 - int 0x30 - mov R10, 0x0C - mov R9, $string - int 0x30 - -end_loop: - jmp $end_loop - hlt - -.fixed 1024, 0x00 \ No newline at end of file diff --git a/extra/dss/bios/entry.dss b/extra/dss/bios/entry.dss index 49173bf..d2ca99c 100644 --- a/extra/dss/bios/entry.dss +++ b/extra/dss/bios/entry.dss @@ -6,6 +6,7 @@ .code 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 + mov reg(S_REG_OFFSET), 0x0000 ## Zero the offset register movb [{MemoryAddresses.INT_VEC + (0x20 * 3)}], 0xFF ## Setting up int 0x20 handler mov [{MemoryAddresses.INT_VEC + (0x20 * 3) + 1}], $_int_20_handler ## -- @@ -26,10 +27,6 @@ mov FL, ACC ## ---- - mov reg(S_REG_OFFSET), 0x0000 ## Zero the offset register - or FL, 0b0000000000000010 ## Enable offset mode for memory addressing - mov FL, ACC - %low INST_BIOS_NODE_TOGGLE 0x00 ## Disable BIOS Mode before leaving the BIOS jmp MemoryAddresses.MBR ## Jump to start of MBR in memory hlt ## Just in case somehow execution reaches this point diff --git a/extra/dss/bios_api.dss b/extra/dss/bios_api.dss index 1db7612..3abbff7 100644 --- a/extra/dss/bios_api.dss +++ b/extra/dss/bios_api.dss @@ -1,5 +1,5 @@ ## -- -## -- This file is automatically generated by the DragonAssembler (version 0.3.1595) +## -- This file is automatically generated by the DragonAssembler (version 0.3.1596) ## -- Please do not modify this file in any way. ## -- diff --git a/extra/dss/drake/bootsector.dss b/extra/dss/drake/bootsector.dss index 8a32f4b..13fbfb0 100644 --- a/extra/dss/drake/bootsector.dss +++ b/extra/dss/drake/bootsector.dss @@ -1,7 +1,11 @@ -.load [dynamic, base=0x1740] +.load 0x1740 + +@include .data $string "Hello BOOTSECTOR!!" + $ddd + $dpt_block .code mov R10, 0xE0 @@ -10,3 +14,34 @@ mov R9, $string int 0x30 + push 0 + call $_load_dpt_block + debug_break + +end_loop: + jmp $end_loop + hlt + + + +_load_dpt_block: + mov [$ddd.Mode], DiskMode.READ + mov [$ddd.Disk], 0x00 + mov [$ddd.Sector], 0x0000 + mov [$ddd.Address], 0x0200 + mov [$ddd.DataSize], 512 + mov [$ddd.MemoryAddress], $dpt_block.ID + and FL, 0b1111111111111110 ## Disable interrupts + mov FL, ACC + push $ddd.Mode + push 1 + call $_disk_load_from_ddd_blocking + or FL, 0b0000000000000001 ## Enable interrupts + mov FL, ACC + ret + + +@include + +.fixed 5120, 0xFF + diff --git a/extra/dss/drake/disk.dss b/extra/dss/drake/disk.dss index 7b9b803..9d5fc0f 100644 --- a/extra/dss/drake/disk.dss +++ b/extra/dss/drake/disk.dss @@ -41,7 +41,7 @@ Sector:2 > 0x00, 0x00 Address:2 > 0x00, 0x00 DataSize:2 > 0x00, 0x00 - MemoryAddress: > 0x00, 0x00 + MemoryAddress:2 > 0x00, 0x00 @end .code diff --git a/extra/dss/drake/dpt.dss b/extra/dss/drake/dpt.dss index 9a3be0c..e19a87c 100644 --- a/extra/dss/drake/dpt.dss +++ b/extra/dss/drake/dpt.dss @@ -1,4 +1,4 @@ -@uard _DPT_DSS_ +@guard _DPT_DSS_ @group DPTStructure DISK_ADDR 0x0200 diff --git a/extra/dss/drake/loader.dss b/extra/dss/drake/loader.dss index e674d0f..236a2f1 100644 --- a/extra/dss/drake/loader.dss +++ b/extra/dss/drake/loader.dss @@ -2,10 +2,6 @@ @include <../bios_api.dss> -@define S_REG_1 0x07 -@define S_REG_2 0x08 -@define S_REG_3 0x09 - .data $_drake_logo_line_1 " ___ ___ ___ __ ______" $_drake_logo_line_2 " / _ \/ _ \/ _ | / //_/ __/" @@ -13,25 +9,30 @@ $_drake_logo_line_4 "/____/_/|_/_/ |_/_/|_/___/ " $_logo_padding " " + $ddd + .code mov R10, 0xE0 int 0x30 push 0 call $_print_logo + + mov [$ddd.Mode], DiskMode.READ + mov [$ddd.Disk], 0x00 + mov [$ddd.Sector], 0x0000 + mov [$ddd.Address], 0x0400 + mov [$ddd.DataSize], 5120 + mov [$ddd.MemoryAddress], MemoryAddresses.RAM + and FL, 0b1111111111111110 ## Disable interrupts - mov FL, ACC - push 0 - call $_load_boot_sector + mov FL, ACC + push $ddd.Mode + push 1 + call $_disk_load_from_ddd_blocking or FL, 0b0000000000000001 ## Enable interrupts mov FL, ACC - - mov reg(S_REG_3), MemoryAddresses.RAM - inc reg(S_REG_3) - mov R1, *reg(S_REG_3) - addip R1, reg(S_REG_3) - mov *reg(S_REG_3), R1 - dec reg(S_REG_3) + jmp MemoryAddresses.RAM _infinite_loop: @@ -55,20 +56,6 @@ _print_logo_loop: jls $_print_logo_loop, R6 ret -_load_boot_sector: - movb [{MemoryAddresses.DISK_INTERFACE + 0x1}], 0x00 ## Mode = Read - movb R1, [{ MemoryAddresses.CMOS + 0x10 }] - movb [{MemoryAddresses.DISK_INTERFACE + 0x2}], R1 ## Disk = Default - mov [{MemoryAddresses.DISK_INTERFACE + 0x3}],0x0000 ## Sector - mov [{MemoryAddresses.DISK_INTERFACE + 0x5}],0x0400 ## Address - mov [{MemoryAddresses.DISK_INTERFACE + 0x7}], 1024 ## Size - mov [{MemoryAddresses.DISK_INTERFACE + 0x9}], MemoryAddresses.RAM ## MemoryAddress - movb [MemoryAddresses.DISK_INTERFACE], 0x00 ## Signal = Start -_load_boot_sector_block_wait_loop: - debug_break - mov ACC, [{MemoryAddresses.DISK_INTERFACE + 0xB}] ## Moving register into ACC - jne $_load_boot_sector_block_wait_loop, 0x00 ## If register not Free, loop around and wait - ret - +@include .fixed 512, 0x00 diff --git a/extra/info/info b/extra/info/info new file mode 100644 index 0000000..93a4db7 --- /dev/null +++ b/extra/info/info @@ -0,0 +1,219 @@ +#========================================================================================================================================== +# Memory Map +#========================================================================================================================================== + 0x0000 BIOS (4096 Bytes) (write to this section is disabled) + 0x0FFF + ------- + 0x1000 CMOS (128 Bytes) (write to this section is only allowed in BIOS Mode) + MemStart: (2 bytes) 0x0000 + MemSize: (2 bytes) 0x0002 + ClockSpeed: (2 bytes) 0x0004 + BootDisk: 0x0010 + + DiskList: (2 bytes) 0x007E + 0x107F + ------- + 0x1080 INTERRUPT VECTOR (512 Bytes) + Status: 1 Byte + 0xFF: Enabled + 0x00: Disabled + Handler Address: 2 Byte + ...Repeat... + 0x127F + ------- + 0x1280 KEYBOARD MAPPING (224 Bytes) (write to this section is only allowed in BIOS Mode) + 0x00: Modifiers (2 bytes) + 0b00000000.00000001: Left Shift + 0b00000000.00000010: Left Ctrl + 0b00000000.00000100: Left Alt + 0b00000000.00001000: Left Super + 0b00000000.00010000: Right Shift + 0b00000000.00100000: Right Control + 0b00000000.01000000: Right Alt + 0b00000000.10000000: Right Super + 0b00000001.00000000: Caps Lock + 0b00000010.00000000: Num Lock + 0b00000100.00000000: Scroll Lock + 0x02: KeyCode (2 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 + 0x07: Text Single Color - Print Buffered String + + 0xE0: Refresh Screen + 0xE1: Clear Screen + 0x04: Text Single Color Character (1 Byte) + 0x05: Text Single Color Inverted colors (1 Byte) + 0x06: (2 bytes) Text Single Color Buffered String Address + 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) + 0x0B: Toggles inverted colors in Text Single Mode + + 0xE0: Refresh Screen + 0xE1: Clear Screen + + + Machine Specific - Hardware Interrupts: + ======================================= + MAX INTERRUPT 0xAA + 0x80: Disk Interface Finished + 0xA0: Keyboard Interface - Key Pressed + 0xA1: Keyboard Interface - Key Released + 0xA2: Keyboard Interface - Text Entered + + + +#========================================================================================================================================== +# DPT - Partition Table +#========================================================================================================================================== + + Disk Address: 0x200 - 0x400 (512 bytes) + Header: (0x200 - 0x20B) (12 bytes) + 0x000: (2 bytes) - 0xF1CA - This number identifies this block as a DPT partition table + 0x002: (2 bytes) - DPT version + 0x004: (1 byte) - Partition count (max 5) + 0x005: (7 bytes) - Reserved + Partition Table: (0x20C - 0x3FF) (500 bytes) + Entry: (100 bytes) (x5) + 0x00: (4 bytes) - Start Address + 0x04: (4 bytes) - Size + 0x08: (2 bytes) - Flags + 0x08: (26 bytes) - Reserved + 0x24: (64 bytes) - Partition label + + + +#========================================================================================================================================== +# Regex +#========================================================================================================================================== + Type: Javascript + + Number Constants + 0x[0-9A-Fa-f]+|0b[0-1]+|(? 0x0000.03FF + ------------- + boot-ssector 5 kb 5120 bytes (0x1400) 0x0000.0400 -> 0x0000.17FF + Unallocated 90 kb 92160 bytes (0x16800) 0x0000.1800 -> 0x0001.7FFF + DragonOS 2000 kb 2048000 bytes (0x1F4000) 0x0001.8000 -> 0x0020.BFFF + Test Partition 2000 kb 2048000 bytes (0x1F4000) 0x0020.C000 -> 0x0040.0000 diff --git a/extra/info/todo.txt b/extra/info/todo.txt new file mode 100644 index 0000000..7958361 --- /dev/null +++ b/extra/info/todo.txt @@ -0,0 +1,26 @@ +Assembler: + Add evaluation of direct struct instance name as as struct's first member + Add Struct export functionality + Add single Define export functionality + Add subroutine addres export functionality + +Bugs: + Fix VM closing when reaching hlt instruction + +Refactor: + Move all possible default values into data::DefaultValues class (in ) + +External: + Redo script to create windows Release + + + +Done: + *Inverted Colors in Text-Single-Color + *show where interrupts are disabled (in call-tree view) + *Text visualization for hardware interrupts (in call-tree view) + *Add possibility to specify instruction sets in machine config + *Add possibility to specify required instruction sets in dasm + *Add "Extended mov" instruction set + *Remove old offset mov + *Implelemnt extmov mnemonics in dasm: \ No newline at end of file diff --git a/extra/make_and_debug b/extra/make_and_debug index 6ee9871..b8e5a76 100755 --- a/extra/make_and_debug +++ b/extra/make_and_debug @@ -15,13 +15,9 @@ if [ $? -eq 0 ]; then cd scripts /bin/bash ./bios_flash cp ../dss/bios_api.dss ../../extra/dss/bios_api.dss - /bin/bash ./load_mbr + /bin/bash ./build_drake cd .. - printf "${green}Compiling Test Program...\n" - ./dasm dss/activeTest.dss -o activeTest.bin --extmov --extalu --save-disassembly disassembly/activeTest.dds --verbose printf "\n${green}Running Application...\n\n${clear}" - ./dtools load-binary dragon/disk1.dr activeTest.bin 0x00000400 - # ./ddb config/testMachine.dvm --force-load activeTest.bin 0x00 --verbose-load ./ddb config/testMachine.dvm --verbose-load cp dragon/disk1.dr ../extra/dragon/disk1.dr cp dragon/cmos.dr ../extra/dragon/cmos.dr diff --git a/extra/make_and_run b/extra/make_and_run index 4605bc7..4c67165 100755 --- a/extra/make_and_run +++ b/extra/make_and_run @@ -15,14 +15,10 @@ if [ $? -eq 0 ]; then cd scripts /bin/bash ./bios_flash cp ../dss/bios_api.dss ../../extra/dss/bios_api.dss - /bin/bash ./load_mbr + /bin/bash ./build_drake cd .. - printf "${green}Compiling Test Program...\n" - ./dasm dss/activeTest.dss -o activeTest.bin --extmov --extalu --save-disassembly disassembly/activeTest.dds --verbose printf "\n${green}Running Application...\n\n${clear}" - ./dtools load-binary dragon/disk1.dr activeTest.bin 0x00000400 ./dvm config/testMachine.dvm - # ./dvm config/testMachine.dvm --force-load activeTest.bin 0x00 cp dragon/disk1.dr ../extra/dragon/disk1.dr cp dragon/cmos.dr ../extra/dragon/cmos.dr fi diff --git a/extra/scripts/build_drake b/extra/scripts/build_drake new file mode 100644 index 0000000..4e7ce0e --- /dev/null +++ b/extra/scripts/build_drake @@ -0,0 +1,23 @@ +#!/bin/bash + +green='\033[0;32m' +clear='\033[0m' + +printf "\n${green}Compiling Drake...\n${clear}" +cd .. +cp ../extra/dss/drake/* ./dss/drake/ + +printf "\n${green}1) loader.dss\n${clear}" +./dasm dss/drake/loader.dss -o dragon/drake_loader.bin --extalu --save-disassembly disassembly/drake_loader.dds --verbose +./dtools load-binary dragon/disk1.dr dragon/drake_loader.bin 0x00000000 + +printf "\n${green}2) bootsector.dss\n${clear}" +./dasm dss/drake/bootsector.dss -o dragon/drake_bootsector.bin --extalu --save-disassembly disassembly/drake_bootsector.dds --verbose +./dtools load-binary dragon/disk1.dr dragon/drake_bootsector.bin 0x00000400 + +printf "\n" + + +cp dragon/disk1.dr ../extra/dragon/disk1.dr +rm dragon/drake_loader.bin +rm dragon/drake_bootsector.bin diff --git a/extra/scripts/load_mbr b/extra/scripts/load_mbr deleted file mode 100644 index d6e376c..0000000 --- a/extra/scripts/load_mbr +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -green='\033[0;32m' -clear='\033[0m' - -printf "\n${green}Reloading MBR Block...\n${clear}" -cd .. -cp ../extra/dss/drake/* ./dss/drake/ -./dasm dss/drake/loader.dss -o dragon/drake_loader.bin --extalu --save-disassembly disassembly/drake_loader.dds --verbose -./dtools load-binary dragon/disk1.dr dragon/drake_loader.bin 0x00000000 -printf "\n" -cp dragon/disk1.dr ../extra/dragon/disk1.dr -rm dragon/drake_loader.bin diff --git a/extra/scripts/run-test-1.bat b/extra/scripts/run-test-1.bat deleted file mode 100644 index a1e9d8c..0000000 --- a/extra/scripts/run-test-1.bat +++ /dev/null @@ -1,2 +0,0 @@ -dasm dss/tests/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 deleted file mode 100644 index 918bb9a..0000000 --- a/extra/scripts/run-test-2.bat +++ /dev/null @@ -1,2 +0,0 @@ -dasm dss/tests/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/src/hardware/VirtualIODevices.cpp b/src/hardware/VirtualIODevices.cpp index c3cdd4b..503728c 100644 --- a/src/hardware/VirtualIODevices.cpp +++ b/src/hardware/VirtualIODevices.cpp @@ -44,13 +44,13 @@ namespace dragon int8_t VirtualBIOS::write8(uint16_t addr, int8_t value) { - data::ErrorHandler::pushError(data::ErrorCodes::BIOS_WriteAttempt, "Attempting to write to BIOS memory map."); + data::ErrorHandler::pushError(data::ErrorCodes::BIOS_WriteAttempt, ostd::String("Attempting to write to BIOS memory map.").add(addr)); return 0x00; } int16_t VirtualBIOS::write16(uint16_t addr, int16_t value) { - data::ErrorHandler::pushError(data::ErrorCodes::BIOS_WriteAttempt, "Attempting to write to BIOS memory map."); + data::ErrorHandler::pushError(data::ErrorCodes::BIOS_WriteAttempt, ostd::String("Attempting to write to BIOS memory map.").add(addr)); return 0x0000; } diff --git a/src/hardware/VirtualMMU.cpp b/src/hardware/VirtualMMU.cpp new file mode 100644 index 0000000..a36d5ee --- /dev/null +++ b/src/hardware/VirtualMMU.cpp @@ -0,0 +1,14 @@ +#include "VirtualMMU.hpp" + +namespace dragon +{ + namespace hw + { + VirtualMMU& VirtualMMU::init(void) + { + m_data.init(VirtualMMU::MMUSize); + m_data.enableAutoResize(false); + return *this; + } + } +} \ No newline at end of file diff --git a/src/hardware/VirtualMMU.hpp b/src/hardware/VirtualMMU.hpp new file mode 100644 index 0000000..f0b42c6 --- /dev/null +++ b/src/hardware/VirtualMMU.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace dragon +{ + namespace hw + { + class VirtualMMU //TODO: Implement for later use + { + public: + inline VirtualMMU(void) { init(); } + VirtualMMU& init(void); + + private: + ostd::serial::SerialIO m_data; + + public: + inline static constexpr uint16_t MMUSize = 6144; + inline static constexpr uint16_t PageSize = 512; + }; + } +} \ No newline at end of file diff --git a/src/hardware/VirtualRAM.cpp b/src/hardware/VirtualRAM.cpp index 3c87002..986af3b 100644 --- a/src/hardware/VirtualRAM.cpp +++ b/src/hardware/VirtualRAM.cpp @@ -10,6 +10,7 @@ namespace dragon VirtualRAM::VirtualRAM(void) { m_memory.init(0xFFFF); + m_memory.enableAutoResize(false); } int8_t VirtualRAM::read8(uint16_t addr) diff --git a/src/runtime/ConfigLoader.cpp b/src/runtime/ConfigLoader.cpp index 9e9e2bc..46c5a55 100644 --- a/src/runtime/ConfigLoader.cpp +++ b/src/runtime/ConfigLoader.cpp @@ -89,6 +89,19 @@ namespace dragon config.fixed_clock = false; else continue; //TODO: Error } + else if (lineEdit == "memory_extension_pages") + { + lineEdit = tokens.next(); + lineEdit.trim().toLower(); + if (!lineEdit.isNumeric()) continue; //TODO: Error + config.memory_extension_pages = lineEdit.toInt(); + + //TODO: Warnings + if (config.memory_extension_pages < 0) + config.memory_extension_pages = 0; + if (config.memory_extension_pages > data::DefaultValues::MaxMemoryExtensionPages) + config.memory_extension_pages = data::DefaultValues::MaxMemoryExtensionPages; + } else continue; //TODO: Warning } return validate_machine_config(config); diff --git a/src/runtime/ConfigLoader.hpp b/src/runtime/ConfigLoader.hpp index f9a8ad8..3c5108d 100644 --- a/src/runtime/ConfigLoader.hpp +++ b/src/runtime/ConfigLoader.hpp @@ -11,6 +11,7 @@ namespace dragon std::map vdisk_paths; std::map cpuext_list; int32_t clock_rate_sec { 500 }; + uint8_t memory_extension_pages { 0 }; bool fixed_clock { true }; ostd::String bios_path; ostd::String cmos_path; diff --git a/src/tools/GlobalData.hpp b/src/tools/GlobalData.hpp index 58e2f16..bccc62b 100644 --- a/src/tools/GlobalData.hpp +++ b/src/tools/GlobalData.hpp @@ -227,7 +227,6 @@ namespace dragon inline static constexpr uint8_t Boot = 0; }; public: - inline static constexpr uint16_t DiskAddress = 0x200; inline static constexpr uint16_t DPTID = 0x000; inline static constexpr uint16_t DPTVersionMaj = 0x002; @@ -241,8 +240,20 @@ namespace dragon inline static constexpr uint16_t EntryFlags = 0x008; inline static constexpr uint16_t EntryPartitionLabel = 0x024; + + + inline static constexpr uint16_t HeaderReservedSizeBytes= 7; + inline static constexpr uint16_t DiskAddress = 0x200; inline static constexpr uint16_t DPT_ID_CODE = 0xF1CA; inline static constexpr uint16_t EntrySizeBytes = 100; + inline static constexpr uint16_t EntryLabelSizeBytes = 64; + inline static constexpr uint16_t EntryReservedSizeBytes = 26; + inline static constexpr uint16_t HeaderSizeBytes = 12; + inline static constexpr uint16_t DPTBlockSizeBytes = 512; + inline static constexpr uint8_t CurrentDPTVersionMaj = 0x00; + inline static constexpr uint8_t CurrentDPTVersionMin = 0x02; + inline static constexpr uint32_t DiskStartAddr = 0x00000400; + inline static constexpr uint8_t MaxPartCount = 5; }; class CPUExtension @@ -362,5 +373,11 @@ namespace dragon static ostd::String getOpCodeString(uint8_t opCode); static uint8_t getInstructionSIze(uint8_t opCode); }; + + class DefaultValues + { + public: + inline static constexpr uint8_t MaxMemoryExtensionPages = 255; + }; } } \ No newline at end of file diff --git a/src/tools/Tools.cpp b/src/tools/Tools.cpp index 2728acf..ccd04de 100644 --- a/src/tools/Tools.cpp +++ b/src/tools/Tools.cpp @@ -45,6 +45,12 @@ namespace dragon if (rValue != ErrorNoError) return rValue; } + else if (tool == "new-dpt") + { + rValue = tool_new_dpt(argc, argv); + if (rValue != ErrorNoError) + return rValue; + } else if (tool == "--help") { print_application_help(); @@ -151,7 +157,7 @@ namespace dragon return ErrorReadDPTSmallDisk; } ostd::ByteStream outData; - if (!vHDD.read(data::DPTStructure::DiskAddress, 512, outData)) + if (!vHDD.read(data::DPTStructure::DiskAddress, data::DPTStructure::DPTBlockSizeBytes, outData)) { out.fg(ostd::ConsoleColors::Red).p("Error: Unable to read from disk.").reset().nl(); return ErrorReadDPTUnableToRead; @@ -175,6 +181,11 @@ namespace dragon uint32_t version_min = (uint32_t)outData8; dpt_block.r_Byte(data::DPTStructure::PartitionCount, outData8); uint32_t part_count = (uint32_t)outData8; + if (part_count > data::DPTStructure::MaxPartCount) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Too many partitions. Maximum is ").p(data::DPTStructure::MaxPartCount).p(".").reset().nl(); + return ErrorReadDPTNoPartitionTable; + } struct tPartitionData { uint32_t startAddress { 0 }; uint32_t size { 0 }; @@ -222,7 +233,6 @@ namespace dragon out.p(ostd::String("=").new_fixedLength(5 * len, '=')).nl(); for (int32_t i = 0; i < partitionList.size(); i++) { - bool comma = false; auto& part = partitionList[i]; if (part.label == "") part.label = ""; @@ -231,13 +241,18 @@ namespace dragon print_part_size(part.size, out, len); out.p(ostd::Utils::getHexStr(part.startAddress, true, 4).new_fixedLength(len)); out.p(ostd::Utils::getHexStr(part.startAddress + part.size, true, 4).new_fixedLength(len)); - if (ostd::Bits::get(part.flags, data::DPTStructure::tFlags::Boot)) + ostd::String flags_str = ""; + for (uint8_t bit = 0; bit < sizeof(part.flags) * 8; bit++) { - if (comma) - out.p(", "); - out.fg(ostd::ConsoleColors::Yellow).p("Boot").fg(ostd::ConsoleColors::Cyan); + if (m_dpt_flags_str.count(bit) == 0) + continue; + if (ostd::Bits::get(part.flags, bit)) + flags_str += m_dpt_flags_str[bit] + ","; } - out.nl(); + if (flags_str.len() > 0) + flags_str.substr(0, flags_str.len() - 1); + + out.fg(ostd::ConsoleColors::Yellow).p(flags_str).fg(ostd::ConsoleColors::Cyan).nl(); } out.fg(ostd::ConsoleColors::BrightGray); out.p(ostd::String("=").new_fixedLength(5 * len, '=')).nl(); @@ -245,6 +260,188 @@ namespace dragon return ErrorNoError; } + int32_t Tools::tool_new_dpt(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 new-dpt -p -s SIZE [-l LABEL] [-f FLAG1] [-f FLAG2] [-p SIZE ...]").reset().nl(); + return ErrorLoadProgTooFewArgs; + } + ostd::String vdisk_file = argv[2]; + 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; + } + uint64_t disk_size = vHDD.getSize(); + + auto& _dpt_flags_str = m_dpt_flags_str; + auto get_flag_from_str = [_dpt_flags_str](const ostd::String& flag_str) -> int8_t { + for (auto& flag : _dpt_flags_str) + { + if (flag.second == flag_str) + return flag.first; + } + return -1; + }; + auto make_bytestream = [](uint16_t size, ostd::Byte value = 0xFF) -> ostd::ByteStream { + + ostd::ByteStream stream; + for (int16_t i = 0; i < size; i++) + stream.push_back(value); + return stream; + }; + struct tPartData { + uint32_t size { 0 }; + uint32_t address { 0 }; + std::vector flags; + ostd::String label { "" }; + }; + + std::vector partitions; + + int32_t arg_index = 3; + bool has_args = true; + bool part_started = false; + tPartData _part_data; + uint32_t part_start_addr = data::DPTStructure::DiskStartAddr; + while (has_args) + { + ostd::String arg = argv[arg_index]; + arg.trim(); + if (part_started) + { + if (arg.new_toLower() == "-p") + { + part_started = false; + partitions.push_back(_part_data); + _part_data = tPartData(); + continue; + } + else if (arg.new_toLower() == "-l") + { + if (arg_index >= argc - 1) + { + out.fg(ostd::ConsoleColors::Red).p("Error: No partition label specified after -l parameter.").reset().nl(); + return ErrorNewDPTNoPartitionLabel; + } + arg_index++; + _part_data.label = argv[arg_index]; + } + else if (arg.new_toLower() == "-f") + { + if (arg_index >= argc - 1) + { + out.fg(ostd::ConsoleColors::Red).p("Error: No partition flag specified after -f parameter.").reset().nl(); + return ErrorNewDPTNoPartitionFlag; + } + arg_index++; + int8_t flag = get_flag_from_str(argv[arg_index]); + if (flag < 0) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Unknown partition flag.").reset().nl(); + return ErrorNewDPTUnknownFlag; + } + _part_data.flags.push_back(flag); + } + } + else + { + if (arg.new_toLower() == "-p") + { + if (arg_index >= argc - 1) + { + out.fg(ostd::ConsoleColors::Red).p("Error: No partition size specified.").reset().nl(); + return ErrorNewDPTNoPartitionSize; + } + else if (!ostd::Utils::isInt(argv[arg_index + 1])) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Partition size must be an integer.").reset().nl(); + return ErrorNewDPTInvalidPartitionSize; + } + arg_index++; + uint32_t part_size = ostd::String(argv[arg_index]).toInt(); + if (part_start_addr + part_size > disk_size) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Not enough space on disk.").reset().nl(); + return ErrorNewDPTDiskOverflow; + } + _part_data.size = part_size; + _part_data.address = part_start_addr; + part_start_addr += part_size; + part_started = true; + } + } + arg_index++; + if (arg_index >= argc || partitions.size() > data::DPTStructure::MaxPartCount) + has_args = false; + } + if (part_started) + partitions.push_back(_part_data); + + if (partitions.size() > data::DPTStructure::MaxPartCount) + { + out.fg(ostd::ConsoleColors::Red).p("Error: Too many partitions.").reset().nl(); + return ErrorNewDPTTooManyPartitions; + } + + //HEADER + ostd::StreamIndex addr = 0; + ostd::serial::SerialIO dpt_block(data::DPTStructure::DPTBlockSizeBytes); + dpt_block.enableAutoResize(false); + dpt_block.w_Word(addr, data::DPTStructure::DPT_ID_CODE); + addr += ostd::tTypeSize::WORD; + dpt_block.w_Byte(addr, data::DPTStructure::CurrentDPTVersionMaj); + addr += ostd::tTypeSize::BYTE; + dpt_block.w_Byte(addr, data::DPTStructure::CurrentDPTVersionMin); + addr += ostd::tTypeSize::BYTE; + dpt_block.w_Byte(addr, (ostd::Byte)(partitions.size())); + addr += ostd::tTypeSize::BYTE; + uint16_t reserved_size = data::DPTStructure::HeaderReservedSizeBytes; + dpt_block.w_ByteStream(addr, make_bytestream(reserved_size), false); + addr += reserved_size; + + //PARTITIONS + for (auto& part : partitions) + { + dpt_block.w_DWord(addr, part.address); + addr += ostd::tTypeSize::DWORD; + dpt_block.w_DWord(addr, part.size); + addr += ostd::tTypeSize::DWORD; + + ostd::BitField_16 flags; + flags.value = 0; + for (auto& bit : part.flags) + ostd::Bits::set(flags, bit); + dpt_block.w_Word(addr, flags.value); + addr += ostd::tTypeSize::WORD; + + reserved_size = data::DPTStructure::EntryReservedSizeBytes; + dpt_block.w_ByteStream(addr, make_bytestream(reserved_size), false); + addr += reserved_size; + + if (part.label.len() >= data::DPTStructure::EntryLabelSizeBytes) + part.label.fixedLength(data::DPTStructure::EntryLabelSizeBytes - 1, ' ', ""); + dpt_block.w_String(addr, part.label, false, true); + addr += data::DPTStructure::EntryLabelSizeBytes; + } + + int16_t index = 0; + for (auto& b : dpt_block.getData()) + { + vHDD.write(data::DPTStructure::DiskAddress + index, b); + index++; + } + vHDD.unmount(); + out.nl().fg(ostd::ConsoleColors::Green).p("Success. DPT Block created on Virtual Disk:").nl(); + out.p(" Disk Path: ").p(vdisk_file.cpp_str()).nl(); + out.p(" DPT Block Address: ").p(ostd::Utils::getHexStr(data::DPTStructure::DiskAddress, true, 4).cpp_str()).nl(); + out.p(" DPT Block Size: ").p(data::DPTStructure::DPTBlockSizeBytes).nl(); + return ErrorNoError; + } + void Tools::print_application_help(void) { out.nl().fg(ostd::ConsoleColors::Yellow).p("List of available tools:").nl().nl(); @@ -264,6 +461,14 @@ namespace dragon out.fg(ostd::ConsoleColors::Green).p("The tool is used to read the partition table of a Virtual Disk File.").nl(); out.p(" Path to the Virtual Disk file.").nl().nl(); + out.fg(ostd::ConsoleColors::Blue).p("new-dpt -p -s SIZE [-l LABEL] [-f FLAG1] [-f FLAG2] [-p SIZE ...]").nl(); + out.fg(ostd::ConsoleColors::Green).p("The tool is used to create a new DPT-BLOCK on Virtual Disk File.").nl(); + out.p(" Path to the Virtual Disk file.").nl(); + out.p(" -p Used to start creating a partition.").nl(); + out.p(" -s Used to specify the partition's size.").nl(); + out.p(" -l (optional) Used to specify the partition's label.").nl(); + out.p(" -f (optional) Used to specify one single flag for the partition. Use multiple -f parameters for multiple flags.").nl().nl(); + out.fg(ostd::ConsoleColors::Magenta).p("Usage: ./dtools [...arguments...]").nl().nl().reset(); } diff --git a/src/tools/Tools.hpp b/src/tools/Tools.hpp index 65a2a35..4585dc7 100644 --- a/src/tools/Tools.hpp +++ b/src/tools/Tools.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "GlobalData.hpp" namespace dragon { @@ -15,12 +16,17 @@ namespace dragon static int32_t tool_new_virtual_disk(int argc, char** argv); static int32_t tool_load_binary(int argc, char** argv); static int32_t tool_read_dpt(int argc, char** argv); + static int32_t tool_new_dpt(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; + inline static std::unordered_map m_dpt_flags_str { + { data::DPTStructure::tFlags::Boot, "boot" } + }; + public: inline static constexpr int32_t ErrorNoError = 0; inline static constexpr int32_t ErrorTopLevelTooFewArgs = 1; @@ -37,6 +43,13 @@ namespace dragon inline static constexpr int32_t ErrorReadDPTSmallDisk = 12; inline static constexpr int32_t ErrorReadDPTUnableToRead = 13; inline static constexpr int32_t ErrorReadDPTNoPartitionTable = 14; + inline static constexpr int32_t ErrorNewDPTNoPartitionSize = 15; + inline static constexpr int32_t ErrorNewDPTInvalidPartitionSize = 16; + inline static constexpr int32_t ErrorNewDPTNoPartitionLabel = 17; + inline static constexpr int32_t ErrorNewDPTNoPartitionFlag = 18; + inline static constexpr int32_t ErrorNewDPTDiskOverflow = 19; + inline static constexpr int32_t ErrorNewDPTUnknownFlag = 20; + inline static constexpr int32_t ErrorNewDPTTooManyPartitions = 21; }; } \ No newline at end of file