diff --git a/.vscode/settings.json b/.vscode/settings.json index 61116ba..18b5f5e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "C_Cpp.errorSquiggles": "Enabled", + "C_Cpp.errorSquiggles": "enabled", "files.associations": { "functional": "cpp", "thread": "cpp", @@ -105,7 +105,7 @@ "tab.inactiveBackground": "#000", "tab.activeBackground": "#1b1b1b", "tab.activeBorder": "#bb5400", - "editorIndentGuide.background": "#252525", + "editorIndentGuide.background1": "#252525", "editorWhitespace.foreground": "#683700", "editor.lineHighlightBorder": "#1a1a1a", "editor.lineHighlightBackground": "#490050", diff --git a/CMakeLists.txt b/CMakeLists.txt index f2969fa..ed6b729 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ list(APPEND RUNTIME_SOURCE_FILES ${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/hardware/CPUExtensions.cpp ${CMAKE_CURRENT_LIST_DIR}/src/tools/Utils.cpp ) @@ -57,6 +58,7 @@ list(APPEND DEBUGGER_SOURCE_FILES ${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/hardware/CPUExtensions.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 6ad20cc..b1e142d 100644 --- a/build.nr +++ b/build.nr @@ -1 +1 @@ -1581 +1584 diff --git a/extra/config/testMachine.dvm b/extra/config/testMachine.dvm index 90cb646..c3297a4 100644 --- a/extra/config/testMachine.dvm +++ b/extra/config/testMachine.dvm @@ -1,5 +1,7 @@ Disks = dragon/disk1.dr Bios = dragon/bios.bin CMOS = dragon/cmos.dr +cpuext = extmov + 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 index eaa340f..dabf942 100644 --- a/extra/dragon info/DragonVM Information Sheet +++ b/extra/dragon info/DragonVM Information Sheet @@ -3,10 +3,10 @@ #========================================================================================================================================== -0x0000 BIOS (4096 Bytes) +0x0000 BIOS (4096 Bytes) (write to this section is disabled) 0x0FFF ------- -0x1000 CMOS (128 Bytes) +0x1000 CMOS (128 Bytes) (write to this section is only allowed in BIOS Mode) BootDisk: 0x0010 0x107F ------- @@ -18,7 +18,20 @@ ...Repeat... 0x127F ------- -0x1280 KEYBOARD MAPPING (224 Bytes) +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) @@ -119,4 +132,8 @@ BIOS Specific - Software Interrupts: Machine Specific - Hardware Interrupts: ======================================= -0x80: Disk Interface Finished \ No newline at end of file +MAX INTERRUPT 0xAA +0x80: Disk Interface Finished +0xA0: Keyboard Interface - Key Pressed +0xA1: Keyboard Interface - Key Released +0xA2: Keyboard Interface - Text Entered \ No newline at end of file diff --git a/extra/dragon info/dscript_mockup_1.dsc b/extra/dragon info/dscript_mockup_1.dsc new file mode 100644 index 0000000..dccdebc --- /dev/null +++ b/extra/dragon info/dscript_mockup_1.dsc @@ -0,0 +1,57 @@ +#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 new file mode 100644 index 0000000..d7b37e4 --- /dev/null +++ b/extra/dragon info/dscript_mockup_2.dsc @@ -0,0 +1,102 @@ +#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/todo.txt b/extra/dragon info/todo.txt new file mode 100644 index 0000000..10d4878 --- /dev/null +++ b/extra/dragon info/todo.txt @@ -0,0 +1,51 @@ +Text visualization for hardware interrupts (in call-tree view) +Inverted Colors in Text-Single-Color +show where interrupts are disabled (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 Instructions: + + ## Offset on first operand + omov *R1, 0xFAAB, 4 ## Move word immediate into (deref reg + immediate offset word) + omov *R1, 0xFAAB, R10 ## Move word immediate into (deref reg + reg offset) + omovb *R1, 0xFA, 4 ## Move byte immediate into (deref reg + immediate offset word) + omovb *R1, 0xFA, R10 ## Move byte immediate into (deref reg + reg offset) + omov *R1, *R2, 4 ## Move word deref reg into (deref reg + immediate offset word) + omov *R1, *R2, R10 ## Move word deref reg into (deref reg + reg offset) + omovb *R1, *R2, 4 ## Move byte deref reg into (deref reg + immediate offset word) + omovb *R1, *R2, R10 ## Move byte deref reg into (deref reg + reg offset) + + omov R1, [0xFAAB], 4 ## Move word Memory into (reg + immediate offset word) + omov R1, [0xFAAB], R10 ## Move word Memory into (reg + reg offset) + omovb R1, [0xFAAB], 4 ## Move byte Memory into (reg + immediate offset word) + omovb R1, [0xFAAB], R10 ## Move byte Memory into (reg + reg offset) + omov R1, *R2, 4 ## Move word deref reg into (reg + immediate offset word) + omov R1, *R2, R10 ## Move word deref reg into (reg + reg offset) + omovb R1, *R2, 4 ## Move byte deref reg into (reg + immediate offset word) + omovb R1, *R2, R10 ## Move byte deref reg into (reg + reg offset) + + omov [0x1800], 0xACAB, 4 ## Move word immediate into (Memory + immediate offset word) + omov [0x1800], 0xACAB, R10 ## Move word immediate into (Memory + reg offset) + omovb [0x1800], 0xAC, 4 ## Move byte immediate into (Memory + immediate offset word) + omovb [0x1800], 0xAC, R10 ## Move byte immediate into (Memory + reg offset) + + + ## Offset on second operand + movo *R1, *R2, 4 ## Move word (deref reg + immediate offset word) into deref reg + movo *R1, *R2, R10 ## Move word (deref reg + reg offset) into deref reg + movbo *R1, *R2, 4 ## Move byte (deref reg + immediate offset word) into deref reg + movbo *R1, *R2, R10 ## Move byte (deref reg + reg offset) into deref reg + + movo R1, [0xFAAB], 4 ## Move word (Memory + immediate offset word) into reg + movo R1, [0xFAAB], R10 ## Move word (Memory + reg offset) into reg + movbo R1, [0xFAAB], 4 ## Move byte (Memory + immediate offset word) into reg + movbo R1, [0xFAAB], R10 ## Move byte (Memory + reg offset) into reg + movo R1, *R2, 4 ## Move word (deref reg + immediate offset word) into reg + movo R1, *R2, R10 ## Move word (deref reg + reg offset) into reg + movbo R1, *R2, 4 ## Move byte (deref reg + immediate offset word) into reg + movbo R1, *R2, R10 ## Move byte (deref reg + reg offset) into reg + + ## Plus every (immediate offset) variant with byte offset instead of word offset \ No newline at end of file diff --git a/extra/dragon/bios.bin b/extra/dragon/bios.bin index e0fb35a..61fa67b 100644 Binary files a/extra/dragon/bios.bin and b/extra/dragon/bios.bin differ diff --git a/extra/dss/bios/data.dss b/extra/dss/bios/data.dss index 72874a7..cf8d684 100644 --- a/extra/dss/bios/data.dss +++ b/extra/dss/bios/data.dss @@ -41,6 +41,8 @@ @define S_REG_1 0x07 @define S_REG_2 0x08 @define S_REG_3 0x09 + +@define INST_BIOS_NODE_TOGGLE 0x02 ## =============================================================================================== .data diff --git a/extra/dss/bios/entry.dss b/extra/dss/bios/entry.dss index 7d4b69b..538c2c2 100644 --- a/extra/dss/bios/entry.dss +++ b/extra/dss/bios/entry.dss @@ -41,6 +41,7 @@ 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/newTest.dss b/extra/dss/newTest.dss index cd39984..b8881a5 100644 --- a/extra/dss/newTest.dss +++ b/extra/dss/newTest.dss @@ -10,6 +10,23 @@ $string "Hello World!!" .code + debug_break + %low 0xE0 0x01 + mov R10, 0x00 + mov R9, $_text_entered_handler + mov R8, 0xA2 + int 0x20 + mov R10, 0x00 + mov R9, $_key_pressed_handler + mov R8, 0xA0 + int 0x20 + + + mov R10, 0x01 + mov R9, 62 + int 0x30 + + mov R1, 0 ## Zero the counter infinite_loop: inc R1 ## Increment the counter @@ -17,32 +34,57 @@ infinite_loop: 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 + ##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 + ##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 + ##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 + ##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 + ##int 0x30 ## BIOS Video Interrupt jmp $infinite_loop ## jump to the beginning of infinite loop hlt +_text_entered_handler: + mov R9, [0x1282] + mov R10, 0x01 + int 0x30 + rti + +_key_pressed_handler: + mov ACC, [0x1282] + jeq $_key_pressed_handler_backspace, 8 + jne $_key_pressed_handler_end, 13 + mov R10, 0x02 + int 0x30 + mov R10, 0x01 + mov R9, 62 + int 0x30 + jmp $_key_pressed_handler_end +_key_pressed_handler_backspace: + debug_break + mov R9, ACC + mov R10, 0x01 + int 0x30 +_key_pressed_handler_end: + rti + + .fixed 512, 0x00 \ No newline at end of file diff --git a/src/assembler/Assembler.cpp b/src/assembler/Assembler.cpp index ec228a4..2799aa9 100644 --- a/src/assembler/Assembler.cpp +++ b/src/assembler/Assembler.cpp @@ -852,6 +852,25 @@ namespace dragon m_labelTable["$" + lineEdit].address = m_dataSize + m_loadAddress + m_code.size() + 3; continue; } + else if (lineEdit.startsWith("%low ")) //Low level code injection + { + _disassembly_line.addr = m_dataSize + m_loadAddress + m_code.size() + 3; + lineEdit.substr(4).trim(); + auto tok = lineEdit.tokenize(); + for (auto& token : tok) + { + if (!token.isNumeric()) + { + std::cout << "Invalid code in .low directive. Must be numeric byte strem, space-separated: " << line << "\n"; + return; + } + m_code.push_back((int8_t)token.toInt()); + } + _disassembly_line.code = lineEdit; + _disassembly_line.code.add(" (%low)"); + m_disassembly.push_back(_disassembly_line); + continue; + } else if (commaCount == 0 && spaceCount <= 0) //0 Operands { _disassembly_line.addr = m_dataSize + m_loadAddress + m_code.size() + 3; diff --git a/src/debugger/Debugger.cpp b/src/debugger/Debugger.cpp index 24f6e84..65f9bbf 100644 --- a/src/debugger/Debugger.cpp +++ b/src/debugger/Debugger.cpp @@ -136,6 +136,7 @@ namespace dragon rgxrstr.fg("\\{|\\}|\\+|\\*|\\-|\\/|\\(|\\)|\\[|\\]", "Red"); //Operators rgxrstr.fg("0x[0-9A-Fa-f]+|0b[0-1]+|(? +#include namespace dragon { namespace hw { - class IMemoryDevice + class IMemoryDevice : public ostd::BaseObject { public: virtual int8_t read8(uint16_t addr) = 0; diff --git a/src/hardware/VirtualCPU.cpp b/src/hardware/VirtualCPU.cpp index 4768678..a7f4aee 100644 --- a/src/hardware/VirtualCPU.cpp +++ b/src/hardware/VirtualCPU.cpp @@ -5,6 +5,8 @@ #include #include +#include "../runtime/DragonRuntime.hpp" + namespace dragon { namespace hw @@ -13,6 +15,9 @@ namespace dragon { writeRegister(data::Registers::SP, (uint16_t)(0xFFFF - 1)); writeRegister(data::Registers::FP, (uint16_t)(0xFFFF - 1)); + + for (int32_t i = 0; i < 16; i++) + m_extensions[i] = nullptr; } int16_t VirtualCPU::readRegister(uint8_t reg) @@ -31,6 +36,7 @@ namespace dragon int8_t VirtualCPU::fetch8(void) { uint16_t nextInstAddr = readRegister(data::Registers::IP); + m_currentAddr = nextInstAddr; int8_t inst = m_memory.read8(nextInstAddr); writeRegister(data::Registers::IP, nextInstAddr + 1); return inst; @@ -39,6 +45,7 @@ namespace dragon int16_t VirtualCPU::fetch16(void) { uint16_t nextInstAddr = readRegister(data::Registers::IP); + m_currentAddr = nextInstAddr; int16_t inst = m_memory.read16(nextInstAddr); writeRegister(data::Registers::IP, nextInstAddr + 2); return inst; @@ -143,27 +150,53 @@ namespace dragon writeRegister(data::Registers::FL, m_tempFlags.value); } - void VirtualCPU::handleInterrupt(uint8_t intValue) + void VirtualCPU::handleInterrupt(uint8_t intValue, bool hardware) { uint16_t entryPointer = data::MemoryMapAddresses::IntVector_Start + (intValue * 3); uint8_t interruptStatus = m_memory.read8(entryPointer); if (interruptStatus != 0xFF) return; uint16_t handlerAddress = m_memory.read16(entryPointer + 1); - if (!m_isInInterruptHandler) - { - pushToStack(0); - pushStackFrame(); - } - m_isInInterruptHandler = true; + pushToStack(0); + pushStackFrame(); + m_subroutineCounter++; + m_interruptHandlerCount++; writeRegister(data::Registers::IP, handlerAddress); + if (m_debugModeEnabled && hardware) + { + DragonRuntime::tCallInfo interruptData; + interruptData.info = "HW INT"; + interruptData.addr = intValue; + interruptData.inst_addr = 0x0000; + ostd::SignalHandler::emitSignal(DragonRuntime::SignalListener::Signal_HardwareInterruptOccurred, ostd::tSignalPriority::RealTime, interruptData); + } + } + + bool VirtualCPU::loadExtension(void) + { + if (m_currentInst < data::OpCodes::Ext01 || m_currentInst > data::OpCodes::Ext16) + return false; + for (int32_t i = 0; i < 16; i++) + { + if (m_extensions[i] == nullptr) + continue; + if (m_extensions[i]->m_code == m_currentInst) + { + m_currentExtension = m_extensions[i]; + return true; + } + } + return false; } bool VirtualCPU::execute(void) { if (m_halt) return false; + m_currentExtension = nullptr; m_isDebugBreakPoint = false; uint8_t inst = fetch8(); m_currentInst = inst; + if (loadExtension()) + return m_currentExtension->execute(*this); switch (inst) { case data::OpCodes::NoOp: @@ -176,6 +209,16 @@ namespace dragon m_isDebugBreakPoint = true; } break; + case data::OpCodes::BIOSModeImm: + { + uint16_t tmpAddr = m_currentAddr; + int8_t value = fetch8(); + if (tmpAddr >= data::MemoryMapAddresses::BIOS_End) + m_biosMode = false; + else + m_biosMode = value != 0; + } + break; case data::OpCodes::MovImmReg: { uint8_t regAddr = fetch8(); @@ -744,7 +787,7 @@ namespace dragon break; case data::OpCodes::RetInt: { - m_isInInterruptHandler = false; + m_interruptHandlerCount--; popStackFrame(); // m_subroutineCounter = ZERO(m_subroutineCounter - 1); m_subroutineCounter--; @@ -755,10 +798,32 @@ namespace dragon uint8_t intValue = fetch8(); if (!readFlag(data::Flags::InterruptsEnabled)) return true; - handleInterrupt(intValue); + handleInterrupt(intValue, false); m_subroutineCounter++; } break; + case data::OpCodes::Ext01: + case data::OpCodes::Ext02: + case data::OpCodes::Ext03: + case data::OpCodes::Ext04: + case data::OpCodes::Ext05: + case data::OpCodes::Ext06: + case data::OpCodes::Ext07: + case data::OpCodes::Ext08: + case data::OpCodes::Ext09: + case data::OpCodes::Ext10: + case data::OpCodes::Ext11: + case data::OpCodes::Ext12: + case data::OpCodes::Ext13: + case data::OpCodes::Ext14: + case data::OpCodes::Ext15: + case data::OpCodes::Ext16: + { + data::ErrorHandler::pushError(data::ErrorCodes::CPU_UnsupportedExtension, ostd::String("Unsupported Extension: ").add(ostd::Utils::getHexStr(inst, true, 1))); + m_halt = true; + return false; + } + break; default: { data::ErrorHandler::pushError(data::ErrorCodes::CPU_UnknownInstruction, ostd::String("Unknown instruction: ").add(ostd::Utils::getHexStr(inst, true, 1))); diff --git a/src/hardware/VirtualCPU.hpp b/src/hardware/VirtualCPU.hpp index 854a6e3..6b6793a 100644 --- a/src/hardware/VirtualCPU.hpp +++ b/src/hardware/VirtualCPU.hpp @@ -6,6 +6,7 @@ #include "IMemoryDevice.hpp" #include "../debugger/Debugger.hpp" +#include "../tools/GlobalData.hpp" namespace dragon { @@ -31,13 +32,15 @@ namespace dragon bool readFlag(uint8_t flg); void setFlag(uint8_t flg, bool val = true); - void handleInterrupt(uint8_t intValue); + void handleInterrupt(uint8_t intValue, bool hardware); + bool loadExtension(void); bool execute(void); inline bool isHalted(void) const { return m_halt; } inline uint8_t getCurrentInstruction(void) const { return m_currentInst; } inline bool isInDebugBreakPoint(void) const { return m_isDebugBreakPoint; } + inline bool isInBIOSMOde(void) const { return m_biosMode; } inline bool isInSubRoutine(void) const { return m_subroutineCounter > 0; } inline int32_t getSubRoutineCounter(void) const { return m_subroutineCounter; } @@ -51,12 +54,16 @@ namespace dragon uint16_t m_stackFrameSize { 0 }; bool m_halt { false }; uint8_t m_currentInst { 0x00 }; + uint8_t m_currentAddr { 0x00 }; bool m_biosMode { true }; - bool m_isInInterruptHandler { false }; + int32_t m_interruptHandlerCount { 0 }; bool m_isDebugBreakPoint { false }; bool m_debugModeEnabled { false }; int32_t m_subroutineCounter { 0 }; + data::CPUExtension* m_extensions[16]; + data::CPUExtension* m_currentExtension { nullptr }; + std::vector m_debug_stackFrameStrings; friend class dragon::DragonRuntime; diff --git a/src/hardware/VirtualIODevices.cpp b/src/hardware/VirtualIODevices.cpp index 3bd3e8e..2469249 100644 --- a/src/hardware/VirtualIODevices.cpp +++ b/src/hardware/VirtualIODevices.cpp @@ -6,6 +6,13 @@ #include "VirtualCPU.hpp" #include "VirtualRAM.hpp" +#include "../runtime/DragonRuntime.hpp" + +//TODO: Fix all access functions (reads and writes) ensuring the address is not out of bounds. +// Right now the check is done, but just to push an error if out of bounds; the address +// gets still used even in that case, which is really dumb and will probably crash the +// runtime most of the time. + namespace dragon { namespace hw @@ -104,39 +111,251 @@ namespace dragon - VirtualKeyboard::VirtualKeyboard(void) + void VirtualKeyboard::init(void) { + uint32_t dataSize = data::MemoryMapAddresses::Keyboard_End - data::MemoryMapAddresses::Keyboard_Start; + for (int32_t i = 0; i < dataSize; i++) + m_data.push_back(0x00); + enableSignals(); + validate(); + connectSignal(ostd::tBuiltinSignals::KeyPressed); + connectSignal(ostd::tBuiltinSignals::KeyReleased); + connectSignal(ostd::tBuiltinSignals::TextEntered); } int8_t VirtualKeyboard::read8(uint16_t addr) { - return 0x00; + if (addr >= m_data.size()) + data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Byte KeyboardController location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + return m_data[addr]; } int16_t VirtualKeyboard::read16(uint16_t addr) { - return 0x0000; + if (addr >= m_data.size() - 1) + data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word KeyboardController location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + return ((m_data[addr + 0] << 8) & 0xFF00U) + | ( m_data[addr + 1] & 0x00FFU); } int8_t VirtualKeyboard::write8(uint16_t addr, int8_t value) { - return 0x00; + if (addr >= m_data.size()) + data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word KeyboardController location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + if (!DragonRuntime::cpu.isInBIOSMOde()) + { + data::ErrorHandler::pushError(data::ErrorCodes::AccessViolation_BiosModeRequired, ostd::String("Attempting to write byte to KeyboardController while not in BIOS mode. Address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + return 0; + } + return __write8(addr, value); } int16_t VirtualKeyboard::write16(uint16_t addr, int16_t value) { - return 0x0000; + if (addr >= m_data.size() - 1) + data::ErrorHandler::pushError(data::ErrorCodes::IntVector_InvalidAddress, ostd::String("Invalid Word KeyboardController location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + if (!DragonRuntime::cpu.isInBIOSMOde()) + { + data::ErrorHandler::pushError(data::ErrorCodes::AccessViolation_BiosModeRequired, ostd::String("Attempting to write word to KeyboardController while not in BIOS mode. Address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + return 0; + } + return __write16(addr, value); } ostd::ByteStream* VirtualKeyboard::getByteStream(void) { - return nullptr; + return &m_data; + } + + void VirtualKeyboard::handleSignal(ostd::tSignal& signal) + { + auto& cpu = DragonRuntime::cpu; + const auto& state = SDL_GetKeyboardState(nullptr); + if (signal.ID == ostd::tBuiltinSignals::KeyPressed || signal.ID == ostd::tBuiltinSignals::KeyReleased) + { + KeyEventData& ked = (KeyEventData&)signal.userData; + m_modifiersBitFiels = __construct_modifiers_bitfield(); + __write16(tRegisters::Modifiers, (int16_t)m_modifiersBitFiels.value); + __write16(tRegisters::KeyCode, __sdl_key_code_convert(ked.keyCode)); + if (ked.eventType == KeyEventData::eKeyEvent::Pressed) + cpu.handleInterrupt(data::InterruptCodes::KeyPressed, true); + else if (ked.eventType == KeyEventData::eKeyEvent::Pressed) + cpu.handleInterrupt(data::InterruptCodes::KeyReleased, true); + } + else if (signal.ID == ostd::tBuiltinSignals::TextEntered) + { + KeyEventData& ked = (KeyEventData&)signal.userData; + m_modifiersBitFiels = __construct_modifiers_bitfield(); + __write16(tRegisters::Modifiers, (int16_t)m_modifiersBitFiels.value); + __write16(tRegisters::KeyCode, (int16_t)ked.text); + cpu.handleInterrupt(data::InterruptCodes::TextEntered, true); + } + } + + ostd::BitField_16 VirtualKeyboard::__construct_modifiers_bitfield(void) + { + ostd::BitField_16 bitfield; + auto mod_state = SDL_GetModState(); + ostd::Bits::val(bitfield, tModifierBits::LeftShift, (mod_state & KMOD_LSHIFT)); + ostd::Bits::val(bitfield, tModifierBits::LeftControl, (mod_state & KMOD_LCTRL)); + ostd::Bits::val(bitfield, tModifierBits::LeftAlt, (mod_state & KMOD_LALT)); + ostd::Bits::val(bitfield, tModifierBits::LeftSuper, (mod_state & KMOD_LGUI)); + ostd::Bits::val(bitfield, tModifierBits::RightShift, (mod_state & KMOD_RSHIFT)); + ostd::Bits::val(bitfield, tModifierBits::RightControl, (mod_state & KMOD_RCTRL)); + ostd::Bits::val(bitfield, tModifierBits::RightAlt, (mod_state & KMOD_RALT)); + ostd::Bits::val(bitfield, tModifierBits::RightSuper, (mod_state & KMOD_RGUI)); + ostd::Bits::val(bitfield, tModifierBits::CapsLock, (mod_state & KMOD_CAPS)); + ostd::Bits::val(bitfield, tModifierBits::NumLock, (mod_state & KMOD_NUM)); + ostd::Bits::val(bitfield, tModifierBits::ScrolLLock, (mod_state & KMOD_SCROLL)); + return bitfield; + } + + int8_t VirtualKeyboard::__write8(uint16_t addr, int8_t value) + { + m_data[addr] = value; + return value; + } + + int16_t VirtualKeyboard::__write16(uint16_t addr, int16_t value) + { + m_data[addr + 0] = (value >> 8) & 0xFF; + m_data[addr + 1] = value & 0xFF; + return value; + } + + int16_t VirtualKeyboard::__sdl_key_code_convert(int32_t keyCode) + { + switch (keyCode) + { + case SDLK_LCTRL: return (int16_t)eKeys::LeftCTRL; + case SDLK_LSHIFT: return (int16_t)eKeys::LeftShift; + case SDLK_LALT: return (int16_t)eKeys::LeftALT; + case SDLK_LGUI: return (int16_t)eKeys::LeftSuper; + case SDLK_RCTRL: return (int16_t)eKeys::RightCTRL; + case SDLK_RSHIFT: return (int16_t)eKeys::RightShift; + case SDLK_RGUI: return (int16_t)eKeys::RightSuper; + case SDLK_RALT: return (int16_t)eKeys::RightALT; + case SDLK_KP_DIVIDE: return (int16_t)eKeys::KeyPadDivide; + case SDLK_KP_MULTIPLY: return (int16_t)eKeys::KeyPadMultiply; + case SDLK_KP_MINUS: return (int16_t)eKeys::KeyPadMinus; + case SDLK_KP_PLUS: return (int16_t)eKeys::KeyPadPlus; + case SDLK_KP_ENTER: return (int16_t)eKeys::KeyPadEnter; + case SDLK_KP_1: return (int16_t)eKeys::KeyPad1; + case SDLK_KP_2: return (int16_t)eKeys::KeyPad2; + case SDLK_KP_3: return (int16_t)eKeys::KeyPad3; + case SDLK_KP_4: return (int16_t)eKeys::KeyPad4; + case SDLK_KP_5: return (int16_t)eKeys::KeyPad5; + case SDLK_KP_6: return (int16_t)eKeys::KeyPad6; + case SDLK_KP_7: return (int16_t)eKeys::KeyPad7; + case SDLK_KP_8: return (int16_t)eKeys::KeyPad8; + case SDLK_KP_9: return (int16_t)eKeys::KeyPad9; + case SDLK_KP_0: return (int16_t)eKeys::KeyPad0; + case SDLK_KP_PERIOD: return (int16_t)eKeys::KeyPadPeriod; + case SDLK_PRINTSCREEN: return (int16_t)eKeys::PrintScreen; + case SDLK_INSERT: return (int16_t)eKeys::Insert; + case SDLK_HOME: return (int16_t)eKeys::Home; + case SDLK_PAGEUP: return (int16_t)eKeys::PageUp; + case SDLK_END: return (int16_t)eKeys::End; + case SDLK_PAGEDOWN: return (int16_t)eKeys::PageDown; + case SDLK_RIGHT: return (int16_t)eKeys::RightArrow; + case SDLK_LEFT: return (int16_t)eKeys::LeftArrow; + case SDLK_DOWN: return (int16_t)eKeys::DownArrow; + case SDLK_UP: return (int16_t)eKeys::UpArrow; + case SDLK_CAPSLOCK: return (int16_t)eKeys::CapsLock; + case SDLK_NUMLOCKCLEAR: return (int16_t)eKeys::NumLock; + case SDLK_SCROLLLOCK: return (int16_t)eKeys::ScrollLock; + case SDLK_F1: return (int16_t)eKeys::F1; + case SDLK_F2: return (int16_t)eKeys::F2; + case SDLK_F3: return (int16_t)eKeys::F3; + case SDLK_F4: return (int16_t)eKeys::F4; + case SDLK_F5: return (int16_t)eKeys::F5; + case SDLK_F6: return (int16_t)eKeys::F6; + case SDLK_F7: return (int16_t)eKeys::F7; + case SDLK_F8: return (int16_t)eKeys::F8; + case SDLK_F9: return (int16_t)eKeys::F9; + case SDLK_F10: return (int16_t)eKeys::F10; + case SDLK_F11: return (int16_t)eKeys::F11; + case SDLK_F12: return (int16_t)eKeys::F12; + case SDLK_DELETE: return (int16_t)eKeys::Delete; + case SDLK_RETURN: return (int16_t)eKeys::Return; + case SDLK_ESCAPE: return (int16_t)eKeys::Escape; + case SDLK_BACKSPACE: return (int16_t)eKeys::Backspace; + case SDLK_TAB: return (int16_t)eKeys::Tab; + case SDLK_SPACE: return (int16_t)eKeys::Spacebar; + case SDLK_EXCLAIM: return (int16_t)eKeys::ExclamationMark; + case SDLK_QUOTEDBL: return (int16_t)eKeys::DoubleQuote; + case SDLK_HASH: return (int16_t)eKeys::Hash; + case SDLK_PERCENT: return (int16_t)eKeys::Percent; + case SDLK_DOLLAR: return (int16_t)eKeys::DollarSign; + case SDLK_AMPERSAND: return (int16_t)eKeys::Ampersand; + case SDLK_QUOTE: return (int16_t)eKeys::SingleQuote; + case SDLK_LEFTPAREN: return (int16_t)eKeys::LeftParenthesis; + case SDLK_RIGHTPAREN: return (int16_t)eKeys::RightParenthesis; + case SDLK_ASTERISK: return (int16_t)eKeys::Asterisk; + case SDLK_PLUS: return (int16_t)eKeys::Plus; + case SDLK_COMMA: return (int16_t)eKeys::Comma; + case SDLK_MINUS: return (int16_t)eKeys::Minus; + case SDLK_PERIOD: return (int16_t)eKeys::Period; + case SDLK_SLASH: return (int16_t)eKeys::ForwardSlash; + case SDLK_0: return (int16_t)eKeys::Num0; + case SDLK_1: return (int16_t)eKeys::Num1; + case SDLK_2: return (int16_t)eKeys::Num2; + case SDLK_3: return (int16_t)eKeys::Num3; + case SDLK_4: return (int16_t)eKeys::Num4; + case SDLK_5: return (int16_t)eKeys::Num5; + case SDLK_6: return (int16_t)eKeys::Num6; + case SDLK_7: return (int16_t)eKeys::Num7; + case SDLK_8: return (int16_t)eKeys::Num8; + case SDLK_9: return (int16_t)eKeys::Num9; + case SDLK_COLON: return (int16_t)eKeys::Colon; + case SDLK_SEMICOLON: return (int16_t)eKeys::Semicolon; + case SDLK_LESS: return (int16_t)eKeys::LessThan; + case SDLK_EQUALS: return (int16_t)eKeys::Equals; + case SDLK_GREATER: return (int16_t)eKeys::GreaterThan; + case SDLK_QUESTION: return (int16_t)eKeys::QuestionMark; + case SDLK_AT: return (int16_t)eKeys::AtSign; + case SDLK_LEFTBRACKET: return (int16_t)eKeys::LeftBracket; + case SDLK_BACKSLASH: return (int16_t)eKeys::BackSlash; + case SDLK_RIGHTBRACKET: return (int16_t)eKeys::RightBracket; + case SDLK_CARET: return (int16_t)eKeys::Caret; + case SDLK_UNDERSCORE: return (int16_t)eKeys::Underscore; + case SDLK_BACKQUOTE: return (int16_t)eKeys::BackQuote; + case SDLK_a: return (int16_t)eKeys::LowerCase_a; + case SDLK_b: return (int16_t)eKeys::LowerCase_b; + case SDLK_c: return (int16_t)eKeys::LowerCase_c; + case SDLK_d: return (int16_t)eKeys::LowerCase_d; + case SDLK_e: return (int16_t)eKeys::LowerCase_e; + case SDLK_f: return (int16_t)eKeys::LowerCase_f; + case SDLK_g: return (int16_t)eKeys::LowerCase_g; + case SDLK_h: return (int16_t)eKeys::LowerCase_h; + case SDLK_i: return (int16_t)eKeys::LowerCase_i; + case SDLK_j: return (int16_t)eKeys::LowerCase_j; + case SDLK_k: return (int16_t)eKeys::LowerCase_k; + case SDLK_l: return (int16_t)eKeys::LowerCase_l; + case SDLK_m: return (int16_t)eKeys::LowerCase_m; + case SDLK_n: return (int16_t)eKeys::LowerCase_n; + case SDLK_o: return (int16_t)eKeys::LowerCase_o; + case SDLK_p: return (int16_t)eKeys::LowerCase_p; + case SDLK_q: return (int16_t)eKeys::LowerCase_q; + case SDLK_r: return (int16_t)eKeys::LowerCase_r; + case SDLK_s: return (int16_t)eKeys::LowerCase_s; + case SDLK_t: return (int16_t)eKeys::LowerCase_t; + case SDLK_u: return (int16_t)eKeys::LowerCase_u; + case SDLK_v: return (int16_t)eKeys::LowerCase_v; + case SDLK_w: return (int16_t)eKeys::LowerCase_w; + case SDLK_x: return (int16_t)eKeys::LowerCase_x; + case SDLK_y: return (int16_t)eKeys::LowerCase_y; + case SDLK_z: return (int16_t)eKeys::LowerCase_z; + default: return (int16_t)eKeys::UpperCase_A; + } + return (int16_t)eKeys::UpperCase_A; } + VirtualMouse::VirtualMouse(void) { } @@ -368,7 +587,7 @@ namespace dragon m_data.w_Byte(tRegisters::Status, tStatusValues::Free); m_data.w_Byte(tRegisters::Signal, tSignalValues::Ignore); m_busy = false; - m_cpu.handleInterrupt(data::InterruptCodes::DiskInterfaceFFinished); + m_cpu.handleInterrupt(data::InterruptCodes::DiskInterfaceFFinished, true); return; } currentAddress++; @@ -594,6 +813,11 @@ namespace dragon data::ErrorHandler::pushError(data::ErrorCodes::CMOS_InvalidAddress, ostd::String("Invalid Byte CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return 0; } + if (!DragonRuntime::cpu.isInBIOSMOde()) + { + data::ErrorHandler::pushError(data::ErrorCodes::AccessViolation_BiosModeRequired, ostd::String("Attempting to write byte to CMOS while not in BIOS mode. Address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + return 0; + } m_dataFile.seekp(addr); m_dataFile.write((char*)(&value), sizeof(value)); return value; @@ -611,6 +835,11 @@ namespace dragon data::ErrorHandler::pushError(data::ErrorCodes::CMOS_InvalidAddress, ostd::String("Invalid Word CMOS location at address: ").add(ostd::Utils::getHexStr(addr, true, 2))); return 0; } + if (!DragonRuntime::cpu.isInBIOSMOde()) + { + data::ErrorHandler::pushError(data::ErrorCodes::AccessViolation_BiosModeRequired, ostd::String("Attempting to write word to CMOS while not in BIOS mode. Address: ").add(ostd::Utils::getHexStr(addr, true, 2))); + return 0; + } int8_t b1 = (value >> 8) & 0xFF; int8_t b2 = (value & 0xFF); write8(addr, b1); diff --git a/src/hardware/VirtualIODevices.hpp b/src/hardware/VirtualIODevices.hpp index 1418da9..3f64bdf 100644 --- a/src/hardware/VirtualIODevices.hpp +++ b/src/hardware/VirtualIODevices.hpp @@ -14,7 +14,6 @@ namespace dragon class VirtualCPU; class VirtualRAM; - class VirtualBIOS : public IMemoryDevice { public: @@ -48,8 +47,184 @@ namespace dragon }; class VirtualKeyboard : public IMemoryDevice { + public: enum class eKeys + { + Delete = (int16_t)'\x7F', + Return = (int16_t)'\r', + Escape = (int16_t)'\x1B', + Backspace = (int16_t)'\b', + Tab = (int16_t)'\t', + Spacebar = (int16_t)' ', + ExclamationMark = (int16_t)'!', + DoubleQuote = (int16_t)'"', + Hash = (int16_t)'#', + Percent = (int16_t)'%', + DollarSign = (int16_t)'$', + Ampersand = (int16_t)'&', + SingleQuote = (int16_t)'\'', + LeftParenthesis = (int16_t)'(', + RightParenthesis = (int16_t)')', + Asterisk = (int16_t)'*', + Plus = (int16_t)'+', + Comma = (int16_t)',', + Minus = (int16_t)'-', + Period = (int16_t)'.', + ForwardSlash = (int16_t)'/', + Num0 = (int16_t)'0', + Num1 = (int16_t)'1', + Num2 = (int16_t)'2', + Num3 = (int16_t)'3', + Num4 = (int16_t)'4', + Num5 = (int16_t)'5', + Num6 = (int16_t)'6', + Num7 = (int16_t)'7', + Num8 = (int16_t)'8', + Num9 = (int16_t)'9', + Colon = (int16_t)':', + Semicolon = (int16_t)';', + LessThan = (int16_t)'<', + Equals = (int16_t)'=', + GreaterThan = (int16_t)'>', + QuestionMark = (int16_t)'?', + AtSign = (int16_t)'@', + + UpperCase_A = (int16_t)'A', + UpperCase_B = (int16_t)'B', + UpperCase_C = (int16_t)'C', + UpperCase_D = (int16_t)'D', + UpperCase_E = (int16_t)'E', + UpperCase_F = (int16_t)'F', + UpperCase_G = (int16_t)'G', + UpperCase_H = (int16_t)'H', + UpperCase_I = (int16_t)'I', + UpperCase_J = (int16_t)'J', + UpperCase_K = (int16_t)'K', + UpperCase_L = (int16_t)'L', + UpperCase_M = (int16_t)'M', + UpperCase_N = (int16_t)'N', + UpperCase_O = (int16_t)'O', + UpperCase_P = (int16_t)'P', + UpperCase_Q = (int16_t)'Q', + UpperCase_R = (int16_t)'R', + UpperCase_S = (int16_t)'S', + UpperCase_T = (int16_t)'T', + UpperCase_U = (int16_t)'U', + UpperCase_V = (int16_t)'V', + UpperCase_W = (int16_t)'W', + UpperCase_X = (int16_t)'X', + UpperCase_Y = (int16_t)'Y', + UpperCase_Z = (int16_t)'Z', + + LeftBracket = (int16_t)'[', + BackSlash = (int16_t)'\\', + RightBracket = (int16_t)']', + Caret = (int16_t)'^', + Underscore = (int16_t)'_', + BackQuote = (int16_t)'`', + LowerCase_a = (int16_t)'a', + LowerCase_b = (int16_t)'b', + LowerCase_c = (int16_t)'c', + LowerCase_d = (int16_t)'d', + LowerCase_e = (int16_t)'e', + LowerCase_f = (int16_t)'f', + LowerCase_g = (int16_t)'g', + LowerCase_h = (int16_t)'h', + LowerCase_i = (int16_t)'i', + LowerCase_j = (int16_t)'j', + LowerCase_k = (int16_t)'k', + LowerCase_l = (int16_t)'l', + LowerCase_m = (int16_t)'m', + LowerCase_n = (int16_t)'n', + LowerCase_o = (int16_t)'o', + LowerCase_p = (int16_t)'p', + LowerCase_q = (int16_t)'q', + LowerCase_r = (int16_t)'r', + LowerCase_s = (int16_t)'s', + LowerCase_t = (int16_t)'t', + LowerCase_u = (int16_t)'u', + LowerCase_v = (int16_t)'v', + LowerCase_w = (int16_t)'w', + LowerCase_x = (int16_t)'x', + LowerCase_y = (int16_t)'y', + LowerCase_z = (int16_t)'z', + + LeftCTRL = (int16_t)0x0100, + RightCTRL = (int16_t)0x0101, + LeftALT = (int16_t)0x0102, + RightALT = (int16_t)0x0103, + LeftShift = (int16_t)0x0104, + RightShift = (int16_t)0x0105, + + KeyPadDivide = (int16_t)0x0106, + KeyPadMultiply = (int16_t)0x0107, + KeyPadMinus = (int16_t)0x0108, + KeyPadPlus = (int16_t)0x0109, + KeyPadEnter = (int16_t)0x010A, + KeyPad1 = (int16_t)0x010B, + KeyPad2 = (int16_t)0x010C, + KeyPad3 = (int16_t)0x010D, + KeyPad4 = (int16_t)0x010E, + KeyPad5 = (int16_t)0x010F, + KeyPad6 = (int16_t)0x0110, + KeyPad7 = (int16_t)0x0111, + KeyPad8 = (int16_t)0x0112, + KeyPad9 = (int16_t)0x0113, + KeyPad0 = (int16_t)0x0114, + KeyPadPeriod = (int16_t)0x0115, + + PrintScreen = (int16_t)0x0116, + Insert = (int16_t)0x0117, + Home = (int16_t)0x0118, + PageUp = (int16_t)0x0119, + End = (int16_t)0x011A, + PageDown = (int16_t)0x011B, + RightArrow = (int16_t)0x011C, + LeftArrow = (int16_t)0x011D, + DownArrow = (int16_t)0x011E, + UpArrow = (int16_t)0x011F, + CapsLock = (int16_t)0x0120, + NumLock = (int16_t)0x0121, + ScrollLock = (int16_t)0x0122, + + F1 = (int16_t)0x0123, + F2 = (int16_t)0x0124, + F3 = (int16_t)0x0125, + F4 = (int16_t)0x0126, + F5 = (int16_t)0x0127, + F6 = (int16_t)0x0128, + F7 = (int16_t)0x0129, + F8 = (int16_t)0x012A, + F9 = (int16_t)0x012B, + F10 = (int16_t)0x012C, + F11 = (int16_t)0x012D, + F12 = (int16_t)0x012E, + + LeftSuper = (int16_t)0x012F, + RightSuper = (int16_t)0x0130, + }; + public: struct tModifierBits + { + inline static constexpr uint8_t LeftShift = 0; + inline static constexpr uint8_t LeftControl = 1; + inline static constexpr uint8_t LeftAlt = 2; + inline static constexpr uint8_t LeftSuper = 3; + inline static constexpr uint8_t RightShift = 4; + inline static constexpr uint8_t RightControl = 5; + inline static constexpr uint8_t RightAlt = 6; + inline static constexpr uint8_t RightSuper = 7; + inline static constexpr uint8_t CapsLock = 8; + inline static constexpr uint8_t NumLock = 9; + inline static constexpr uint8_t ScrolLLock = 10; + }; + public: struct tRegisters + { + inline static constexpr uint16_t Modifiers = 0x00; + inline static constexpr uint16_t KeyCode = 0x02; + }; + public: - VirtualKeyboard(void); + inline VirtualKeyboard(void) { } + void init(void); int8_t read8(uint16_t addr) override; int16_t read16(uint16_t addr) override; int8_t write8(uint16_t addr, int8_t value) override; @@ -57,7 +232,17 @@ namespace dragon ostd::ByteStream* getByteStream(void) override; + void handleSignal(ostd::tSignal& signal) override; + private: + ostd::BitField_16 __construct_modifiers_bitfield(void); + int8_t __write8(uint16_t addr, int8_t value); + int16_t __write16(uint16_t addr, int16_t value); + int16_t __sdl_key_code_convert(int32_t keyCode); + + private: + ostd::ByteStream m_data; + ostd::BitField_16 m_modifiersBitFiels; }; class VirtualMouse : public IMemoryDevice { diff --git a/src/runtime/ConfigLoader.cpp b/src/runtime/ConfigLoader.cpp index fe26747..4534d00 100644 --- a/src/runtime/ConfigLoader.cpp +++ b/src/runtime/ConfigLoader.cpp @@ -1,6 +1,7 @@ #include "ConfigLoader.hpp" #include #include +#include "../hardware/CPUExtensions.hpp" namespace dragon { @@ -30,6 +31,23 @@ namespace dragon config.vdisk_paths[disk_nr++] = lineEdit; } } + else if (lineEdit == "cpuext") + { + lineEdit = tokens.next(); + tokens = lineEdit.tokenize(","); + if (tokens.count() == 0) continue; //TODO: Warning + int32_t ext_nr = 0; + while (tokens.hasNext()) + { + if (ext_nr >= 16) break; //TODO: Warning + lineEdit = tokens.next(); + if (lineEdit == "extmov") + { + config.cpuext_list[ext_nr++] = new hw::cpuext::ExtMov; + } + else continue; //TODO: Warning + } + } else if (lineEdit == "bios") { lineEdit = tokens.next(); diff --git a/src/runtime/ConfigLoader.hpp b/src/runtime/ConfigLoader.hpp index 5b550af..1935b2e 100644 --- a/src/runtime/ConfigLoader.hpp +++ b/src/runtime/ConfigLoader.hpp @@ -2,18 +2,21 @@ #include #include +#include "../tools/GlobalData.hpp" namespace dragon { struct tMachineConfig { std::map vdisk_paths; + std::map cpuext_list; ostd::String bios_path; ostd::String cmos_path; ostd::Color singleColor_background; ostd::Color singleColor_foreground; inline bool isValid(void) const { return m_valid; } + inline void destroy(void) { for (auto& ptr : cpuext_list) delete ptr.second; } private: bool m_valid { false }; diff --git a/src/runtime/DragonRuntime.cpp b/src/runtime/DragonRuntime.cpp index 313039f..79c4e6e 100644 --- a/src/runtime/DragonRuntime.cpp +++ b/src/runtime/DragonRuntime.cpp @@ -4,6 +4,26 @@ namespace dragon { + void DragonRuntime::SignalListener::init(void) + { + setTypeName("dragon::DragonRuntime::SignalListener"); + validate(); + enableSignals(); + connectSignal(Signal_HardwareInterruptOccurred); + } + + void DragonRuntime::SignalListener::handleSignal(ostd::tSignal& signal) + { + if (signal.ID == Signal_HardwareInterruptOccurred) + { + tCallInfo& interruptData = (tCallInfo&)signal.userData; + DragonRuntime::s_machineInfo.callStack.push_back(interruptData); + } + } + + + + void DragonRuntime::printRegisters(dragon::hw::VirtualCPU& cpu) { out.fg("green").p("IP: ").fg("white").p(ostd::Utils::getHexStr(cpu.readRegister(dragon::data::Registers::IP), true, 2).cpp_str()); @@ -129,6 +149,8 @@ namespace dragon bool debugModeEnabled) { ostd::SignalHandler::init(); + s_signalListener.init(); + vKeyboard.init(); if (verbose) out.fg(ostd::ConsoleColors::Magenta).p("Loading machine config: ").fg(ostd::ConsoleColors::BrightYellow).p(configFilePath.cpp_str()).nl(); machine_config = dragon::MachineConfigLoader::loadConfig(configFilePath); @@ -210,7 +232,7 @@ namespace dragon out.p(ostd::Utils::getHexStr(dragon::data::MemoryMapAddresses::Keyboard_End, true, 2).cpp_str()); out.p(" (remap=false)").nl(); } - memMap.mapDevice(vKeyboard, dragon::data::MemoryMapAddresses::Keyboard_Start, dragon::data::MemoryMapAddresses::Keyboard_End, false, "Keyb."); + memMap.mapDevice(vKeyboard, dragon::data::MemoryMapAddresses::Keyboard_Start, dragon::data::MemoryMapAddresses::Keyboard_End, true, "Keyb."); if (verbose) { out.fg(ostd::ConsoleColors::Magenta).p(" vMouse: "); @@ -290,12 +312,33 @@ namespace dragon out.fg(ostd::ConsoleColors::BrightYellow).p(" Debug mode enabled: ").p(STR_BOOL(debugModeEnabled)).nl(); cpu.m_debugModeEnabled = debugModeEnabled; + if (verbose) + out.fg(ostd::ConsoleColors::BrightYellow).p(" BIOS mode enabled").nl(); + cpu.m_biosMode = true; + + if (machine_config.cpuext_list.size() > 0) + { + if (verbose) + out.fg(ostd::ConsoleColors::BrightYellow).p(" Loading CPU Extensions").nl(); + for (auto& ext : machine_config.cpuext_list) + { + cpu.m_extensions[ext.first] = ext.second; + if (verbose) + out.fg(ostd::ConsoleColors::BrightYellow).p(" ").p(ext.first + 1).p(": ").p(ext.second->m_name).nl(); + } + } + out.nl().nl(); s_trackMachineInfo = trackMachineInfoDiff; s_trackCallStack = trackCallStack; return RETURN_VAL_EXIT_SUCCESS; } + void DragonRuntime::shutdownMachine(void) + { + machine_config.destroy(); + } + void DragonRuntime::runMachine(int32_t cycleLimit, bool basic_debug, bool step_exec) { int32_t cycleCounter = 0; @@ -412,7 +455,7 @@ namespace dragon bool debugBreak = cpu.m_isDebugBreakPoint; - bool intHandler = cpu.m_isInInterruptHandler; + int32_t intHandlerCount = cpu.m_interruptHandlerCount; bool biosMode = cpu.m_biosMode; bool isInSubRoutine = cpu.isInSubRoutine(); @@ -434,7 +477,7 @@ namespace dragon minfo.previousInstructionTrackedValues.push_back(memMap.read8(addr)); minfo.previousInstructionDebugBreak = debugBreak; - minfo.previousInstructionInterruptHandler = intHandler; + minfo.previousInstructionInterruptHandlerCount = intHandlerCount; minfo.previousInstructionBiosMode = biosMode; minfo.previousIsInSubRoutine = isInSubRoutine; } @@ -456,7 +499,7 @@ namespace dragon minfo.currentInstructionTrackedValues.push_back(memMap.read8(addr)); minfo.currentInstructionDebugBreak = debugBreak; - minfo.currentInstructionInterruptHandler = intHandler; + minfo.currentInstructionInterruptHandlerCount = intHandlerCount; minfo.currentInstructionBiosMode = biosMode; minfo.currentIsInSubRoutine = isInSubRoutine; } diff --git a/src/runtime/DragonRuntime.hpp b/src/runtime/DragonRuntime.hpp index 2bffa0b..d70e8ad 100644 --- a/src/runtime/DragonRuntime.hpp +++ b/src/runtime/DragonRuntime.hpp @@ -17,8 +17,20 @@ namespace dragon { class DragonRuntime { - public: struct tCallInfo + public: class SignalListener : public ostd::BaseObject { + public: + inline SignalListener(void) { } + void init(void); + void handleSignal(ostd::tSignal& signal) override; + + public: + inline static const int32_t Signal_HardwareInterruptOccurred = ostd::SignalHandler::newCustomSignal(8129); + }; + public: struct tCallInfo : public ostd::BaseObject + { + inline tCallInfo(void) { } + inline tCallInfo(const ostd::String& _info, uint16_t _addr, uint16_t _inst_addr) : info(_info), addr(_addr), inst_addr(_inst_addr) { } ostd::String info; uint16_t addr; uint16_t inst_addr; @@ -54,15 +66,15 @@ namespace dragon int8_t currentInstructionFootprint[5] { 0x00, 0x00, 0x00, 0x00, 0x00 }; int16_t previousInstructionRegisters[20] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int16_t currentInstructionRegisters[20] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - + int32_t previousInstructionInterruptHandlerCount { 0 }; + int32_t currentInstructionInterruptHandlerCount { 0 }; + std::vector trackedAddresses; std::vector previousInstructionTrackedValues; std::vector currentInstructionTrackedValues; bool previousInstructionDebugBreak { false }; bool currentInstructionDebugBreak { false }; - bool previousInstructionInterruptHandler { false }; - bool currentInstructionInterruptHandler { false }; bool previousInstructionBiosMode { false }; bool currentInstructionBiosMode { false }; bool previousIsInSubRoutine { false }; @@ -84,6 +96,7 @@ namespace dragon bool hideVirtualDisplay = false, bool rackCallStack = false, bool debugModeEnabled = false); + static void shutdownMachine(void); static void runMachine(int32_t cycleLimit, bool basic_debug, bool step_exec); static bool runStep(std::vector trackedAddresses = { }); static void forceLoad(const ostd::String& filePath, uint16_t loadAddress); @@ -123,6 +136,7 @@ namespace dragon inline static tMachineDebugInfo s_machineInfo; inline static bool s_trackMachineInfo { false }; inline static bool s_trackCallStack { false }; + inline static SignalListener s_signalListener; public: inline static const int32_t RETURN_VAL_CLOSE_DEBUGGER = 128; @@ -133,5 +147,7 @@ namespace dragon inline static const int32_t RETURN_VAL_MISSING_PARAM = 4; inline static const int32_t RETURN_VAL_PARAMETER_NOT_NUMERIC = 5; inline static const int32_t RETURN_VAL_EXIT_SUCCESS = 0; + + friend class SignalListener; }; } \ No newline at end of file diff --git a/src/tools/GlobalData.hpp b/src/tools/GlobalData.hpp index 01462ec..4eaae3a 100644 --- a/src/tools/GlobalData.hpp +++ b/src/tools/GlobalData.hpp @@ -5,6 +5,7 @@ namespace dragon { + namespace hw { class VirtualCPU; } namespace data { typedef uint32_t VDiskID; @@ -13,10 +14,12 @@ namespace dragon { public: inline static constexpr uint64_t NoError = 0x0000000000000000; + inline static constexpr uint64_t AccessViolation_BiosModeRequired = 0x0000000000000001; inline static constexpr uint64_t MM_RegionNotFound = 0x1000000000000000; inline static constexpr uint64_t CPU_UnknownInstruction = 0x2000000000000000; + inline static constexpr uint64_t CPU_UnsupportedExtension = 0x2000000000000001; inline static constexpr uint64_t BIOS_FailedToLoad = 0x3000000000000000; inline static constexpr uint64_t BIOS_InvalidSize = 0x3000000000000001; @@ -188,14 +191,31 @@ namespace dragon { public: inline static constexpr uint8_t DiskInterfaceFFinished = 0x80; + inline static constexpr uint8_t KeyPressed = 0xA0; + inline static constexpr uint8_t KeyReleased = 0xA1; + inline static constexpr uint8_t TextEntered = 0xA2; + }; + + class CPUExtension + { + public: + inline virtual ~CPUExtension(void) { } + inline CPUExtension(uint8_t code, ostd::String name) : m_code(code), m_name(name) { } + virtual ostd::String getOpCodeString(uint8_t opCode) = 0; + virtual uint8_t getInstructionSIze(uint8_t opCode) = 0; + virtual bool execute(hw::VirtualCPU& vcpu) = 0; + + public: + uint8_t m_code { 0x00 }; + ostd::String m_name { "" }; }; class OpCodes { public: inline static constexpr uint8_t NoOp = 0x00; - inline static constexpr uint8_t DEBUG_Break = 0x01; + inline static constexpr uint8_t BIOSModeImm = 0x02; inline static constexpr uint8_t MovImmReg = 0x10; inline static constexpr uint8_t MovRegReg = 0x11; @@ -268,17 +288,37 @@ namespace dragon inline static constexpr uint8_t JmpLeImm = 0x7A; inline static constexpr uint8_t JmpLeReg = 0x7B; inline static constexpr uint8_t Jmp = 0x7C; + + inline static constexpr uint8_t Ext01 = 0xE0; + inline static constexpr uint8_t Ext02 = 0xE1; + inline static constexpr uint8_t Ext03 = 0xE2; + inline static constexpr uint8_t Ext04 = 0xE3; + inline static constexpr uint8_t Ext05 = 0xE4; + inline static constexpr uint8_t Ext06 = 0xE5; + inline static constexpr uint8_t Ext07 = 0xE6; + inline static constexpr uint8_t Ext08 = 0xE7; + inline static constexpr uint8_t Ext09 = 0xE8; + inline static constexpr uint8_t Ext10 = 0xE9; + inline static constexpr uint8_t Ext11 = 0xEA; + inline static constexpr uint8_t Ext12 = 0xEB; + inline static constexpr uint8_t Ext13 = 0xEC; + inline static constexpr uint8_t Ext14 = 0xED; + inline static constexpr uint8_t Ext15 = 0xEE; + inline static constexpr uint8_t Ext16 = 0xEF; inline static constexpr uint8_t RetInt = 0xFD; inline static constexpr uint8_t Int = 0xFE; inline static constexpr uint8_t Halt = 0xFF; - inline static ostd::String getOpCodeString(uint8_t opCode) + inline static ostd::String getOpCodeString(uint8_t opCode, CPUExtension* ext = nullptr) { + if (ext != nullptr) + return ext->getOpCodeString(opCode); switch (opCode) { case data::OpCodes::NoOp: return "NoOp"; case data::OpCodes::DEBUG_Break: return "debug_break"; + case data::OpCodes::BIOSModeImm: return "BIOSModeImm"; case data::OpCodes::MovImmReg: return "MovImmReg"; case data::OpCodes::MovImmMem: return "MovImmMem"; case data::OpCodes::MovRegReg: return "MovRegReg"; @@ -347,16 +387,35 @@ namespace dragon case data::OpCodes::ArgReg: return "ArgReg"; case data::OpCodes::RetInt: return "RetInt"; case data::OpCodes::Int: return "Int"; + case data::OpCodes::Ext01: return "Ext01"; + case data::OpCodes::Ext02: return "Ext02"; + case data::OpCodes::Ext03: return "Ext03"; + case data::OpCodes::Ext04: return "Ext04"; + case data::OpCodes::Ext05: return "Ext05"; + case data::OpCodes::Ext06: return "Ext06"; + case data::OpCodes::Ext07: return "Ext07"; + case data::OpCodes::Ext08: return "Ext08"; + case data::OpCodes::Ext09: return "Ext09"; + case data::OpCodes::Ext10: return "Ext10"; + case data::OpCodes::Ext11: return "Ext11"; + case data::OpCodes::Ext12: return "Ext12"; + case data::OpCodes::Ext13: return "Ext13"; + case data::OpCodes::Ext14: return "Ext14"; + case data::OpCodes::Ext15: return "Ext15"; + case data::OpCodes::Ext16: return "Ext16"; default: return "UNKNOWN_INST"; } } - inline static uint8_t getInstructionSIze(uint8_t opCode) + inline static uint8_t getInstructionSIze(uint8_t opCode, CPUExtension* ext = nullptr) { + if (ext != nullptr) + return ext->getInstructionSIze(opCode); switch (opCode) { case data::OpCodes::NoOp: return 1; case data::OpCodes::DEBUG_Break: return 1; + case data::OpCodes::BIOSModeImm: return 2; case data::OpCodes::MovImmReg: return 4; case data::OpCodes::MovImmMem: return 5; case data::OpCodes::MovRegReg: return 3; @@ -425,6 +484,22 @@ namespace dragon case data::OpCodes::ArgReg: return 2; case data::OpCodes::RetInt: return 1; case data::OpCodes::Int: return 2; + case data::OpCodes::Ext01: return 0; + case data::OpCodes::Ext02: return 0; + case data::OpCodes::Ext03: return 0; + case data::OpCodes::Ext04: return 0; + case data::OpCodes::Ext05: return 0; + case data::OpCodes::Ext06: return 0; + case data::OpCodes::Ext07: return 0; + case data::OpCodes::Ext08: return 0; + case data::OpCodes::Ext09: return 0; + case data::OpCodes::Ext10: return 0; + case data::OpCodes::Ext11: return 0; + case data::OpCodes::Ext12: return 0; + case data::OpCodes::Ext13: return 0; + case data::OpCodes::Ext14: return 0; + case data::OpCodes::Ext15: return 0; + case data::OpCodes::Ext16: return 0; default: return 0; } }