diff -uNr a/m/MANIFEST.TXT b/m/MANIFEST.TXT --- a/m/MANIFEST.TXT false +++ b/m/MANIFEST.TXT 27a062d2599973684b8fd9888be347d9b492fc94b9b29ad776dd672b11fb49606d226ceabbe5295f72a56fec277aeb465a9072961134be60e8054fbd8a705b6d @@ -0,0 +1,2 @@ +586606 m_genesis "Genesis." + diff -uNr a/m/Makefile b/m/Makefile --- a/m/Makefile false +++ b/m/Makefile 7a4692e9946054a9cb6b2668d3df0d10d982df49fa6114d08e2d3d78a7c23fe09c90dd5d8041a43e2f697a4324d1bedeeb4b3aa51d267864506b97d22d8d546e @@ -0,0 +1,37 @@ +PROJ = m +ASMER = yasm +ASMARGS = -f elf64 -g null +DWARFARGS = -f elf64 -g dwarf2 +LINKER = ld +LNKARGS = --strip-all +BUILD=build +BIN=bin + +all: asm lnk + +asm: + $(ASMER) $(ASMARGS) $(PROJ).asm -o $(BUILD)/$(PROJ).o + +dwarf: + $(ASMER) $(DWARFARGS) $(PROJ).asm -o $(BUILD)/$(PROJ).o + +lnk: + $(LINKER) $(LNKARGS) $(BUILD)/$(PROJ).o -o $(BIN)/$(PROJ) + +dbglnk: + $(LINKER) $(LNKARGS) $(BUILD)/$(PROJ).o -o $(BIN)/$(PROJ) + +dis: dwarf lnk + objdump --disassembler-options=intel -D $(BIN)/$(PROJ) + +dissrc: dwarf dbglnk + objdump --disassembler-options=intel -S -D $(BIN)/$(PROJ) + +disraw: all + objdump --disassembler-options=intel -D $(BIN)/$(PROJ) + +debug: clean dwarf dbglnk + gdb --command misc/m.gdb + +clean: + rm -f $(BUILD)/*.o $(BIN)/$(PROJ) diff -uNr a/m/bin/README b/m/bin/README --- a/m/bin/README false +++ b/m/bin/README 5fdbae897eb301a711bf95707f329517db540e34c182a5beec96e93d5d0d856cec2ed6b01c1191f865e8d1c45709a462c70c3005d4aa3676eb445d1479edf2e5 @@ -0,0 +1 @@ +Placeholder. diff -uNr a/m/build/README b/m/build/README --- a/m/build/README false +++ b/m/build/README 5fdbae897eb301a711bf95707f329517db540e34c182a5beec96e93d5d0d856cec2ed6b01c1191f865e8d1c45709a462c70c3005d4aa3676eb445d1479edf2e5 @@ -0,0 +1 @@ +Placeholder. diff -uNr a/m/bus.asm b/m/bus.asm --- a/m/bus.asm false +++ b/m/bus.asm b94991df360aaa3c332dbef2217079dbf8f67d37cb2e09bd4f3b5c093e789b90e8fcc33831d32204184d79ec320a74bb55f64a2215dde60bb61dd5716983a6d6 @@ -0,0 +1,173 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Universal MMIO Bus Dispatcher. Addition of new simulated devices will NOT +;; require any changes to this mechanism. Be sure to add any device component +;; to 'devices.asm'. +;; NOTE 1: The DECLARE_BUS_DEVICE macro is valid ONLY inside text included by +;; 'devices.asm' ! +;; NOTE 2: Currently there is NO support for devices which demand a +;; non-contiguous MMIO segment ! +;; NOTE 3: Make ABSOLUTELY certain that the MMIO Device ranges do not overlap! + +section .text + +;----------------------------------------------------------------------------- +; MMIO_ADDR +;----------------------------------------------------------------------------- +%define MMIO_ADDR(A) (MMIO_BASE + A) ; Address to be offset from MMIO_BASE +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Proclaim Device and connect it to Bus; %1: Name, %2: Base Addr, %3: Top Addr +;----------------------------------------------------------------------------- +%macro DECLARE_BUS_DEVICE 3 + +%define %1_BASE MMIO_ADDR(%2) ;; Declare DEVICE_BASE MMIO Address (first) +%define %1_TOP MMIO_ADDR(%3) ;; Declare DEVICE_TOP MMIO Address (last) + +;; Put this device's MMIO range in _Phys_Device_Read_Word's set of cases +SECTION .PD_Rd_Word + JMP_If_In eax, %1_BASE, %1_TOP, _PD_Read_Word_%1 + +;; Put this device's MMIO range in _Phys_Device_Write_Word's set of cases +SECTION .PD_Wr_Word + JMP_If_In eax, %1_BASE, %1_TOP, _PD_Write_Word_%1 + +;; Put this device's MMIO range in _Phys_Device_Read_Byte's set of cases +SECTION .PD_Rd_Byte + JMP_If_In eax, %1_BASE, %1_TOP, _PD_Read_Byte_%1 + +;; Put this device's MMIO range in _Phys_Device_Write_Byte's set of cases +SECTION .PD_Wr_Byte + JMP_If_In eax, %1_BASE, %1_TOP, _PD_Write_Byte_%1 + +;; Put this device's initialization routine in the list of +;; called by _Phys_Devices_Initialize: +SECTION .PD_Initialize + call _Device_Init_%1 + +;; Put this device's termination routine in the list of +;; called by _Phys_Devices_Shutdown: +SECTION .PD_Shutdown + call _Device_Shutdown_%1 + +section .text ;; Resume the .text section. + +%endmacro +;----------------------------------------------------------------------------- + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Prologues of Physical Device Bus Wires ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; Device Read Word -- we jump here from _Virt_Read_Word if pAddr in range +;----------------------------------------------------------------------------- +section .PD_Rd_Word progbits exec alloc nowrite +_Phys_Device_Read_Word: +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Device Write Word -- we jump here from _Virt_Write_Word if pAddr in range +;----------------------------------------------------------------------------- +section .PD_Wr_Word progbits exec alloc nowrite +_Phys_Device_Write_Word: +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Device Read Byte -- we jump here from _Virt_Read_Byte if pAddr in range +;----------------------------------------------------------------------------- +section .PD_Rd_Byte progbits exec alloc nowrite +_Phys_Device_Read_Byte: +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Device Write Byte -- we jump here from _Virt_Write_Byte if pAddr in range +;----------------------------------------------------------------------------- +section .PD_Wr_Byte progbits exec alloc nowrite +_Phys_Device_Write_Byte: +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Device Initialization +;----------------------------------------------------------------------------- +section .PD_Initialize progbits exec alloc nowrite +_Phys_Devices_Initialize: +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Device Shutdown +;----------------------------------------------------------------------------- +section .PD_Shutdown progbits exec alloc nowrite +_Phys_Devices_Shutdown: +;----------------------------------------------------------------------------- + +;; ALL Device implementations MUST be included from: +%include "devices/devices.asm" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Epilogues of Physical Device Bus Wires ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +SECTION .PD_Rd_Word + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Unknown Device ? + xor eax, eax ; Always return 0 + ACHTUNG "Read Word from Unknown Device?" ; TODO: print detail + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +SECTION .PD_Wr_Word + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Unknown Device ? + ACHTUNG "Write Word to Unknown Device?" ; TODO: print detail + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +SECTION .PD_Rd_Byte + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Unknown Device ? + xor eax, eax ; Always return 0 + ACHTUNG "Read Byte from Unknown Device?" ; TODO: print detail + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +SECTION .PD_Wr_Byte + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Unknown Device ? + ACHTUNG "Write Byte to Unknown Device?" ; TODO: print detail + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +SECTION .PD_Initialize + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +SECTION .PD_Shutdown + ret +;----------------------------------------------------------------------------- + +;; Back to code section: +section .text diff -uNr a/m/cpustate.asm b/m/cpustate.asm --- a/m/cpustate.asm false +++ b/m/cpustate.asm 6ceecb9422f3d01b04ed2ecb183fb96665ff969087e2967eccef2703337401afb4f0ec91e4b7efda625ae4534ba1c3daba539a1fc794b2e594d7404ddf72399c @@ -0,0 +1,142 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; MIPS Processor State. +; Note: PC, nPC, CP0_Status, CP0_Cause, CP0_Compare, are housed in x86 regs. +struc MCPU + .Regs resd 32 ; The standard MIPS Register Set + .LO resd 1 ; Multiplication/division results - Low Half + .HI resd 1 ; Multiplication/division results - High Half + .CP0_Index resd 1 ; Index into the TLB array + .CP0_EntryHi resd 1 ; High-order portion of the TLB entry + .CP0_EntryLo0 resd 1 ; Low portion of TLB entry for even-#'d pages + .CP0_EntryLo1 resd 1 ; Low portion of TLB entry for odd-#'d pages + .CP0_Context resd 1 + .CP0_Wired resd 1 ; The number of fixed ('wired') TLB entries + .CP0_Epc resd 1 ; Exception program counter return address + .CP0_BadVAddr resd 1 ; Addr. of most recent addr.-caused exception + .CP0_ErrorEpc resd 1 ; Program counter at last exception + .CP0_PageMask resd 1 ; Control variable page sizes in TLB entries + ;; The TLB: + .TLB_Entries resd TLB_ENTRIES_COUNT ; TLB entries (without PFN) + .TLB_PFN_Even resd TLB_ENTRIES_COUNT ; TLB PFN0 + .TLB_PFN_Odd resd TLB_ENTRIES_COUNT ; TLB PFN1 +endstruc +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Refer to the N-th TLB Entry: +;----------------------------------------------------------------------------- +%define TLB_E(N) dword [M_Base_32 + MCPU.TLB_Entries + 4 * (N)] +; N-th PFN : +%define TLB_PFN_E(N) dword [M_Base_32 + MCPU.TLB_PFN_Even + 4 * (N)] +%define TLB_PFN_O(N) dword [M_Base_32 + MCPU.TLB_PFN_Odd + 4 * (N)] +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +section .bss +align GRAIN ; MIPS CPU State + MIPS_State resb MCPU_size +section .text +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Global 'fast' MIPS State: +; TODO: is it possible to use the upper halves of the 64bit regs for anything? +; ... or entirely worthless from intel's idiocy of 'auto-zero on mov' ? +;----------------------------------------------------------------------------- +%define Flag_Reg edi ; Delay, Exception, etc flags +%define RAM_Floor rsi ; Physical (x86) address of 1st RAM word +%define RAM_Ceiling r8 ; Physical (x86) address of last RAM word +%define PC r9d ; Current Program Counter +%define nPC r10d ; 'Next' Program Counter +%define CP0_Status r11d ; Processor status and control +%define CP0_Cause r12d ; Cause of last general exception +%define CP0_Count r13d ; Processor cycle count +%define CP0_Compare r14d ; Timer interrupt control +%define AUX r15d ; Additional TMP for certain ops + +; TODO: 'Suspend to RAM' routine for all of the above. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Access to MIPS Registers that live in MCPU (Emulator State) : +;----------------------------------------------------------------------------- +; Refer to given MIPS special Reg: +;----------------------------------------------------------------------------- +%define Sr(N) dword [M_Base_32 + MCPU. %+ N] +;----------------------------------------------------------------------------- +; Refer to Nth Reg: +;----------------------------------------------------------------------------- +%define R(N) Sr(Regs + 4 * (N)) +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Update given MIPS Reg # with new value, but always avoid overwriting R(0) +;----------------------------------------------------------------------------- +; TODO: measure if this is actually faster than simply R(31) := 0 every time +%macro Wr_Reg 2 ; params: %1: Reg # %2: PC reg with new value + xor AUX, AUX ; Clear AUX + test %1, %1 ; Set Z if destination reg # is R(0) + cmovz %2, AUX ; If Z: Replace written value with zero + mov R(%1), %2 ; Regs[rD] := new value + ;;;; alt variant: + ; mov R(%1), %2 ; Regs[rD] := new value + ; mov R(0), 0 + ;;;; TODO: which is actually faster ? +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Init MIPS CPU: +;----------------------------------------------------------------------------- +_cpu_reset: + xor eax, eax ; eax := 0 + mov AUX, eax ; Clear AUX register + mov M_Base_64, MIPS_State ; Set RBP to point to MIPS CPU State + ;; Init 'fast' MIPS Regs: + mov PC, INIT_PC + mov nPC, eax + mov CP0_Status, eax + mov CP0_Cause, eax + mov CP0_Count, eax + mov CP0_Compare, eax + ;; Init 'slow' MIPS Regs: + mov ecx, 0 +_init_reg: + mov R(ecx), eax + inc ecx + cmp ecx, 32 + jb _init_reg + xor ecx, ecx + mov Sr(HI), eax + mov Sr(LO), eax + mov Sr(CP0_Index), eax + mov Sr(CP0_EntryHi), eax + mov Sr(CP0_EntryLo0), eax + mov Sr(CP0_EntryLo1), eax + mov Sr(CP0_Context), eax + mov Sr(CP0_Wired), eax + mov Sr(CP0_Epc), eax + mov Sr(CP0_BadVAddr), eax + mov Sr(CP0_ErrorEpc), eax + Flg_Clear_All ; Reset all misc Flags to 0 + bts CP0_Status, CP0St_ERL ; Start in kernel mode w/ unmapped useg + ret +;----------------------------------------------------------------------------- diff -uNr a/m/devices/clock.asm b/m/devices/clock.asm --- a/m/devices/clock.asm false +++ b/m/devices/clock.asm f53ed4111870f8286ef3de18eb11e03b3ed2ec4575772ef9d3e581d98fdee7453a1bcd9fdb6054f62fbcd9e4faf78cc9764a231c02fe94bd06f67d94d616b2b1 @@ -0,0 +1,87 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO: Make the interval programmable. Right now hogs MMIO entirely in vain. + +;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Interval Clock Device ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; Interval Clock MMIO: +DECLARE_BUS_DEVICE CLOCK, 0x420, 0x42F +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Interval Clock IRQ: +;----------------------------------------------------------------------------- +%define CLOCK_IRQ 3 ; UART0 Receiver Slave IRQ +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Interval Clock State +;----------------------------------------------------------------------------- +section .bss + CLOCK_Die resd 1 ; Shutdown Trigger +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +_PD_Read_Word_CLOCK: ; Word reads from CLOCK always 0 + xor eax, eax + ret ; Fin. +;----------------------------------------------------------------------------- +_PD_Write_Word_CLOCK: ; Word writes to CLOCK do nothing! + ret +;----------------------------------------------------------------------------- +_PD_Read_Byte_CLOCK: + xor eax, eax ; Read Byte from CLOCK: always 0 + ret +;----------------------------------------------------------------------------- +_PD_Write_Byte_CLOCK: + ret ; Fin. +;----------------------------------------------------------------------------- +_Device_Init_CLOCK: + mov rdi, _Clock_Slave ; Clock Slave + call _Create_Thread ; Start the Clock Slave Thread + ret +;----------------------------------------------------------------------------- +_Device_Shutdown_CLOCK: ; Shutdown + mov dword [CLOCK_Die], 0x01 ; Ask (unblocked) slave to die + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +_Clock_Slave: + mov rax, _clock_interval_ts ; Set the 'nanosleep' interval + call _Nano_Sleep ; Sleep for the given interval (nS) + SetSlaveIRQ CLOCK_IRQ ; Invoke timer slave interrupt + cmp dword [CLOCK_Die], 0x01 ; time to die? + jne _Clock_Slave ; if not, keep going. + jmp _exit_thread ; terminate thread +;----------------------------------------------------------------------------- + +section .rodata + +;----------------------------------------------------------------------------- +; TS for Clock Slave interval: +_clock_interval_ts: + dq 0 ; seconds + dq TIMER_SLAVE_PERIOD ; nanoseconds +;----------------------------------------------------------------------------- diff -uNr a/m/devices/devices.asm b/m/devices/devices.asm --- a/m/devices/devices.asm false +++ b/m/devices/devices.asm ba3687d23de5ccf60651014123dd59fe60b32ecbf2101d6165f7a06448d1e031b340ac7baa886bcdfa375fd971100d45e2d2cbb8b1caf21d761949cb31f70864 @@ -0,0 +1,29 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; All Memory-Mapped Devices MUST be included here (and only here), +;; in DESCENDING order of their MMIO Address range's position in MMIO Segment: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +%include "rtc.asm" ; Real-Time Clock Device (MMIO: 0x430 - 0x434) +%include "clock.asm" ; Interval Clock Device (MMIO: 0x420 - 0x42F; IRQ 3) +%include "power.asm" ; Power Supply (MMIO: 0x410 - 0x410) +%include "uart.asm" ; UART0 Console (MMIO: 0x3F8 - 0x40C; IRQ 2) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff -uNr a/m/devices/power.asm b/m/devices/power.asm --- a/m/devices/power.asm false +++ b/m/devices/power.asm c4ef1f09a367612089de366e1d75f980928a48c40b2b30cb50fae20439ca5fbf59ad0aa71aa231aa0aa75833bb71de1f241a8a6f3fbd747a6bf91c17489a5d4c @@ -0,0 +1,58 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO: 'warm boot' command. Right now, 'shutdown' is the only supported cmd. + +;;;;;;;;;;;;;;;;;;;;;;;;; +;; Power Supply Device ;; +;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +;; Power Supply: +DECLARE_BUS_DEVICE POWER, 0x410, 0x410 +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +%define POWER_CMD_SHUTDOWN 0x01 ; 'Power off' immediately. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +_PD_Read_Word_POWER: ; Word reads from POWER: always 0 + xor eax, eax + ret ; Fin. +;----------------------------------------------------------------------------- +_PD_Write_Word_POWER: ; Word writes to POWER do nothing! + ret +;----------------------------------------------------------------------------- +_PD_Read_Byte_POWER: + xor eax, eax ; Read Byte from POWER: always 0 + ret +;----------------------------------------------------------------------------- +_PD_Write_Byte_POWER: + cmp dl, 1 ; 1 == shutdown + jne _PD_Write_Byte_POWER_No_Shutdown ; if != 1, do not shut down + Flg_On Shutdown ; Set the Shutdown flag. +_PD_Write_Byte_POWER_No_Shutdown: + ret ; Fin. +;----------------------------------------------------------------------------- +_Device_Init_POWER: ; No init needed + ret +;----------------------------------------------------------------------------- +_Device_Shutdown_POWER: ; Nothing needed + ret +;----------------------------------------------------------------------------- diff -uNr a/m/devices/rtc.asm b/m/devices/rtc.asm --- a/m/devices/rtc.asm false +++ b/m/devices/rtc.asm 1232cd80ad35a8a65c9767c3a9cdbe9ce5024f93f609b1f2af4ce225b436366e5b31596f698344ab45b25633382c983aa839b0ce017976614186021a46f11fab @@ -0,0 +1,70 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Real-Time Clock Device ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +;; Real-Time Clock MMIO: +DECLARE_BUS_DEVICE RTC, 0x430, 0x434 +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Epoch Time: 'Sec. since 00:00:00 Thursday, 1 January 1970' +%define RTC_REG_EPOCH_LO 0 ; Lower 32 bits of RTC Epoch Time +%define RTC_REG_EPOCH_HI 4 ; Upper 32 bits of RTC Epoch Time +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +_PD_Read_Word_RTC: ; Word reads from RTC: + sub eax, RTC_BASE ; Adjust for base of RTC MMIO + cmp eax, RTC_REG_EPOCH_LO ; Word at 0x0: Low 32 bits of time + je .rtc_epoch_low_word + cmp eax, RTC_REG_EPOCH_HI ; Word at 0x4: High 32 bits of time + je .rtc_epoch_high_word +.rtc_undefined_reg: ; If unknown reg (how?) : + ACHTUNG "Read Unknown RTC Reg?" + xor eax, eax ; ... return 0 always. + ret ; Fin. +.rtc_epoch_low_word: ; Get LOW 32 bits of Epoch Time : + call _Get_Epoch_Time ; Retrieve epoch time from host + mov eax, edx ; eax := low word + ret ; Fin. +.rtc_epoch_high_word: ; Get HIGH 32 bits of Epoch Time : + call _Get_Epoch_Time ; Retrieve epoch time from host + shr rdx, 32 ; get high word + mov eax, edx ; eax := high word + ret ; Fin. +;----------------------------------------------------------------------------- +_PD_Write_Word_RTC: ; Word writes to RTC do nothing! + ret +;----------------------------------------------------------------------------- +_PD_Read_Byte_RTC: + xor eax, eax ; Read Byte from RTC: always 0 + ret +;----------------------------------------------------------------------------- +_PD_Write_Byte_RTC: + ret ; Fin. +;----------------------------------------------------------------------------- +_Device_Init_RTC: ; Needs no init. + ret +;----------------------------------------------------------------------------- +_Device_Shutdown_RTC: ; Needs no shutdown. + ret +;----------------------------------------------------------------------------- diff -uNr a/m/devices/uart.asm b/m/devices/uart.asm --- a/m/devices/uart.asm false +++ b/m/devices/uart.asm 3c2674a38d90d63804bb65359bbf557c2aba45c46f55158975acb06508ec610d7dae55e9f29cac537bca67d3c2e7645d3f7425fc6e3127c4304b035d5d786013 @@ -0,0 +1,449 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;; +;; UART Console Device ;; +;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; Console UART MMIO: +DECLARE_BUS_DEVICE UART, 0x3F8, 0x40C +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Console UART IRQ: +;----------------------------------------------------------------------------- +%define UART0_IRQ 2 ; UART0 Receiver Slave IRQ +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +_PD_Read_Word_UART: ; Word reads from UART: always 0 + xor eax, eax + ret ; Fin. +;----------------------------------------------------------------------------- +_PD_Write_Word_UART: ; Word writes to POWER do nothing! + ret +;----------------------------------------------------------------------------- +_PD_Read_Byte_UART: ; Read Byte from UART + call _UART_Read_Reg ; Read this byte from UART + mov eax, edx ; Return the resulting byte. + ret ; Fin. +;----------------------------------------------------------------------------- +_PD_Write_Byte_UART: ; Write Byte to UART + call _UART_Write_Reg ; write this byte to UART + ret ; Fin. +;----------------------------------------------------------------------------- +_Device_Init_UART: + call _Cure_TTY ; Cure the tty: + call _UART_Reset ; Reset the UART + mov rdi, _UART_Slave ; UART Slave + call _Create_Thread ; Start the UART Slave Thread + ret +;----------------------------------------------------------------------------- +_Device_Shutdown_UART: ; Nothing needed + mov dword [UART_Die], 0x01 ; Ask (unblocked) slave to die + call _Uncure_TTY ; bring back the old tty settings, for clean shell + ret +;----------------------------------------------------------------------------- + + +;----------------------------------------------------------------------------- +;; UART State +;----------------------------------------------------------------------------- +section .bss + UART0 resb UART_State_size ; UART-0 State + UART_Die resd 1 ; Shutdown Trigger +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +; Slave Thread which connects UART0 to linux console +;----------------------------------------------------------------------------- +_UART_Slave: + call _Read_Char_Blocking + mov dl, byte [IOBUF] + call _UART_Receive_Byte ; put DL in the UART receiver queue + SetSlaveIRQ UART0_IRQ ; trigger the slave IRQ + cmp dword [UART_Die], 0x01 ; time to die? + jne _UART_Slave ; if not, keep going. + jmp _exit_thread ; terminate thread +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; UARTism +;----------------------------------------------------------------------------- +;; UART State +struc UART_State + .LCR resb 1 ; Line Control + .LSR resb 1 ; Line Status + .MSR resb 1 ; Modem Status + .IIR resb 1 ; Interrupt ID + .IER resb 1 ; Interrupt Enable + .DLL resb 1 ; + .DLH resb 1 ; + .FCR resb 1 ; FIFO Control + .MCR resb 1 ; Modem Control + .SCR resb 1 ; Modem Control + ;; FIFO: + .FIFO resb 32 ; The FIFO itself + .FIFO_First resb 1 ; index of 1st byte in the FIFO + .FIFO_Last resb 1 ; index of last byte in the FIFO + .FIFO_Count resb 1 ; number of bytes in the FIFO +endstruc +;----------------------------------------------------------------------------- + +%define UART_LSR_DATA_READY 0x1 +%define UART_LSR_FIFO_EMPTY 0x20 +%define UART_LSR_TRANSMITTER_EMPTY 0x40 + +; Enable Transmitter holding register int. +%define UART_IER_THRI 0x02 + +; Enable receiver data interrupt +%define UART_IER_RDI 0x01 + +; Modem status interrupt (Low priority) +%define UART_IIR_MSI 0x00 + +%define UART_IIR_NO_INT 0x01 + +; Transmitter holding register empty +%define UART_IIR_THRI 0x02 + +; Receiver data interrupt +%define UART_IIR_RDI 0x04 + +; Receiver line status interrupt (High p.) +%define UART_IIR_RLSI 0x06 + +; Character timeout +%define UART_IIR_CTI 0x0c + +; Divisor latch access bit +%define UART_LCR_DLAB 0x80 + +; R/W: Divisor Latch Low, DLAB=1 +%define UART_DLL 0 + +; R/W: Divisor Latch High, DLAB=1 +%define UART_DLH 1 + +; R/W: Interrupt Enable Register +%define UART_IER 1 + +; R: Interrupt ID Register +%define UART_IIR 2 + +; W: FIFO Control Register +%define UART_FCR 2 + +; R/W: Line Control Register +%define UART_LCR 3 + +; W: Modem Control Register +%define UART_MCR 4 + +; R: Line Status Register +%define UART_LSR 5 + +; R: Modem Status Register +%define UART_MSR 6 + +; R/W: +%define UART_SCR 7 +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; Access to UART regs: +%define U0r(R) byte [UART0 + UART_State. %+ R] +;----------------------------------------------------------------------------- +;; Clear FIFO +;----------------------------------------------------------------------------- +_UART_FIFO_Clear: + mov U0r(FIFO_Last), 0 + mov U0r(FIFO_First), 0 + mov U0r(FIFO_Count), 0 + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; Take a byte from the UART FIFO. Byte will be in DL. +;----------------------------------------------------------------------------- +_UART_FIFO_Get: + xor edx, edx ; edx := 0 + cmp U0r(FIFO_Count), 0 ; is FIFO nonempty? + je _UART_FIFO_Get_Nope ; ... if empty, skip and return 0 + ;; if FIFO is nonempty: + xor eax, eax ; eax := 0 + mov al, U0r(FIFO_First) ; al := fifo_first + mov dl, U0r(FIFO + eax) ; dl := FIFO[al] + inc eax ; eax := eax + 1 + and eax, 0x1F ; eax := eax & 0x1f + mov U0r(FIFO_First), al ; fifo_first := al + mov al, U0r(FIFO_Count) ; al := fifo_count + test eax, eax ; is al 0 ? + jz _UART_FIFO_Get_Nope ; ... if 0, skip and do not update count + dec eax ; eax := eax - 1 + mov U0r(FIFO_Count), al ; fifo_count := al +_UART_FIFO_Get_Nope: + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; Reset UART-0 +;----------------------------------------------------------------------------- +_UART_Reset: + mov U0r(LCR), 3 + mov U0r(LSR), UART_LSR_TRANSMITTER_EMPTY | UART_LSR_FIFO_EMPTY + mov U0r(MSR), 0 + mov U0r(IIR), UART_IIR_NO_INT + mov U0r(IER), 0 + mov U0r(DLL), 0 + mov U0r(DLH), 0 + mov U0r(FCR), 0 + mov U0r(MCR), 0 + mov U0r(SCR), 0 + call _UART_FIFO_Clear ; zap the FIFO + ret +;----------------------------------------------------------------------------- +;; Update UART IRQ +;----------------------------------------------------------------------------- +_UART_IRQ: + test U0r(LSR), UART_LSR_DATA_READY + jz _UART_IRQ_Not_RDI + test U0r(IER), UART_IER_RDI + jz _UART_IRQ_Not_RDI +_UART_IRQ_RDI: + mov U0r(IIR), UART_IIR_RDI ; IIR=UART_IIR_RDI + jmp _UART_IRQ_Maybe_Pending ; See if interrupt pending +_UART_IRQ_Not_RDI: + test U0r(LSR), UART_LSR_FIFO_EMPTY + jz _UART_IRQ_Not_THRI + test U0r(IER), UART_IER_THRI + jz _UART_IRQ_Not_THRI +_UART_IRQ_THRI: + mov U0r(IIR), UART_IIR_THRI ; IIR=UART_IIR_THRI + jmp _UART_IRQ_Maybe_Pending ; See if interrupt pending +_UART_IRQ_Not_THRI: + mov U0r(IIR), UART_IIR_NO_INT ; IIR=UART_IIR_NO_INT +_UART_IRQ_Maybe_Pending: ; See if interrupt pending + ;; see if interrupt pending + cmp U0r(IIR), UART_IIR_NO_INT ; IIR==UART_IIR_NO_INT ? + jne _UART_IRQ_Pending ; if !=, interrupt is pending +_UART_IRQ_Not_Pending: ; interrupt is not pending: + ClrIRQ UART0_IRQ + ret +_UART_IRQ_Pending: ; interrupt is pending: + SetIRQ UART0_IRQ + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; Place a byte (DL) into the UART's receiver buffer. +; TODO: mutex? +;----------------------------------------------------------------------------- +_UART_Receive_Byte: + xor eax, eax ; eax := 0 + mov al, U0r(FIFO_Last) ; al := fifo_last + mov U0r(FIFO + eax), dl ; FIFO[al] := dl + inc eax ; eax := eax + 1 + and eax, 0x1F ; eax := eax & 0x1f + mov U0r(FIFO_Last), al ; fifo_last := al + mov al, U0r(FIFO_Count) ; eax := fifo_count + inc eax ; eax := eax + 1 + mov edx, 32 ; edx := 32 + cmp eax, edx ; is eax > 32 ? + cmovg eax, edx ; ... if yes, eax := 32 + mov U0r(FIFO_Count), al ; fifo_count := al + or U0r(LSR), UART_LSR_DATA_READY ; set the 'ready' flag + call _UART_IRQ ; update IRQ + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; Read UART Register. Offset in EAX, result in DL. +;----------------------------------------------------------------------------- +_UART_Read_Reg: + xor edx, edx ; result := 0 + sub eax, UART_BASE ; Adjust for base of UART + and eax, 0x7 ; Adjusted offset is a 4-bit quantity + test U0r(LCR), UART_LCR_DLAB + jz _UART_Read_Reg_Not_DLAB +_UART_Read_Reg_DLAB: + cmp eax, UART_DLL ; offset=DLL? + je _UART_Read_Reg_DLAB_DLL + cmp eax, UART_DLH ; offset=DLH? + je _UART_Read_Reg_DLAB_DLH + jmp _UART_Read_Reg_Not_DLAB ; neither of these +_UART_Read_Reg_DLAB_DLL: + mov dl, U0r(DLL) ; return DLL + ret +_UART_Read_Reg_DLAB_DLH: + mov dl, U0r(DLH) ; return DLH + ret +_UART_Read_Reg_Not_DLAB: + cmp eax, 0 ; offset=0 ? + je _UART_Read_Reg_0 + cmp eax, UART_IER ; offset=IER ? + je _UART_Read_Reg_IER + cmp eax, UART_MSR ; offset=MSR ? + je _UART_Read_Reg_MSR + cmp eax, UART_MCR ; offset=MCR ? + je _UART_Read_Reg_MCR + cmp eax, UART_IIR ; offset=IIR ? + je _UART_Read_Reg_IIR + cmp eax, UART_LCR ; offset=LCR ? + je _UART_Read_Reg_LCR + cmp eax, UART_LSR ; offset=LSR ? + je _UART_Read_Reg_LSR + cmp eax, UART_SCR ; offset=SCR ? + je _UART_Read_Reg_SCR + ACHTUNG "Bad UART Read Command." ; TODO: print detail + ret + +_UART_Read_Reg_0: + cmp U0r(FIFO_Count), 0 ; is FIFO nonempty? + je _UART_Read_Reg_0_Empty ; ... if empty, skip + ;; FIFO is not empty: + call _UART_FIFO_Get ; get byte from FIFO + and U0r(LSR), ~UART_LSR_DATA_READY ; lower 'ready' flag + cmp U0r(FIFO_Count), 0 ; is FIFO still nonempty? + je _UART_Read_Reg_0_Empty ; ... if empty, leave 'ready' flag down + ;; FIFO still has data: + or U0r(LSR), UART_LSR_DATA_READY ; else, raise the 'ready' flag. +_UART_Read_Reg_0_Empty: + call _UART_IRQ ; update IRQ + ret ; return DL + +_UART_Read_Reg_IER: + mov dl, U0r(IER) + and edx, 0xF + ret ; return IER & 0xF + +_UART_Read_Reg_MSR: + mov dl, U0r(MSR) + ret ; return MSR + +_UART_Read_Reg_MCR: + mov dl, U0r(MCR) + ret ; return MCR + +_UART_Read_Reg_IIR: + mov dl, U0r(IIR) + ret ; return IIR + +_UART_Read_Reg_LCR: + mov dl, U0r(LCR) + ret ; return LCR + +_UART_Read_Reg_LSR: + cmp U0r(FIFO_Count), 0 ; is FIFO nonempty? + je _UART_Read_Reg_LSR_Empty ; if empty, go to 'empty' + ;; FIFO is not empty: + or U0r(LSR), UART_LSR_DATA_READY ; raise the 'ready' flag. + jmp _UART_Read_Reg_LSR_Done ; return +_UART_Read_Reg_LSR_Empty: + and U0r(LSR), ~UART_LSR_DATA_READY ; lower the 'ready' flag +_UART_Read_Reg_LSR_Done: + mov dl, U0r(LSR) + ret ; return LSR + +_UART_Read_Reg_SCR: + mov dl, U0r(SCR) + ret ; return SCR +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; Write UART Register. Offset in EAX, payload in DL. +;----------------------------------------------------------------------------- +_UART_Write_Reg: + sub eax, UART_BASE ; Adjust for base of UART + and eax, 0x7 ; Adjusted offset is a 4-bit quantity + test U0r(LCR), UART_LCR_DLAB + jz _UART_Write_Reg_Not_DLAB +_UART_Write_Reg_DLAB: + cmp eax, UART_DLL ; offset=DLL? + je _UART_Write_Reg_DLAB_DLL + cmp eax, UART_DLH ; offset=DLH? + je _UART_Write_Reg_DLAB_DLH + jmp _UART_Write_Reg_Not_DLAB ; neither of these +_UART_Write_Reg_DLAB_DLL: + mov U0r(DLL), dl ; write DLL + ret +_UART_Write_Reg_DLAB_DLH: + mov U0r(DLH), dl ; write DLH + ret +_UART_Write_Reg_Not_DLAB: + cmp eax, 0 ; offset=0 ? + je _UART_Write_Reg_0 + cmp eax, UART_IER ; offset=IER? + je _UART_Write_Reg_IER + cmp eax, UART_FCR ; offset=FCR? + je _UART_Write_Reg_FCR + cmp eax, UART_LCR ; offset=LCR? + je _UART_Write_Reg_LCR + cmp eax, UART_MCR ; offset=MCR? + je _UART_Write_Reg_MCR + cmp eax, UART_SCR ; offset=SCR? + je _UART_Write_Reg_SCR + ACHTUNG "Bad UART Write Command." ; TODO: print detail + ret + +_UART_Write_Reg_0: + and U0r(LSR), ~UART_LSR_FIFO_EMPTY + test U0r(MCR), (1 << 4) + jz _UART_Write_Reg_0_Not_Loopback +_UART_Write_Reg_0_Loopback: + call _UART_Receive_Byte ; put DL in the receiver FIFO + jmp _UART_Write_Reg_0_Fin ; wrap up +_UART_Write_Reg_0_Not_Loopback: ; if NOT loopback : + call _Write_Char ; ... then write char in DL to console +_UART_Write_Reg_0_Fin: + or U0r(LSR), UART_LSR_FIFO_EMPTY ; mark the send buffer as empty + call _UART_IRQ ; refresh IRQ + ret ; fin + +_UART_Write_Reg_IER: + and edx, 0xF ; 4-bit qty + mov U0r(IER), dl ; IER := dl & 0xF + call _UART_IRQ ; refresh IRQ + ret ; fin + +_UART_Write_Reg_FCR: + mov U0r(FCR), dl ; FCR := dl + test dl, 0x2 ; if FCR & 2: + jz _UART_Write_Reg_FCR_No_Clear + call _UART_FIFO_Clear ; ... then zap the FIFO. +_UART_Write_Reg_FCR_No_Clear: + ret + +_UART_Write_Reg_LCR: + mov U0r(LCR), dl ; LCR := dl + ret ; fin + +_UART_Write_Reg_MCR: + mov U0r(MCR), dl ; MCR := dl + ret ; fin + +_UART_Write_Reg_SCR: + mov U0r(SCR), dl ; SCR := dl + ret ; fin +;----------------------------------------------------------------------------- diff -uNr a/m/flags.asm b/m/flags.asm --- a/m/flags.asm false +++ b/m/flags.asm a7d2be62abfd1319dbafaa5f8eedd8718c3364a59d89e89c611d0a95d04ba6d81d089e93a65853f85ff3fdd45b413c2c582cbe9e7b082e40f18f206d2cafdbb1 @@ -0,0 +1,61 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; Emulator Flags (kept in Flag_Reg) +;----------------------------------------------------------------------------- +%define InDelaySlot 0 +%define RunningDelaySlot 1 +%define Waiting 2 +%define LL_Bit 3 +%define IsWriting 4 +%define ExcWasTLBNoMatch 5 +%define Shutdown 6 +;----------------------------------------------------------------------------- +; Set a given Flag: +%macro Flg_On 1 + bts Flag_Reg, %1 ; Set the given Flag. +%endmacro +;----------------------------------------------------------------------------- +; Clear a given Flag: +%macro Flg_Off 1 + btr Flag_Reg, %1 ; Clear the given Flag. +%endmacro + +%define Flg_GOF Flg_Off ; BTR inst also gives the old value +;----------------------------------------------------------------------------- +; Get a given Flag (into CF) : +%macro Flg_Get 1 + bt Flag_Reg, %1 ; Read the given Flag. +%endmacro +;----------------------------------------------------------------------------- +; Clear all Flags: +%macro Flg_Clear_All 0 + xor Flag_Reg, Flag_Reg ; Clear all Flags. +%endmacro +;----------------------------------------------------------------------------- +; Copy one Flag to another Flag. Uses EAX for scratch. +%macro Flg_Cpy 2 ; %1 : destination Flag; %2 : source Flag. + Flg_Off %1 ; Clear the Destination Flag + xor eax, eax ; eax := 0 + Flg_Get %2 ; CF := Source Flag + setc al ; AL := CF (value of Source Flag) + shl eax, %1 ; Slide this bit up to destination pos. + or Flag_Reg, eax ; Write the destination Flag +%endmacro +;----------------------------------------------------------------------------- diff -uNr a/m/i_decode.asm b/m/i_decode.asm --- a/m/i_decode.asm false +++ b/m/i_decode.asm 46635728670946901edca4062d650365295341cf87401ac4fbb7f78423faf76b5c246a07ea8e5fc76fcd4b49ff669102f556f47f7c5a247670fd00ffee665b80 @@ -0,0 +1,284 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; Helper macros for MIPS instruction operands. +; All presume that current MIPS instruction is found in r_I. +; Note that variable shifts on x86 require slide amount to reside in CL. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; These constants are fixed for the MIPS32 architecture and will NEVER change: +;----------------------------------------------------------------------------- +; Masks for Instruction Fields: +%define rD_Mask 0xF800 ; Mask for rD Field +%define rS_Mask 0x3E00000 ; Mask for rS Field +%define rT_Mask 0x1F0000 ; Mask for rT Field +%define Imm_Mask 0xFFFF ; Mask for Imm Field +%define Shamt_Mask 0x7C0 ; Mask for Shamt Field +%define J_Mask 0x3FFFFFF ; Mask for J Field +%define Q_Mask 0x7 ; Mask for Q Field (MFC0/MTC0 instructions) +;----------------------------------------------------------------------------- +; Shifts for Instruction Fields (Imm doesn't need one) : +%define rD_Shr 11 ; Right Shift for rD Field +%define rS_Shr 21 ; Right Shift for rS Field +%define rT_Shr 16 ; Right Shift for rT Field +%define Shamt_Shr 6 ; Right Shift for Shamt Field +%define J_Shl 2 ; Left Shift for J Field +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Register Placements +;----------------------------------------------------------------------------- + +; To point to MIPS state: +%define M_Base_64 rbp ; Pointer to base of MIPS data structure +%define M_Base_32 ebp ; 32-bit component of above + +;----------------------------------------------------------------------------- +; Scratch Registers used inside Opcodes: +;----------------------------------------------------------------------------- +%define rD eax ; Where to extract rD Field +%define Imm rD ; Where IType's Imm goes (they have no rD) +%define JTarg rD ; Where JType's J field goes + +; x86 is braindamaged and DEMANDS variable shift slides in CL. So rS is ECX. +%define rS ecx ; Where to extract rS Field +%define rS_LowByte cl ; Low byte of rS, for some 'flag' ops +%define r_Shamt rS ; must be also this (see above) +%define r_Q rS ; Q field in the coprocessor instructions + +%define rT ebx ; Where to extract rT Field + +; Scratch Register for intermediates: +%define TMP edx ; Scratch register used in all ops +%define TMP_LowByte dl ; Low byte of TMP, for some 'flag' ops + +; After we decode all fields, we no longer need the original instruction : +%define r_I TMP ; Copy of currently-executing instruction +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Instruction Field Manipulations. Arranged so as to satisfy the x86 pipeline. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract rD, rS, rT fields from the currently-executing MIPS instruction +; For instructions encoded as ???? ??ss ssst tttt dddd d??? ???? ???? +;----------------------------------------------------------------------------- +%macro RType 0 ; no params + mov rD, rD_Mask ; Mask for rD Field + mov rS, rS_Mask ; Mask for rS Field + mov rT, rT_Mask ; Mask for rT Field + and rD, r_I ; Mask orig. inst. to get rD Field + and rS, r_I ; Mask orig. inst. to get rS Field + and rT, r_I ; Mask orig. inst. to get rT Field + shr rD, rD_Shr ; rD is now rD Index + shr rS, rS_Shr ; rS is now rS Index + shr rT, rT_Shr ; rT is now rT Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract only rS field from the currently-executing MIPS instruction +; For instructions encoded as ???? ??ss sss? ???? ???? ???? ???? ???? +;----------------------------------------------------------------------------- +%macro RType_S_Only 0 ; no params + mov rS, rS_Mask ; Mask for rS Field + and rS, r_I ; Mask orig. inst. to get rS Field + shr rS, rS_Shr ; rS is now rS Index +%endmacro +;----------------------------------------------------------------------------- + +; Works same +%define IType_S_Only RType_S_Only + +;----------------------------------------------------------------------------- +; Extract only rS and rT fields from the currently-executing MIPS instruction +; For instructions encoded as ???? ??ss ssst tttt ???? ???? ???? ???? +;----------------------------------------------------------------------------- +%macro RType_S_T_Only 0 ; no params + mov rS, rS_Mask ; Mask for rS Field + mov rT, rT_Mask ; Mask for rT Field + and rS, r_I ; Mask orig. inst. to get rS Field + and rT, r_I ; Mask orig. inst. to get rT Field + shr rS, rS_Shr ; rS is now rS Index + shr rT, rT_Shr ; rT is now rT Index +%endmacro +;----------------------------------------------------------------------------- + +; Works same +%define IType_S_T_Only RType_S_T_Only + +;----------------------------------------------------------------------------- +; Extract only rS and rD fields from the currently-executing MIPS instruction +; For instructions encoded as ???? ??ss sss? ???? dddd d??? ???? ???? +;----------------------------------------------------------------------------- +%macro RType_S_D_Only 0 ; no params + mov rD, rD_Mask ; Mask for rD Field + mov rS, rS_Mask ; Mask for rS Field + and rD, r_I ; Mask orig. inst. to get rD Field + and rS, r_I ; Mask orig. inst. to get rS Field + shr rD, rD_Shr ; rD is now rD Index + shr rS, rS_Shr ; rS is now rS Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract only rD field from the currently-executing MIPS instruction +; For instructions encoded as ???? ???? ???? ???? dddd d??? ???? ???? +;----------------------------------------------------------------------------- +%macro RType_D_Only 0 ; no params + mov rD, rD_Mask ; Mask for rD Field + and rD, r_I ; Mask orig. inst. to get rD Field + shr rD, rD_Shr ; rD is now rD Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract rD, rT, Shamt fields from the currently-executing MIPS instruction +; For instructions encoded as ???? ???? ???t tttt dddd dhhh hh?? ???? +;----------------------------------------------------------------------------- +%macro RType_D_T_Shamt 0 ; no params + mov rD, rD_Mask ; Mask for rD Field + mov r_Shamt, Shamt_Mask ; Mask for Shamt Field + mov rT, rT_Mask ; Mask for rT Field + and rD, r_I ; Mask orig. inst. to get rD Field + and r_Shamt, r_I ; Mask orig. inst. to get Shamt Field + and rT, r_I ; Mask orig. inst. to get rT Field + shr rD, rD_Shr ; rD is now rD Index + shr r_Shamt, Shamt_Shr ; r_Shamt is now Shamt value + shr rT, rT_Shr ; rT is now rT Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract rD, rT, Q fields from the currently-executing MIPS instruction +; For MFC/MTC instructions encoded as 0100 0000 ?00t tttt dddd d000 0000 0qqq +;----------------------------------------------------------------------------- +%macro CPType 0 ; no params + mov rD, rD_Mask ; Mask for rD Field + mov r_Q, Q_Mask ; Mask for Q Field + mov rT, rT_Mask ; Mask for rT Field + and rD, r_I ; Mask orig. inst. to get rD Field + and r_Q, r_I ; Mask orig. inst. to get Q Field + and rT, r_I ; Mask orig. inst. to get rT Field + shr rD, rD_Shr ; rD is now rD Index + shr rT, rT_Shr ; rT is now rT Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract Imm, rS, rT fields from the currently-executing MIPS instruction +;----------------------------------------------------------------------------- +%macro IType 0 ; no params + mov Imm, Imm_Mask ; Mask for Imm Field; needs no shift + mov rS, rS_Mask ; Mask for rS Field + mov rT, rT_Mask ; Mask for rT Field + and Imm, r_I ; Mask orig. inst. to get Imm Field + and rS, r_I ; Mask orig. inst. to get rS Field + and rT, r_I ; Mask orig. inst. to get rT Field + shr rS, rS_Shr ; rS is now rS Index + shr rT, rT_Shr ; rT is now rT Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract only Imm, rS fields from the currently-executing MIPS instruction +;----------------------------------------------------------------------------- +%macro IType_I_S_Only 0 ; no params + mov Imm, Imm_Mask ; Mask for Imm Field; needs no shift + mov rS, rS_Mask ; Mask for rS Field + and Imm, r_I ; Mask orig. inst. to get Imm Field + and rS, r_I ; Mask orig. inst. to get rS Field + shr rS, rS_Shr ; rS is now rS Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract only Imm field from the currently-executing MIPS instruction +;----------------------------------------------------------------------------- +%macro IType_I_Only 0 ; no params + mov Imm, Imm_Mask ; Mask for Imm Field; needs no shift + and Imm, r_I ; Mask orig. inst. to get Imm Field +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract Imm, rT fields from the currently-executing MIPS instruction +;----------------------------------------------------------------------------- +%macro IType_I_T_Only 0 ; no params + mov Imm, Imm_Mask ; Mask for Imm Field; needs no shift + mov rT, rT_Mask ; Mask for rT Field + and Imm, r_I ; Mask orig. inst. to get Imm Field + and rT, r_I ; Mask orig. inst. to get rT Field + shr rT, rT_Shr ; rT is now rT Index +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Extract J field from the currently-executing MIPS instruction +; Encoding: ???? 11ii iiii iiii iiii iiii iiii iiii +;----------------------------------------------------------------------------- +%macro JType 0 ; no params + mov JTarg, J_Mask ; Mask for J Field; needs no shift + and JTarg, r_I ; Mask orig. inst. to get J Field + shl JTarg, J_Shl ; Shift the J Field to get Target Address +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Sign-Extend((16bit Address) * 4) (where 18th bit is considered 'sign') +;----------------------------------------------------------------------------- +%macro SX18_4N 1 ; param is A, the address to extend + shl %1, 16 ; upper 16 bits are now equal to A + sar %1, 14 ; Shift back to A*4, extending sign bit. +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Sign-Extend(16bit Address) (where 16th bit is considered 'sign') +;----------------------------------------------------------------------------- +%macro SX16 1 ; param is A, the address to extend + shl %1, 16 ; upper 16 bits are now equal to A + sar %1, 16 ; Shift back to A, extending sign bit. +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Denote privileged (permitted in Kernel-Mode strictly) instructions. +;----------------------------------------------------------------------------- +%macro PRIVILEGED 0 + bt CP0_Status, CP0St_UM ; CF := CP0St_UM Flag + jnc %%proceed ; If UM = 0: Kernel Mode, proceed. + test CP0_Status, (1 << CP0St_EXL) | (1 << CP0St_ERL) ; EXL or ERL + jnz %%proceed ; If EXL && ERL: Kernel Mode, proceed. + ;; We are NOT in kernel mode, but trying to execute a privileged inst: + SetEXC EXC_RI ; Set the 'Reserved Instr.' Exception. + jmp _Handle_Exception ; Go straight to exception handler. +%%proceed ; We're in Kernel Mode, so proceed with the privileged instruction. +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Store the significant 32 bits of locative +;----------------------------------------------------------------------------- +%macro A32 1 ; %1: locative +dd %1 +%endmacro +;----------------------------------------------------------------------------- diff -uNr a/m/irq.asm b/m/irq.asm --- a/m/irq.asm false +++ b/m/irq.asm e55da0353da36afdbe061d4ae3aaaf02c58d2ca7189786c7d13684aaf4bf39d428ee4787c6b858f467513b35f14272fac53af48cb5864af19a03def98a0d4622 @@ -0,0 +1,70 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +;; State +;----------------------------------------------------------------------------- +section .bss + SlaveIRQ resd 1 ; External Interrupt from Slaves +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +; SetIRQ : Trigger External Interrupt ( Parameter: constant IRQ # ) +;----------------------------------------------------------------------------- +%macro SetIRQ 1 ; param is the IRQ # + bts CP0_Cause, (CP0Cau_IRQ_Bottom + %1) +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ClrIRQ : Clear External Interrupt ( Parameter: constant IRQ # ) +;----------------------------------------------------------------------------- +%macro ClrIRQ 1 ; param is the IRQ # + btr CP0_Cause, (CP0Cau_IRQ_Bottom + %1) +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Built-In Iron IRQs: +;----------------------------------------------------------------------------- +%define TIMER_IRQ 7 ; MIPS Timer (trigger when CP0_Count==CP0_Compare) +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; IRQ from Slave: +;; TODO: locking and wait/sleep +;----------------------------------------------------------------------------- +%macro SetSlaveIRQ 1 + bts dword [SlaveIRQ], (CP0Cau_IRQ_Bottom + %1) +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Poll for any Slave IRQ: +;----------------------------------------------------------------------------- +%macro GetSlaveIRQ 0 + mov eax, [SlaveIRQ] ; Get the Slave IRQ shadow register + test eax, eax ; Is it zero? + jz %%skip ; If was zero, skip; + mov dword [SlaveIRQ], 0 ; Else, 1) Zero shadow register + or CP0_Cause, eax ; 2) OR it into CP0_Cause. +%%skip: +%endmacro +;----------------------------------------------------------------------------- diff -uNr a/m/knobs.asm b/m/knobs.asm --- a/m/knobs.asm false +++ b/m/knobs.asm e75680eee6b4d6dab5e13fd02db2a86702136633846d4e9d9ca17ffaae25ce6c1d0d138db69081802520d9b418b7027a8150271e15e954971ba44d2506f70ad1 @@ -0,0 +1,60 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; Endianism. LITTLE_ENDIAN -- 'little' ; otherwise 'big' +;----------------------------------------------------------------------------- +; %define LITTLE_ENDIAN 1 +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Alignment Grain +;----------------------------------------------------------------------------- +%define GRAIN 32 +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Memory Size (MB) +;----------------------------------------------------------------------------- +%define RAM_SIZE_MB 1040 +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Start and End of MMIO Peripheral Space +;----------------------------------------------------------------------------- +%define MMIO_BASE 0x14000000 ;; Bottom of Memory-Mapped Dev. Bus +%define MMIO_TOP 0x1400FFFF ;; Top of Memory-Mapped Dev. Bus +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Size of Slave thread stacks +;----------------------------------------------------------------------------- +%define SLAVE_STACK_SIZE (4 * 1024) ;; 4kB +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Timer Slave Frequency (nsec) +;----------------------------------------------------------------------------- +%define TIMER_SLAVE_PERIOD 10000000 ; for 100Hz +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Command Line Argument Count +;----------------------------------------------------------------------------- +%define CMDLINE_ARG_COUNT 1 +;----------------------------------------------------------------------------- diff -uNr a/m/log.asm b/m/log.asm --- a/m/log.asm false +++ b/m/log.asm 7b3db846a77dd59146b68f83c06d01c6be491c8e5a78ba0b1d5ea49ba93020aea4b1080d287566c6f5fce1436657dddc54811fe0bed35c918c2165f630e9ec88 @@ -0,0 +1,39 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +%macro ACHTUNG 1 + [section .rodata] +%%text: +dd %%endstr - %%str ; Store 32-bit length of text string +%%str: db %1, 10 ; Store the text string itself + newline +%%endstr: + __SECT__ + push rdx + mov rdx, %%text + call _Print_Text_STDERR + pop rdx +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +%macro EGGOG 1 + ACHTUNG %1 + jmp _exit +%endmacro; +;----------------------------------------------------------------------------- diff -uNr a/m/m.asm b/m/m.asm --- a/m/m.asm false +++ b/m/m.asm ca9d23d0ada3927d70455ec847e8c92ecf88e4b8a4295f07d89155d8322f7a096c47895c506c451f9d1f12f58bc4fbea4d01d76e673755d76a93ed81206554d4 @@ -0,0 +1,156 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 'M', a MIPS system emulator. ;; +;; Version: 300K. ;; +;; ;; +;; Will run lightly-modified Linux kernels with full isolation vs. host. ;; +;; To keep things simple, dynamic recompilation a la Bellard's is NOT USED! ;; +;; ;; +;; 'M' aims to 'fit-in-head'. As such, a reasonably complete description ;; +;; of the emulated machine architecture is included in the comments. ;; +;; ;; +;; Dependencies/Libraries required : NONE!!! ;; +;; Where Runs: Any AMD64 linux. ;; +;; ;; +;; To build: ;; +;; 'make' (needs 'gnumake') ;; +;; or: ;; +;; (1) yasm -f elf64 -g null m.asm ;; +;; (2) ld m.o -o m ;; +;; (3) strip m ;; +;; At the time of writing, yields a <13kB ELF. ;; +;; ;; +;; To run: ;; +;; ./bin/m kernel.bin ;; +;; ;; +;; Note: currently the only means to exit (other than 'kill -9') is to ;; +;; shut down the guest OS (In 'busybox' -- 'poweroff' command.) ;; +;; ;; +;; Devices Currently Emulated (see 'devices' dir) : ;; +;; MIPS Timer, 100Hz Timer, UART Console, Realtime Clock, Power Switch. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +%include "knobs.asm" ; User-adjustable Knobs +%include "log.asm" ; Eggogology and Warning reporting +%include "os/linux.asm" ; Linux invariants and threading knob +%include "os/linux_io.asm" ; Linux I/O routines +%include "flags.asm" ; Emulator State Flags +%include "mips.asm" ; MIPS CPU Invariants +%include "i_decode.asm" ; MIPS Instruction Decoding +%include "cpustate.asm" ; MIPS CPU State +%include "mips_exc.asm" ; MIPS Exception Handler +%include "irq.asm" ; MIPS Interrupts +%include "ram.asm" ; Memory +%include "bus.asm" ; Memory-Mapped Devices +%include "mips_cpu.asm" ; MIPS CPU Cycle Execution +%include "mipsinst/i_instrs.asm" ; I-Type MIPS Instructions +%include "mipsinst/r_instrs.asm" ; R-Type MIPS Instructions +%include "mipsinst/b_instrs.asm" ; B-Type MIPS Instructions +%include "mipsinst/m_instrs.asm" ; M-Type MIPS Instructions +%include "shutdown.asm" ; Termination Cleanup +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; State +;----------------------------------------------------------------------------- +section .bss + arg0 resb 8 ; First Command Line Argument + fd resb 8 ; FD of file containing kernel image +;----------------------------------------------------------------------------- + +;; TODO: 1) Make RAM size adjustable. 2) Eat kernels in ELF form ? +;; 3) Move ALL os-istic calls into os/linux.asm ? +;; 4) Right now we use 100% of host CPU. Need sleep in wait and futex. +;; 5) Need devices! particularly 'disk' and 'NIC'. (And FG...!) +;; 6) Bring UART0 console out as TCP-able? +;; 7) 'Suspend/Resume', out-of-band debuggisms? +;; 8) Tests!!! Particularly, per-instruction test cases! Can you write? + +;----------------------------------------------------------------------------- +; Start of Program. +;----------------------------------------------------------------------------- +section .text +global _start +_start: + ;; Get argc (# of command line arguments) : + mov rax, [rsp] + cmp rax, CMDLINE_ARG_COUNT + 1 ; The required arg. count + je ._run + ;; Not correct number of args? then print usage and exit: +._usage: + EGGOG "Usage: ./M KERNEL" +._run: + ;; Test if SSE2 instructions are available on this machine: + mov eax, 1 + cpuid + test edx, 0x4000000 + jnz ._xmm_ok + EGGOG "Needs SSE2!" +._xmm_ok: + + ;; Get 1st cmdline arg (path) + mov rdi, [rsp + 16] + mov [arg0], rdi + + ;; fd = open(path, O_RDONLY) + mov rax, SYS_OPEN + mov rdi, [arg0] ; first arg + mov rsi, 0 ; O_RDONLY + syscall + test rax, rax ; see if eggog + jns ._ok ; if worked + EGGOG "Could not read kernel from disk!" +._ok: + mov [fd], rax ; else, save fd + + ;; Mbytes = fstat(fd).st_size (footprint of initial image) + mov rax, SYS_FSTAT + sub rsp, statbuf.size ; make scratch + mov rsi, rsp + mov rdi, [fd] + syscall + mov rax, [rsp + statbuf.st_size] + mov [Mbytes], rax + add rsi, statbuf.size ; unmake scratch + + ;; RAM size + mov qword [RAMbytes], RAM_SIZE_MB * (1024 * 1024) + call _ram_allocate + mov [M], rax + + ;; Load the given kernel into bottom of sim RAM: + mov rax, SYS_READ + mov rdi, [fd] ; fd of memory snapshot + mov rsi, [M] ; where to put + mov rdx, [Mbytes] ; read whole snapshot into bottom of sim ram + syscall + + ;; close(fd) + mov rax, SYS_CLOSE + mov rdi, [fd] + syscall + + ;; Initialize all MMIO Devices (and start all slave threads) : + call _Phys_Devices_Initialize + +;----------------------------------------------------------------------------- +_Master_Thread: + call _ram_init ; Initialize RAM + call _cpu_reset ; Reset the MIPS CPU + jmp _cycle ; Start the engine +;----------------------------------------------------------------------------- diff -uNr a/m/mips.asm b/m/mips.asm --- a/m/mips.asm false +++ b/m/mips.asm f82ea8febceb149c589262c1387c2a99d641219dac217de1ba1e3e99ed8b815b5fe4f6b68128cb55fefed5bd41b4d764c802b6ebce34bd4580769027ca001cd7 @@ -0,0 +1,117 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +; Value of Program Counter (PC) on Warmup +;----------------------------------------------------------------------------- +%define INIT_PC 0xBFC00000 ; Per traditional MIPS standard. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; # of TLB entries. Could have more; but would have to change not only here. +;----------------------------------------------------------------------------- +%define TLB_ENTRIES_COUNT 16 ; in principle could have more. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MIPS Exceptions Codes (See CP0_Cause Flags re: where they sit) +;----------------------------------------------------------------------------- +%define EXC_Int 0 ; Interrupt +%define EXC_Mod 1 ; TLB modification exception +%define EXC_TLBL 2 ; TLB exception (load or instruction fetch) +%define EXC_TLBS 3 ; TLB exception (store) +%define EXC_AdEL 4 ; Address error exception (load or instruction fetch) +%define EXC_AdES 5 ; Address error exception (store) +%define EXC_IBE 6 ; Bus error exception (instruction fetch) +%define EXC_DBE 7 ; Bus error exception (data reference: load or store) +%define EXC_SYS 8 ; Syscall exception +%define EXC_BP 9 ; Breakpoint exception +%define EXC_RI 10 ; Reserved instruction exception +%define EXC_CpU 11 ; Coprocessor Unusable exception +%define EXC_Ov 12 ; Arithmetic Overflow exception +%define EXC_Tr 13 ; Trap exception +%define EXC_Watch 23 ; Reference to WatchHi/WatchLo address +%define EXC_MCheck 24 ; Machine check +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MIPS CP0_Cause Register +;----------------------------------------------------------------------------- +; 11111111111111110000000000000000 EEEEE - Exception Code; IIIIIIII - IRQ; +; FEDCBA9876543210FEDCBA9876543210 IV - Indicates whether an interrupt +; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ exception uses the general exception +; B0CC0000IW000000IIIIIIII0EEEEE00 vector or a special interrupt vector: +; D EE VP 76543210 0: General exception vector (0x180) +; 1: Special interrupt vector (0x200) +; BD - Indicates whether last exception taken occurred in a branch delay slot. +; 0: Not in delay slot; 1: In delay slot. +; BD bit is not updated on a new exception if the EXL bit is set. +; IRQ0, IRQ1 -- 'software IRQ'; IRQ2..IRQ7 -- iron IRQ. +; MIPS Timer (powered by CP0_Count and CP0_Compare) is perma-soldered to IRQ7. +;----------------------------------------------------------------------------- +%define CP0Cau_IRQ_Bottom 8 ; Index of bit in CP0_Cause where IRQ # lives +%define CP0Cau_IV 23 ; Whether exception uses general or special vect +%define CP0Cau_BD 31 ; Whether exception occurred in branch delay slt +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MIPS CP0_Status Register +;----------------------------------------------------------------------------- +%define CP0St_CU3 31 ; Coprocessor Access CU3 (unused) +%define CP0St_CU2 30 ; Coprocessor Access CU2 (unused) +%define CP0St_CU1 29 ; Coprocessor Access CU1 (unused) +%define CP0St_CU0 28 ; Coprocessor Access CU0 (unused) +%define CP0St_RP 27 ; Reduced Power Mode (unused) +%define CP0St_FR 26 ; Floating Point Reg Mode -- Unused on 32-bit MIPS +%define CP0St_RE 25 ; Reverse-Endianism (0: user, 1: flipped) - unused +%define CP0St_MX 24 ; Unused on 32-bit MIPS +%define CP0St_PX 23 ; Unused on 32-bit MIPS +%define CP0St_BEV 22 ; BEV (0: Normal Exception Vector, 1: Bootstrap) +%define CP0St_TS 21 ; TLB 'multi-match' shutdown (unused) +%define CP0St_SR 20 ; Soft Reset (0: not soft, 1: soft) +%define CP0St_NMI 19 ; NMI (unused) +%define CP0St_IM 8 ; Start of Interrupt Mask (0: IRQ OFF, 1: IRQ ON) +%define CP0St_KX 7 ; Unused on 32-bit MIPS +%define CP0St_SX 6 ; Unused on 32-bit MIPS +%define CP0St_UX 5 ; Unused on 32-bit MIPS +%define CP0St_UM 4 ; User Mode (0: Kernel Mode, 1: User Mode) +%define CP0St_KSU 3 ; Unused +%define CP0St_ERL 2 ; Error Level (0: Normal, 1: Error) +%define CP0St_EXL 1 ; Exception Level (0: Normal, 1: Kernel) +%define CP0St_IE 0 ; Interrupt Enable +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MIPS TLB Entry. +; We don't use C0 and C1 anywhere! and so we can put all of it in 32bits: +;----------------------------------------------------------------------------- +; 11111111111111110000000000000000 +; FEDCBA9876543210FEDCBA9876543210 +; -------------------------------- +; GVVDDAAAAAAAAVVVVVVVVVVVVVVVVVVV +; |1010| ASID || VPN2 | +;----------------------------------------------------------------------------- +%define TLB_VPN2_Mask 0x7FFFF ; 19 bits +%define TLB_ASID_Mask 0xFF ; 8 bits +%define TLB_ASID_Shift 19 ; sits after VPN2 Mask +%define TLB_D0 27 ; 27th bit +%define TLB_D1 28 ; 28th bit +%define TLB_V0 29 ; 29th bit +%define TLB_V1 30 ; 30th bit +%define TLB_G 31 ; 31st bit (last) +;----------------------------------------------------------------------------- diff -uNr a/m/mips_cpu.asm b/m/mips_cpu.asm --- a/m/mips_cpu.asm false +++ b/m/mips_cpu.asm 93767fbf28ac3ca8a8d7cea92c2295d7f4a041c9b67b003edc7ca1a32ae624b423f8f33e5d099403a9e68e794f0f5d90f4f3f2a12e8d3cee4fcafb12e7502684 @@ -0,0 +1,113 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +section .text + +;----------------------------------------------------------------------------- +; Dispatch via rax, given index Register, and given Table (of 32-bit offsets) +;----------------------------------------------------------------------------- +%macro JTABLE 2 ; %1 : index register, %2 : table offset + mov eax, dword [%2 + 4 * %1] + jmp rax +%endmacro +;----------------------------------------------------------------------------- + +;---------------------------- +; Start of MIPS CPU Cycle : | +;----------------------------------------------------------------------------- +_wait_cycle: +_cycle: + inc CP0_Count ; Timer: CP0_Count := CP0_Count + 1 + + ;; Timer Interrupt + cmp CP0_Count, CP0_Compare ; Has timer reached limit? + jne _cycle_no_mips_timer ; If not, do not invoke interrupt + SetIRQ TIMER_IRQ ; Timer reached limit, invoke timer IRQ + +_cycle_no_mips_timer: + + ;; Test if Interrupts are Disabled: + bt CP0_Status, CP0St_IE ; CF := CP0St_IE + jnc _cycle_no_irq ; If 0, IRQs are disabled, go to no_irq + test CP0_Status, (1 << CP0St_ERL) | (1 << CP0St_EXL) ; If ERL/EXL: + jnz _cycle_no_irq ; ... then also interrupts are disabled + + GetSlaveIRQ ; See if slave threw interrupt + + ;; Interrupts are Enabled; handle any pending Interrupt: + mov eax, 0xFC00 ; ( 0xFC00 & + and eax, CP0_Cause ; CP0_Cause & + and eax, CP0_Status ; CP0_Status ) + jz _cycle_no_irq ; == 0 ? Then no pending IRQ, go to no_irq. + + ;; If here, there is a pending interrupt, service it: +_cycle_irq_handler: + + Flg_Off Waiting ; Clear Waiting Flag + SetEXC EXC_Int ; Set the EXC_Int Exception Code + + ;; Copy InDelaySlot Flag to RunningDelaySlot Flag: + Flg_Cpy RunningDelaySlot, InDelaySlot + + jmp _Handle_Exception ; Handle exception and end cycle. + +_cycle_no_irq: ; If ints disabled or none pending + + Flg_Get Waiting ; CF := Waiting Flag + jc _wait_cycle ; If Waiting, start next cycle, else: + + ;; Copy InDelaySlot Flag to RunningDelaySlot Flag: + Flg_Cpy RunningDelaySlot, InDelaySlot + + ;; Fetch the instruction to execute: + mov eax, PC ; vAddr := PC + call _Virt_Read_Word ; EAX := MEM[Virt2Phys(PC)] + mov r_I, eax ; Store fetched instruction + + ;; Dispatch the current instruction: + mov eax, r_I ; Get the original instruction : + + ;; 'Accelerated NOPs' + test eax, eax ; Is the current instruction null? + jnz _not_nop ; ... if so, skip dispatch entirely! + jmp _end_cycle ; go to tail of this cycle +_not_nop: ; If not a NOP: + + shr eax, 26 ; Get I-field (upper 6 bits of instr) ; + JTABLE eax, _I_Table ; Dispatch on I-field via I-Table. + ;;; Instruction executes, after which goes to _end_cycle +;----------------------------------------------------------------------------- + +;--------------------------------- +; Tail End of a MIPS CPU Cycle : | +;----------------------------------------------------------------------------- +_end_cycle: + Flg_GOF RunningDelaySlot ; Were we running the delay slot? + jnc _end_cycle_was_not_in_delay_slot ; if not, skip, else: + ;; We WERE running in the delay slot: +_end_cycle_was_in_delay_slot: ; If we were running the delay slot: + mov PC, nPC ; PC := nPC + Flg_Off InDelaySlot ; Clear the InDelaySlot Flag + jmp _cycle ; Go to next cycle without advancing PC +_end_cycle_was_not_in_delay_slot: ; If we were NOT running the delay slot: + add PC, 0x4 ; PC := PC + 4 + ;; Test for Shutdown Condition: + Flg_Get Shutdown ; CF := Shutdown Flag + jc _shutdown ; ... if CF, then shut down. + jmp _cycle ; ... otherwise, go to next cycle. +;----------------------------------------------------------------------------- diff -uNr a/m/mips_exc.asm b/m/mips_exc.asm --- a/m/mips_exc.asm false +++ b/m/mips_exc.asm fd686b4adbdb138a08f6b8f95620fd0533b2bd68c5b5056d24100f28b552cb6e7af74b32bfd739f029e065d257f456024d223073da3f0f8168e6d98f75be053d @@ -0,0 +1,92 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +section .text + +;----------------------------------------------------------------------------- +; Get the current Exception Code, write to the specified destination +;----------------------------------------------------------------------------- +%macro GetEXC 1 ; param is destination to write the exception code + mov %1, CP0_Cause ; Dest := CP0_Cause + shr %1, 2 ; Dest := Dest >> 2 + and %1, 0x1F ; Dest := Dest & 0x1F +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Set the current Exception Code from the given constant +;----------------------------------------------------------------------------- +%macro SetEXC 1 ; param is the exception code to set + and CP0_Cause, ~(0x1F << 2) ; Clear the current Exception Code + or CP0_Cause, (%1 & 0x1F) << 2 ; set the new one +%endmacro +;----------------------------------------------------------------------------- + +;------------------------------ +; Exception (and IRQ) Handler | +;----------------------------------------------------------------------------- +_Handle_Exception: + Flg_Off InDelaySlot ; Clear the InDelaySlot Flag + bt CP0_Status, CP0St_EXL ; CF := EXL + jc _H_Exc_Off_Is_180 ; If EXL is 1, Offset := 0x180; else: + ;; EXL is 0: + mov Sr(CP0_Epc), PC ; CP0_Epc := PC + Flg_Get RunningDelaySlot ; Are we running the delay slot? + jnc _H_Exc_Not_R_Delay ; If yes, then skip, else: +_H_Exc_R_Delay: ; We ARE running the delay slot: + sub Sr(CP0_Epc), 0x4 ; CP0_Epc := CP0_Epc - 0x4 + bts CP0_Cause, CP0Cau_BD ; Set BD Flag in CP0_Cause + jmp _H_Exc_Test_TLB ; Proceed to test for TLBism +_H_Exc_Not_R_Delay: ; We are NOT running the delay slot: + btr CP0_Cause, CP0Cau_BD ; Clear BD Flag in CP0_Cause +_H_Exc_Test_TLB: + ;; Test whether this was a TLB Exception: + GetEXC eax ; EAX := the current exception code + cmp eax, EXC_TLBL ; was it EXC_TLBL ? + je __H_Exc_Was_TLB ; ... if yes, go to H_Exc_Was_TLB + cmp eax, EXC_TLBS ; was it EXC_TLBS ? + je __H_Exc_Was_TLB ; ... if yes, go to H_Exc_Was_TLB + ;; This was NOT a TLB Exception: + cmp eax, EXC_Int ; was code EXC_Int ? + jne _H_Exc_Off_Is_180 ; if not, Offset := 0x180 + bt CP0_Cause, CP0Cau_IV ; Was CP0_Cause bit 23 (IV) zero? + jnc _H_Exc_Off_Is_180 ; if was zero, Offset := 0x180 + ;; If CP0_Cause bit 23 != 0: Offset := 0x200 + mov eax, 0x200 ; Offset := 0x200 + jmp _H_Exc_Have_Offset ; Go to H_Exc_Have_Offset +__H_Exc_Was_TLB: ; This WAS a TLB Exception: + Flg_Get ExcWasTLBNoMatch ; CF := 'TLB No Match' + ;; TODO: in 'cmips', this case was reversed? why? + ;; For now, we'll do likewise: + jnc _H_Exc_Off_Is_180 ; ... if 0, Offset := 0x180 + ;; Else: Offset := 0x0 + xor eax, eax ; Clear EAX (Offset := 0) + jmp _H_Exc_Have_Offset ; Finish up +_H_Exc_Off_Is_180: ; Offset := 0x180 + mov eax, 0x180 ; Set the Offset +_H_Exc_Have_Offset: ; We finished calculating the Offset: + bts CP0_Status, CP0St_EXL ; Set the EXL Flag + mov PC, eax ; PC := Offset (eax) + mov eax, 0x80000000 ; Base that will be used if BEV=0 + mov ebx, 0xBFC00200 ; Base that will be used if BEV=1 + bt CP0_Status, CP0St_BEV ; CF := the BEV Flag + cmovc eax, ebx ; If CF = 1, use the BEV=1 Base + add PC, eax ; PC := PC + Base + ;; Done handling exception + jmp _cycle ; Start next cycle. +;----------------------------------------------------------------------------- diff -uNr a/m/mipsinst/b_instrs.asm b/m/mipsinst/b_instrs.asm --- a/m/mipsinst/b_instrs.asm false +++ b/m/mipsinst/b_instrs.asm 0049b58d97622dd2210f1547d96a7a69606c9ef7fd46349b1b16db6e5a6f686d84296bcb2cceea2240a5bd82e46d1ac5a03c6c131f14906fc0e173c1a9a46b58 @@ -0,0 +1,225 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +section .rodata +align GRAIN, db 0x90 +_B_Table: + A32 _b_bltz ; 0x00 : bltz (000001?????00000????????????????) + A32 _b_bgez ; 0x01 : bgez (000001?????00001????????????????) + A32 _b_bltzl ; 0x02 : bltzl (000001?????00010????????????????) + A32 _b_bgezl ; 0x03 : bgezl (000001?????00011????????????????) + A32 _bad ; 0x04 : UNDEFINED + A32 _bad ; 0x05 : UNDEFINED + A32 _bad ; 0x06 : UNDEFINED + A32 _bad ; 0x07 : UNDEFINED + A32 _bad ; 0x08 : UNDEFINED + A32 _bad ; 0x09 : UNDEFINED + A32 _bad ; 0x0a : UNDEFINED + A32 _bad ; 0x0b : UNDEFINED + A32 _bad ; 0x0c : UNDEFINED + A32 _bad ; 0x0d : UNDEFINED + A32 _bad ; 0x0e : UNDEFINED + A32 _bad ; 0x0f : UNDEFINED + A32 _b_bltzal ; 0x10 : bltzal (000001????x10000????????????????) + A32 _b_bgezal ; 0x11 : bgezal (000001????x10001????????????????) + A32 _bad ; 0x12 : UNDEFINED + A32 _bad ; 0x13 : UNDEFINED + A32 _bad ; 0x14 : UNDEFINED + A32 _bad ; 0x15 : UNDEFINED + A32 _bad ; 0x16 : UNDEFINED + A32 _bad ; 0x17 : UNDEFINED + A32 _bad ; 0x18 : UNDEFINED + A32 _bad ; 0x19 : UNDEFINED + A32 _bad ; 0x1a : UNDEFINED + A32 _bad ; 0x1b : UNDEFINED + A32 _bad ; 0x1c : UNDEFINED + A32 _bad ; 0x1d : UNDEFINED + A32 _bad ; 0x1e : UNDEFINED + A32 _bad ; 0x1f : UNDEFINED +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +; B-Type Instructions : +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BLTZ -- Branch on less than zero. +; Branches if the register is less than zero +; Operation: if $s < 0 advance_pc(offset << 2)); else advance_pc(4); +; Syntax: bltz $s, offset +; Encoding: 0000 01ss sss0 0000 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_b_bltz: + IType_I_S_Only ; load rS and Imm Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, 0x4 ; TMP := 4 + cmp R(rS), 0 ; If Regs[rS] < 0: + cmovl TMP, Imm ; ... then TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BGEZ -- Branch on greater than or equal to zero. +; Branches if the register is greater than or equal to zero +; Operation: if $s >= 0 advance_pc(offset << 2)); else advance_pc(4); +; Syntax: bgez $s, offset +; Encoding: 0000 01ss sss0 0001 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_b_bgez: + IType_I_S_Only ; load rS and Imm Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, 0x4 ; TMP := 4 + cmp R(rS), 0 ; If Regs[rS] >= 0: + cmovge TMP, Imm ; ... then TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BLTZL - Branch on Less Than Zero Likely. +; Test a register, then do a PC-relative conditional branch; +; execute the delay slot only if the branch is taken. +; An 18-bit signed offset (the 16-bit offset field shifted left 2 bits) is +; added to the address of the instruction following the branch (not the branch +; itself), in the branch delay slot, to form a PC-relative effective target +; address. If the contents of rS are less than zero (sign bit is 1), branch to +; the effective target address after the instruction in the delay slot is +; executed. If the branch is not taken, the instruction in the delay slot is +; not executed. +; Operation: if rS < 0 then PC = PC + sign_extend(offset) +; else: nullify_current_instruction +; Syntax: bltzl $s, offset +; Encoding: 0000 01ss sss0 0010 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_b_bltzl: + IType_S_Only ; load only rS Field just now + cmp R(rS), 0 ; CMP(Regs[rS], 0) + jnl _i_bltzl_not_taken ; If !(Regs[rS] < 0) then NotTaken + ; Taken: + IType_I_Only ; load only Imm Field just now + SX18_4N Imm ; Sign Extend Imm * 4 + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + add nPC, Imm ; nPC := nPC + Offset + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle + ; Not Taken: +_i_bltzl_not_taken: + add PC, 0x4 ; PC := PC + 4 + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BGEZL - Branch on Greater Than or Equal to Zero Likely. +; Test a register, then do a PC-relative conditional branch; +; execute the delay slot only if the branch is taken. +; An 18-bit signed offset (the 16-bit offset field shifted left 2 bits) is +; added to the address of the instruction following the branch (not the branch +; itself), in the branch delay slot, to form a PC-relative effective target +; address. If the contents of rS are greater than or equal to zero +; (sign bit is 0), branch to the effective target address after the +; instruction in the delay slot is executed. If the branch is not taken, the +; instruction in the delay slot is not executed. +; Operation: if rS >= 0 then PC = PC + sign_extend(offset) +; else: nullify_current_instruction +; Syntax: bgezl $s, offset +; Encoding: 0000 01ss sss0 0011 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_b_bgezl: + IType_S_Only ; Load only rS Field just now + cmp R(rS), 0 ; CMP(Regs[rS], 0) + jnge _i_bgezl_not_taken ; If !(Regs[rS] >= 0) then NotTaken + ; Taken: + IType_I_Only ; Load only Imm Field just now + SX18_4N Imm ; Sign Extend Imm * 4 + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + add nPC, Imm ; nPC := nPC + Offset + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle + ; Not Taken: +_i_bgezl_not_taken: + add PC, 0x4 ; PC := PC + 4 + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BLTZAL -- Branch on less than zero and link. +; Branches if the register is less than zero and saves the return address +; in $31 +; Operation: if $s < 0 $31 = PC + 8 (or nPC + 4); advance_pc(offset << 2)); +; else advance_pc(4); +; Syntax: bltzal $s, offset +; Encoding: 0000 01ss sss1 0000 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_b_bltzal: + IType_I_S_Only ; Load rS and Imm Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + mov TMP, nPC ; TMP := nPC + add TMP, 0x4 ; TMP := TMP + 4 + mov R(31), TMP ; Regs[31] := TMP + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, 0x4 ; TMP := 4 + cmp R(rS), 0 ; If Regs[rS] < 0: + cmovl TMP, Imm ; ... then TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BGEZAL -- Branch on greater than or equal to zero and link. +; Branches if the register is greater than or equal to zero and saves the +; return address in $31 +; Operation: if $s >= 0 $31 = PC + 8 (or nPC + 4); advance_pc(offset << 2)); +; else advance_pc(4); +; Syntax: bgezal $s, offset +; Encoding: 0000 01ss sss1 0001 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_b_bgezal: + IType_I_S_Only ; Load rS and Imm Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + mov TMP, nPC ; TMP := nPC + add TMP, 0x4 ; TMP := TMP + 4 + mov R(31), TMP ; Regs[31] := TMP + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, 0x4 ; TMP := 4 + cmp R(rS), 0 ; If Regs[rS] < 0: + cmovnl TMP, Imm ; ... then TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- diff -uNr a/m/mipsinst/i_instrs.asm b/m/mipsinst/i_instrs.asm --- a/m/mipsinst/i_instrs.asm false +++ b/m/mipsinst/i_instrs.asm 5b25235b8644d82e985ed6cc354e5e6e0d94aaaf0d0b5a7b33d8a5586cf67a90f13885398e5c10b935c9ee991b1cc720ae9b832dff9961d6090cf5e17a0445b9 @@ -0,0 +1,1123 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO: 1) Trap for ADDI (gcc 4.8.1 doesn't appear to use, but it exists...) +;; 2) Tests!!! + +;------------------------ +; I-Type Instructions : | +;------------------------ + +;----------------------------------------------------------------------------- +section .rodata +align GRAIN, db 0x90 +_I_Table: + A32 _i_000000 ; 0x00 : All R-Type Instructions + A32 _i_000001 ; 0x01 : bltz, bgez, bltzl, bgezl, bltzal, bgezal + A32 _i_j ; 0x02 : j (000010??????????????????????????) + A32 _i_jal ; 0x03 : jal (000011??????????????????????????) + A32 _i_beq ; 0x04 : beq (000100??????????????????????????) + A32 _i_bne ; 0x05 : bne (000101??????????????????????????) + A32 _i_blez ; 0x06 : blez (000110??????????????????????????) + A32 _i_bgtz ; 0x07 : bgtz (000111??????????????????????????) + A32 _i_addi ; 0x08 : addi (001000??????????????????????????) + A32 _i_addiu ; 0x09 : addiu (001001??????????????????????????) + A32 _i_slti ; 0x0a : slti (001010??????????????????????????) + A32 _i_sltiu ; 0x0b : sltiu (001011??????????????????????????) + A32 _i_andi ; 0x0c : andi (001100??????????????????????????) + A32 _i_ori ; 0x0d : ori (001101??????????????????????????) + A32 _i_xori ; 0x0e : xori (001110??????????????????????????) + A32 _i_lui ; 0x0f : lui (001111??????????????????????????) + A32 _i_010000 ; 0x10 : mfc0, mtc0, tlbwi, tlbwr, tlbp, eret, wait + A32 _bad ; 0x11 : UNDEFINED + A32 _bad ; 0x12 : UNDEFINED + A32 _bad ; 0x13 : UNDEFINED + A32 _i_beql ; 0x14 : beql (010100??????????????????????????) + A32 _i_bnel ; 0x15 : bnel (010101??????????????????????????) + A32 _i_blezl ; 0x16 : blezl (010110??????????????????????????) + A32 _i_bgtzl ; 0x17 : bgtzl (010111?????00000????????????????) + A32 _bad ; 0x18 : UNDEFINED + A32 _bad ; 0x19 : UNDEFINED + A32 _bad ; 0x1a : UNDEFINED + A32 _bad ; 0x1b : UNDEFINED + A32 _i_mul ; 0x1c : mul (011100???????????????00000000010) + A32 _bad ; 0x1d : UNDEFINED + A32 _bad ; 0x1e : UNDEFINED + A32 _bad ; 0x1f : UNDEFINED + A32 _i_lb ; 0x20 : lb (100000??????????????????????????) + A32 _i_lh ; 0x21 : lh (100001??????????????????????????) + A32 _i_lwl ; 0x22 : lwl (100010??????????????????????????) + A32 _i_lw ; 0x23 : lw (100011??????????????????????????) + A32 _i_lbu ; 0x24 : lbu (100100??????????????????????????) + A32 _i_lhu ; 0x25 : lhu (100101??????????????????????????) + A32 _i_lwr ; 0x26 : lwr (100110??????????????????????????) + A32 _bad ; 0x27 : UNDEFINED + A32 _i_sb ; 0x28 : sb (101000??????????????????????????) + A32 _i_sh ; 0x29 : sh (101001??????????????????????????) + A32 _i_swl ; 0x2a : swl (101010??????????????????????????) + A32 _i_sw ; 0x2b : sw (101011??????????????????????????) + A32 _bad ; 0x2c : UNDEFINED + A32 _bad ; 0x2d : UNDEFINED + A32 _i_swr ; 0x2e : swr (101110??????????????????????????) + A32 _i_cache ; 0x2f : cache (101111??????????????????????????) + A32 _i_ll ; 0x30 : ll (110000??????????????????????????) + A32 _bad ; 0x31 : UNDEFINED + A32 _bad ; 0x32 : UNDEFINED + A32 _i_pref ; 0x33 : pref (110011??????????????????????????) + A32 _bad ; 0x34 : UNDEFINED + A32 _bad ; 0x35 : UNDEFINED + A32 _bad ; 0x36 : UNDEFINED + A32 _bad ; 0x37 : UNDEFINED + A32 _i_sc ; 0x38 : sc (111000??????????????????????????) + A32 _bad ; 0x39 : UNDEFINED + A32 _bad ; 0x3a : UNDEFINED + A32 _bad ; 0x3b : UNDEFINED + A32 _bad ; 0x3c : UNDEFINED + A32 _bad ; 0x3d : UNDEFINED + A32 _bad ; 0x3e : UNDEFINED + A32 _bad ; 0x3f : UNDEFINED +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_MFC0_Table: + A32 _mfc0_r00 ; 0x00 + A32 _mfc0_r01 ; 0x01 + A32 _mfc0_r02 ; 0x02 + A32 _mfc0_r03 ; 0x03 + A32 _mfc0_r04 ; 0x04 + A32 _mfc0_r05 ; 0x05 + A32 _mfc0_r06 ; 0x06 + A32 _mfc0_r07 ; 0x07 + A32 _mfc0_r08 ; 0x08 + A32 _mfc0_r09 ; 0x09 + A32 _mfc0_r10 ; 0x0a + A32 _mfc0_r11 ; 0x0b + A32 _mfc0_r12 ; 0x0c + A32 _mfc0_r13 ; 0x0d + A32 _mfc0_r14 ; 0x0e + A32 _mfc0_r15 ; 0x0f + A32 _mfc0_r16 ; 0x10 + A32 _mfc0_r17 ; 0x11 + A32 _mfc0_r18 ; 0x12 + A32 _mfc0_r19 ; 0x13 + A32 _mfc0_r20 ; 0x14 + A32 _mfc0_r21 ; 0x15 + A32 _mfc0_r22 ; 0x16 + A32 _mfc0_r23 ; 0x17 + A32 _mfc0_r24 ; 0x18 + A32 _mfc0_r25 ; 0x19 + A32 _mfc0_r26 ; 0x1a + A32 _mfc0_r27 ; 0x1b + A32 _mfc0_r28 ; 0x1c + A32 _mfc0_r29 ; 0x1d + A32 _mfc0_r30 ; 0x1e + A32 _mfc0_r31 ; 0x1f +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_MTC0_Table: + A32 _mtc0_r00 ; 0x00 + A32 _mtc0_r01 ; 0x01 + A32 _mtc0_r02 ; 0x02 + A32 _mtc0_r03 ; 0x03 + A32 _mtc0_r04 ; 0x04 + A32 _mtc0_r05 ; 0x05 + A32 _mtc0_r06 ; 0x06 + A32 _mtc0_r07 ; 0x07 + A32 _mtc0_r08 ; 0x08 + A32 _mtc0_r09 ; 0x09 + A32 _mtc0_r10 ; 0x0a + A32 _mtc0_r11 ; 0x0b + A32 _mtc0_r12 ; 0x0c + A32 _mtc0_r13 ; 0x0d + A32 _mtc0_r14 ; 0x0e + A32 _mtc0_r15 ; 0x0f + A32 _mtc0_r16 ; 0x10 + A32 _mtc0_r17 ; 0x11 + A32 _mtc0_r18 ; 0x12 + A32 _mtc0_r19 ; 0x13 + A32 _mtc0_r20 ; 0x14 + A32 _mtc0_r21 ; 0x15 + A32 _mtc0_r22 ; 0x16 + A32 _mtc0_r23 ; 0x17 + A32 _mtc0_r24 ; 0x18 + A32 _mtc0_r25 ; 0x19 + A32 _mtc0_r26 ; 0x1a + A32 _mtc0_r27 ; 0x1b + A32 _mtc0_r28 ; 0x1c + A32 _mtc0_r29 ; 0x1d + A32 _mtc0_r30 ; 0x1e + A32 _mtc0_r31 ; 0x1f +;----------------------------------------------------------------------------- + +section .text + +;------------------------ +; Invalid Instruction : | +;------------------------ +align GRAIN, db 0x90 +_bad: + SetEXC EXC_RI ; Set the EXC_RI Exception + jmp _Handle_Exception ; Go straight to exception handler. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Eat 000000?????????????????????????? (R-Type) Instructions: +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_000000: + mov eax, r_I ; Get the original instruction : + and eax, 0x3F ; Get funct field, (lower 6 bits) ; + JTABLE eax, _R_Table ; Dispatch on funct-field via R-Table. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Eat 000001?????QQQQQ???????????????? Branch Instructions : +; i.e. bltz, bgez, bltzl, bgezl, bltzal, bgezal +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_000001: + mov eax, r_I ; Get the original instruction : + shr eax, 16 ; Get the 5-bit field QQQQQ + and eax, 0x1F ; which distinguishes these; + JTABLE eax, _B_Table ; Dispatch via B-Table. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Eat 010000?????????????????????????? Misc. Instructions : +; mfc0, mtc0, tlbwi, tlbwr, tlbp, eret, wait +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_010000: + PRIVILEGED ; Permitted in Kernel Mode only. + mov eax, r_I ; Get the original instruction : + shr eax, 5 ; Get the top 27 bits + cmp eax, 0x2100000 ; Equal 010000100000000000000000000..? + je _tlbwi_tlbwr_tlbp_eret ; 010000100000000000000000000QQQQQ + ;; At this point, we have either mfc0, mtc0, wait, or a bad instr: + shr eax, 16 ; We already shifted by 5, so now 21: + cmp eax, 0x200 ; ... are they equal to 0b01000000000 ? + je _m_mfc0 ; mfc0 (01000000000?????????????????????) + cmp eax, 0x204 ; ... are they equal to 0b01000000100 ? + je _m_mtc0 ; mtc0 (01000000100?????????????????????) + ;; Now determine if it is 'wait' (0100001???????????????????100000) : + mov eax, r_I ; Get the original instruction : + and eax, 0xFE00003F ; Get top 7 bits and bottom 6 bits only + cmp eax, 0x42000020 ; Top must be 0b0100001, bottom 0b100000 + je _m_wait ; wait (0100001???????????????????100000) + ;; Now, if it wasn't the 'wait' instruction, we must then have a turd: + jmp _bad ; If we're here : undefined inst. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Eat 010000100000000000000000000QQQQQ Instructions (via _i_010000) : +; tlbwi, tlbwr, tlbp, eret +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_tlbwi_tlbwr_tlbp_eret: + mov eax, r_I ; Get the original instruction : + and eax, 0x1F ; Get the the 5-bit field QQQQQ from it + JTABLE eax, _M_Table ; Dispatch via M-Table. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; J -- Jump. +; Jumps to the calculated address. +; Operation: PC = nPC; nPC = (PC & 0xF0000000) | (target << 2); +; Syntax: j target +; Encoding: 0000 10ii iiii iiii iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_j: + JType ; load J Field (ii..iii << 2) + mov nPC, PC ; nPC := PC + and nPC, 0xF0000000 ; keep only top 4 bits of nPC, zero rest + or nPC, JTarg ; nPC := nPC | ( target << 2 ) + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; JAL -- Jump and link. +; Jumps to the calculated address and stores the return address in $31. +; Operation: $31 = PC + 8 (or nPC + 4); +; PC = nPC; +; nPC = (PC & 0xF0000000) | (target << 2); +; Syntax: jal target +; Encoding: 0000 11ii iiii iiii iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_jal: + JType ; load J Field (ii..iii << 2) + mov TMP, PC ; TMP := PC + add TMP, 0x8 ; TMP := TMP + 8 + mov R(31), TMP ; Regs[31] := TMP + sub TMP, 0x8 ; TMP := TMP - 8 + and TMP, 0xF0000000 ; keep only top 4 bits of TMP, zero rest + or TMP, JTarg ; TMP := TMP | ( target << 2 ) + mov nPC, TMP ; nPC := TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BEQ -- Branch on equal. +; Branches if the two registers are equal. +; Operation: if $s == $t advance_pc(offset << 2)); +; else advance_pc(4); +; Syntax: beq $s, $t, offset +; Encoding: 0001 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_beq: + IType ; Load Imm, rS, rT Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + mov TMP, 0x4 ; TMP := 4 + cmove TMP, Imm ; If Regs[rS] = Regs[rT], TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BNE -- Branch on not equal. +; Branches if the two registers are not equal. +; Operation: if $s != $t advance_pc(offset << 2)); +; else advance_pc(4); +; Syntax: bne $s, $t, offset +; Encoding: 0001 01ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_bne: + IType ; Load Imm, rS, rT Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + mov TMP, 0x4 ; TMP := 4 + cmovne TMP, Imm ; If Regs[rS] != Regs[rT], TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BLEZ -- Branch on less than or equal to zero. +; Branches if the register is less than or equal to zero +; Operation: if $s <= 0 advance_pc(offset << 2)); +; else advance_pc(4); +; Syntax: blez $s, offset +; Encoding: 0001 10ss sss0 0000 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_blez: + IType_I_S_Only ; load rS and Imm Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, 0x4 ; TMP := 4 + cmp R(rS), 0 ; If Regs[rS] <= 0: + cmovle TMP, Imm ; ... then TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BGTZ -- Branch on greater than zero. +; Branches if the register is greater than zero +; Operation: if $s > 0 advance_pc(offset << 2)); +; else advance_pc(4); +; Syntax: bgtz $s, offset +; Encoding: 0001 11ss sss0 0000 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_bgtz: + IType_I_S_Only ; load rS and Imm Fields + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + SX18_4N Imm ; Sign Extend Imm * 4 + mov TMP, 0x4 ; TMP := 4 + cmp R(rS), 0 ; If Regs[rS] > 0: + cmovg TMP, Imm ; ... then TMP := Imm + add nPC, TMP ; nPC := nPC + TMP + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ADDI -- Add immediate (with overflow). +; Adds a register and a sign-extended immediate value and stores the +; result in a register +; Operation: $t = $s + imm; advance_pc(4); +; Syntax: addi $t, $s, imm +; Encoding: 0010 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +; TODO: untested +align GRAIN, db 0x90 +_i_addi: + ; UNDONE_INST + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + mov TMP, R(rS) ; TMP := Regs[rS] + add TMP, Imm ; TMP := TMP + Imm + ; TODO: detect and trap overflow + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ADDIU -- Add immediate unsigned (no overflow). +; Adds a register and a sign-extended immediate value and stores the +; result in a register +; Operation: $t = $s + imm; advance_pc(4); +; Syntax: addiu $t, $s, imm +; Encoding: 0010 01ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_addiu: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + mov TMP, R(rS) ; TMP := Regs[rS] + add TMP, Imm ; TMP := TMP + Imm + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SLTI -- Set on less than immediate (signed). +; If $s is less than immediate, $t is set to one. It gets zero otherwise. +; Operation: if $s < imm $t = 1; advance_pc(4); +; else $t = 0; advance_pc(4); +; Syntax: slti $t, $s, imm +; Encoding: 0010 10ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_slti: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + xor TMP, TMP ; Clear TMP + cmp R(rS), Imm ; CMP(R(rS), Imm) + setl TMP_LowByte ; if $s <(sgn) imm then TMP := 1; else 0 + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SLTIU -- Set on less than immediate unsigned. +; If $s is less than the unsigned immediate, $t is set to one. +; It gets zero otherwise. +; Operation: if $s < imm $t = 1; advance_pc(4); +; else $t = 0; advance_pc(4); +; Syntax: sltiu $t, $s, imm +; Encoding: 0010 11ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +; Apparently even here we sign-extend the immediate. +align GRAIN, db 0x90 +_i_sltiu: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + xor TMP, TMP ; Clear TMP + cmp R(rS), Imm ; CMP(R(rS), Imm) + setb TMP_LowByte ; if $s < imm then TMP := 1; else 0 + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ANDI -- Bitwise AND immediate. +; Bitwise ANDs a register and an immediate value and stores the result +; in a register +; Operation: $t = $s & imm; advance_pc(4); +; Syntax: andi $t, $s, imm +; Encoding: 0011 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_andi: + IType ; Load Imm, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + and TMP, Imm ; TMP := TMP & Imm + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ORI -- Bitwise OR immediate. +; Bitwise OrS a register and an immediate value and stores the result +; in a register +; Operation: $t = $s | imm; advance_pc(4); +; Syntax: ori $t, $s, imm +; Encoding: 0011 01ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_ori: + IType ; Load Imm, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + or TMP, Imm ; TMP := TMP | Imm + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; XORI -- Bitwise Exclusive Or immediate. +; Bitwise XOrS a register and an immediate value and stores the +; result in a register +; Operation: $t = $s ^ imm; advance_pc(4); +; Syntax: xori $t, $s, imm +; Encoding: 0011 10ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_xori: + IType ; Load Imm, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + xor TMP, Imm ; TMP := TMP ^ Imm + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LUI -- Load upper immediate. +; The immediate value is shifted left 16 bits and stored in the register. +; The lower 16 bits are zeroes. +; Operation: $t = (imm << 16); advance_pc(4); +; Syntax: lui $t, imm +; Encoding: 0011 11-- ---t tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_lui: + IType_I_T_Only ; Load Imm and rT Fields + mov TMP, Imm ; TMP := Imm + shl TMP, 16 ; TMP := TMP << 16 + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BEQL - Branch on Equal Likely. +; Compare registers, then do a PC-relative conditional branch; +; execute the delay slot only if the branch is taken. +; An 18-bit signed offset (the 16-bit offset field shifted left 2 bits) is +; added to the address of the instruction following the branch (not the +; branch itself), in the branch delay slot, to form a PC-relative effective +; target address. If the contents of rS and rT are equal, branch to the target +; address after the instruction in the delay slot is executed. If the branch +; is not taken, the instruction in the delay slot is not executed. +; Operation: if rS = rT then branch_likely +; Syntax: beql $s, $t, offset +; Encoding: 0101 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_beql: + IType ; Load Imm, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + jne _i_beql_not_taken ; If !(TMP = Regs[rT]) then NotTaken + ; Taken: + SX18_4N Imm ; Sign Extend Imm * 4 + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + add nPC, Imm ; nPC := nPC + Offset + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle + ; Not Taken: +_i_beql_not_taken: + add PC, 0x4 ; PC := PC + 4 + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BNEL - Branch on Not Equal Likely. +; Compare registers, then do a PC-relative conditional branch; +; execute the delay slot only if the branch is taken. +; An 18-bit signed offset (the 16-bit offset field shifted left 2 bits) is +; added to the address of the instruction following the branch (not the +; branch itself), in the branch delay slot, to form a PC-relative effective +; target address. If the contents of rS and rT are NOT equal, branch to the +; target address after the instruction in the delay slot is executed. If the +; branch is not taken, the instruction in the delay slot is not executed. +; Operation: if rS != rT then branch_likely +; Syntax: bnel $s, $t, offset +; Encoding: 0101 01ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_bnel: + IType ; Load Imm, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + je _i_bnel_not_taken ; If !(TMP != Regs[rT]) then NotTaken + ; Taken: + SX18_4N Imm ; Sign Extend Imm * 4 + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + add nPC, Imm ; nPC := nPC + Offset + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle + ; Not Taken: +_i_bnel_not_taken: + add PC, 0x4 ; PC := PC + 4 + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BLEZL - Branch on Less Than or Equal to Zero Likely. +; Test a register, then do a PC-relative conditional branch; +; execute the delay slot only if the branch is taken. +; An 18-bit signed offset (the 16-bit offset field shifted left 2 bits) is +; added to the address of the instruction following the branch (not the branch +; itself), in the branch delay slot, to form a PC-relative effective target +; address. If the contents of rS are less than or equal to zero (sign bit is +; 1 or value is zero), branch to the effective target address after the +; instruction in the delay slot is executed. If the branch is not taken, the +; instruction in the delay slot is not executed. +; Operation: If rS <= 0 then branch_likely +; Syntax: bltzl $s, offset +; Encoding: 0101 10ss sss0 0000 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_blezl: + IType_S_Only ; load only rS Field just now + cmp R(rS), 0 ; CMP(Regs[rS], 0) + jnle _i_blezl_not_taken ; If !(Regs[rS] <= 0) then NotTaken + ; Taken: + IType_I_Only ; load only Imm Field just now + SX18_4N Imm ; Sign Extend Imm * 4 + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + add nPC, Imm ; nPC := nPC + Offset + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle + ; Not Taken: +_i_blezl_not_taken: + add PC, 0x4 ; PC := PC + 4 + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; BGTZL - Branch on Greater Than Zero Likely. +; Test a register, then do a PC-relative conditional branch; +; execute the delay slot only if the branch is taken. +; An 18-bit signed offset (the 16-bit offset field shifted left 2 bits) is +; added to the address of the instruction following the branch (not the branch +; itself), in the branch delay slot, to form a PC-relative effective target +; address. If the contents of rS are greater than zero (sign bit is 0 but +; value not zero), branch to the effective target address after the +; instruction in the delay slot is executed. If the branch is not taken, the +; instruction in the delay slot is not executed. +; Operation: If rS > 0 then branch_likely +; Syntax: bgtzl $s, offset +; Encoding: 0101 11ss sss0 0000 iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_bgtzl: + IType_S_Only ; load only rS Field just now + cmp R(rS), 0 ; CMP(Regs[rS], 0) + jng _i_bgtzl_not_taken ; If !(Regs[rS] > 0) then NotTaken + ; Taken: + IType_I_Only ; load only Imm Field just now + SX18_4N Imm ; Sign Extend Imm * 4 + mov nPC, PC ; nPC := PC + add nPC, 0x4 ; nPC := nPC + 4 + add nPC, Imm ; nPC := nPC + Offset + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle + ; Not Taken: +_i_bgtzl_not_taken: + add PC, 0x4 ; PC := PC + 4 + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MUL - Multiply Word to Register. +; The 32-bit word value in rS is multiplied by the 32-bit value in rT, +; treating both operands as signed values, to produce a 64-bit result. +; The least significant 32 bits of the product are written to rD. +; The contents of HI and LO are UNPREDICTABLE after the operation. +; No arithmetic exception occurs under any circumstances. +; Syntax: mul $d, $s, $t +; Encoding: 0111 00ss ssst tttt dddd d000 0000 0010 +;----------------------------------------------------------------------------- +; TODO: untested +align GRAIN, db 0x90 +_i_mul: + RType ; load rD, rS, rT Fields + mov TMP, R(rT) ; TMP := Regs[rT] + imul TMP, R(rS) ; TMP := TMP * Regs[rS] (Bottom 32 bits) + Wr_Reg rD, TMP ; Regs[rD] := Bottom 32 bits of prod + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LB -- Load Byte. +; A byte is loaded into a register from the specified address. +; Operation: $t = MEM[$s + offset]; advance_pc(4); +; Syntax: lb $t, offset($s) +; Encoding: 1000 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_lb: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + call _Virt_Read_Byte ; AL := Virt_Read_Byte(EAX) + shl eax, 24 ; Shift left for sign-extension: + sar eax, 24 ; Shift back right, dragging sign bit: + Wr_Reg rT, EAX ; Regs[rT] := EAX + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LH - Load Halfword. +; Load a halfword from memory as a signed value. +; The contents of the 16-bit halfword at the memory location specified by the +; aligned effective address are fetched, sign-extended, and placed in rT. +; The 16-bit signed offset is added to the contents of rS to form the +; effective address. The effective address must be naturally-aligned. +; If the least-significant bit of the address is non-zero, an Address +; Error exception occurs +; Operation: $t = MEM[$s + offset]; advance_pc(4); +; Syntax: lh $t, offset($s) +; Encoding: 1000 01ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +; NOTE: LHU is exactly same thing as LH, but without sign-extend. +align GRAIN, db 0x90 +_i_lh: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + ;; rS (ecx) is not needed any more, can be reused + bt Imm, 0 ; Get low bit of vAddr + jnc _i_lh_aligned_ok ; If zero, vAddr was properly aligned; + SetEXC EXC_AdEL ; If vAddr was NOT properly aligned: + jmp _Handle_Exception ; Go straight to exception handler. +_i_lh_aligned_ok: ; Load is to proceed normally: + mov ecx, Imm ; Save vAddr to ECX + xor TMP, TMP ; Clear TMP, where we will put the HW +%ifdef LITTLE_ENDIAN + ;; Read the low byte of the HW first: + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr) + mov TMP, eax ; TMP := eax (save the low byte) + ;; Now read the high byte: + mov eax, ecx ; Restore the original saved vAddr + or eax, 1 ; Will read vAddr + 1 + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr + 1) +%else + or Imm, 1 ; First, read vAddr + 1 : + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr + 1) + mov TMP, eax ; TMP := eax (save low byte) + mov eax, ecx ; Restore the original saved vAddr + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr) +%endif + shl eax, 8 ; eax := eax << 8 + or eax, TMP ; eax := eax | TMP (high | low) + shl eax, 16 ; left shift eax to put the HW on top + sar eax, 16 ; right shift it back, to sign-extend. + Wr_Reg rT, eax ; Regs[rT] := EAX (write back result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LWL - Load Word Left. +; Load the most-significant part of a word as a signed value from an unaligned +; memory address. +; Operation: $t = $t MERGE MEM[$s + offset]; advance_pc(4); +; Syntax: lwl $t, offset($s) +; Encoding: 1000 10ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_lwl: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + ;; rS (ecx) is not needed any more, can be reused + mov TMP, R(rT) ; TMP := Regs[rT] (old reg value) + mov AUX, Imm ; Save the original unaligned address + and Imm, ~(0x3) ; Clear bottom two bits of address + call _Virt_Read_Word ; EAX := Virt_Read_Word(EAX (aligned)) + ;; Proceed with write. EAX : old word, TMP : new word, ebx : still rT + mov ecx, AUX ; ecx := original unaligned address + and ecx, 0x3 ; keep only bottom two bits of it + shl ecx, 3 ; ecx := ecx (8 * (addr[0..2])) + shl eax, cl ; left shift eax (orig word) by above + xor ecx, 24 ; ecx := 24 - ecx + mov AUX, 0xFFFFFF ; AUX := 0xFFFFFF + shr AUX, cl ; AUX := AUX >> cl + and TMP, AUX ; TMP := TMP & AUX + or TMP, eax ; TMP := TMP | eax (the writeback value) + Wr_Reg rT, TMP ; Regs[rT] := TMP (the writeback value) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LW -- Load Word. +; The contents of the 32-bit word at the memory location specified by the +; aligned effective address are fetched, signextended to the register length +; if necessary, and placed in register rT. The 16-bit signed offset is added +; to the contents of the base to form the effective address. +; Operation: $t = MEM[$s + offset]; advance_pc(4); +; Syntax: lw $t, offset($s) +; Encoding: 1000 11ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_lw: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + call _Virt_Read_Word ; EAX := Virt_Read_Word(EAX) + Wr_Reg rT, EAX ; Regs[rT] := EAX + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LBU - Load Byte Unsigned. +; Load a byte from memory as an unsigned value. +; The contents of the 8-bit byte at the memory location specified by the +; effective address are fetched, zero-extended, and placed in rT. +; The 16-bit signed offset is added to the contents of rS to form the +; effective address. +; Operation: $t = MEM[$s + offset]; advance_pc(4); +; Syntax: lbu $t, offset($s) +; Encoding: 1001 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_lbu: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + call _Virt_Read_Byte ; AL := Virt_Read_Byte(EAX) + Wr_Reg rT, EAX ; Regs[rT] := EAX + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LHU - Load Halfword Unsigned. +; Load a halfword from memory as an unsigned value. +; The contents of the 16-bit halfword at the memory location specified by the +; aligned effective address are fetched, zero-extended, and placed in reg rT. +; The 16-bit signed offset is added to the contents of register rS to form the +; effective address. The effective address must be naturally-aligned. +; If the least-significant bit of the address is non-zero, +; an Address Error exception occurs. +; Operation: $t = MEM[$s + offset]; advance_pc(4); +; Syntax: lhu $t, offset($s) +; Encoding: 1001 01ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +; NOTE: exactly same thing as LH, but WITHOUT sign-extend. +align GRAIN, db 0x90 +_i_lhu: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + ;; rS (ecx) is not needed any more, can be reused + bt Imm, 0 ; Get low bit of vAddr + jnc _i_lhu_aligned_ok ; If zero, vAddr was properly aligned; + SetEXC EXC_AdEL ; If vAddr was NOT properly aligned: + jmp _Handle_Exception ; Go straight to exception handler. +_i_lhu_aligned_ok: ; Load is to proceed normally: + mov ecx, Imm ; Save vAddr to ECX + xor TMP, TMP ; Clear TMP, where we will put the HW +%ifdef LITTLE_ENDIAN + ;; Read the low byte of the HW first: + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr + 1) + mov TMP, eax ; TMP := eax (save the low byte) + ;; Now read the high byte: + mov eax, ecx ; Restore the original saved vAddr + or eax, 1 ; Will read vAddr + 1 + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr + 1) +%else + or Imm, 1 ; First, read vAddr + 1 : + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr + 1) + mov TMP, eax ; TMP := eax (save low byte) + mov eax, ecx ; Restore the original saved vAddr + call _Virt_Read_Byte ; EAX := Virt_Read_Byte(vAddr) +%endif + shl eax, 8 ; eax := eax << 8 + or eax, TMP ; eax := eax | TMP (high | low) + Wr_Reg rT, eax ; Regs[rT] := EAX (write back result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LWL - Load Word Right. +; Load the least-significant part of a word from an unaligned memory address +; as a signed value. The 16-bit signed offset is added to the contents of rS +; to form an effective address (EffAddr). EffAddr is the address of the +; least-significant of 4 consecutive bytes forming a word (W) in memory +; starting at an arbitrary byte boundary. A part of W, the least-significant +; 1 to 4 bytes, is in the aligned word containing EffAddr. This part of W is +; loaded into the least-significant (right) part of the word in register rT. +; The remaining most-significant part of the word in register rT is unchanged. +; Executing both LWR and LWL, in either order, delivers a sign-extended word +; value in the destination register. +; Operation: $t = $t MERGE MEM[$s + offset]; advance_pc(4); +; Syntax: lwr $t, offset($s) +; Encoding: 1001 10ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_lwr: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + ;; rS (ecx) is not needed any more, can be reused + mov TMP, R(rT) ; TMP := Regs[rT] (value to write) + mov AUX, Imm ; Save the original unaligned address + and Imm, ~(0x3) ; Clear bottom two bits of address + call _Virt_Read_Word ; EAX := Virt_Read_Word(EAX (aligned)) + ;; Proceed with write. EAX : old word, TMP : new word, ebx : still rT + mov ecx, AUX ; ecx := original unaligned address + and ecx, 0x3 ; keep only bottom two bits of it + shl ecx, 3 ; ecx := ecx (8 * (addr[0..2])) + mov AUX, 0xFFFFFF00 ; AUX := 0xFFFFFF00 + shl AUX, cl ; AUX := AUX << cl + and TMP, AUX ; TMP := TMP & AUX + xor ecx, 24 ; ecx := 24 - ecx + shr eax, cl ; right shift eax (orig word) by above + or TMP, eax ; TMP := TMP | eax (the writeback value) + Wr_Reg rT, TMP ; Regs[rT] := TMP (the writeback value) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SB -- Store Byte. +; The least significant byte of $t is stored at the specified address. +; Operation: MEM[$s + offset] = (0xff & $t); advance_pc(4); +; Syntax: sb $t, offset($s) +; Encoding: 1010 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_sb: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + mov TMP, R(rT) ; TMP (EDX) := Regs[rT] + and TMP, 0xFF ; Keep only low byte of TMP + call _Virt_Write_Byte ; Virt_Write_Byte(EAX, DL) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SH - Store Halfword. +; Store a halfword to memory. The least-significant 16-bit halfword of +; register rT is stored in memory at the location specified by the aligned +; effective address. The 16-bit signed offset is added to the contents of rS +; to form the effective address. +; The effective address must be naturally-aligned. If the least-significant +; bit of the address is non-zero, an Address Error exception occurs. +; Operation: MEM[$s + offset] = $t; advance_pc(4); +; Syntax: sh $t, offset($s) +; Encoding: 1010 01ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_sh: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + bt Imm, 0 ; Get low bit of vAddr + jnc _i_sh_aligned_ok ; If zero, vAddr was properly aligned; + SetEXC EXC_AdES ; If vAddr was NOT properly aligned: + jmp _Handle_Exception ; Go straight to exception handler. +_i_sh_aligned_ok: + mov AUX, Imm ; Save Imm (vAddr) to AUX + ;; rS (ecx) is not needed any more, can be reused + mov ecx, R(rT) ; ecx := Regs[rT] (HW to be written) + mov TMP, ecx ; TMP := ecx +%ifdef LITTLE_ENDIAN + and TMP, 0xFF ; isolate low byte + call _Virt_Write_Byte ; Virt_Write_Byte(vAddr, DL) + mov TMP, ecx ; TMP := the whole HW again + and TMP, 0xFF00 ; isolate high byte + shr TMP, 8 ; TMP := TMP >> 8 (get high byte) +%else ;; Big-Endian + shr TMP, 8 ; TMP := TMP >> 8 (get high byte) + call _Virt_Write_Byte ; Virt_Write_Byte(vAddr, DL) + mov TMP, ecx ; TMP := ecx again, now for low byte + and TMP, 0xFF ; isolate low byte +%endif + mov eax, AUX ; restore vAddr from AUX + or eax, 1 ; set bottom bit, for vAddr + 1 + call _Virt_Write_Byte ; Virt_Write_Byte(vAddr + 1, DL) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SWL - Store Word Left. +; Store the most-significant part of a word to an unaligned memory address. +; Operation: MEM[$s + offset] = $t; advance_pc(4); +; Syntax: swl $t, offset($s) +; Encoding: 1010 10ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_swl: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + ;; rS (ecx) is not needed any more, can be reused + mov TMP, R(rT) ; TMP := Regs[rT] (old reg value) + ;; rT (ebx) is not needed any more, can be reused + mov AUX, Imm ; Save the original unaligned address + and Imm, ~(0x3) ; Clear bottom two bits of address + mov ebx, Imm ; Save the aligned address for writeback + call _Virt_Read_Word ; EAX := Virt_Read_Word(EAX (aligned)) + ;; Proceed with write. EAX : old word, TMP : new word, ebx : WB addr. + mov ecx, AUX ; ecx := original unaligned address + and ecx, 0x3 ; keep only bottom two bits + shl ecx, 3 ; ecx := ecx (8 * (addr[0..2])) + shr TMP, cl ; right shift TMP by above + xor ecx, 24 ; ecx := 24 - ecx + mov AUX, 0xFFFFFF00 ; AUX := 0xFFFFFF00 + shl AUX, cl ; AUX := AUX << cl + and eax, AUX ; eax := eax & AUX + or TMP, eax ; TMP := TMP | eax (the writeback value) + mov eax, ebx ; restore aligned address for writeback + call _Virt_Write_Word ; Virt_Write_Word(EAX, TMP) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SW -- Store Word. +; The contents of $t is stored at the specified address. +; The effective address must be naturally-aligned. +; If either of the 2 least-significant bits of the address is non-zero, +; an Address Error exception occurs. +; Operation: MEM[$s + offset] = $t; advance_pc(4); +; Syntax: sw $t, offset($s) +; Encoding: 1010 11ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_sw: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + mov TMP, R(rT) ; TMP := Regs[rT] + call _Virt_Write_Word ; Virt_Write_Word(EAX, TMP) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SWR - Store Word Right. +; Store the least-significant part of a word to an unaligned memory address. +; Operation: MEM[$s + offset] = $t; advance_pc(4); +; Syntax: swr $t, offset($s) +; Encoding: 1011 10ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_swr: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + ;; rS (ecx) is not needed any more, can be reused + mov TMP, R(rT) ; TMP := Regs[rT] (value to write) + ;; rT (ebx) is not needed any more, can be reused + mov AUX, Imm ; Save the original unaligned address + and Imm, ~(0x3) ; Clear bottom two bits of address + mov ebx, Imm ; Save the aligned address for writeback + call _Virt_Read_Word ; EAX := Virt_Read_Word(EAX (aligned)) + ;; Proceed with write. EAX : old word, TMP : new word, ebx : WB addr. + mov ecx, AUX ; ecx := original unaligned address + and ecx, 0x3 ; keep only bottom two bits + shl ecx, 3 ; ecx := ecx * 8 + mov AUX, 0xFFFFFF ; AUX := 0xFFFFFF + shr AUX, cl ; AUX := AUX >> cl + and eax, AUX ; eax := eax & AUX + xor ecx, 24 ; ecx := 24 - ecx (24 - 8 * (addr[0..2])) + shl TMP, cl ; left shift TMP (value to write) by above + or TMP, eax ; TMP := TMP | eax (the writeback value) + mov eax, ebx ; restore aligned address for writeback + call _Virt_Write_Word ; Virt_Write_Word(EAX, TMP) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; CACHE. +; Operation: This is a NOP in this emulator. +; Syntax: cache $t, offset($s) +; Encoding: 1011 11ss ssst tttt iiii iiii iiii iiii +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_cache: + ; no fields + PRIVILEGED ; Permitted in Kernel Mode only. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; LL - Load Linked Word. +; Load a word from memory for an atomic read-modify-write. +; The contents of the 32-bit word at the memory location specified by the +; aligned effective address are fetched and written into register rT. +; The 16-bit signed offset is added to the contents of rS to form +; an effective address. +; Operation: $t = MEM[$s + offset]; llbit = 1; advance_pc(4); +; Syntax: ll $t, offset($s) +; Encoding: 1100 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_ll: + IType ; Load Imm, rS, rT Fields + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + call _Virt_Read_Word ; EAX := Virt_Read_Word(EAX) + Wr_Reg rT, EAX ; Regs[rT] := EAX + Flg_On LL_Bit ; Set the LL_Bit Flag + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; PREF - Prefetch. +; Operation: This is a NOP in this emulator. +; Syntax: pref $t, offset($s) +; Encoding: 1100 11ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_pref: + ; no fields + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SC - Store Conditional Word. +; Store a word to memory to complete an atomic read-modify-write. +; The 32-bit word in rT is conditionally stored in memory at the location +; specified by the aligned effective address. +; The 16-bit signed offset is added to the contents of rS to form an +; effective address +; Operation: if llbit == 1 then: MEM[$s + offset] = $t; rt = 1; advance_pc(4); +; else: rt = 0; advance_pc(4); +; Syntax: sc $t, offset($s) +; Encoding: 1110 00ss ssst tttt iiii iiii iiii iiii +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_i_sc: + IType ; Load Imm, rS, rT Fields + Flg_Get LL_Bit ; CF := LL_Bit + jnc _i_sc_not_taken ; If LL_Bit = 0, then do not store; else: + SX16 Imm ; Sign-extend the Imm offset + add Imm, R(rS) ; Imm (EAX) := Imm + Regs[rS] (vAddr) + mov TMP, R(rT) ; TMP := Regs[rT] + call _Virt_Write_Word ; Virt_Write_Word(EAX, TMP) + mov R(rT), 1 ; Regs[rT] := 1 ('yes, stored word') + jmp _i_sc_done ; Done. +_i_sc_not_taken: + mov R(rT), 0 ; Regs[rT] := 0 ('no, did not store') +_i_sc_done: + jmp _end_cycle +;----------------------------------------------------------------------------- diff -uNr a/m/mipsinst/m_instrs.asm b/m/mipsinst/m_instrs.asm --- a/m/mipsinst/m_instrs.asm false +++ b/m/mipsinst/m_instrs.asm c5afa1184e847ba45411669a68d1cc0f62a2bacb17155541f525a70d4edefd9a62789675f30c1eb79621d8737674fe39ba7a5d913d30fe18e8b785f3cf48276d @@ -0,0 +1,557 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO: Tests!!! + +;----------------------------------------------------------------------------- +section .rodata +align GRAIN, db 0x90 +_M_Table: + A32 _bad ; 0x00 : UNDEFINED + A32 _bad ; 0x01 : UNDEFINED + A32 _m_tlbwi ; 0x02 : tlbwi (01000010000000000000000000000010) + A32 _bad ; 0x03 : UNDEFINED + A32 _bad ; 0x04 : UNDEFINED + A32 _bad ; 0x05 : UNDEFINED + A32 _m_tlbwr ; 0x06 : tlbwr (01000010000000000000000000000110) + A32 _bad ; 0x07 : UNDEFINED + A32 _m_tlbp ; 0x08 : tlbp (01000010000000000000000000001000) + A32 _bad ; 0x09 : UNDEFINED + A32 _bad ; 0x0a : UNDEFINED + A32 _bad ; 0x0b : UNDEFINED + A32 _bad ; 0x0c : UNDEFINED + A32 _bad ; 0x0d : UNDEFINED + A32 _bad ; 0x0e : UNDEFINED + A32 _bad ; 0x0f : UNDEFINED + A32 _bad ; 0x10 : UNDEFINED + A32 _bad ; 0x11 : UNDEFINED + A32 _bad ; 0x12 : UNDEFINED + A32 _bad ; 0x13 : UNDEFINED + A32 _bad ; 0x14 : UNDEFINED + A32 _bad ; 0x15 : UNDEFINED + A32 _bad ; 0x16 : UNDEFINED + A32 _bad ; 0x17 : UNDEFINED + A32 _m_eret ; 0x18 : eret (01000010000000000000000000011000) + A32 _bad ; 0x19 : UNDEFINED + A32 _bad ; 0x1a : UNDEFINED + A32 _bad ; 0x1b : UNDEFINED + A32 _bad ; 0x1c : UNDEFINED + A32 _bad ; 0x1d : UNDEFINED + A32 _bad ; 0x1e : UNDEFINED + A32 _bad ; 0x1f : UNDEFINED +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------- +; 'M-Type' Instructions (tlbwi, tlbwr, tlbp, eret, mfc0, mtc0, wait) : | +;----------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; TLBWI - Write Indexed TLB Entry. +; Write a TLB entry indexed by the Index register. +; The TLB entry pointed to by the Index register is written from the contents +; of the EntryHi, EntryLo0, EntryLo1, and PageMask registers. +; Syntax: tlbwi +; Encoding: 0100 0010 0000 0000 0000 0000 0000 0010 +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_m_tlbwi: + ; no fields + mov AUX, Sr(CP0_Index) ; Get CP0_Index register + call _write_tlb_entry ; Write the indexed TLB entry. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; TLBWR - Write Random TLB Entry. +; Write a TLB entry indexed by the Random register. +; The TLB entry pointed to by the Random register is written from the contents +; of the EntryHi, EntryLo0, EntryLo1, and PageMask registers. +; Syntax: tlbwr +; Encoding: 0100 0010 0000 0000 0000 0000 0000 0110 +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_m_tlbwr: + ; no fields + mov ecx, Sr(CP0_Wired) ; ecx := CP0_Wired + mov ebx, TLB_ENTRIES_COUNT ; ebx := #tlbentries + sub ebx, ecx ; ebx := #tlbentries - Wired + mov edx, 0 ; edx (upper half of dividend) := 0 + mov eax, CP0_Count ; eax (lower half of divident) := cnt + div ebx ; edx:eax / ebx + add edx, ecx ; edx (remainder) := edx + wired + mov AUX, edx ; make edx the index for tlb write + call _write_tlb_entry ; Write the indexed TLB entry. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; TLBP - Probe TLB for Matching Entry. +; Find a matching entry in the TLB. +; The Index register is loaded with the address of the TLB entry whose +; contents match the contents of the EntryHi register. If no TLB entry +; matches, the high-order bit of the Index register is set. +; Syntax: tlbp +; Encoding: 0100 0010 0000 0000 0000 0000 0000 1000 +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_m_tlbp: + ; no fields + mov Sr(CP0_Index), 0x80000000 ; CP0_Index := 0x80000000 + ;; Get the active ASID: + mov edx, Sr(CP0_EntryHi) ; edx := CP0_EntryHi + mov ecx, edx ; ecx := edx + and edx, 0xFF ; edx := edx & 0xFF (get current ASID) + ;; Get the desired tag: + and ecx, 0xFFFFF000 ; ecx := ecx & 0xFFFFF000 + shr ecx, 13 ; ecx := ecx >> 13 (current Tag) + ;; For each slot in table (0 .. 15), attempt lookup + xor AUX, AUX ; Start with the 0-th entry in table +_m_tlbp_lookup_entry: + mov eax, TLB_E(AUX) ; eax := current TLB entry + mov ebx, eax ; ebx := eax + and ebx, TLB_VPN2_Mask ; get VPN2 of this entry + cmp ebx, ecx ; cmp(entry.VPN2, vAddr.tag) + jne _m_tlbp_lookup_nope ; if entry.VPN2 != vAddr.tag: no match + bt eax, TLB_G ; is entry.G = 1? + jc _m_tlbp_lookup_match ; then match. + shr eax, TLB_ASID_Shift ; eax := eax >> TLB_ASID_Shift + and eax, TLB_ASID_Mask ; eax := entry.ASID + cmp eax, edx ; entry.ASID = current ASID ? + jne _m_tlbp_lookup_nope ; if neither G=1 nor ASID match. + ;; otherwise: +_m_tlbp_lookup_match: ; TLB Match: + mov Sr(CP0_Index), AUX ; Save the index + jmp _end_cycle ; Fin. +_m_tlbp_lookup_nope: ; try next one in the table, if any + inc AUX ; index := index + 1 + cmp AUX, TLB_ENTRIES_COUNT ; see if still in range 0 .. n-1 + jb _m_tlbp_lookup_entry ; if in range, go to next entry + ;; if we found nothing, we end up with CP0_Index = 0x80000000 + jmp _end_cycle ; Fin. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ERET - Exception Return. +; Return from interrupt, exception, or error trap. +; Syntax: eret +; Encoding: 0100 0010 0000 0000 0000 0000 0001 1000 +; PRIVILEGED (permitted in Kernel Mode only.) +;;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_m_eret: + ; no fields + Flg_Get InDelaySlot ; CF := whether we are in delay slot + jc _m_eret_abort ; ... if so, abort this instruction; else: + ;; proceed: + Flg_Off LL_Bit ; Clear the LL_Bit Flag + btr CP0_Status, CP0St_ERL ; CF := CP0St_ERL Flag, and clear it + jnc _m_eret_erl_false ; If ERL was not set, do erl_false; else: +_m_eret_erl_true: + mov PC, Sr(CP0_ErrorEpc) ; PC := CP0_ErrorEpc + jmp _m_eret_done ; Done with this case +_m_eret_erl_false: + mov PC, Sr(CP0_Epc) ; PC := CP0_Epc + btr CP0_Status, CP0St_EXL ; Clear the EXL Flag +_m_eret_done: + sub PC, 0x4 ; Counteract the usual PC = PC + 4 +_m_eret_abort: + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MFC0 - Move from Coprocessor 0. +; Move the contents of a coprocessor 0 register to a general register. +; The contents of the coprocessor 0 register specified by the combination of +; rd and sel (q) are loaded into general register rt. Note that not all +; coprocessor 0 registers support the sel field. In those instances, the sel +; field must be zero. +; Operation: $t = CPR[0, rD, q] +; Syntax: mfc0 $t, $d, q +; Encoding: 0100 0000 000t tttt dddd d000 0000 0qqq +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_m_mfc0: + CPType ; Load Q, rD, rT (ecx, eax, ebx) + JTABLE eax, _MFC0_Table ; Dispatch on rD via MFC0 Table. + ;; We continue in _mfc0_rXX where XX is rD. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MFC0 Cases. Parameters: Q (ecx), rD (eax), rT (ebx) +;----------------------------------------------------------------------------- +_mfc0_r00: ; 0x00 + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, Sr(CP0_Index) ; return CP0_Index + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r01: ; 0x01 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r02: ; 0x02 + mov TMP, Sr(CP0_EntryLo0) ; return CP0_EntryLo0 + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r03: ; 0x03 + mov TMP, Sr(CP0_EntryLo1) ; return CP0_EntryLo1 + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r04: ; 0x04 + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, Sr(CP0_Context) ; return CP0_Context + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r05: ; 0x05 + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, Sr(CP0_PageMask) ; return CP0_PageMask + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r06: ; 0x06 + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, Sr(CP0_Wired) ; return CP0_Wired + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r07: ; 0x07 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r08: ; 0x08 + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, Sr(CP0_BadVAddr) ; return CP0_BadVAddr + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r09: ; 0x09 + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, CP0_Count ; return CP0_Count ('fast reg') + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r10: ; 0x0a + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, Sr(CP0_EntryHi) ; return CP0_EntryHi + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r11: ; 0x0b + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, CP0_Compare ; return CP0_Compare ('fast reg') + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r12: ; 0x0c + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, CP0_Status ; return CP0_Status ('fast reg') + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r13: ; 0x0d + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, CP0_Cause ; return CP0_Cause ('fast reg') + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r14: ; 0x0e + test ecx, ecx ; Sel != 0 ? + jnz _mfc0_unknown ; ... then unknown; else: + mov TMP, Sr(CP0_Epc) ; return CP0_Epc + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r15: ; 0x0f + mov TMP, 0x00018000 ; processor id (qemu 4kc) + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r16: ; 0x10 + cmp r_Q, 0 ; Is sel = 0? + je _mfc0_r16_q_0 ; If sel = 0... + cmp r_Q, 1 ; Is sel = 1? + je _mfc0_r16_q_1 ; If sel = 1... + jmp _mfc0_unknown ; Unknown sel +_mfc0_r16_q_0: + mov TMP, 0x80008082 ; return 0x80008082 + jmp _mfc0_writeback ; Done +_mfc0_r16_q_1: + mov TMP, 0x1e190c8a ; return 0x1e190c8a + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r17: ; 0x11 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r18: ; 0x12 + xor TMP, TMP ; TMP = 0 + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r19: ; 0x13 + xor TMP, TMP ; TMP = 0 + jmp _mfc0_writeback ; Done +;----------------------------------------------------------------------------- +_mfc0_r20: ; 0x14 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r21: ; 0x15 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r22: ; 0x16 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r23: ; 0x17 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r24: ; 0x18 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r25: ; 0x19 + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r26: ; 0x1a + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r27: ; 0x1b + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r28: ; 0x1c + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r29: ; 0x1d + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r30: ; 0x1e + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_r31: ; 0x1f + jmp _mfc0_unknown +;----------------------------------------------------------------------------- +_mfc0_writeback: ; Write result and go to next cycle + Wr_Reg rT, TMP ; Regs[rT] := TMP (result) + jmp _end_cycle ; Fin +;----------------------------------------------------------------------------- +_mfc0_unknown: + ; 'The results are UNDEFINED if coprocessor 0 does not contain a + ; register as specified by rd and sel.' + ACHTUNG "Unknown CP0 Reg Selector in MFC0!" ;; TODO: print detail + jmp _end_cycle ; Do nothing, carry on. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MTC0 - Move to Coprocessor 0. +; Move the contents of a general register to a coprocessor 0 register. +; The contents of general register rt are loaded into the coprocessor 0 +; register specified by the combination of rd and sel (q). Not all +; coprocessor 0 registers support the the sel field. In those instances, the +; sel field must be set to zero. +; Operation: CPR[0, rD, q] = $t +; Syntax: mfc0 $t, $d, q +; Encoding: 0100 0000 100t tttt dddd d000 0000 0qqq +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_m_mtc0: + CPType ; Load Q, rD, rT (ecx, eax, ebx) + mov rT, R(rT) ; ebx := Regs[rT] + JTABLE eax, _MTC0_Table ; Dispatch on rD via MTC0 Table. + ;; We continue in _mtc0_rXX where XX is rD. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MTC0 Cases. Parameters: Q (ecx), rD (eax), Regs[rT] (ebx) +;----------------------------------------------------------------------------- +_mtc0_r00: ; 0x00 + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: + mov eax, Sr(CP0_Index) ; eax := CP0_Index + and ebx, 0xF ; T := T & 0xF + and eax, 0x80000000 ; eax := eax & 0x80000000 + or eax, ebx ; eax := eax | T + mov Sr(CP0_Index), eax ; CP0_Index := eax + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r01: ; 0x01 + jmp _mtc0_unknown +;----------------------------------------------------------------------------- +_mtc0_r02: ; 0x02 + and ebx, 0x3FFFFFF ; T := T & 0x3FFFFFF + mov Sr(CP0_EntryLo0), ebx ; CP0_EntryLo0 := T + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r03: ; 0x03 + and ebx, 0x3FFFFFF ; T := T & 0x3FFFFFF + mov Sr(CP0_EntryLo1), ebx ; CP0_EntryLo1 := T + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r04: ; 0x04 +%define CONTEXTMASK 0xFF800000 +%define nCONTEXTMASK (0xFFFFFFFF ^ 0xFF800000) + mov eax, Sr(CP0_Context) ; eax := CP0_Context + and ebx, CONTEXTMASK ; T := T & CONTEXTMASK + and eax, nCONTEXTMASK ; eax := eax & ~CONTEXTMASK + or eax, ebx ; eax := eax | T + mov Sr(CP0_Context), eax ; CP0_Context := eax + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r05: ; 0x05 + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: + test ebx, ebx ; T != 0 ? + jnz _mtc0_r05_untest_pgmask ; If so, eggog + and ebx, 0x1FFE000 ; T := T & 0x1FFE000 + mov Sr(CP0_PageMask), ebx ; CP0_PageMask := T + jmp _end_cycle ; Done +_mtc0_r05_untest_pgmask: ; Proper kernel won't do this. But if someone does: + ACHTUNG "MTC0: Unsupported Page Mask!" ; maybe halt w/ sirens? + jmp _end_cycle ; Let's continue (probably wedged...) +;----------------------------------------------------------------------------- +_mtc0_r06: ; 0x06 + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: + and ebx, 0xF ; T := T & 0xF + mov Sr(CP0_Wired), ebx ; CP0_Wired := T + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r07: ; 0x07 + jmp _mtc0_unknown +;----------------------------------------------------------------------------- +_mtc0_r08: ; 0x08 + jmp _mtc0_unknown +;----------------------------------------------------------------------------- +_mtc0_r09: ; 0x09 + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: + mov CP0_Count, ebx ; CP0_Count := T ('fast reg') + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r10: ; 0x0a + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: + and ebx, ~0x1F00 ; T := T & ~0x1F00 + mov Sr(CP0_EntryHi), ebx ; CP0_EntryHi := T + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r11: ; 0x0b + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: + ClrIRQ TIMER_IRQ ; Clear MIPS Timer IRQ + mov CP0_Compare, ebx ; CP0_Compare := T ('fast reg') + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r12: ; 0x0c + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: +%define STATUSMASK 0x7D7CFF17 + mov eax, CP0_Status ; eax := CP0_Status + and ebx, STATUSMASK ; T := T & STATUSMASK + and eax, ~STATUSMASK ; eax := eax & ~STATUSMASK + or eax, ebx ; eax := eax | T + mov CP0_Status, eax ; CP0_Status := eax + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r13: ; 0x0d + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: +%define CAUSEMASK ((1 << 23) | (1 << 22) | (3 << 8)) + mov eax, CP0_Cause ; eax := CP0_Cause + and ebx, CAUSEMASK ; T := T & CAUSEMASK + and eax, ~CAUSEMASK ; eax := eax & ~CAUSEMASK + or eax, ebx ; eax := eax | T + mov CP0_Cause, eax ; CP0_Cause := eax + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r14: ; 0x0e + test ecx, ecx ; Sel != 0 ? + jnz _mtc0_unknown ; ... then unknown; else: + mov Sr(CP0_Epc), ebx ; CP0_Epc := T + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r15: ; 0x0f + jmp _mtc0_unknown +;----------------------------------------------------------------------------- +_mtc0_r16: ; 0x10 + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r17: ; 0x11 + jmp _mtc0_unknown +;----------------------------------------------------------------------------- +_mtc0_r18: ; 0x12 + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r19: ; 0x13 + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- +_mtc0_r20: ; 0x14 + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r21: ; 0x15 + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r22: ; 0x16 + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r23: ; 0x17 + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r24: ; 0x18 + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r25: ; 0x19 + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r26: ; 0x1a + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r27: ; 0x1b + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r28: ; 0x1c + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r29: ; 0x1d + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r30: ; 0x1e + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_r31: ; 0x1f + jmp _mtc0_unknown ; Done +;----------------------------------------------------------------------------- +_mtc0_unknown: + ; 'The results are UNDEFINED if coprocessor 0 does not contain a + ; register as specified by rd and sel.' + ACHTUNG "Unknown CP0 Reg Selector in MTC0!" ;; TODO: print detail + jmp _end_cycle ; Done +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; WAIT - Enter Standby Mode. +; Wait for Event. +; Puts the emulator into 'wait' mode. +; Encoding: 0100 001- ---- ---- ---- ---- --10 0000 +; PRIVILEGED (permitted in Kernel Mode only.) +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_m_wait: + ; no fields + Flg_On Waiting ; Set 'Waiting' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- diff -uNr a/m/mipsinst/r_instrs.asm b/m/mipsinst/r_instrs.asm --- a/m/mipsinst/r_instrs.asm false +++ b/m/mipsinst/r_instrs.asm 40ef74622647a11ac973984a9f831ad39bbec47eac267e3f62fb30e36058ac4b01074b97585e9b36e0b32deb7344ec4c5aa6585e9571baa2e0432e8d78deb254 @@ -0,0 +1,662 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; TODO: 1) Traps in ADD and SUB (gcc 4.8.1 doesn't seem to use. but do exist) +;; 2) Tests!!! + +;---------------------------- +; R-Type MIPS Instructions: | +;---------------------------- + +;----------------------------------------------------------------------------- +section .rodata +align GRAIN, db 0x90 +_R_Table: + A32 _r_sll ; 0x00 : sll (000000????????????????????000000) + A32 _bad ; 0x01 : UNDEFINED + A32 _r_srl ; 0x02 : srl (000000????????????????????000010) + A32 _r_sra ; 0x03 : sra (000000????????????????????000011) + A32 _r_sllv ; 0x04 : sllv (000000????????????????????000100) + A32 _bad ; 0x05 : UNDEFINED + A32 _r_srlv ; 0x06 : srlv (000000????????????????????000110) + A32 _r_srav ; 0x07 : srav (000000????????????????????000111) + A32 _r_jr ; 0x08 : jr (000000????????????????????001000) + A32 _r_jalr ; 0x09 : jalr (000000????????????????????001001) + A32 _r_movz ; 0x0a : movz (000000???????????????00000001010) + A32 _r_movn ; 0x0b : movn (000000???????????????00000001011) + A32 _r_syscall ; 0x0c : syscall (000000????????????????????001100) + A32 _bad ; 0x0d : UNDEFINED + A32 _bad ; 0x0e : UNDEFINED + A32 _r_sync ; 0x0f : sync (000000????????????????????001111) + A32 _r_mfhi ; 0x10 : mfhi (000000????????????????????010000) + A32 _r_mthi ; 0x11 : mthi (000000????????????????????010001) + A32 _r_mflo ; 0x12 : mflo (000000????????????????????010010) + A32 _r_mtlo ; 0x13 : mtlo (000000????????????????????010011) + A32 _bad ; 0x14 : UNDEFINED + A32 _bad ; 0x15 : UNDEFINED + A32 _bad ; 0x16 : UNDEFINED + A32 _bad ; 0x17 : UNDEFINED + A32 _r_mult ; 0x18 : mult (000000????????????????????011000) + A32 _r_multu ; 0x19 : multu (000000????????????????????011001) + A32 _r_div ; 0x1a : div (000000????????????????????011010) + A32 _r_divu ; 0x1b : divu (000000????????????????????011011) + A32 _bad ; 0x1c : UNDEFINED + A32 _bad ; 0x1d : UNDEFINED + A32 _bad ; 0x1e : UNDEFINED + A32 _bad ; 0x1f : UNDEFINED + A32 _r_add ; 0x20 : add (000000????????????????????100000) + A32 _r_addu ; 0x21 : addu (000000????????????????????100001) + A32 _r_sub ; 0x22 : sub (000000????????????????????100010) + A32 _r_subu ; 0x23 : subu (000000????????????????????100011) + A32 _r_and ; 0x24 : and (000000????????????????????100100) + A32 _r_or ; 0x25 : or (000000????????????????????100101) + A32 _r_xor ; 0x26 : xor (000000????????????????????100110) + A32 _r_nor ; 0x27 : nor (000000????????????????????100111) + A32 _bad ; 0x28 : UNDEFINED + A32 _bad ; 0x29 : UNDEFINED + A32 _r_slt ; 0x2a : slt (000000????????????????????101010) + A32 _r_sltu ; 0x2b : sltu (000000????????????????????101011) + A32 _bad ; 0x2c : UNDEFINED + A32 _bad ; 0x2d : UNDEFINED + A32 _bad ; 0x2e : UNDEFINED + A32 _bad ; 0x2f : UNDEFINED + A32 _bad ; 0x30 : UNDEFINED + A32 _bad ; 0x31 : UNDEFINED + A32 _bad ; 0x32 : UNDEFINED + A32 _bad ; 0x33 : UNDEFINED + A32 _r_teq ; 0x34 : teq (000000????????????????????110100) + A32 _bad ; 0x35 : UNDEFINED + A32 _r_tne ; 0x36 : tne (000000????????????????????110110) + A32 _bad ; 0x37 : UNDEFINED + A32 _bad ; 0x38 : UNDEFINED + A32 _bad ; 0x39 : UNDEFINED + A32 _bad ; 0x3a : UNDEFINED + A32 _bad ; 0x3b : UNDEFINED + A32 _bad ; 0x3c : UNDEFINED + A32 _bad ; 0x3d : UNDEFINED + A32 _bad ; 0x3e : UNDEFINED + A32 _bad ; 0x3f : UNDEFINED +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +; SLL -- Shift left logical. +; Shifts a register value left by the shift amount listed in the instruction +; and places the result in a third register. Zeroes are shifted in. +; Operation: $d = $t << h; advance_pc(4); +; Syntax: sll $d, $t, h +; Encoding: 0000 00-- ---t tttt dddd dhhh hh00 0000 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_sll: + RType_D_T_Shamt ; load rS, rT, Shamt Fields + mov TMP, R(rT) ; TMP := Regs[rT] + shl TMP, cl ; TMP := TMP << CL (Shamt is in ECX) + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SRL -- Shift right logical. +; Shifts a register value right by the shift amount (shamt) and places the +; value in the destination register. Zeroes are shifted in. +; Operation: $d = $t >> h; advance_pc(4); +; Syntax: srl $d, $t, h +; Encoding: 0000 00-- ---t tttt dddd dhhh hh00 0010 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_srl: + RType_D_T_Shamt ; load rS, rT, Shamt Fields + mov TMP, R(rT) ; TMP := Regs[rT] + shr TMP, cl ; TMP := TMP >> CL (Shamt is in ECX) + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SRA -- Shift right arithmetic. +; Shifts a register value right by the shift amount (shamt) and places the +; value in the destination register. The sign bit is shifted in. +; Operation: $d = $t >> h; advance_pc(4); +; Syntax: sra $d, $t, h +; Encoding: 0000 00-- ---t tttt dddd dhhh hh00 0011 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_sra: + RType_D_T_Shamt ; load rS, rT, Shamt Fields + mov TMP, R(rT) ; TMP := Regs[rT] + sar TMP, cl ; TMP := TMP >>(sgn) CL (Shamt is in ECX) + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SLLV -- Shift left logical variable. +; Shifts a register value left by the value in a second register and places +; the result in a third register. Zeroes are shifted in. +; Operation: $d = $t << $s; advance_pc(4); +; Syntax: sllv $d, $t, $s +; Encoding: 0000 00ss ssst tttt dddd d--- --00 0100 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_sllv: + RType ; load rD, rS, rT Fields + mov ecx, R(rS) ; Now CL is the shift amount + mov TMP, R(rT) ; TMP := Regs[rT] + shl TMP, cl ; TMP := TMP << CL + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SRLV -- Shift right logical variable. +; Shifts a register value right by the amount specified in $s and places the +; value in the destination register. Zeroes are shifted in. +; Operation: $d = $t >> $s; advance_pc(4); +; Syntax: srlv $d, $t, $s +; Encoding: 0000 00ss ssst tttt dddd d000 0000 0110 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_srlv: + RType ; load rD, rS, rT Fields + mov ecx, R(rS) ; Now CL is the shift amount + mov TMP, R(rT) ; TMP := Regs[rT] + shr TMP, cl ; TMP := TMP >> CL + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SRAV - Shift Word Right Arithmetic Variable. +; Execute an arithmetic right-shift of a word by a variable number of bits. +; The contents of the low-order 32-bit word of rT are shifted right, +; duplicating the sign-bit (bit 31) in the emptied bits; the word result is +; placed in rD. The bit-shift amount is specified by the low-order 5 bits +; of rS. +; Operation: $d = $t >> $s; advance_pc(4); +; Syntax: srav $d, $t, $s +; Encoding: 0000 00ss ssst tttt dddd d000 0000 0111 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_srav: + RType ; load rD, rS, rT Fields + mov ecx, R(rS) ; Now CL is the shift amount + mov TMP, R(rT) ; TMP := Regs[rT] + sar TMP, cl ; TMP := TMP >> CL (extends sign bit) + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; JR -- Jump register. +; Jump to the address contained in register $s +; Operation: PC = nPC; nPC = $s; +; Syntax: jr $s +; Encoding: 0000 00ss sss0 0000 0000 0000 0000 1000 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_jr: + RType_S_Only ; Load rS Field + mov nPC, R(rS) ; nPC := Regs[rS] + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; JALR - Jump and Link Register. +; Execute a procedure call to an instruction address in a register. +; Place the return address link in rD. The return link is the address of the +; second instruction following the branch, where execution continues after a +; procedure call. Jump to the effective target address in rS. Execute the +; instruction that follows the jump, in the branch delay slot, before +; executing the jump itself. +; Operation: $d = PC + 8; PC = nPC; nPC = $s +; Syntax: jalr $s +; Encoding: 0000 00ss sss0 0000 dddd 0000 0000 1001 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_jalr: + RType_S_D_Only ; Load rD and rS Fields + mov nPC, R(rS) ; nPC := Regs[rS] + mov TMP, PC ; TMP := PC + add TMP, 0x8 ; TMP := TMP + 8 + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + Flg_On InDelaySlot ; Set 'Delay Slot' Flag. + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MOVZ - Move Conditional on Zero. +; Conditionally move a register after testing a register value. +; If the value in rT is equal to zero, then the contents of rS are placed +; into rD. +; Operation: if $t = 0 then $d = $s +; Syntax: movz $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0000 1010 +;----------------------------------------------------------------------------- +; TODO: measure if the cmov is actually faster than a branch here ! +; TODO: untested +align GRAIN, db 0x90 +_r_movz: + ; UNDONE_INST + RType ; load rD, rS, rT Fields + mov TMP, R(rT) ; TMP := Regs[rT] + test TMP, TMP ; Set Z flag if TMP = 0 + mov TMP, R(rD) ; First, move Regs[rD] to TMP + cmovz TMP, R(rS) ; If Z flag set, overwrite with Regs[rS] + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MOVN - Move Conditional on Not Zero. +; Conditionally move a register after testing a register value. +; If the value in rT is NOT equal to zero, then the contents of rS are placed +; into rD. +; Operation: if $t != 0 then $d = $s +; Syntax: movn $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0000 1011 +;----------------------------------------------------------------------------- +; TODO: measure if the cmov is actually faster than a branch here ! +; TODO: untested +align GRAIN, db 0x90 +_r_movn: + ; UNDONE_INST + RType ; load rD, rS, rT Fields + mov TMP, R(rT) ; TMP := Regs[rT] + test TMP, TMP ; Set Z flag if TMP = 0 + mov TMP, R(rD) ; First, move Regs[rD] to TMP + cmovnz TMP, R(rS) ; If Z not set, overwrite with Regs[rS] + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SYSCALL -- System call. +; Generates a software interrupt. +; Operation: advance_pc(4); +; Syntax: syscall +; Encoding: 0000 00-- ---- ---- ---- ---- --00 1100 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_syscall: + ; no fields + SetEXC EXC_SYS ; Set the EXC_SYS Exception + jmp _Handle_Exception ; Go straight to exception handler. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SYNC - Synchronize Shared Memory. +; Operation: This is a NOP in this emulator. +; Syntax: sync +; Encoding: 0000 0000 0000 0000 0000 0000 0000 1111 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_sync: + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MFHI -- Move from HI. +; The contents of special register HI are moved to the specified register. +; Operation: $d = $HI; advance_pc(4); +; Syntax: mfhi $d +; Encoding: 0000 0000 0000 0000 dddd d000 0001 0000 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_mfhi: + RType_D_Only ; Load rD Field + mov TMP, Sr(HI) ; TMP := Sr(HI) + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MTHI - Move to HI Register. +; The contents of rS are loaded to the special register HI. +; Operation: $HI = $s +; Syntax: mthi $s +; Encoding: 0000 00ss sss0 0000 0000 0000 0001 0001 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_mthi: + RType_S_Only ; Load rS Field + mov TMP, R(rS) ; TMP := Regs[rS] + mov Sr(HI), TMP ; HI := TMP + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MFLO -- Move from LO. +; The contents of register LO are moved to the specified register. +; Operation: $d = $LO; advance_pc(4); +; Syntax: mflo $d +; Encoding: 0000 0000 0000 0000 dddd d000 0001 0010 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_mflo: + RType_D_Only ; Load rD Field + mov TMP, Sr(LO) ; TMP := Sr(LO) + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MTLO - Move to LO. +; The contents of rS are loaded to the special register LO. +; Operation: $LO = $s +; Syntax: mtlo $s +; Encoding: 0000 00ss sss0 0000 0000 0000 0001 0011 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_mtlo: + RType_S_Only ; Load rS Field + mov TMP, R(rS) ; TMP := Regs[rS] + mov Sr(LO), TMP ; LO := TMP + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MULT -- Multiply signed. +; Multiplies $s by $t and stores the result in $HI:$LO. +; Operation: $HI:$LO = $s * $t; advance_pc(4); +; Syntax: mult $s, $t +; Encoding: 0000 00ss ssst tttt 0000 0000 0001 1000 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_mult: + RType_S_T_Only + mov eax, R(rT) ; eax := Regs[rT] + imul R(rS) ; edx:eax := Regs[rS] * eax (signed) + mov Sr(LO), eax ; LO := Bottom 32 bits of product + mov Sr(HI), edx ; HI := Top 32 bits of product + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; MULTU -- Multiply unsigned. +; Multiplies $s by $t and stores the result in $HI:$LO. +; Operation: $HI:$LO = $s * $t; advance_pc(4); +; Syntax: multu $s, $t +; Encoding: 0000 00ss ssst tttt 0000 0000 0001 1001 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_multu: + RType_S_T_Only ; Load rS and rT Fields + mov eax, R(rT) ; eax := Regs[rT] + mul R(rS) ; edx:eax := Regs[rS] * eax (unsigned) + mov Sr(LO), eax ; LO := Bottom 32 bits of product + mov Sr(HI), edx ; HI := Top 32 bits of product + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; DIV -- Divide signed. +; Divides $s by $t and stores the quotient in $LO and the remainder in $HI +; Operation: $LO = $s / $t; $HI = $s % $t; advance_pc(4); +; Syntax: div $s, $t +; Encoding: 0000 00ss ssst tttt 0000 0000 0001 1010 +;----------------------------------------------------------------------------- +; TODO: detect div0 and other special cases +align GRAIN, db 0x90 +_r_div: + RType_S_T_Only ; Load rS and rT Fields + mov eax, R(rS) ; eax := Regs[rS] + cdq ; extend sign of eax into edx + cmp R(rT), 0 ; test for div0 + je _r_div_div0 ; div0 is undefined result + idiv R(rT) ; edx:eax / Regs[rT]; edx := r, eax := q + mov Sr(LO), eax ; LO := edx:eax /(signed) ecx (quotient) + mov Sr(HI), edx ; HI := edx:eax %(signed) ecx (remainder) +_r_div_div0: + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; DIVU -- Divide unsigned. +; Divides $s by $t and stores the quotient in $LO and the remainder in $HI +; Operation: $LO = $s / $t; $HI = $s % $t; advance_pc(4); +; Syntax: divu $s, $t +; Encoding: 0000 00ss ssst tttt 0000 0000 0001 1011 +;----------------------------------------------------------------------------- +; TODO: detect div0 and other special cases +align GRAIN, db 0x90 +_r_divu: + RType_S_T_Only ; Load rS and rT Fields + xor edx, edx ; edx := 0 + mov eax, R(rS) ; eax := Regs[rS] + cmp R(rT), 0 ; test for div0 + je _r_divu_div0 ; div0 is undefined result + div R(rT) ; edx:eax / Regs[rT]; edx := r, eax := q + mov Sr(LO), eax ; LO := edx:eax / ecx (quotient) + mov Sr(HI), edx ; HI := edx:eax % ecx (remainder) +_r_divu_div0: + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ADD -- Add (with overflow). +; Adds two registers and stores the result in a register +; Operation: $d = $s + $t; advance_pc(4); +; Syntax: add $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 0000 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_add: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + add TMP, R(rT) ; TMP := Regs[rS] + Regs[rT] + ; TODO: detect/trap overflow + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ADDU -- Add unsigned (no overflow). +; Adds two registers and stores the result in a register +; Operation: $d = $s + $t; advance_pc(4); +; Syntax: addu $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 0001 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_addu: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + add TMP, R(rT) ; TMP := Regs[rS] + Regs[rT] + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SUB -- Subtract. +; Subtracts two registers and stores the result in a register +; Operation: $d = $s - $t; advance_pc(4); +; Syntax: sub $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 0010 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_sub: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + sub TMP, R(rT) ; TMP := Regs[rS] - Regs[rT] + ; TODO: detect and trap overflow + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SUBU -- Subtract unsigned. +; Subtracts two registers and stores the result in a register +; Operation: $d = $s - $t; advance_pc(4); +; Syntax: subu $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 0011 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_subu: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + sub TMP, R(rT) ; TMP := Regs[rS] - Regs[rT] + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; AND -- Bitwise AND. +; Bitwise ANDs two registers and stores the result in a register +; Operation: $d = $s & $t; advance_pc(4); +; Syntax: and $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 0100 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_and: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + and TMP, R(rT) ; TMP := TMP & Regs[rT] + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; OR -- Bitwise OR. +; Bitwise logical ORs two registers and stores the result in a register +; Operation: $d = $s | $t; advance_pc(4); +; Syntax: or $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 0101 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_or: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + or TMP, R(rT) ; TMP := TMP | Regs[rT] + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; XOR -- Bitwise Exclusive OR. +; Exclusive ORs two registers and stores the result in a register +; Operation: $d = $s ^ $t; advance_pc(4); +; Syntax: xor $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d--- --10 0110 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_xor: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + xor TMP, R(rT) ; TMP := TMP ^ Regs[rT] + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; NOR - Not OR. +; Do a bitwise logical NOT OR. +; The contents of rS are combined with the contents of rT in a bitwise logical +; NOR operation. The result is placed into rD. +; Operation: $d = $s NOR $t +; Syntax: nor $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 0111 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_nor: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + or TMP, R(rT) ; TMP := TMP | Regs[rT] + not TMP ; TMP := ~TMP + Wr_Reg rD, TMP ; Regs[rD] := TMP (result) + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SLT -- Set on less than (signed). +; If $s is less than $t, $d is set to one. It gets zero otherwise. +; Operation: if $s < $t $d = 1; advance_pc(4); else $d = 0; advance_pc(4); +; Syntax: slt $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 1010 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_slt: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + ; We do not need rS, so can reuse it here: + setl rS_LowByte ; 1 if R(rS) <(signed) R(rT), else 0 + and rS, 0x1 ; Only want the bit that we have set + Wr_Reg rD, rS ; Regs[rD] := result + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; SLTU -- Set on less than unsigned. +; If $s is less than $t, $d is set to one. It gets zero otherwise. +; Operation: if $s < $t $d = 1; advance_pc(4); else $d = 0; advance_pc(4); +; Syntax: sltu $d, $s, $t +; Encoding: 0000 00ss ssst tttt dddd d000 0010 1011 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_sltu: + RType ; load rD, rS, rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + ; We do not need rS, so can reuse it here: + setb rS_LowByte ; 1 if R(rS) <(signed) R(rT), else 0 + and rS, 0x1 ; Only want the bit that we have set + Wr_Reg rD, rS ; Regs[rD] := result + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; TEQ - Trap if Equal. +; TODO: document +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_teq: + RType_S_T_Only ; Load rS and rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + jne _r_teq_neql + SetEXC EXC_Tr ; Spring the Trap Exception. + jmp _Handle_Exception ; Go straight to exception handler. +_r_teq_neql: + jmp _end_cycle +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; TNE - Trap if Not Equal. +; Compare registers and do a conditional trap. +; Compare the contents of rS and rT as signed integers; +; if rS is not equal to rT, then take a Trap exception. +; The contents of the code field are ignored by hardware and may be used to +; encode information for system software. To retrieve the information, +; system software must load the instruction word from memory. +; Operation: if $s != $t then Trap +; Syntax: TNE $s, $t +; Encoding: 0000 00ss ssst tttt cccc cccc cc11 0110 +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_r_tne: + RType_S_T_Only ; Load rS and rT Fields + mov TMP, R(rS) ; TMP := Regs[rS] + cmp TMP, R(rT) ; CMP(TMP, Regs[rT]) + je _r_tne_eql + SetEXC EXC_Tr ; Spring the Trap Exception. + jmp _Handle_Exception ; Go straight to exception handler. +_r_tne_eql: + jmp _end_cycle +;----------------------------------------------------------------------------- diff -uNr a/m/misc/m.gdb b/m/misc/m.gdb --- a/m/misc/m.gdb false +++ b/m/misc/m.gdb 519ef7801eacb6d41c79e217d6e4ce28ea37f45c50eb4f77d968459c8143957a5ec15c68448ffae06cc5da31a15f53c258db402acf1ae08c3c940e502b734d73 @@ -0,0 +1,11 @@ +set architecture i386:x86-64 +set disassemble-next-line on +set disassembly-flavor intel +set confirm off +set height 0 + +file bin/m + +b _start + +r lin_be.bin diff -uNr a/m/os/linux.asm b/m/os/linux.asm --- a/m/os/linux.asm false +++ b/m/os/linux.asm 63181e522486b260324663a2c7cb928e8110114503a0711ac596f35176cb774bca680f59c83b3723a3abc4eb57a3fa3d10a657ca6e6bc79a4a3706325279068b @@ -0,0 +1,236 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +%macro PUSHA 0 + push rax + push rbx + push rcx + push rdx + push rbp + push rdi + push rsi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + ; lea rsp,[rsp-16*4] + ; movdqu [rsp+16*0], xmm0 + ; movdqu [rsp+16*1], xmm1 + ; movdqu [rsp+16*2], xmm2 + ; movdqu [rsp+16*3], xmm2 + +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +%macro POPA 0 + + ; movdqu xmm1,[rsp+16*3] + ; movdqu xmm1,[rsp+16*2] + ; movdqu xmm1,[rsp+16*1] + ; movdqu xmm0,[rsp+16*0] + ; lea rsp,[rsp+16*4] + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rsi + pop rdi + pop rbp + pop rdx + pop rcx + pop rbx + pop rax +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; x64 sys calls +%define SYS_BRK 12 +%define SYS_EXIT 60 +%define SYS_EXIT_GROUP 231 +%define SYS_READ 0 +%define SYS_WRITE 1 +%define SYS_OPEN 2 +%define SYS_CLOSE 3 +%define SYS_FSTAT 5 +%define SYS_MMAP 9 +%define SYS_MUNMAP 11 +%define SYS_NANOSLEEP 35 +%define SYS_TIME 201 + +; For threadism: +%define SYS_CLONE 56 + +; For tty cure: +%define SYS_IOCTL 16 + +; For sys_mmap +%define PROT_READ 1 +%define PROT_WRITE 2 +%define MAP_PRIVATE 2 + +; Output fd's: +%define STDOUT 1 +%define STDERR 2 +;----------------------------------------------------------------------------- +; For threadism: +;; sched.h +%define CLONE_VM 0x00000100 +%define CLONE_FS 0x00000200 +%define CLONE_FILES 0x00000400 +%define CLONE_SIGHAND 0x00000800 +%define CLONE_PARENT 0x00008000 +%define CLONE_THREAD 0x00010000 +%define CLONE_IO 0x80000000 + +;; sys/mman.h +%define MAP_GROWSDOWN 0x0100 +%define MAP_ANONYMOUS 0x0020 +%define MAP_PRIVATE 0x0002 +%define PROT_READ 0x1 +%define PROT_WRITE 0x2 +%define PROT_EXEC 0x4 + +%define THREAD_FLAGS \ + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT | \ + CLONE_THREAD | CLONE_IO + +;----------------------------------------------------------------------------- +; Pill for linux's tty buffering retardation +%define ICANON 2 +%define ECHO 8 +%define TCGETS 21505 ; to read back the termios config +%define TCPUTS 21508 ; to set the termios config +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; For output of sys_fstat +struc statbuf + .st_dev resq 1 + .st_ino resq 1 + .st_nlink resq 1 + .st_mode resd 1 + .st_uid resd 1 + .st_gid resd 1 + .__pad0 resd 1 + .st_rdev resq 1 + .st_size resq 1 ; file size in bytes + .st_blksize resq 1 + .st_blocks resq 1 + .st_atime resq 1 + .st_atime_nsec resq 1 + .st_mtime resq 1 + .st_mtime_nsec resq 1 + .st_ctime resq 1 + .st_ctime_nsec resq 1 + .__unused resq 3 + .size +endstruc +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +;; State +;----------------------------------------------------------------------------- +section .bss + linux_epoch resq 1 ; for _Get_Epoch_Time +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +; For spawning slave threads: +;----------------------------------------------------------------------------- +_Create_Thread: + push rdi + ;; Allocate stack for the new thread: + mov rdi, 0 + mov rsi, SLAVE_STACK_SIZE + mov rdx, PROT_WRITE | PROT_READ + mov r10, MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN + mov rax, SYS_MMAP + syscall ; invoke mmap + ;; Verify whether we actually got our memory: + cmp rax, 0 + jg .ok + ;; ... if not: + EGGOG "Could not allocate memory for slave stack!" +.ok: ;; we got the memory; + ;; Actually spawn the thread: + lea rsi, [rax + SLAVE_STACK_SIZE - 8] + pop qword [rsi] + mov rdi, THREAD_FLAGS + mov rax, SYS_CLONE + syscall + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Get Epoch Time from OS. Return in RDX. +;----------------------------------------------------------------------------- +_Get_Epoch_Time: + PUSHA + mov rax, SYS_TIME ; sys_time + mov rdi, linux_epoch ; where it will be written + syscall ; invoke the call + POPA + mov rdx, qword [linux_epoch] ; return all 64 bits of epoch time + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; 'NanoSleep' (rax : pointer to TS structure) +;----------------------------------------------------------------------------- +_Nano_Sleep: + mov rdi, rax + mov rax, SYS_NANOSLEEP ; nanosleep + xor rsi, rsi + syscall ; Invoke sleep + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Terminate Current Thread Only +;----------------------------------------------------------------------------- +_Stop: + mov rax, SYS_EXIT ; Terminate (current thread only) + mov rdi, 0 ; exit code (always 0, for now) + syscall + ;; no more after this, we're through +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Hard Stop (kill self and all child threads) +;----------------------------------------------------------------------------- +_Hard_Stop: + mov rax, SYS_EXIT_GROUP ; Terminate Master and all slaves + mov rdi, 0 ; exit code (always 0, for now) + syscall + ;; no more after this, we're through +;----------------------------------------------------------------------------- diff -uNr a/m/os/linux_io.asm b/m/os/linux_io.asm --- a/m/os/linux_io.asm false +++ b/m/os/linux_io.asm 8f9465318a06392de5a881b9e47b5912a206b79803f64169b63448f42cda7123180f42cc90836dafee0612015c624e9df583c7ceaad80895218ce5b1673f22f0 @@ -0,0 +1,160 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +;----------------------------------------------------------------------------- +;; State +;----------------------------------------------------------------------------- +section .bss + tty resb TTY_Config_size ; Linux tty state + IOBUF resb 1 ; I/O buffer for print +;----------------------------------------------------------------------------- + +;---------------------------------------------------------------------------- +;; TTY State Storage +struc TTY_Config + ;; Shell's old tty config (restore on quit) : + .old_tty resb 12 + .old_lflag resb 4 + .old_brest resb 44 + ;; Modified tty config (to run with) : + .tty resb 12 + .lflag resb 4 + .brest resb 44 +endstruc +;---------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +_Cure_TTY: + ;; Get initial tty state (for restore after quit) : + mov rdx, tty + TTY_Config.old_tty + mov rax, SYS_IOCTL + mov rdi, 0 + mov rsi, TCGETS + syscall + + ;; Get another copy of initial tty state (to modify) : + mov rdx, tty + TTY_Config.tty + mov rax, SYS_IOCTL + mov rdi, 0 + mov rsi, TCGETS + syscall + + ;; Switch off linux console 'cooked mode' idiocy: + and dword [tty + TTY_Config.lflag], ~(0xF) + + ;; Set the modified tty state: + mov rdx, tty + TTY_Config.tty + mov rax, SYS_IOCTL + mov rdi, 0 + mov rsi, TCPUTS + syscall + ret +;----------------------------------------------------------------------------- +_Uncure_TTY: ;; Set original tty state again: + mov rdx, tty + TTY_Config.old_tty + mov rax, SYS_IOCTL + mov rdi, 0 + mov rsi, TCPUTS + syscall + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; I/O +;----------------------------------------------------------------------------- +_Write_Char: + PUSHA + mov byte [IOBUF], dl + mov rdx, 1 + mov rax, SYS_WRITE + mov rsi, IOBUF + mov rdi, STDOUT + syscall + POPA + ret + +;; return char in IOBUF +_Read_Char_Blocking: + PUSHA + mov rdi, 0 ; STDIN + mov rsi, IOBUF ; where to put + mov rdx, 1 ; read one char + mov rax, 0 + syscall + POPA + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Transmit string to STDERR. RDX points to length (32bit) after which is text. +;----------------------------------------------------------------------------- +_Print_Text_STDERR: + PUSHA + mov rdi, STDERR ; Destination + lea rsi, [rdx + 4] ; Start of text + mov rax, SYS_WRITE ; SYS_WRITE + mov edx, dword [edx] ; Length of text + syscall ; Transmit the text to STDERR + POPA + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Print hex value of EAX to STDOUT. +;----------------------------------------------------------------------------- +; _Dump_Byte: +; PUSHA +; mov ebp, eax +; shr eax, 4 +; call _dig +; mov eax, ebp +; call _dig +; POPA +; ret + +; _Dump_EAX: +; PUSHA +; mov ebp, eax +; mov ebx, 28 +; _do_dig: +; mov eax, ebp +; mov ecx, ebx +; shr eax, cl +; call _dig +; sub ebx, 4 +; jnc _do_dig +; POPA +; ret +; _dig: +; and eax, 0xF +; mov rsi, _digits +; add rsi, rax +; mov rdx, 0x1 +; mov rax, SYS_WRITE +; mov rdi, STDOUT +; syscall +; ret +;----------------------------------------------------------------------------- + +section .rodata + +; _linefeed: db 0xA, 0 +_digits: db "0123456789abcdef", 0 diff -uNr a/m/ram.asm b/m/ram.asm --- a/m/ram.asm false +++ b/m/ram.asm 6ad0018398efc679130db6e4abdf4082d644bcbb623ce66c49899053b0a032a27ff1de0702efecd0a5bf6718adfbdf9f1c38b7e2f1efae34cfaf0258e0731f8d @@ -0,0 +1,420 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;----------------------------------------------------------------------------- +;; State +;----------------------------------------------------------------------------- +section .bss + M resb 8 ; Addr mmap'd M (message) buffer + Mbytes resb 8 ; Length of image (bytes) + RAMbytes resb 8 ; Total size of RAM (bytes) +;----------------------------------------------------------------------------- + +section .text + +;----------------------------------------------------------------------------- +; Jump if register value resides in given range +;----------------------------------------------------------------------------- +%macro JMP_If_In 4 ; %1: register, %2: r.bottom, %3: r.top, %4: dest. + cmp %1, %3 ; Compare register to TOP of range + ja %%nope ; If above top -- skip. + cmp %1, %2 ; Compare register to BOTTOM of range + jb %%nope ; If below bottom -- skip. + jmp %4 ; ... Else, register is in the range, and jump. +%%nope +%endmacro +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Allocate RAM: +;----------------------------------------------------------------------------- +_ram_allocate: + ;; Allocate memory for sim RAM: + mov rax, SYS_MMAP + xor r9, r9 ; offset=0 + xor r8, r8 + mov r10, MAP_PRIVATE | MAP_ANONYMOUS + mov rdx, PROT_READ | PROT_WRITE + mov rsi, [RAMbytes] ; # of bytes to allocate + xor rdi, rdi ; os will give addr + syscall ; invoke mmap + ;; Verify whether we actually got our memory: + cmp rax, 0 + jg _mmap_ok + ;; ... if not: + EGGOG "Could not allocate memory for sim RAM!" +_mmap_ok: ;; we got the memory; + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Free RAM: +;----------------------------------------------------------------------------- +_ram_deallocate: + mov rax, SYS_MUNMAP + mov rsi, [RAMbytes] + mov rdi, [M] + syscall ; munmap(M, Mbytes) + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Init RAM: +;----------------------------------------------------------------------------- +_ram_init: + mov RAM_Floor, [M] ; Set 'floor' of RAM + mov RAM_Ceiling, RAM_Floor ; 'ceiling' + add RAM_Ceiling, [RAMbytes]; ... size of the mmap + ;; TODO: test for unaligned floor or ceiling + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Translate_Address : virtual address in eax; output (physical addr) in eax +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_Virt_To_Phys: + bt eax, 31 ; CF := 31st (top) bit of vAddr + jc _Above_7FFFFFFF ; If 31st bit = 1, kseg 0/1/2; else: + ;; 0x00000000 <= vAddr <= 0x7FFFFFFF (kuseg) : + bt CP0_Status, CP0St_ERL ; CF := CP0St_ERL Flag + jnc _TLB_Lookup ; If ERL = 0: TLB Lookup required; else: + jmp _No_Tlb_Lookup ; pAddr is equal to vAddr, return. +_Above_7FFFFFFF: + bt eax, 30 ; CF := 30th (2nd from top) bt of vAddr + jc _Above_BFFFFFFF ; If 30th bit = 1 : kseg2; else: + ;; 0x80000000 <= vAddr <= 0x9FFFFFFF (kseg0) : + ;; 0xA0000000 <= vAddr <= 0xBFFFFFFF (kseg1) : + and eax, 0x1FFFFFFF ; kseg0 and kseg1: clear top 3 bits, + jmp _No_Tlb_Lookup ; i.e. pAddr := bottom 29 bts of vAddr. +_Above_BFFFFFFF: ;; 0xC0000000 <= vAddr <= 0xFFFFFFFF (kseg2) : + bt CP0_Status, CP0St_UM ; CF := CP0St_UM Flag + jnc _TLB_Lookup ; If UM = 0: K. Mode, so do TLB; else: + test CP0_Status, (1 << CP0St_EXL) | (1 << CP0St_ERL) ; EXL or ERL + jnz _TLB_Lookup ; If EXL && ERL, K. Mode, do TLB + ;; Else: vAddr is in kseg2, but we are NOT in Kernel Mode: + Flg_Get IsWriting ; Is Writing? + jc _V2P_Eggog_Wr ; If so, we want to set AdES; +_V2P_Eggog_Rd: ; ... otherwise, set AdEL. + SetEXC EXC_AdEL ; Fetch address error. + jmp _V2P_Eggog_Fin ; Proceed to abort. +_V2P_Eggog_Wr: + SetEXC EXC_AdES ; Store address error. +_V2P_Eggog_Fin: + ;; Will go into exception handler instead of back to _Virt_xxx etc + add rsp, 16 ; squelch return to _Virt_xxx and its caller + push _Handle_Exception ; 'return' directly to exc handler. +_No_Tlb_Lookup: + Flg_Off IsWriting ; Consume 'is writing' flag. + ret ; Done. +_TLB_Lookup: ; TLB Lookup Required: + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Save ebx, ecx, edx, AUX, to xmm ;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + movd xmm0, ebx + movd xmm1, ecx + movd xmm2, edx + movd xmm3, AUX + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + Flg_Off ExcWasTLBNoMatch ; Clear the ExcWasTLBNoMatch Flag + ;; Get the active ASID: + mov edx, Sr(CP0_EntryHi) ; edx := CP0_EntryHi + and edx, 0xFF ; edx := edx & 0xFF (get current ASID) + ;; For each slot in table (0 .. 15), attempt lookup + xor AUX, AUX ; Start with the 0-th entry in table +_Lookup_TLB_E: + mov ecx, eax ; ecx := eax (vAddr) + and ecx, 0xFFFFF000 ; ecx := ecx & 0xFFFFF000 + shr ecx, 13 ; ecx := ecx >> 13 (get vAddr's Tag) + mov ebx, TLB_E(AUX) ; ebx := current TLB entry + and ebx, TLB_VPN2_Mask ; get VPN2 of this entry + cmp ebx, ecx ; cmp(entry.VPN2, vAddr.tag) + jne _Lookup_TLB_E_Not_Here ; if entry.VPN2 != vAddr.tag: no match + mov ebx, TLB_E(AUX) ; ebx := current TLB entry + bt ebx, TLB_G ; is entry.G = 1? + jc _Lookup_TLB_E_Match ; then match. + shr ebx, TLB_ASID_Shift ; ebx := ebx >> TLB_ASID_Shift + and ebx, TLB_ASID_Mask ; ebx := entry.ASID + cmp ebx, edx ; entry.ASID = current ASID ? + jne _Lookup_TLB_E_Not_Here ; if neither G=1 nor ASID match. + mov ebx, TLB_E(AUX) ; ebx := current TLB entry +_Lookup_TLB_E_Match: ; TLB Match: + bt eax, 12 ; Test odd/even junior bit + jc _Lookup_TLB_E_Match_Odd ; If odd: test V1, D1 +_Lookup_TLB_E_Match_Even: ; If even: test V0, D0 + lea ecx, TLB_PFN_E(AUX) ; prepare to load even PFN entry + bt ebx, TLB_V0 ; Is entry.V0=1 ? + jnc _Lookup_TLB_E_Invalid ; If not, TLBRET_INVALID + bt ebx, TLB_D0 ; Is entry.D0=1 ? + jc _Lookup_TLB_E_Match_Yes ; If entry.D0=1, then Match Yes + jmp _Lookup_TLB_E_Match_Wr ; else, go to 'is writing?' +_Lookup_TLB_E_Match_Odd: ; Odd bit: + lea ecx, TLB_PFN_O(AUX) ; prepare to load odd PFN entry + bt ebx, TLB_V1 ; Is entry.V1=1 ? + jnc _Lookup_TLB_E_Invalid ; If not, TLBRET_INVALID + bt ebx, TLB_D1 ; Is entry.D1=1 ? + jc _Lookup_TLB_E_Match_Yes ; If entry.D1=1, then Match Yes +_Lookup_TLB_E_Match_Wr: + Flg_Get IsWriting ; Is Writing? + jnc _Lookup_TLB_E_Match_Yes ; If not writing, go to Match Yes +_Lookup_TLB_E_Dirty: ; ... else, Dirty: + SetEXC EXC_Mod ; Set the EXC_Mod Exception + jmp _Lookup_TLB_E_WriteExtr ; Write the 'extra data' and finish. +_Lookup_TLB_E_Match_Yes: ; This is the 'success' case + mov ebx, dword [ecx] ; Actually load the current PFN entry + and eax, 0xFFF ; vAddr := vAddr & 0xFFF + or eax, ebx ; vAddr := vAddr | entry.PFN[lowbit] + jmp _Lookup_TLB_Done ; vAddr is now correct pAddr, done. +_Lookup_TLB_E_Not_Here: ; try next one in the table, if any + inc AUX ; index := index + 1 + cmp AUX, TLB_ENTRIES_COUNT ; see if still in range 0 .. n-1 + jb _Lookup_TLB_E ; if in range, go to next entry + ;; ... else: + Flg_On ExcWasTLBNoMatch ; Set the ExcWasTLBNoMatch Flag + ;; ... now drop down into 'invalid' : +_Lookup_TLB_E_Invalid: + Flg_Get IsWriting ; Was Writing? + jc _Lookup_TLB_E_Invalid_W ; If so, we want to set EXC_TLBS +_Lookup_TLB_E_Invalid_R: ; Otherwise, set EXC_TLBL exception + SetEXC EXC_TLBL ; Set the EXC_TLBL Exception + jmp _Lookup_TLB_E_WriteExtr ; Write the 'extra data' and finish. +_Lookup_TLB_E_Invalid_W: + SetEXC EXC_TLBS ; Set the EXC_TLBS Exception + ;; then drop down to 'write extra' : +_Lookup_TLB_E_WriteExtr: ; Write the 'extra data' and finish + mov Sr(CP0_BadVAddr), eax ; CP0_BadVAddr := vAddr + mov ecx, eax ; ecx := vAddr + mov ebx, Sr(CP0_Context) ; ebx := CP0_Context + and ebx, ~0x007FFFFF ; ebx := ebx & ~0x007FFFFF + shr ecx, 9 ; ecx := ecx >> 9 + and ecx, 0x007FFFF0 ; ecx := ecx & 0x007FFFF0 + or ebx, ecx ; ebx := ebx | ecx + mov Sr(CP0_Context), ebx ; CP0_Context := ebx + mov ecx, eax ; ecx := vAddr + mov ebx, Sr(CP0_EntryHi) ; ebx := CP0_EntryHi + and ebx, 0xFF ; ebx := ebx & 0xFF + and ecx, 0xFFFFE000 ; ecx := ecx & 0xFFFFE000 + or ebx, ecx ; ebx := ebx | ecx + mov Sr(CP0_EntryHi), ebx ; CP0_EntryHi := ebx + ;; Will go into exception handler instead of back to _Virt_xxx etc + add rsp, 16 ; squelch return to _Virt_xxx and its caller + push _Handle_Exception ; 'return' directly to exc handler. + ;; and drop into 'done' : +_Lookup_TLB_Done: + Flg_Off IsWriting ; Consume 'is writing' flag. + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Restore ebx, ecx, edx, AUX, from xmm ;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + movd ebx, xmm0 + movd ecx, xmm1 + movd edx, xmm2 + movd AUX, xmm3 + ret +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ReadWord - Source Address (Virtual) is EAX; Resulting Word Read is EAX. +;----------------------------------------------------------------------------- +; TODO: 1) is the 'unaligned' case in fact physically possible? +; 2) would go faster if used tmp instead of self-clobbering rax ? +align GRAIN, db 0x90 +_Virt_Read_Word: + call _Virt_To_Phys ; Transform vAddr to pAddr + test eax, 0x3 ; Are any of the bottom 2 bits set? + jnz _V_Rd_Word_Unaligned ; If yes, go to eggog. Else: + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; If pAddr is in Memory-Mapped Device space: + JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Read_Word + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + add rax, RAM_Floor ; Calculate the target PC RAM addr + cmp rax, RAM_Ceiling ; compare the above with RAM ceiling + jnb _V_Rd_Word_Hit_Ceiling ; if not below: RAM ceiling eggog! + ;; Address is valid, so load the word: + mov eax, dword [rax] ; eax := dword [rax] + ret ; Done, return with read result in eax. +_V_Rd_Word_Unaligned: ;; TODO: print address + ACHTUNG "Virt_Read_Word: Unaligned Physical Address!" + jmp _V_Rd_Word_Eggog ; Go to eggogology +_V_Rd_Word_Hit_Ceiling: ;; TODO: print address + ACHTUNG "Virt_Read_Word: Hit RAM Ceiling!" +_V_Rd_Word_Eggog: + SetEXC EXC_AdEL ; Fetch address error. + ;; Will go into exception handler instead of back to caller + add rsp, 8 ; squelch return to original caller + push _Handle_Exception ; 'return' directly to exc handler. + ret ; Go there. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; WriteWord - Destination Address (Virtual) is EAX; Word to be Written is TMP. +;----------------------------------------------------------------------------- +; TODO: 1) is the 'unaligned' case in fact physically possible? +; 2) would go faster if used tmp instead of self-clobbering rax ? +; 3) do we need to explicitly zero-extend rax here? +align GRAIN, db 0x90 +_Virt_Write_Word: + Flg_On IsWriting ; Tell the translator that we're writing + call _Virt_To_Phys ; Transform vAddr to pAddr + test eax, 0x3 ; Are any of the bottom 2 bits set? + jnz _V_Wr_Word_Unaligned ; If yes, go to eggog. Else: + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; If pAddr is in Memory-Mapped Device space: + JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Write_Word + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + add rax, RAM_Floor ; Calculate the target PC RAM addr + cmp rax, RAM_Ceiling ; compare the above with RAM ceiling + jnb _V_Wr_Word_Hit_Ceiling ; if not below: RAM ceiling eggog! + ;; Address is valid, so write the word: + mov dword [rax], TMP ; dword [rax] := TMP + ret ; Done. +_V_Wr_Word_Unaligned: ;; TODO: print address + ACHTUNG "Virt_Write_Word: Unaligned Physical Address!" + jmp _V_Wr_Word_Eggog +_V_Wr_Word_Hit_Ceiling: ;; TODO: print address + ACHTUNG "Virt_Write_Word: Hit RAM Ceiling!" +_V_Wr_Word_Eggog: + SetEXC EXC_AdES ; Store address error. + ;; Will go into exception handler instead of back to caller + add rsp, 8 ; squelch return to original caller + push _Handle_Exception ; 'return' directly to exc handler. + ret ; Go there. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; ReadByte - Source Address (Virtual) is EAX; Resulting Byte Read is EAX. +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_Virt_Read_Byte: + call _Virt_To_Phys ; Transform vAddr to pAddr + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; If pAddr is in Memory-Mapped Device space: + JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Read_Byte + ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;; +%ifdef LITTLE_ENDIAN + ;; little -- do nothing +%else + ;; big -- flip : + xor eax, 0x3 ; Flip the 3 unalignment bits. +%endif + ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;; + add rax, RAM_Floor ; Calculate the target PC RAM addr + cmp rax, RAM_Ceiling ; compare the above with RAM ceiling + jnb _V_Rd_Byte_Hit_Ceiling ; if not below: RAM ceiling eggog! + ;; Address is valid, so proceed to load: + movzx ax, byte [rax] ; ax := byte [rax] + and eax, 0xFF ; keep only bottom byte + ret ; Done, return with read result in eax. +_V_Rd_Byte_Hit_Ceiling: + ACHTUNG "Virt_Read_Byte: Hit RAM Ceiling!" ;; TODO: print address + SetEXC EXC_AdEL ; Fetch address error. + ;; Will go into exception handler instead of back to caller + add rsp, 8 ; squelch return to original caller + push _Handle_Exception ; 'return' directly to exc handler. + ret ; Go there. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; WriteByte - Destination Address (Virtual) is EAX; Byte to be Written is DL. +;----------------------------------------------------------------------------- +align GRAIN, db 0x90 +_Virt_Write_Byte: + Flg_On IsWriting ; Tell the translator that we're writing + call _Virt_To_Phys ; Transform vAddr to pAddr + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; If pAddr is in Memory-Mapped Device space: + JMP_If_In eax, MMIO_BASE, MMIO_TOP, _Phys_Device_Write_Byte + ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;; +%ifdef LITTLE_ENDIAN + ;; little -- do nothing +%else + ;; big -- flip : + xor eax, 0x3 ; Flip the 3 unalignment bits. +%endif + ;;;;;;;;;;;;;;;;;; ENDIANISM ;;;;;;;;;;;;;;;;;; + add rax, RAM_Floor ; Calculate the target PC RAM addr + cmp rax, RAM_Ceiling ; compare the above with RAM ceiling + jnb _V_Wr_Byte_Hit_Ceiling ; if not below: RAM ceiling eggog! + ;; Address is valid, so write the byte: + mov byte [rax], DL ; dword [rax] := TMP + ret ; Done. +_V_Wr_Byte_Hit_Ceiling: + ACHTUNG "Virt_Write_Byte: Hit RAM Ceiling!" ;; TODO: print address + SetEXC EXC_AdES ; Store address error. + ;; Will go into exception handler instead of back to caller + add rsp, 8 ; squelch return to original caller + push _Handle_Exception ; 'return' directly to exc handler. + ret ; Go there. +;----------------------------------------------------------------------------- + +;----------------------------------------------------------------------------- +; Write TLB Entry at the given index. Index (0 .. 15) is in AUX. +; Kills eax, ebx, ecx, edx. +;----------------------------------------------------------------------------- +_write_tlb_entry: + mov edx, Sr(CP0_EntryHi) ; edx := CP0_EntryHi + mov ecx, edx ; ecx := edx + shr ecx, 13 ; ecx := ecx >> 13 to get VPN2 + and edx, TLB_ASID_Mask ; edx := edx & 0xFF to get ASID + shl edx, TLB_ASID_Shift ; edx := edx << 19 to put ASID in place + or ecx, edx ; now we have VPN2 and ASID in ecx + ;; done with edx, can reuse + mov edx, Sr(CP0_EntryLo0) ; edx := CP0_EntryLo0 + mov ebx, Sr(CP0_EntryLo1) ; ebx := CP0_EntryLo1 + ;; get G: + mov eax, edx ; eax := CP0_EntryLo0 + and eax, ebx ; eax := eax & CP0_EntryLo1 + and eax, 0x1 ; eax := eax & 1 to get G + shl eax, TLB_G ; move G bit into position + or ecx, eax ; ecx := ecx | eax to put in G bit + ;; now ecx contains VPN2, ASID, G + ;; Get V0 from CP0_EntryLo0 and put in ecx where belongs: + mov eax, edx ; eax := CP0_EntryLo0 + and eax, 0x2 ; eax := eax & 0x2 to get V0 bit + shl eax, (TLB_V0 - 1) ; put V0 bit in position + or ecx, eax ; ecx := ecx | eax to put in V0 bit + ;; Get D0 from CP0_EntryLo0 and put in ecx where belongs: + mov eax, edx ; eax := CP0_EntryLo0 + and eax, 0x4 ; eax := eax & 0x4 to get D0 bit + shl eax, (TLB_D0 - 2) ; put D0 bit in position + or ecx, eax ; ecx := ecx | eax to put in D0 bit + ;; Get V1 from CP0_EntryLo1 and put in ecx where belongs: + mov eax, ebx ; eax := CP0_EntryLo1 + and eax, 0x2 ; eax := eax & 0x2 to get V1 bit + shl eax, (TLB_V1 - 1) ; put V1 bit in position + or ecx, eax ; ecx := ecx | eax to put in V1 bit + ;; Get D1 from CP0_EntryLo1 and put in ecx where belongs: + mov eax, ebx ; eax := CP0_EntryLo1 + and eax, 0x4 ; eax := eax & 0x4 to get D1 bit + shl eax, (TLB_D1 - 2) ; put D1 bit in position + or ecx, eax ; ecx := ecx | eax to put in D1 bit + ;; Write the TLB entry to the given index (in AUX) : + and AUX, 0xF ; Index of TLB entry is bottom 4 bits + mov TLB_E(AUX), ecx ; Write TLB entry in ecx to n-th slot. + ;; Transform CP0_EntryLo0 (edx) into PFN0: + shr edx, 6 + and edx, 0xFFFFF + shl edx, 12 + ;; Transform CP0_EntryLo1 (ebx) into PFN1: + shr ebx, 6 + and ebx, 0xFFFFF + shl ebx, 12 + ;; Store PFN: + mov TLB_PFN_E(AUX), edx ; Store PFN0 + mov TLB_PFN_O(AUX), ebx ; Store PFN1 + ret ; Done. +;----------------------------------------------------------------------------- diff -uNr a/m/shutdown.asm b/m/shutdown.asm --- a/m/shutdown.asm false +++ b/m/shutdown.asm 1f88a9e52105c1f6c33ba0f19e67c4158e5df93ea374f9f7774c89d126a0714c830712eea555ff51881a26d2be47861cddcd254395467770d2c34ef60d02c524 @@ -0,0 +1,31 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; This file is part of 'M', a MIPS system emulator. ;; +;; ;; +;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;; +;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;; +;; ;; +;; You do not have, nor can you ever acquire the right to use, copy or ;; +;; distribute this software ; Should you use this software for any purpose, ;; +;; or copy and distribute it to anyone or in any manner, you are breaking ;; +;; the laws of whatever soi-disant jurisdiction, and you promise to ;; +;; continue doing so for the indefinite future. In any case, please ;; +;; always : read and understand any software ; verify any PGP signatures ;; +;; that you use - for any purpose. ;; +;; ;; +;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +section .text + +;----------------------------------------------------------------------------- +_shutdown: + call _Phys_Devices_Shutdown ; Initiate shutdown of all slaves + call _ram_deallocate ; free memory + +_exit: + jmp _Hard_Stop + +_exit_thread: ;; Thread exits go here + jmp _Stop +;-----------------------------------------------------------------------------