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