;
; Project UnReality: The First Nintendo 64 Emulator
; Copyright (C) 1997-1998, Cold Fusion Entertainment
; All rights reserved
;
; This source code is unpublished propietary information of Cold Fusion Entertainment.  No portion of this software
; may be disclosed to any third party or copied or duplicated in any form, in whole or in part, without the prior
; written consent of Cold Fusion Entertainment.
; 

;
; $Log: R4300I.ASM $
; Revision 1.23  1998/11/14 20:18:21  MIKE
; Fixed a BIG bug in DSLLV (was using SA instead of GPR[RS]), and added DSLL.
; Revision 1.22  1998/05/25 04:39:33  BREAKPOINT
; Added 'Call Stack' to debugger which shows the history of JAL[R]/JR RA instructions.
; Revision 1.21  1998/05/24 23:16:49  BREAKPOINT
; Added DSLLV.
; Revision 1.20  1998/05/24 20:49:40  BREAKPOINT
; Beginning of TLB implementation... fixed c0reg.Context to be 32 bit, changed exception handler to support TLB and modified
; assembly core to be re-entrant.
; Revision 1.19  1998/04/19 16:33:01  BREAKPOINT
; Added Trap Exceptions and included TEQ opcode.
; Revision 1.18  1998/04/10 00:11:43  BREAKPOINT
; Optimized code, removed SHLs before reading from registers by using a direct *4 in the mov.  Also added ABS.S and ABS.D.
; Revision 1.17  1998/04/07 23:29:42  BREAKPOINT
; Fixed up debugger... added 'Safe_Read*()' calls to access memory without causing crashes when reading from unmapped memory.
; Revision 1.16  1998/04/05 15:20:31  BREAKPOINT
; duh, didn't try compiling :)
; Revision 1.15  1998/04/05 15:09:13  BREAKPOINT
; Added extra variables to MEM_* functions so RSP can call as well...
; Revision 1.14  1998/04/05 01:35:18  BREAKPOINT
; Added C.EQ.S.
; Revision 1.13  1998/04/04 16:43:16  BREAKPOINT
; Fixed problems with COP1 compare opcodes, added C.LT.D.
; Revision 1.12  1998/04/03 00:04:14  BREAKPOINT
; Added LWU, BC1F, BC1FL, BC1T, BC1TL, SQRT.S, SQRT.D, C.LT.S, C.LE.S, and C.LE.D
; Revision 1.11  1998/04/02 21:18:55  BREAKPOINT
; Implemented new memory mapping routines and changed ROM loading code.
; Revision 1.10  1998/03/29 20:27:00  BREAKPOINT
; Added arithmetic overflow checks to numerous opcodes, removed CACHE callback, added overlow callback.
; Revision 1.9  1998/03/26 23:28:58  BREAKPOINT
; Added SYNC opcode (which is a NOP).
; Revision 1.8  1998/03/22 22:55:53  BREAKPOINT
; Fixed COP1 *.D opcodes, added more COP1, added LDC1 and SDC1... Fixed SCD1 and SCD2 to be SDC.
; Revision 1.7  1998/03/22 17:52:33  BREAKPOINT
; New breakpoint detection routine to speed up debugging and emulation by changing the opcode to and invalid one.
; Needs more work, also Breakpoint on RegV is broken too.
; Revision 1.6  1998/03/08 19:13:28  BREAKPOINT
; Version 0.3b.3 checkin... I need to really check this stuff in more often..
; Revision 1.5  1998/01/31 20:02:09  BREAKPOINT
; *MANY* opcodes implemented.  The Firedemo works once again...
; Revision 1.4  1998/01/20 00:33:19  BREAKPOINT
; Major changes... made 'ret' at end of opcode a 'jmp @MainLoop'.
; Also added structures and began initial opcode decoding.  (Still needs
; some work)
; Revision 1.3  1998/01/12 00:29:31  BREAKPOINT
; First compileable ASM core
; Revision 1.2  1998/01/10 21:57:49  BREAKPOINT
; Added placeholders for basic opcodes
; Revision 1.1  1998/01/10 02:10:05  BREAKPOINT
; Initial revision
;

;---------------------------------------------------------------------------------------------------------------------

.486p										; Needed for BSWAP instruction
.model flat

_DATA SEGMENT DWORD PUBLIC USE32 'DATA'

align 4
include R4300str.inc						; Include R4K structures
include R4300tbl.inc						; Include R4K opcode jump tables

align 4
	_r4reg			R4RegSet				?	; *ALL* structures -- CPU + COP + MMIO
align 4
	_tlb      		TLB						?
align 4
	_c0reg			C0RegSet				?
align 4
	_c1reg			C1RegSet				?
align 4
	_rdreg			RDRAM_RegSet			?
align 4
	_spreg			SP_RegSet				?
align 4
	_sp_pc			SP_PC_RegSet			?
align 4
	_sp_ibist		SP_IMEM_BIST_RegSet		?
align 4
	_dpcreg			DPC_RegSet				?
align 4
	_dpsreg			DPS_RegSet				?
align 4
	_mireg			MIPS_Intf_RegSet		?
align 4
	_vireg			VI_RegSet				?
align 4
	_aireg			AI_RegSet				?
align 4
	_pireg			PI_RegSet				?
align 4
	_rireg			RI_RegSet				?
align 4
	_sireg			SI_RegSet				?
align 4
	_leoreg			LEO_RegSet				?
align 4
	_memmap			MemMap					?
align 4
    _CSTACK_data    CallStackDef            32768 dup(<>)

PUBLIC _r4reg, _tlb, _c0reg, _c1reg, _rdreg, _spreg, _sp_pc, _sp_ibist, _dpcreg, _dpsreg, _mireg, _vireg
PUBLIC _aireg, _pireg, _rireg, _sireg, _leoreg, _memmap, _CSTACK_data

align 4
    _CSTACK_count       dd      0           ; Call stack count.

PUBLIC _CSTACK_count

align 4
	_DMA_running		db		0			; DMA stuffs...
	_DMA_src_addr		dd		0
	_DMA_dest_addr		dd		0
	_DMA_bytes_to_copy	dd		0
	_DMA_len			dd		0

PUBLIC _DMA_running, _DMA_src_addr, _DMA_dest_addr, _DMA_bytes_to_copy, _DMA_len

align 4
	_MMIO_offs			dd		0			; MMIO stuffs...  (_MMIO_offs holds offset into memory entry)
	_MMIO_size			db		0
	_MMIO_val			dq		0

PUBLIC _MMIO_offs, _MMIO_size, _MMIO_val

align 4
	_MEM_addr			dd		0			; C interface to read/write to N64 memory
	_MEM_val			dq		0
	_MEM_return_val		dq		0

PUBLIC _MEM_addr, _MEM_val, _MEM_return_val

align 4
	_RSP_MEM_addr		dd		0			; RSP's C interface to read/write to N64 memory
	_RSP_MEM_val		dq		0
	_RSP_MEM_return_val	dq		0

PUBLIC _RSP_MEM_addr, _RSP_MEM_val, _RSP_MEM_return_val

align 4
	_FUNC_MemCopy			dd	0			; C function callbacks
	_FUNC_MemReadFail		dd	0
	_FUNC_MemWriteFail		dd	0
	_FUNC_UnknownOpcode		dd	0
	_FUNC_COP_Unusable		dd	0
	_FUNC_COP0_Unknown		dd	0
	_FUNC_COP1_Unknown		dd	0
	_FUNC_COP2_Unknown		dd	0
	_FUNC_UnhandledOpcode	dd	0

    _FUNC_GenArithOflow     dd  0
    _FUNC_GenTrapExcept     dd  0
    _FUNC_RegenIntr         dd  0

PUBLIC _FUNC_MemCopy, _FUNC_MemReadFail, _FUNC_MemWriteFail, _FUNC_UnknownOpcode, _FUNC_COP_Unusable, _FUNC_COP0_Unknown
PUBLIC _FUNC_COP1_Unknown, _FUNC_COP2_Unknown, _FUNC_UnhandledOpcode, _FUNC_GenArithOflow, _FUNC_GenTrapExcept
PUBLIC _FUNC_RegenIntr

align 4
	_EventFUNC_Compare		dd	0			; C function callbacks to add events to the heap.
    _EventFUNC_PI           dd  0

PUBLIC _EventFUNC_Compare, _EventFUNC_PI

align 4
	_R4PC_newPC			dd		0			; Counter.. if > 0, dec 1 each opcode. If == 0 after dec, set r4reg.PC to below.
	_R4PC_count			db		0

PUBLIC _R4PC_newPC, _R4PC_count

align 4
    FPU_Temp            dw      ?           ; FPU control words
    FPU_CVT             dw      ?
    FPU_TRUNC           dw      ?


;----------------------------------------------------------------------------------------------------------------------

	EXTRN _running:BYTE
	EXTRN _want_to_exit:BYTE
	EXTRN _hRunEvent:DWORD
	EXTRN _hDebugEvent:DWORD

    EXTRN _N64Memory:DWORD
	EXTRN _RDRAM:DWORD
	EXTRN _SP_DATA_MEM:DWORD
	EXTRN _SP_INST_MEM:DWORD
	EXTRN _RDRAM_REGS:DWORD
	EXTRN _RDRAM_TEMP_MEM:DWORD
	EXTRN _SP_REGS:DWORD
	EXTRN _SP_PC:DWORD
	EXTRN _SP_IMEM_BIST:DWORD
	EXTRN _DP_CTRL_REGS:DWORD
	EXTRN _DP_SPAN_REGS:DWORD
	EXTRN _MIPS_REGS:DWORD
	EXTRN _VIDEO_REGS:DWORD
	EXTRN _AUDIO_REGS:DWORD
	EXTRN _PERIF_REGS:DWORD
	EXTRN _RDRAM_INTF_REGS:DWORD
	EXTRN _SERIAL_REGS:DWORD
	EXTRN _PIF_ROM:DWORD
	EXTRN _PIF_RAM:DWORD
	EXTRN _LEO:DWORD

    EXTRN _N64EventArray1Ptr:DWORD

_DATA ENDS

_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
	ASSUME CS:_TEXT, DS:DGROUP

PUBLIC _InitMainEmulation, _MainEmulation

EXTRN __imp__WaitForSingleObject@8:PTR
EXTRN __imp__SetEvent@4:PTR

;----------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------

DECODE_JUMP MACRO reg						; Decodes a JUMP opcode, modifies EBX!!!
	mov   reg, ebp
	and   reg, 03FFFFFFh
	shl   reg, 2
	mov   ebx, DWORD PTR [_r4reg.PC]
	and   ebx, 0F0000000h
	or    reg, ebx
ENDM

DECODE_IMMED MACRO reg						; Decodes IMMED portion of opcode
	mov   reg, ebp
	and   reg, 0FFFFh
ENDM

DECODE_RS MACRO	reg							; Decodes RS portion of opcode
	mov   reg, ebp
	shr   reg, 21
	and   reg, 1Fh
ENDM

DECODE_RT MACRO reg							; Decodes RT portion of opcode
	mov   reg, ebp
	shr   reg, 16
	and   reg, 1Fh
ENDM

DECODE_RD MACRO reg							; Decodes RD portion of opcode
	mov   reg, ebp
	shr   reg, 11
	and   reg, 1Fh
ENDM

DECODE_SA MACRO reg							; Decodes SA portion of opcode
	mov   reg, ebp
	shr   reg, 6
	and   reg, 1Fh
ENDM

DECODE_FS MACRO reg							; Decodes FS portion of opcode
	DECODE_RD reg
ENDM

DECODE_FT MACRO reg							; Decodes FT portion of opcode
	DECODE_RT reg
ENDM

DECODE_FD MACRO reg							; Decodes FD portion of opcode
	DECODE_SA reg
ENDM

DECODE_BASE MACRO reg						; Decodes BASE portion of opcode
	DECODE_RS reg
ENDM

DECODE_OFFSET MACRO reg32, reg16			; Decodes OFFSET portion of opcode (reg16 is 16bit portion, ie: DECODE_OFFSET eax ax)
	DECODE_IMMED reg32
	movsx reg32, reg16
	shl   reg32, 2
	add   reg32, DWORD PTR [_r4reg.PC]
	add   reg32, 4
ENDM

DECODE_SYSCALL MACRO reg					; Decodes a SYSCALL opcode
	mov   reg, ebp
	and   reg, 03FFFFFFh
	shr   reg, 6
ENDM

DECODE_BREAK MACRO reg						; Decodes a BREAK opcode
	DECODE_SYSCALL reg
ENDM

DECODE_OPCODE MACRO	reg						; Decodes an opcode
	mov   reg, ebp
	shr   reg, 26
ENDM

DECODE_FUNCTION MACRO reg					; Decodes a COP0/COP1 function opcode
	mov   reg, ebp
	and   reg, 3Fh
ENDM

DECODE_C0FMT MACRO reg						; Decodes COP0 format opcode
	DECODE_RS reg
ENDM

DECODE_C0FUNC MACRO reg						; Decodes a COP0 function opcode
	DECODE_FUNCTION reg
ENDM

DECODE_C1FMT MACRO reg						; Decodes COP1 format opcode
	DECODE_RS reg
ENDM

DECODE_C1BC MACRO reg						; Decodes COP1BC opcode
	mov   reg, ebp
	shr   reg, 16
	and   reg, 03h
ENDM

DECODE_C1S MACRO reg						; Decodes COP1S opcode
	DECODE_FUNCTION reg
ENDM

DECODE_C1D MACRO reg						; Decodes COP1D opcode
	DECODE_FUNCTION reg
ENDM

DECODE_C1W MACRO reg						; Decodes COP1W opcode
	DECODE_FUNCTION reg
ENDM

DECODE_C1L MACRO reg						; Decodes COP1L opcode
	DECODE_FUNCTION reg
ENDM

;----------------------------------------------------------------------------------------------------------------------

SIGN_EXTEND_8TO64 MACRO sreg, dreg1, dreg2	; Sign-extends 8-bit to 64-bit
LOCAL @@skip, @@done

	cmp sreg, 80h
	jb @@skip

	movsx dreg2, sreg
	mov dreg1, 0FFFFFFFFh
	jmp @@done

@@skip:
	movzx dreg2, sreg
	xor dreg1, dreg1

@@done:
ENDM

SIGN_EXTEND_16TO64 MACRO sreg, dreg1, dreg2	; Sign-extends 16-bit to 64-bit
LOCAL @@skip, @@done

	cmp sreg, 8000h
	jb @@skip

	movsx dreg2, sreg
	mov dreg1, 0FFFFFFFFh
	jmp @@done

@@skip:
	movzx dreg2, sreg
	xor dreg1, dreg1

@@done:
ENDM

SIGN_EXTEND_32TO64 MACRO sreg, dreg1, dreg2	; Sign-extends 32-bit to 64-bit
LOCAL @@skip, @@done

	mov dreg2, sreg
	cmp sreg, 80000000h
	jb @@skip

	mov dreg1, 0FFFFFFFFh
	jmp @@done

@@skip:
	xor dreg1, dreg1

@@done:
ENDM

;----------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------

;----------------------------------------------------------------------------------------------------------------------
; MEM_READ8 MACRO		- Reads a byte from N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX - Byte of data returned, carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_READ8 MACRO
	xor esi, 03h				; Fixup address for DWORD swap.
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    movzx eax, BYTE PTR [esi]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MEM_READ16 MACRO		- Reads a word from N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX - Word of data returned, carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_READ16 MACRO
	xor esi, 02h				; Fixup address for DWORD swap.
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    movzx eax, WORD PTR [esi]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MEM_READ32 MACRO		- Reads a doubleword from N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX - DWord of data returned, carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_READ32 MACRO
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    mov eax, DWORD PTR [esi]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MEM_READ64 MACRO		- Reads a quadword from N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX:EDX - Quadword to 64-bit data, carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_READ64 MACRO
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    mov eax, DWORD PTR [esi]
    mov edx, DWORD PTR [esi + 4]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MEM_WRITE8 MACRO		- Writes a byte to N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX - Byte of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_WRITE8 MACRO
	xor esi, 03h				; Fixup address for DWORD swap.
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    mov BYTE PTR [esi], al
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MEM_WRITE16 MACRO		- Writes a word to N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX - Word of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_WRITE16 MACRO
	xor esi, 02h				; Fixup address for DWORD swap.
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    mov WORD PTR [esi], ax
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MEM_WRITE32 MACRO		- Writes a dword to N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX - Dword of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_WRITE32 MACRO
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    mov DWORD PTR [esi], eax
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MEM_WRITE64 MACRO		- Writes a qword to N64 memory, does *NOT* call MMIO read function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX:EDX - Qword of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MEM_WRITE64 MACRO
	and esi, 1FFFFFFFh
    add esi, DWORD PTR [_N64Memory]
    mov DWORD PTR [esi], eax
    mov DWORD PTR [esi + 4], edx
ENDM

;======================================================================================================================
;======================================================================================================================
;======================================================================================================================
;
;  MMIO is now a LOT hairier than it used to be.  This is mostly done for performance reasons, to keep the x86 pipeline
;  as full as possible and without unnecessarily clearing the prefetch queue.
;
;  The overall handling of an MMIO macro would be:
;
;  @LB:
;    ; parse stuff, get address into ESI
;
;    MMIO_READ8 @LB_CheckHi
;
;    ; if we got here, the address is *NOT* MMIO related.
;    add DWORD PTR [_r4reg.PC], 4
;    jmp @MainLoop
;
;  @LB_CheckHi:
;    MMIO_READ8_CHECKHI @LB_DoMMIO
;
;    ; again, the address is *NOT* MMIO related.
;    add DWORD PTR [_r4reg.PC], 4
;    jmp @MainLoop
;
;  @LB_DoMMIO:
;    DO_MMIO_READ8
;
;    ; the MMIO callback has now been done, and memory has been retreived
;    add DWORD PTR [_r4reg.PC], 4
;    jmp @MainLoop
;
;======================================================================================================================
;======================================================================================================================
;======================================================================================================================

DO_MMIO_READ8 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov eax, DWORD PTR [edi + MemMapDef.read_func]

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 1
    push esi
	call eax											; Call MMIO function
    pop esi

    xor esi, 03h                                        ; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]                     ; Read memory
    movzx eax, BYTE PTR [esi]
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemReadFail
	stc

@@done:
ENDM

;======================================================================================================================

DO_MMIO_READ16 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov eax, DWORD PTR [edi + MemMapDef.read_func]

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 2
    push esi
	call eax											; Call MMIO function
    pop esi

    xor esi, 03h                                        ; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]                     ; Read memory
    movzx eax, WORD PTR [esi]
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemReadFail
	stc

@@done:
ENDM

;======================================================================================================================

DO_MMIO_READ32 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov eax, DWORD PTR [edi + MemMapDef.read_func]

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 4
    push esi
	call eax											; Call MMIO function
    pop esi

    add esi, DWORD PTR [_N64Memory]                     ; Read memory
    mov eax, DWORD PTR [esi]
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemReadFail
	stc

@@done:
ENDM

;======================================================================================================================

DO_MMIO_READ64 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov eax, DWORD PTR [edi + MemMapDef.read_func]

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 8
    push esi
	call eax											; Call MMIO function
    pop esi

    add esi, DWORD PTR [_N64Memory]                     ; Read memory
    mov eax, DWORD PTR [esi]
    mov edx, DWORD PTR [esi + 4]
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemReadFail
	stc

@@done:
ENDM

;======================================================================================================================

DO_MMIO_WRITE8 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov edi, DWORD PTR [edi + MemMapDef.write_func]

	xor esi, 03h				                        ; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
	mov BYTE PTR [esi], al                              ; Write memory

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 1
	mov DWORD PTR [_MMIO_val], eax

	call edi											; Call MMIO function
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemWriteFail
	stc

@@done:
ENDM

;======================================================================================================================

DO_MMIO_WRITE16 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov edi, DWORD PTR [edi + MemMapDef.write_func]

	xor esi, 02h				                        ; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
	mov WORD PTR [esi], ax                              ; Write memory

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 2
	mov DWORD PTR [_MMIO_val], eax

	call edi											; Call MMIO function
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemWriteFail
	stc

@@done:
ENDM

;======================================================================================================================

DO_MMIO_WRITE32 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov edi, DWORD PTR [edi + MemMapDef.write_func]

    add esi, DWORD PTR [_N64Memory]
	mov DWORD PTR [esi], eax                            ; Write memory

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 4
	mov DWORD PTR [_MMIO_val], eax

	call edi											; Call MMIO function
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemWriteFail
	stc

@@done:
ENDM

;======================================================================================================================

DO_MMIO_WRITE64 MACRO
LOCAL @@loop, @@next, @@done

	mov ecx, NUM_MEMMAPS
	mov edi, OFFSET [_memmap]

@@loop:
	cmp DWORD PTR [edi + MemMapDef.ptr], 0
	jz  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.start_addr]
	jb  @@next

	cmp esi, DWORD PTR [edi + MemMapDef.end_addr]
	ja  @@next

    mov ecx, esi
	sub ecx, DWORD PTR [edi + MemMapDef.start_addr]
	mov edi, DWORD PTR [edi + MemMapDef.write_func]

    add esi, DWORD PTR [_N64Memory]
	mov DWORD PTR [esi], eax                            ; Write memory
	mov DWORD PTR [esi + 4], edx

	mov DWORD PTR [_MMIO_offs], ecx
	mov DWORD PTR [_MMIO_size], 8
	mov DWORD PTR [_MMIO_val], eax

	call edi											; Call MMIO function
	jmp @@done

@@next:
	add edi, SIZEOF_MEMMAPDEF
	dec ecx
	jnz @@loop

	mov DWORD PTR [_MEM_addr], esi
	call _FUNC_MemWriteFail
	stc

@@done:
ENDM

;======================================================================================================================

MMIO_READ8_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

	xor esi, 03h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    movzx eax, BYTE PTR [esi]
ENDM

;======================================================================================================================

MMIO_READ16_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

	xor esi, 02h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    movzx eax, WORD PTR [esi]
ENDM

;======================================================================================================================

MMIO_READ32_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

    add esi, DWORD PTR [_N64Memory]
    mov eax, DWORD PTR [esi]
ENDM

;======================================================================================================================

MMIO_READ64_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

    add esi, DWORD PTR [_N64Memory]
    mov eax, DWORD PTR [esi]
    mov edx, DWORD PTR [esi + 4]
ENDM

;======================================================================================================================

MMIO_WRITE8_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

	xor esi, 03h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    mov BYTE PTR [esi], al
ENDM

;======================================================================================================================

MMIO_WRITE16_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

	xor esi, 02h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    mov WORD PTR [esi], ax
ENDM

;======================================================================================================================

MMIO_WRITE32_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

    add esi, DWORD PTR [_N64Memory]
    mov DWORD PTR [esi], eax
ENDM

;======================================================================================================================

MMIO_WRITE64_CHECKHI MACRO DO_MMIO
    cmp esi, MMIO_HIGHEST_ADDR
    jb  DO_MMIO

    add esi, DWORD PTR [_N64Memory]
    mov DWORD PTR [esi], eax
    mov DWORD PTR [esi + 4], edx
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_READ8 MACRO		- Reads a byte from N64 memory, *DOES* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX - Byte of data returned, carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_READ8 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

	xor esi, 03h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    movzx eax, BYTE PTR [esi]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_READ16 MACRO		- Reads a word from N64 memory, *DOES* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX - Word of data returned, carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_READ16 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

	xor esi, 02h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    movzx eax, WORD PTR [esi]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_READ32 MACRO		- Reads a dword from N64 memory, *DOES* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX - DWord of data returned, carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_READ32 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

    add esi, DWORD PTR [_N64Memory]
    mov eax, DWORD PTR [esi]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_READ64 MACRO		- Reads a qword from N64 memory, *DOES* call MMIO read function!
;
; In:  ESI - Unrelocated address to read from.
; Out: EAX:EDX - QWord of data returned, carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_READ64 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

    add esi, DWORD PTR [_N64Memory]
    mov eax, DWORD PTR [esi]
    mov edx, DWORD PTR [esi + 4]
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_WRITE8 MACRO		- Writes a byte to N64 memory, *DOES* call MMIO write function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX - Byte of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_WRITE8 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

	xor esi, 03h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    mov BYTE PTR [esi], al
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_WRITE16 MACRO		- Writes a word to N64 memory, *DOES* call MMIO write function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX - Word of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_WRITE16 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

	xor esi, 02h				; Fixup address for DWORD swap.
    add esi, DWORD PTR [_N64Memory]
    mov WORD PTR [esi], ax
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_WRITE32 MACRO		- Writes a dword to N64 memory, *DOES* call MMIO write function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX - DWord of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_WRITE32 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

    add esi, DWORD PTR [_N64Memory]
    mov DWORD PTR [esi], eax
ENDM

;----------------------------------------------------------------------------------------------------------------------
; MMIO_WRITE64 MACRO		- Writes a qword from N64 memory, *DOES* call MMIO write function!
;
; In:  ESI - Unrelocated address to write to.
;      EAX:EDX - QWord of data to write.
; Out: Carry set if address was not in the memory map.
; Mod: ECX, EDI

MMIO_WRITE64 MACRO DO_MMIO_CHECK
	and esi, 1FFFFFFFh
    cmp esi, MMIO_LOWEST_ADDR
    jae DO_MMIO_CHECK

    add esi, DWORD PTR [_N64Memory]
    mov DWORD PTR [esi], eax
    mov DWORD PTR [esi + 4], edx
ENDM

;----------------------------------------------------------------------------------------------------------------------

ENTER64BITMODE MACRO							; Transition to 64-bit mode.  Uses ECX, ESI, and EDI!
LOCAL @@loop

	mov ecx, 32
	mov esi, OFFSET [_r4reg.GPR]
	mov edi, OFFSET [_r4reg.GPR64]
	add edi, 4

@@loop:
	movsd
	add edi, 4
	dec ecx
	jnz @@loop

	mov ecx, DWORD PTR [_r4reg.MultLO]
	mov DWORD PTR [_r4reg.MultLO64 + 4], ecx

	mov ecx, DWORD PTR [_r4reg.MultHI]
	mov DWORD PTR [_r4reg.MultHI64 + 4], ecx
ENDM

LEAVE64BITMODE MACRO							; Transition back to 32-bit mode.  Uses ECX, ESI, and EDI!
LOCAL @@loop

	mov ecx, 32
	mov esi, OFFSET [_r4reg.GPR64]
	mov edi, OFFSET [_r4reg.GPR]
	add esi, 4

@@loop:
	movsd
	add esi, 4
	dec ecx
	jnz @@loop

	mov ecx, DWORD PTR [_r4reg.MultLO64 + 4]
	mov DWORD PTR [_r4reg.MultLO], ecx

	mov ecx, DWORD PTR [_r4reg.MultHI64 + 4]
	mov DWORD PTR [_r4reg.MultHI], ecx
ENDM

;----------------------------------------------------------------------------------------------------------------------

align 4
_MEM_Read8:										; C interface to read a byte from N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MEM_READ8
	mov DWORD PTR [_MEM_return_val], eax
	pop edi esi ecx eax
	ret

align 4
_MEM_Read16:									; C interface to read a word from N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MEM_READ16
	mov DWORD PTR [_MEM_return_val], eax
	pop edi esi ecx eax
	ret

align 4
_MEM_Read32:									; C interface to read a dword from N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MEM_READ32
	mov DWORD PTR [_MEM_return_val], eax
	pop edi esi ecx eax
	ret

align 4
_MEM_Read64:									; C interface to read a qword from N64 memory
	push eax ecx edx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MEM_READ64
	mov DWORD PTR [_MEM_return_val], eax
	mov DWORD PTR [_MEM_return_val + 4], edx
	pop edi esi edx ecx eax
	ret

align 4
_MEM_Write8:									; C interface to write a byte to N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	MEM_WRITE8
	pop edi esi ecx eax
	ret

align 4
_MEM_Write16:									; C interface to write a word to N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	MEM_WRITE16
	pop edi esi ecx eax
	ret

align 4
_MEM_Write32:									; C interface to write a dword to N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	MEM_WRITE32
	pop edi esi ecx eax
	ret

align 4
_MEM_Write64:									; C interface to write a qword to N64 memory
	push eax ecx edx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	mov edx, DWORD PTR [_MEM_val + 4]
	MEM_WRITE64
	pop edi esi edx ecx eax
	ret

PUBLIC _MEM_Read8,  _MEM_Read16,  _MEM_Read32,  _MEM_Read64
PUBLIC _MEM_Write8, _MEM_Write16, _MEM_Write32, _MEM_Write64

;----------------------------------------------------------------------------------------------------------------------

align 4
_RSP_MEM_Read8:									; RSP's C interface to read a byte from N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	MEM_READ8
	mov DWORD PTR [_RSP_MEM_return_val], eax
	pop edi esi ecx eax
	ret

align 4
_RSP_MEM_Read16:								; RSP's C interface to read a word from N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	MEM_READ16
	mov DWORD PTR [_RSP_MEM_return_val], eax
	pop edi esi ecx eax
	ret

align 4
_RSP_MEM_Read32:								; RSP's C interface to read a dword from N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	MEM_READ32
	mov DWORD PTR [_RSP_MEM_return_val], eax
	pop edi esi ecx eax
	ret

align 4
_RSP_MEM_Read64:								; RSP's C interface to read a qword from N64 memory
	push eax ecx edx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	MEM_READ64
	mov DWORD PTR [_RSP_MEM_return_val], eax
	mov DWORD PTR [_RSP_MEM_return_val + 4], edx
	pop edi esi edx ecx eax
	ret

align 4
_RSP_MEM_Write8:								; RSP's C interface to write a byte to N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	mov eax, DWORD PTR [_RSP_MEM_val]
	MEM_WRITE8
	pop edi esi ecx eax
	ret

align 4
_RSP_MEM_Write16:								; RSP's C interface to write a word to N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	mov eax, DWORD PTR [_RSP_MEM_val]
	MEM_WRITE16
	pop edi esi ecx eax
	ret

align 4
_RSP_MEM_Write32:								; RSP's C interface to write a dword to N64 memory
	push eax ecx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	mov eax, DWORD PTR [_RSP_MEM_val]
	MEM_WRITE32
	pop edi esi ecx eax
	ret

align 4
_RSP_MEM_Write64:								; RSP's C interface to write a qword to N64 memory
	push eax ecx edx esi edi
	mov esi, DWORD PTR [_RSP_MEM_addr]
	mov eax, DWORD PTR [_RSP_MEM_val]
	mov edx, DWORD PTR [_RSP_MEM_val + 4]
	MEM_WRITE64
	pop edi esi edx ecx eax
	ret

PUBLIC _RSP_MEM_Read8,  _RSP_MEM_Read16,  _RSP_MEM_Read32,  _RSP_MEM_Read64
PUBLIC _RSP_MEM_Write8, _RSP_MEM_Write16, _RSP_MEM_Write32, _RSP_MEM_Write64

;---------------------------------------------------

align 4
_MMIO_Read8:									; C interface to read a byte from N64 memory with MMIO callbacks
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MMIO_READ8 @MMIO_Read8_CheckHi
@MMIO_Read8_Done:
	mov DWORD PTR [_MEM_return_val], eax
	pop edi esi ecx eax
	ret

@MMIO_Read8_CheckHi:
    MMIO_READ8_CHECKHI @MMIO_Read8_DoMMIO
    jmp @MMIO_Read8_Done

@MMIO_Read8_DoMMIO:
    DO_MMIO_READ8
    jmp @MMIO_Read8_Done

;---------------------------------------------------

align 4
_MMIO_Read16:									; C interface to read a word from N64 memory with MMIO callbacks
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MMIO_READ16 @MMIO_Read16_CheckHi
@MMIO_Read16_Done:
	mov DWORD PTR [_MEM_return_val], eax
	pop edi esi ecx eax
	ret

@MMIO_Read16_CheckHi:
    MMIO_READ16_CHECKHI @MMIO_Read16_DoMMIO
    jmp @MMIO_Read16_Done

@MMIO_Read16_DoMMIO:
    DO_MMIO_READ16
    jmp @MMIO_Read16_Done

;---------------------------------------------------

align 4
_MMIO_Read32:									; C interface to read a dword from N64 memory with MMIO callbacks
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MMIO_READ32 @MMIO_Read32_CheckHi
@MMIO_Read32_Done:
	mov DWORD PTR [_MEM_return_val], eax
	pop edi esi ecx eax
	ret

@MMIO_Read32_CheckHi:
    MMIO_READ32_CHECKHI @MMIO_Read32_DoMMIO
    jmp @MMIO_Read32_Done

@MMIO_Read32_DoMMIO:
    DO_MMIO_READ32
    jmp @MMIO_Read32_Done

;---------------------------------------------------

align 4
_MMIO_Read64:									; C interface to read a qword from N64 memory with MMIO callbacks
	push eax ecx edx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	MMIO_READ64 @MMIO_Read64_CheckHi
@MMIO_Read64_Done:
	mov DWORD PTR [_MEM_return_val], eax
	mov DWORD PTR [_MEM_return_val + 4], edx
	pop edi esi edx ecx eax
	ret

@MMIO_Read64_CheckHi:
    MMIO_READ64_CHECKHI @MMIO_Read64_DoMMIO
    jmp @MMIO_Read64_Done

@MMIO_Read64_DoMMIO:
    DO_MMIO_READ64
    jmp @MMIO_Read64_Done

;---------------------------------------------------

align 4
_MMIO_Write8:									; C interface to write a byte to N64 memory with MMIO callbacks
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	MMIO_WRITE8 @MMIO_Write8_CheckHi
@MMIO_Write8_Done:
	pop edi esi ecx eax
	ret

@MMIO_Write8_CheckHi:
    MMIO_WRITE8_CHECKHI @MMIO_Write8_DoMMIO
    jmp @MMIO_Write8_Done

@MMIO_Write8_DoMMIO:
    DO_MMIO_WRITE8
    jmp @MMIO_Write8_Done

;---------------------------------------------------

align 4
_MMIO_Write16:									; C interface to write a word to N64 memory with MMIO callbacks
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	MMIO_WRITE16 @MMIO_Write16_CheckHi
@MMIO_Write16_Done:
	pop edi esi ecx eax
	ret

@MMIO_Write16_CheckHi:
    MMIO_WRITE16_CHECKHI @MMIO_Write16_DoMMIO
    jmp @MMIO_Write16_Done

@MMIO_Write16_DoMMIO:
    DO_MMIO_WRITE16
    jmp @MMIO_Write16_Done

;---------------------------------------------------

align 4
_MMIO_Write32:									; C interface to write a dword to N64 memory with MMIO callbacks
	push eax ecx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	MMIO_WRITE32 @MMIO_Write32_CheckHi
@MMIO_Write32_Done:
	pop edi esi ecx eax
	ret

@MMIO_Write32_CheckHi:
    MMIO_WRITE32_CHECKHI @MMIO_Write32_DoMMIO
    jmp @MMIO_Write32_Done

@MMIO_Write32_DoMMIO:
    DO_MMIO_WRITE32
    jmp @MMIO_Write32_Done

;---------------------------------------------------

align 4
_MMIO_Write64:									; C interface to write a qword to N64 memory with MMIO callbacks
	push eax ecx edx esi edi
	mov esi, DWORD PTR [_MEM_addr]
	mov eax, DWORD PTR [_MEM_val]
	mov edx, DWORD PTR [_MEM_val + 4]
	MMIO_WRITE64 @MMIO_Write64_CheckHi
@MMIO_Write64_Done:
	pop edi esi edx ecx eax
	ret

@MMIO_Write64_CheckHi:
    MMIO_WRITE64_CHECKHI @MMIO_Write64_DoMMIO
    jmp @MMIO_Write64_Done

@MMIO_Write64_DoMMIO:
    DO_MMIO_WRITE64
    jmp @MMIO_Write64_Done

;---------------------------------------------------

PUBLIC _MMIO_Read8,  _MMIO_Read16,  _MMIO_Read32,  _MMIO_Read64
PUBLIC _MMIO_Write8, _MMIO_Write16, _MMIO_Write32, _MMIO_Write64

;----------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------

align 4
_InitMainEmulation:                         ; Initializes one-time emulation variables.
	push  eax ebx ecx edx esi edi ebp

    fstcw WORD PTR [FPU_Temp]               ; Setup one-time FPU control words.
    mov   ax, WORD PTR [FPU_Temp]
    and   ax, 00FFh
    or    ax, 0300h
    mov   WORD PTR [FPU_CVT], ax            ; Control word for CVT.x.y opcodes.
    and   ax, 00FFh
    or    ax, 0F00h
    mov   WORD PTR [FPU_TRUNC], ax          ; Control word for TRUNC.x.y opcodes.
    fldcw WORD PTR [FPU_CVT]                ; Default to CVT handling (used more often).

	pop   ebp edi esi edx ecx ebx eax
	ret

align 4
_MainEmulation:								; Main emulation loop.
	push  eax ebx ecx edx esi edi ebp

@MainLoop:
    xor   ebx, ebx                          ; Hardwire R0 to zero.
    mov   DWORD PTR [_r4reg.GPR], ebx

	mov   al, BYTE PTR [_R4PC_count]		; Check for PC relocation (due to delay-slot execution or interrupts).
	cmp   al, bl                                ; NOTE: EBX == 0
	jnz   @DoPCUpdate

@DonePCUpdate:
	cmp	  [_running], bl                        ; NOTE: EBX == 0
	jz	  @NotRunning

@ExecuteOpcode:
	; Update general CPU-related things.
	mov   eax, DWORD PTR [_c0reg.Random]	; Process COP0 stuff.
	cmp   eax, DWORD PTR [_c0reg.Wired]
	je    @ResetRandom

	dec   DWORD PTR [_c0reg.Random]

@DoneCOP0Update:
	cmp   BYTE PTR [_DMA_running], 1		; Update DMA stuff...
	je    @DoDMAUpdate

@DoneDMAUpdate:
    mov   eax, DWORD PTR [_N64EventArray1Ptr]   ; Get pre-calced pointer to N64EventArray[1].Time
    mov   ebx, DWORD PTR [eax]              ; Grab LO portion of .Time

    cmp   ebx, DWORD PTR [_c0reg.Count]     ; Compare LO portion with count
    je    @CheckHI_N64Event                 ; If equal, go check the HI portion

@DoneEventCheck:
	inc   DWORD PTR [_c0reg.Count]		    ; Add to the count register.
    jo    @DoIncCountHI                     ; If overflowed, increment HI portion

@ReadNextOpcode:
	mov	  esi, DWORD PTR [_r4reg.PC]		; Read next opcode.
    MEM_READ32
    mov   ebp, eax                          ; Save opcode in EBP for use later...

	DECODE_OPCODE eax
	shl   eax, 2
	add   eax, OFFSET [OpcodeTable]
	jmp   [eax]

align 4
@NotRunning:
	cmp   [_want_to_exit], 1
	je    @DoExit

	mov   eax, DWORD PTR [_hDebugEvent]
	push  eax								; Push the debugger syncronization handle.
	call  __imp__SetEvent@4

	push  0FFFFFFFFh						; Push 'INFINITE' on the stack.
	mov   eax, DWORD PTR [_hRunEvent]
	push  eax								; Push the handle to our event.
	call  __imp__WaitForSingleObject@8		; Call 'WaitForSingleObject(hRunEvent, INFINITE);'

	cmp   [_want_to_exit], 1				; Check if we should exit.
	jne   @ExecuteOpcode					; Nope, so jump to execute next opcode.  If we got signaled, we must want to run...

@DoExit:
	pop   ebp edi esi edx ecx ebx eax
	ret

;----------------------------------------------------------------------------------------------------------------------

@DoPCUpdate:
	dec   al
	cmp   al, bl                            ; NOTE: This only works because EBX == 0!
	jnz   @Update_R4PC_count

	mov   edx, DWORD PTR [_R4PC_newPC]
	mov   DWORD PTR [_r4reg.PC], edx

@Update_R4PC_count:
	mov   BYTE PTR [_R4PC_count], al
	jmp   @DonePCUpdate

;----------------------------------------------------------------------------------------------------------------------

@ResetRandom:
	mov   eax, TLBENTRIES - 1
	and   eax, 0000003Fh
	mov   DWORD PTR [_c0reg.Random], eax
	jmp   @DoneCOP0Update

@DoDMAUpdate:
	; Update DMA.
	mov   eax, DMA_TRANSFER_BYTES
	mov   ebx, DWORD PTR [_DMA_bytes_to_copy]
	cmp   ebx, eax
	jae   @DoDMAMemCopy

	; DMA request is complete... reset PI status and set off a PI interrupt.
	mov   eax, ebx
	mov   BYTE PTR [_DMA_running], 0
	and   DWORD PTR [_pireg.pi_status], NOT PI_STATUS_DMA_BUSY
    push  eax
    call  DWORD PTR [_EventFUNC_PI]
    pop   eax

@DoDMAMemCopy:
	mov   DWORD PTR [_DMA_len], eax
	push  eax
	call  _FUNC_MemCopy
	pop   eax

	add   DWORD PTR [_DMA_src_addr], eax
	add   DWORD PTR [_DMA_dest_addr], eax
	sub   DWORD PTR [_DMA_bytes_to_copy], eax
	jmp   @DoneDMAUpdate

;----------------------------------------------------------------------------------------------------------------------

@CheckHI_N64Event:
    mov   ebx, DWORD PTR [eax + 4]          ; Grab HI portion of .Time
    cmp   ebx, DWORD PTR [_c0reg.Count + 4] ; Compare HI portion with count
    jne   @DoneEventCheck					; If not equal, then bomb out.

    add   eax, N64EVENTARRAY_OFFSET_TO_ACTION   ; Advance pointer from .Time to .Action
    call  DWORD PTR [eax]                   ; Call function
    jmp   @DoneEventCheck

@DoIncCountHI:
    inc   DWORD PTR [_c0reg.Count + 4]      ; Increment HI portion of Count
    jmp   @ReadNextOpcode

;----------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------
;
;													OPCODES
;
;----------------------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------------------

align 4
@InvCOP:							; Coprocessor unusable exception
	call  _FUNC_COP_Unusable
	jmp   @MainLoop

align 4
@Unk:								; Unknown opcode
	call  _FUNC_UnknownOpcode
	jmp   @MainLoop

align 4
@C0Unk:								; Unknown COP0 opcode
	call  _FUNC_COP0_Unknown
	jmp   @MainLoop

align 4
@C1Unk:								; Unknown COP1 opcode
	call  _FUNC_COP1_Unknown
	jmp   @MainLoop

align 4
@C2Unk:								; Unknown COP2 opcode
	call  _FUNC_COP2_Unknown
	jmp   @MainLoop

align 4
@Unh:								; Unhandled opcode
	call  _FUNC_UnhandledOpcode
	jmp   @MainLoop

align 4
@Special:							; Special opcode
	DECODE_FUNCTION eax
	shl   eax, 2
	add   eax, OFFSET [SpecialTable]
	jmp   [eax]

align 4
@REGIMM:							; REGIMM opcode
	DECODE_RT eax
	shl   eax, 2
	add   eax, OFFSET [REGIMMTable]
	jmp   [eax]

align 4
@COP0:								; COP0 opcode
	DECODE_C0FMT eax
	shl   eax, 2
	add   eax, OFFSET [COP0Table]
	jmp   [eax]

align 4
@C0Func:							; COP0 function
	DECODE_C0FUNC eax
	shl   eax, 2
	add   eax, OFFSET [COP0Func]
	jmp   [eax]

align 4
@COP1:								; COP1 opcode
	DECODE_C1FMT eax
	shl   eax, 2
	add   eax, OFFSET [COP1Table]
	jmp   [eax]

align 4
@C1BC:								; COP1 BC opcode
	DECODE_C1BC eax
	shl   eax, 2
	add   eax, OFFSET [COP1BCTable]
	jmp   [eax]

align 4
@C1S:								; COP1 S opcode
	DECODE_C1S eax
	shl   eax, 2
	add   eax, OFFSET [COP1STable]
	jmp   [eax]

align 4
@C1D:								; COP1 D opcode
	DECODE_C1D eax
	shl   eax, 2
	add   eax, OFFSET [COP1DTable]
	jmp   [eax]

align 4
@C1W:								; COP1 W opcode
	DECODE_C1W eax
	shl   eax, 2
	add   eax, OFFSET [COP1WTable]
	jmp   [eax]

align 4
@C1L:								; COP1 L opcode
	DECODE_C1L eax
	shl   eax, 2
	add   eax, OFFSET [COP1LTable]
	jmp   [eax]

align 4
@COP2:								; COP2 opcode
	jmp   @C2Unk
;	jmp   @MainLoop


;----------------------------------------------------------------------------------------------------------------------
;												Basic Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@J:
	DECODE_JUMP edx

    ; Check for idle loop.
    cmp   edx, DWORD PTR [_r4reg.PC]
    je    @J_Idle

	mov   DWORD PTR [_R4PC_newPC], edx
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@J_Idle:
    ; We're in an idle loop, so set count to the first entry in the heap.
    mov   eax, DWORD PTR [_N64EventArray1Ptr]   ; Get pre-calced pointer to N64EventArray[1].Time
    mov   ebx, DWORD PTR [eax]                  ; Grab LO portion of .Time
    mov   DWORD PTR [_c0reg.Count], ebx         ; Set LO portion to Count LO
    mov   ebx, DWORD PTR [eax + 4]              ; Grab HI portion of .Time
    mov   DWORD PTR [_c0reg.Count + 4], ebx     ; Set HI portion to Count HI
    jmp   @MainLoop

;===================================================

align 4
@JAL:
	DECODE_JUMP edx

	mov   eax, DWORD PTR [_r4reg.PC]
	add   eax, 08h
	mov   ebx, (31 SHL 2)
	mov   DWORD PTR [_r4reg.GPR + ebx], eax

	mov   DWORD PTR [_R4PC_newPC], edx
	mov   BYTE PTR [_R4PC_count], 2

    mov   ebx, DWORD PTR [_CSTACK_count]                ; Don't exceed bounds..
    cmp   ebx, 32768
    jae   @JAL_done

    shl   ebx, 3
    mov   eax, DWORD PTR [_r4reg.PC]
    mov   DWORD PTR [_CSTACK_data + ebx], eax           ; Write PC
    mov   DWORD PTR [_CSTACK_data + ebx + 4], edx       ; Write call
    inc   DWORD PTR [_CSTACK_count]

@JAL_done:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BEQ:
	DECODE_RS ebx
	DECODE_RT edx

    ; Check if this is an idle loop (addr: BEQ R0, R0, addr)
    cmp   ebx, 0
    jz    @BEQ_IdleCheck

@BEQ_NotIdle:
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	jne   @BEQDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BEQDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@BEQ_IdleCheck:
    cmp   edx, 0
    jnz   @BEQ_NotIdle

    DECODE_IMMED eax
    cmp   eax, 0FFFFh
    jne   @BEQ_NotIdle

    ; We're in an idle loop, so set count to the first entry in the heap.
    mov   eax, DWORD PTR [_N64EventArray1Ptr]   ; Get pre-calced pointer to N64EventArray[1].Time
    mov   ebx, DWORD PTR [eax]                  ; Grab LO portion of .Time
    mov   DWORD PTR [_c0reg.Count], ebx         ; Set LO portion to Count LO
    mov   ebx, DWORD PTR [eax + 4]              ; Grab HI portion of .Time
    mov   DWORD PTR [_c0reg.Count + 4], ebx     ; Set HI portion to Count HI
    jmp   @MainLoop

;===================================================

align 4
@BNE:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	je    @BNEDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BNEDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BLEZ:
	DECODE_RS ebx

	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jg    @BLEZDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BLEZDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BGTZ:
	DECODE_RS ebx

	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jle   @BGTZDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BGTZDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@ADDI:
	DECODE_IMMED eax
	DECODE_RS ebx

	movsx eax, ax					; Sign extend
	mov   edx, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	add   edx, eax
    jo    @ADDI_Oflow

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], edx
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@ADDI_Oflow:
    call  DWORD PTR [_FUNC_GenArithOflow]
    jmp   @MainLoop

;===================================================

align 4
@ADDIU:
	DECODE_IMMED eax
	DECODE_RS ebx

	movsx eax, ax					; Sign extend
	mov   edx, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	add   edx, eax

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], edx
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SLTI:
	DECODE_IMMED eax
	DECODE_RS ebx
	DECODE_RT edx

	movsx eax, ax					; Sign extend
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	jl    @SLTISet1

@SLTISet0:
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], 0
	jmp   @SLTIDone

align 4
@SLTISet1:
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], 1

@SLTIDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SLTIU:
	DECODE_IMMED eax
	DECODE_RS ebx
	DECODE_RT edx

	movsx eax, ax					; Sign extend
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	jb    @SLTIUSet1

@SLTIUSet0:
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], 0
	jmp   @SLTIUDone

align 4
@SLTIUSet1:
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], 1

@SLTIUDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@ANDI:
	DECODE_IMMED eax
	DECODE_RS ebx

	mov   edx, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	and   edx, eax

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], edx

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@ORI:
	DECODE_IMMED eax
	DECODE_RS ebx

	mov   edx, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	or    edx, eax

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], edx

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@XORI:
	DECODE_IMMED eax
	DECODE_RS ebx

	mov   edx, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	xor   edx, eax

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], edx

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LUI:
	DECODE_IMMED eax
	DECODE_RT ebx

	shl   eax, 16
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BEQL:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	jne   @BEQLAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BEQLAdd8:
	add   DWORD PTR [_r4reg.PC], 8				; Skip delay slot.
	jmp   @MainLoop

;===================================================

align 4
@BNEL:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	je    @BNELAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BNELAdd8:
	add   DWORD PTR [_r4reg.PC], 8				; Skip delay slot.
	jmp   @MainLoop

;===================================================

align 4
@BLEZL:
	DECODE_RS ebx

	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jg    @BLEZLAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BLEZLAdd8:
	add   DWORD PTR [_r4reg.PC], 8				; Skip delay slot.
	jmp   @MainLoop

;===================================================

align 4
@BGTZL:
	DECODE_RS ebx

	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jle   @BGTZLAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BGTZLAdd8:
	add   DWORD PTR [_r4reg.PC], 8				; Skip delay slot.
	jmp   @MainLoop

;===================================================

align 4
@DADDI:
	ENTER64BITMODE

	DECODE_IMMED ebx
	SIGN_EXTEND_16TO64 bx, eax, edx

	DECODE_RS ecx
	shl   ecx, 3								; SHL 3 due to 64-bit registers
	mov   ebx, DWORD PTR [_r4reg.GPR64 + ecx]
	mov   ecx, DWORD PTR [_r4reg.GPR64 + ecx + 4]

	mov   esi, ecx			; add both low portions
	add   esi, edx

	cmp   esi, ecx
	jae   @DADDINoInc

	inc   ebx				; rolled over, inc high portion
    jo    @DADDI_Oflow

@DADDINoInc:
	add   ebx, eax			; EBX:ESI is now the result

	DECODE_RT edx
	shl   edx, 3
	mov   DWORD PTR [_r4reg.GPR64 + edx], ebx
	mov   DWORD PTR [_r4reg.GPR64 + edx + 4], esi

    LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@DADDI_Oflow:
    call  DWORD PTR [_FUNC_GenArithOflow]
    jmp   @MainLoop

;===================================================

align 4
@DADDIU:
	ENTER64BITMODE

	DECODE_IMMED ebx
	SIGN_EXTEND_16TO64 bx, eax, edx

	DECODE_RS ecx
	shl   ecx, 3								; SHL 3 due to 64-bit registers
	mov   ebx, DWORD PTR [_r4reg.GPR64 + ecx]
	mov   ecx, DWORD PTR [_r4reg.GPR64 + ecx + 4]

	mov   esi, ecx			; add both low portions
	add   esi, edx

	cmp   esi, ecx
	jae   @DADDIUNoInc

	inc   ebx				; rolled over, inc high portion

@DADDIUNoInc:
	add   ebx, eax			; EBX:ESI is now the result

	DECODE_RT edx
	shl   edx, 3
	mov   DWORD PTR [_r4reg.GPR64 + edx], ebx
	mov   DWORD PTR [_r4reg.GPR64 + edx + 4], esi

    LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LDL:
	jmp   @Unh

;===================================================

align 4
@LDR:
	jmp   @Unh

;===================================================

align 4
@LB:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ8 @LB_CheckHi

	DECODE_RT ebx
	movsx eax, al								; Sign-extend
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LB_CheckHi:
    MMIO_READ8_CHECKHI @LB_DoMMIO

	DECODE_RT ebx
	movsx eax, al								; Sign-extend
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LB_DoMMIO:
    DO_MMIO_READ8

	DECODE_RT ebx
	movsx eax, al								; Sign-extend
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LH:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ16 @LH_CheckHi

	DECODE_RT ebx
	movsx eax, ax								; Sign-extend
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LH_CheckHi:
	MMIO_READ16_CHECKHI @LH_DoMMIO

	DECODE_RT ebx
	movsx eax, ax								; Sign-extend
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LH_DoMMIO:
    DO_MMIO_READ16

	DECODE_RT ebx
	movsx eax, ax								; Sign-extend
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LWL:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	mov   ecx, esi								; Find count
	and   ecx, 03h
	mov   edx, 4
	sub   edx, ecx

	mov   ecx, 24								; Find position

	DECODE_RT ebx
	mov   ebx, DWORD PTR [_r4reg.GPR + (ebx * 4)]	; Get register

@LWLLoop:
	push  esi ecx
	MMIO_READ8 @LWL_CheckHi
@LWL_MMIO_Done:
	pop   ecx esi

	mov   edi, 0FFh								; Remove unwanted bits from register
	shl   edi, cl
	not   edi
	and   ebx, edi

	mov   edi, eax								; Add result, shifted into position
	shl   edi, cl
	or    ebx, edi

	sub   ecx, 8								; Shift mask position
	inc   esi									; Increment memory offset
	dec   edx									; Decrement count
	jnz   @LWLLoop

	DECODE_RT edx								; Store register
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], ebx
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LWL_CheckHi:
    MMIO_READ8_CHECKHI @LWL_DoMMIO
    jmp   @LWL_MMIO_Done

@LWL_DoMMIO:
    DO_MMIO_READ8
    jmp   @LWL_MMIO_Done

;===================================================

align 4
@LW:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ32 @LW_CheckHi

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LW_CheckHi:
    MMIO_READ32_CHECKHI @LW_DoMMIO

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LW_DoMMIO:
    DO_MMIO_READ32

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LBU:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ8 @LBU_CheckHi

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LBU_CheckHi:
    MMIO_READ8_CHECKHI @LBU_DoMMIO

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LBU_DoMMIO:
    DO_MMIO_READ8

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LHU:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ16 @LHU_CheckHi

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LHU_CheckHi:
    MMIO_READ16_CHECKHI @LHU_DoMMIO

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LHU_DoMMIO:
    DO_MMIO_READ16

	DECODE_RT ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LWR:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	mov   edx, esi								; Find count
	and   edx, 03h
	inc   edx

	xor   ecx, ecx								; Find position

	DECODE_RT ebx
	mov   ebx, DWORD PTR [_r4reg.GPR + (ebx * 4)]   ; Get register

@LWRLoop:
	push  esi ecx
	MMIO_READ8 @LWR_CheckHi
@LWR_MMIO_Done:
	pop   ecx esi

	mov   edi, 0FFh								; Remove unwanted bits from register
	shl   edi, cl
	not   edi
	and   ebx, edi

	mov   edi, eax								; Add result, shifted into position
	shl   edi, cl
	or    ebx, edi

	add   ecx, 8								; Shift mask position
	dec   esi									; Decrement memory offset
	dec   edx									; Decrement count
	jnz   @LWRLoop

	DECODE_RT edx								; Store register
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], ebx
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LWR_CheckHi:
    MMIO_READ8_CHECKHI @LWR_DoMMIO
    jmp   @LWR_MMIO_Done

@LWR_DoMMIO:
    DO_MMIO_READ8
    jmp   @LWR_MMIO_Done

;===================================================

align 4
@LWU:
    ENTER64BITMODE

	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ32 @LWU_CheckHi

	DECODE_RT ebx
	shl   ebx, 2                                ; Faster cuz it's used twice
    mov   DWORD PTR [_r4reg.GPR + ebx], 0
	mov   DWORD PTR [_r4reg.GPR + ebx + 4], eax

    LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LWU_CheckHi:
    MMIO_READ32_CHECKHI @LWU_DoMMIO

	DECODE_RT ebx
	shl   ebx, 2
    mov   DWORD PTR [_r4reg.GPR + ebx], 0
	mov   DWORD PTR [_r4reg.GPR + ebx + 4], eax

    LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LWU_DoMMIO:
    DO_MMIO_READ32

	DECODE_RT ebx
	shl   ebx, 2
    mov   DWORD PTR [_r4reg.GPR + ebx], 0
	mov   DWORD PTR [_r4reg.GPR + ebx + 4], eax

    LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SB:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	DECODE_RT ebx
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	MMIO_WRITE8 @SB_CheckHi

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SB_CheckHi:
    MMIO_WRITE8_CHECKHI @SB_DoMMIO

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SB_DoMMIO:
    DO_MMIO_WRITE8

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SH:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	DECODE_RT ebx
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	MMIO_WRITE16 @SH_CheckHi

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SH_CheckHi:
    MMIO_WRITE16_CHECKHI @SH_DoMMIO

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SH_DoMMIO:
    DO_MMIO_WRITE16

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SWL:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	mov   ecx, esi								; Find count
	and   ecx, 03h
	mov   edx, 4
	sub   edx, ecx

	mov   ecx, 24								; Find position

	DECODE_RT ebx
	mov   ebx, DWORD PTR [_r4reg.GPR + (ebx * 4)]	; Get register

@SWLLoop:
	mov   eax, ebx

	mov   edi, 0FFh								; Remove unwanted bits
	shl   edi, cl
	and   eax, edi
	shr   eax, cl								; Shift temp register down to get byte to write

	push  esi ecx
	MMIO_WRITE8 @SWL_CheckHi
@SWL_MMIO_Done:
	pop   ecx esi

	sub   ecx, 8								; Shift mask position
	inc   esi									; Increment memory offset
	dec   edx									; Decrement count
	jnz   @SWLLoop

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SWL_CheckHi:
    MMIO_WRITE8_CHECKHI @SWL_DoMMIO
    jmp @SWL_MMIO_Done

@SWL_DoMMIO:
    DO_MMIO_WRITE8
    jmp @SWL_MMIO_Done

;===================================================

align 4
@SW:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	DECODE_RT ebx
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	MMIO_WRITE32 @SW_CheckHi

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SW_CheckHi:
    MMIO_WRITE32_CHECKHI @SW_DoMMIO

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SW_DoMMIO:
    DO_MMIO_WRITE32

	add   DWORD PTR [_r4reg.PC], 4
    jmp   @MainLoop

;===================================================

align 4
@SDL:
	jmp   @Unh

;===================================================

align 4
@SDR:
	jmp   @Unh

;===================================================

align 4
@SWR:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	mov   edx, esi								; Find count
	and   edx, 03h
	inc   edx

	xor   ecx, ecx								; Find position

	DECODE_RT ebx
	mov   ebx, DWORD PTR [_r4reg.GPR + (ebx * 4)]	; Get register

@SWRLoop:
	mov   eax, ebx

	mov   edi, 0FFh								; Remove unwanted bits
	shl   edi, cl
	and   eax, edi
	shr   eax, cl								; Shift temp register down to get byte to write

	push  esi ecx
	MMIO_WRITE8 @SWR_CheckHi
@SWR_MMIO_Done:
	pop   ecx esi

	add   ecx, 8								; Shift mask position
	dec   esi									; Decrement memory offset
	dec   edx									; Decrement count
	jnz   @SWRLoop

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SWR_CheckHi:
    MMIO_WRITE8_CHECKHI @SWR_DoMMIO
    jmp   @SWR_MMIO_Done

@SWR_DoMMIO:
    DO_MMIO_WRITE8
    jmp   @SWR_MMIO_Done

;===================================================

align 4
@CACHE:
    ; No caching supported!
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LL:
	jmp   @Unh

;===================================================

align 4
@LWC1:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ32 @LWC1_CheckHi

	DECODE_FT ebx
	mov   DWORD PTR [_r4reg.FGR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LWC1_CheckHi:
    MMIO_READ32_CHECKHI @LWC1_DoMMIO

	DECODE_FT ebx
	mov   DWORD PTR [_r4reg.FGR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LWC1_DoMMIO:
    DO_MMIO_READ32

	DECODE_FT ebx
	mov   DWORD PTR [_r4reg.FGR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LWC2:
	jmp   @Unh

;===================================================

align 4
@PREF:
	jmp   @Unh

;===================================================

align 4
@LLD:
	jmp   @Unh

;===================================================

align 4
@LDC1:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ64 @LDC1_CheckHi

	DECODE_FT ebx
	shl   ebx, 2
	mov   DWORD PTR [_r4reg.FGR + ebx], edx
    mov   DWORD PTR [_r4reg.FGR + ebx + 4], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LDC1_CheckHi:
    MMIO_READ64_CHECKHI @LDC1_DoMMIO

	DECODE_FT ebx
	shl   ebx, 2
	mov   DWORD PTR [_r4reg.FGR + ebx], edx
    mov   DWORD PTR [_r4reg.FGR + ebx + 4], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LDC1_DoMMIO:
    DO_MMIO_READ64

	DECODE_FT ebx
	shl   ebx, 2
	mov   DWORD PTR [_r4reg.FGR + ebx], edx
    mov   DWORD PTR [_r4reg.FGR + ebx + 4], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@LDC2:
	jmp   @Unh

;===================================================

align 4
@LD:
	ENTER64BITMODE
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	MMIO_READ64 @LD_CheckHi

@LD_MMIO_Done:
	DECODE_RT ebx
	shl   ebx, 3										; SHL 3 for 64-bit register!
	mov   DWORD PTR [_r4reg.GPR64 + ebx], eax
	mov   DWORD PTR [_r4reg.GPR64 + ebx + 4], edx
	LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@LD_CheckHi:
    MMIO_READ64_CHECKHI @LD_DoMMIO
    jmp   @LD_MMIO_Done

@LD_DoMMIO:
    DO_MMIO_READ64
    jmp   @LD_MMIO_Done

;===================================================

align 4
@SC:
	jmp   @Unh

;===================================================

align 4
@SWC1:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	DECODE_FT ebx
	mov   eax, DWORD PTR [_r4reg.FGR + (ebx * 4)]

	MMIO_WRITE32 @SWC1_CheckHi

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SWC1_CheckHi:
    MMIO_WRITE32_CHECKHI @SWC1_DoMMIO

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SWC1_DoMMIO:
    DO_MMIO_WRITE32

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SWC2:
	jmp   @Unh

;===================================================

align 4
@SCD:
	jmp   @Unh

;===================================================

align 4
@SDC1:
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	DECODE_FT ebx
	shl   ebx, 2
	mov   edx, DWORD PTR [_r4reg.FGR + ebx]
    mov   eax, DWORD PTR [_r4reg.FGR + ebx + 4]

	MMIO_WRITE64 @SDC1_CheckHi

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SDC1_CheckHi:
    MMIO_WRITE64_CHECKHI @SDC1_DoMMIO

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SDC1_DoMMIO:
    DO_MMIO_WRITE64

    add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SDC2:
	jmp   @Unh

;===================================================

align 4
@SD:
	ENTER64BITMODE
	DECODE_BASE ebx
	mov   esi, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_IMMED eax
	movsx eax, ax
	add   esi, eax

	DECODE_RT ebx
	shl   ebx, 3										; SHL 3 for 64-bit register!
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx]
	mov   edx, DWORD PTR [_r4reg.GPR64 + ebx + 4]

	MMIO_WRITE64 @SD_CheckHi
@SD_MMIO_Done:
	LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SD_CheckHi:
    MMIO_WRITE64_CHECKHI @SD_DoMMIO
    jmp   @SD_MMIO_Done

@SD_DoMMIO:
    DO_MMIO_WRITE64
    jmp   @SD_MMIO_Done

;----------------------------------------------------------------------------------------------------------------------
;												Special Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@SLL:
	cmp   ebp, 0					; Check for NOP
	je    @SLLDone

	DECODE_RT ebx
	DECODE_SA ecx
	DECODE_RD edx
	
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	shl   eax, cl
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax

@SLLDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SRL:
	DECODE_RT ebx
	DECODE_SA ecx
	DECODE_RD edx
	
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	shr   eax, cl

	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SRA:
	DECODE_RT ebx
	DECODE_SA ecx
	DECODE_RD edx
	
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	sar   eax, cl

	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SLLV:
	DECODE_RT ebx
	DECODE_RS edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	mov   ecx, DWORD PTR [_r4reg.GPR + (edx * 4)]
	shl   eax, cl

	DECODE_RD edx
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SRLV:
	DECODE_RT ebx
	DECODE_RS edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	mov   ecx, DWORD PTR [_r4reg.GPR + (edx * 4)]
	shr   eax, cl

	DECODE_RD edx
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SRAV:
	DECODE_RT ebx
	DECODE_RS edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	mov   ecx, DWORD PTR [_r4reg.GPR + (edx * 4)]
	sar   eax, cl

	DECODE_RD edx
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@JR:
	DECODE_RS ebx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

    cmp   ebx, 31
    je    @JR_dec_CSTACK

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@JR_dec_CSTACK:                             ; Decrement call stack pointer if 'JR RA'.
    dec   DWORD PTR [_CSTACK_count]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@JALR:
	mov   eax, DWORD PTR [_r4reg.PC]
	add   eax, 08h
	mov   ebx, (31 SHL 2)
	mov   DWORD PTR [_r4reg.GPR + ebx], eax

	DECODE_RS ebx
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

    mov   ebx, DWORD PTR [_CSTACK_count]                ; Don't exceed bounds..
    cmp   ebx, 32768
    jae   @JALR_done

    shl   ebx, 3
    mov   edx, DWORD PTR [_r4reg.PC]
    mov   DWORD PTR [_CSTACK_data + ebx], edx           ; Write PC
    mov   DWORD PTR [_CSTACK_data + ebx + 4], eax       ; Write call
    inc   DWORD PTR [_CSTACK_count]

@JALR_done:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SYSCALL:
	jmp   @Unh

;===================================================

align 4
@BREAK:
	jmp   @Unh

;===================================================

align 4
@SYNC:
    ; This doesn't happen in emulation, we can ignore it.

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@MFHI:
	ENTER64BITMODE

	DECODE_RD ebx
	shl   ebx, 3								; SHL 3 due to 64-bit registers

	mov   eax, DWORD PTR [_r4reg.MultHI64]
	mov   DWORD PTR [_r4reg.GPR64 + ebx], eax
	mov   eax, DWORD PTR [_r4reg.MultHI64 + 4]
	mov   DWORD PTR [_r4reg.GPR64 + ebx + 4], eax

    LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@MTHI:
	ENTER64BITMODE

	DECODE_RS ebx
	shl   ebx, 3								; SHL 3 becuz of 64-bit reg !

	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx]
	mov   DWORD PTR [_r4reg.MultHI64], eax
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx + 4]
	mov   DWORD PTR [_r4reg.MultHI64 + 4], eax

	LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@MFLO:
	ENTER64BITMODE

	DECODE_RD ebx
	shl   ebx, 3								; SHL 3 due to 64-bit registers

	mov   eax, DWORD PTR [_r4reg.MultLO64]
	mov   DWORD PTR [_r4reg.GPR64 + ebx], eax
	mov   eax, DWORD PTR [_r4reg.MultLO64 + 4]
	mov   DWORD PTR [_r4reg.GPR64 + ebx + 4], eax

    LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@MTLO:
	ENTER64BITMODE

	DECODE_RS ebx
	shl   ebx, 3								; SHL 3 becuz of 64-bit reg !

	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx]
	mov   DWORD PTR [_r4reg.MultLO64], eax
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx + 4]
	mov   DWORD PTR [_r4reg.MultLO64 + 4], eax

	LEAVE64BITMODE

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DSLLV:                         ; r4reg.GPR64[DECODE_RD] = r4reg.GPR64[DECODE_RT] << r4reg.GPR[DECODE_RS];
    DECODE_RS ecx
    shl   ecx, 2
    mov   ecx, DWORD PTR [_r4reg.GPR + ecx]

	ENTER64BITMODE

	DECODE_RT ebx
	DECODE_RD edx

    cmp   ecx, 0                                    ; Optimization if it's zero.
    jz    @DSLLV_zero

	shl   ebx, 3									; SHL 3 becuz of 64-bit reg !
	shl   edx, 3

    cmp   ecx, 32                                   ; If shift amount is 32 or greater, we can do a nice optimization...
    jae   @DSLLV_ae32

    mov   esi, DWORD PTR [_r4reg.GPR64 + ebx]       ; Load upper half
    mov   edi, DWORD PTR [_r4reg.GPR64 + ebx + 4]   ; Load lower half

@DSLLV_loop:
    shl   esi, 1                                    ; Shift upper half by one
    mov   eax, edi                                  ; Copy lower half
    shr   eax, 31                                   ; Grab highest bit
    or    esi, eax                                  ; OR in to upper half
    shl   edi, 1                                    ; Shift lower half by one
    dec   ecx
    jnz   @DSLLV_loop

    mov   DWORD PTR [_r4reg.GPR64 + edx], esi       ; Store upper half
    mov   DWORD PTR [_r4reg.GPR64 + edx + 4], edi   ; Store lower half
    jmp   @DSLLV_done

@DSLLV_ae32:
    mov   esi, DWORD PTR [_r4reg.GPR64 + ebx + 4]   ; Load lower half
    sub   ecx, 32                                   ; Subtract 32 bits from shift count
    shl   esi, cl                                   ; Shift left
    mov   DWORD PTR [_r4reg.GPR64 + edx], esi       ; Store result to upper half
    mov   DWORD PTR [_r4reg.GPR64 + edx + 4], 0     ; And zap lower half
    jmp   @DSLLV_done

@DSLLV_zero:
    mov   esi, DWORD PTR [_r4reg.GPR64 + ebx]       ; Load upper half
    mov   edi, DWORD PTR [_r4reg.GPR64 + ebx + 4]   ; Load lower half
    mov   DWORD PTR [_r4reg.GPR64 + edx], esi       ; Store upper half
    mov   DWORD PTR [_r4reg.GPR64 + edx + 4], edi   ; Store lower half

@DSLLV_done:
	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DSRLV:
	jmp   @Unh

;===================================================

align 4
@DSRAV:
	jmp   @Unh

;===================================================

align 4
@MULT:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	mov   ebx, DWORD PTR [_r4reg.GPR + (edx * 4)]

	imul  ebx
	mov   DWORD PTR [_r4reg.MultHI], edx
	mov   DWORD PTR [_r4reg.MultLO], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@MULTU:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	mov   ebx, DWORD PTR [_r4reg.GPR + (edx * 4)]

	mul   ebx
	mov   DWORD PTR [_r4reg.MultHI], edx
	mov   DWORD PTR [_r4reg.MultLO], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DIV:
	DECODE_RS ebx
	xor   edx, edx
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_RT ebx
	mov   ecx, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   ecx, 0
	jz    @DIVSkipDIV									; Check for div by zero!

	idiv  ecx

@DIVSkipDIV:
	mov   DWORD PTR [_r4reg.MultLO], eax
	mov   DWORD PTR [_r4reg.MultHI], edx
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DIVU:
	DECODE_RS ebx
	xor   edx, edx
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]

	DECODE_RT ebx
	mov   ecx, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   ecx, 0
	jz    @DIVUSkipDIV									; Check for div by zero!

	div   ecx

@DIVUSkipDIV:
	mov   DWORD PTR [_r4reg.MultLO], eax
	mov   DWORD PTR [_r4reg.MultHI], edx
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DMULT:
	jmp   @Unh

;===================================================

;
; ************ NOTE: DMULTU currently only supports a 64-bit result ! ***********
;

align 4
@DMULTU:
	ENTER64BITMODE

	DECODE_RT ebx
	DECODE_RS edx

	shl   ebx, 3									; SHL 3 becuz of 64-bit regs !
	shl   edx, 3
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx + 4]
	mov   ecx, DWORD PTR [_r4reg.GPR64 + edx + 4]
	mul   ecx

	mov   DWORD PTR [_r4reg.MultLO64], edx
	mov   DWORD PTR [_r4reg.MultLO64 + 4], eax
	mov   DWORD PTR [_r4reg.MultHI64], 0
	mov   DWORD PTR [_r4reg.MultHI64 + 4], 0

	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DDIV:
	ENTER64BITMODE

	DECODE_RS ebx
	shl   ebx, 3										; SHL 3 becuz of 64-bit reg !
	mov   edx, DWORD PTR [_r4reg.GPR64 + ebx]
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx + 4]

	DECODE_RT ebx
	shl   ebx, 3
	mov   ecx, DWORD PTR [_r4reg.GPR64 + ebx + 4]		; Note that we can only use the _LOWER_ half!!!
	cmp   ecx, 0
	jz    @DDIVSkipDIV									; Check for div by zero!

	idiv  ecx

@DDIVSkipDIV:
	mov   DWORD PTR [_r4reg.MultLO64], 0
	mov   DWORD PTR [_r4reg.MultLO64 + 4], eax
	mov   DWORD PTR [_r4reg.MultHI64], 0
	mov   DWORD PTR [_r4reg.MultHI64 + 4], edx

	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DDIVU:
	ENTER64BITMODE

	DECODE_RS ebx
	shl   ebx, 3										; SHL 3 becuz of 64-bit reg !
	mov   edx, DWORD PTR [_r4reg.GPR64 + ebx]
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx + 4]

	DECODE_RT ebx
	shl   ebx, 3
	mov   ecx, DWORD PTR [_r4reg.GPR64 + ebx + 4]		; Note that we can only use the _LOWER_ half!!!
	cmp   ecx, 0
	jz    @DDIVUSkipDIV									; Check for div by zero!

	div   ecx

@DDIVUSkipDIV:
	mov   DWORD PTR [_r4reg.MultLO64], 0
	mov   DWORD PTR [_r4reg.MultLO64 + 4], eax
	mov   DWORD PTR [_r4reg.MultHI64], 0
	mov   DWORD PTR [_r4reg.MultHI64 + 4], edx

	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@ADD:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	add   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
    jo    @ADD_Oflow

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@ADD_Oflow:
    call  DWORD PTR [_FUNC_GenArithOflow]
    jmp   @MainLoop

;===================================================

align 4
@ADDU:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	add   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SUB:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	sub   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
    jo    @SUB_Oflow

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

@SUB_Oflow:
    call  DWORD PTR [_FUNC_GenArithOflow]
    jmp   @MainLoop

;===================================================

align 4
@SUBU:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	sub   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@AND:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	and   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@OR:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	or    eax, DWORD PTR [_r4reg.GPR + (edx * 4)]

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@XOR:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	xor   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@NOR:
	DECODE_RS ebx
	DECODE_RT edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	or    eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	not   eax

	DECODE_RD ebx
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SLT:
	DECODE_RS ebx
	DECODE_RT edx
	DECODE_RD ecx
	
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	jl    @SLTSet1

@SLTSet0:
	mov   DWORD PTR [_r4reg.GPR + (ecx * 4)], 0
	jmp   @SLTDone

align 4
@SLTSet1:
	mov   DWORD PTR [_r4reg.GPR + (ecx * 4)], 1

@SLTDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@SLTU:
	DECODE_RS ebx
	DECODE_RT edx
	DECODE_RD ecx
	
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	jb    @SLTUSet1

@SLTUSet0:
	mov   DWORD PTR [_r4reg.GPR + (ecx * 4)], 0
	jmp   @SLTUDone

align 4
@SLTUSet1:
	mov   DWORD PTR [_r4reg.GPR + (ecx * 4)], 1

@SLTUDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DADD:
	jmp   @Unh

;===================================================

align 4
@DADDU:
	jmp   @Unh

;===================================================

align 4
@DSUB:
	jmp   @Unh

;===================================================

align 4
@DSUBU:
	jmp   @Unh

;===================================================

align 4
@TGE:
	jmp   @Unh

;===================================================

align 4
@TGEU:
	jmp   @Unh

;===================================================

align 4
@TLT:
	jmp   @Unh

;===================================================

align 4
@TLTU:
	jmp   @Unh

;===================================================

align 4
@TEQ:
	DECODE_RS ebx
	DECODE_RT edx
	
	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	cmp   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	jne   @TEQDone

    call  DWORD PTR [_FUNC_GenTrapExcept]

@TEQDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@TNE:
	jmp   @Unh

;===================================================

align 4
@DSLL:                         ; r4reg.GPR64[DECODE_RD] = r4reg.GPR64[DECODE_RT] << DECODE_SA;
	ENTER64BITMODE

	DECODE_RT ebx
	DECODE_SA ecx
	DECODE_RD edx

    cmp   ecx, 0                                    ; Optimization if it's zero.
    jz    @DSLL_zero

	shl   ebx, 3									; SHL 3 becuz of 64-bit reg !
	shl   edx, 3

    cmp   ecx, 32                                   ; If shift amount is 32 or greater, we can do a nice optimization...
    jae   @DSLL_ae32

    mov   esi, DWORD PTR [_r4reg.GPR64 + ebx]       ; Load upper half
    mov   edi, DWORD PTR [_r4reg.GPR64 + ebx + 4]   ; Load lower half

@DSLL_loop:
    shl   esi, 1                                    ; Shift upper half by one
    mov   eax, edi                                  ; Copy lower half
    shr   eax, 31                                   ; Grab highest bit
    or    esi, eax                                  ; OR in to upper half
    shl   edi, 1                                    ; Shift lower half by one
    dec   ecx
    jnz   @DSLL_loop

    mov   DWORD PTR [_r4reg.GPR64 + edx], esi       ; Store upper half
    mov   DWORD PTR [_r4reg.GPR64 + edx + 4], edi   ; Store lower half
    jmp   @DSLL_done

@DSLL_ae32:
    mov   esi, DWORD PTR [_r4reg.GPR64 + ebx + 4]   ; Load lower half
    sub   ecx, 32                                   ; Subtract 32 bits from shift count
    shl   esi, cl                                   ; Shift left
    mov   DWORD PTR [_r4reg.GPR64 + edx], esi       ; Store result to upper half
    mov   DWORD PTR [_r4reg.GPR64 + edx + 4], 0     ; And zap lower half
    jmp   @DSLL_done

@DSLL_zero:
    mov   esi, DWORD PTR [_r4reg.GPR64 + ebx]       ; Load upper half
    mov   edi, DWORD PTR [_r4reg.GPR64 + ebx + 4]   ; Load lower half
    mov   DWORD PTR [_r4reg.GPR64 + edx], esi       ; Store upper half
    mov   DWORD PTR [_r4reg.GPR64 + edx + 4], edi   ; Store lower half

@DSLL_done:
	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DSRL:
	jmp   @Unh

;===================================================

align 4
@DSRA:
	jmp   @Unh

;===================================================

align 4
@DSLL32:						; r4reg.GPR64[DECODE_RD] = r4reg.GPR64[DECODE_RT] << (32 + DECODE_SA);
	ENTER64BITMODE

	DECODE_RT ebx
	DECODE_SA ecx
	DECODE_RD edx

	shl   ebx, 3									; SHL 3 becuz of 64-bit reg !
	shl   edx, 3
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx + 4]	; Note that we only want the _LOWER_ 32-bits!
	shl   eax, cl

	mov   DWORD PTR [_r4reg.GPR64 + edx], eax		; Write result to _UPPER_ half, and...
	mov   DWORD PTR [_r4reg.GPR64 + edx + 4], 0		; ...zap LOWER half.

	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DSRL32:						; r4reg.GPR64[DECODE_RD] = r4reg.GPR64[DECODE_RT] >> (32 + DECODE_SA);
	ENTER64BITMODE

	DECODE_RT ebx
	DECODE_SA ecx
	DECODE_RD edx

	shl   ebx, 3									; SHL 3 becuz of 64-bit reg !
	shl   edx, 3
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx]		; Note that we only want the _UPPER_ 32-bits!
	shr   eax, cl

	mov   DWORD PTR [_r4reg.GPR64 + edx], 0			; Zap UPPER half, and...
	mov   DWORD PTR [_r4reg.GPR64 + edx + 4], eax	; ...write result to _LOWER_ half.

	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DSRA32:
	ENTER64BITMODE

	DECODE_RT ebx
	DECODE_SA ecx
	DECODE_RD edx

	shl   ebx, 3									; SHL 3 becuz of 64-bit reg !
	shl   edx, 3
	mov   eax, DWORD PTR [_r4reg.GPR64 + ebx]		; Note that we only want the _UPPER_ 32-bits!
	sar   eax, cl

	mov   DWORD PTR [_r4reg.GPR64 + edx], 0			; Zap UPPER half, and...
	mov   DWORD PTR [_r4reg.GPR64 + edx + 4], eax	; ...write result to _LOWER_ half.

	LEAVE64BITMODE
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;----------------------------------------------------------------------------------------------------------------------
;												REGIMM Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@BLTZ:
	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jge   @BLTZDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BLTZDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BGEZ:
	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jl    @BGEZDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BGEZDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BLTZL:
	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jge   @BLTZLAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BLTZLAdd8:
	add   DWORD PTR [_r4reg.PC], 8
	jmp   @MainLoop

;===================================================

align 4
@BGEZL:
	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jl    @BGEZLAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BGEZLAdd8:
	add   DWORD PTR [_r4reg.PC], 8
	jmp   @MainLoop

;===================================================

align 4
@TGEI:
	jmp   @Unh

;===================================================

align 4
@TGEIU:
	jmp   @Unh

;===================================================

align 4
@TLTI:
	jmp   @Unh

;===================================================

align 4
@TLTIU:
	jmp   @Unh

;===================================================

align 4
@TEQI:
	jmp   @Unh

;===================================================

align 4
@TNEI:
	jmp   @Unh

;===================================================

align 4
@BLTZAL:
	mov   eax, DWORD PTR [_r4reg.PC]
	add   eax, 08h
	mov   ebx, (31 SHL 2)
	mov   DWORD PTR [_r4reg.GPR + ebx], eax

	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jge   @BLTZALDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BLTZALDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BGEZAL:
	mov   eax, DWORD PTR [_r4reg.PC]
	add   eax, 08h
	mov   ebx, (31 SHL 2)
	mov   DWORD PTR [_r4reg.GPR + ebx], eax

	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jl    @BGEZALDone

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BGEZALDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BLTZALL:
	mov   eax, DWORD PTR [_r4reg.PC]
	add   eax, 08h
	mov   ebx, (31 SHL 2)
	mov   DWORD PTR [_r4reg.GPR + ebx], eax

	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jge   @BLTZALLAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BLTZALLAdd8:
	add   DWORD PTR [_r4reg.PC], 8
	jmp   @MainLoop

;===================================================

align 4
@BGEZALL:
	mov   eax, DWORD PTR [_r4reg.PC]
	add   eax, 08h
	mov   ebx, (31 SHL 2)
	mov   DWORD PTR [_r4reg.GPR + ebx], eax

	DECODE_RS ebx
	cmp   DWORD PTR [_r4reg.GPR + (ebx * 4)], 0
	jl    @BGEZALLAdd8

	DECODE_OFFSET eax, ax
	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

align 4
@BGEZALLAdd8:
	add   DWORD PTR [_r4reg.PC], 8
	jmp   @MainLoop

;----------------------------------------------------------------------------------------------------------------------
;												COP0 Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@MFC0:
	DECODE_FS ebx
	DECODE_RT edx

	mov   ecx, DWORD PTR [c0reg_offsets + (ebx * 4)]		; Lookup offset to COP0 register.
	mov   eax, DWORD PTR [_c0reg + ecx]
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DMFC0:
	jmp   @Unh

;===================================================

align 4
@CFC0:
	jmp   @Unh

;===================================================

align 4
@MTC0:
	DECODE_FS eax
	DECODE_RT ebx
	shl   ebx, 2						; Shift left... EBX can be used as an offset into the GPR array, but regs are 4 bytes.

	cmp   eax, C0REG_RANDOM				; Random is read-only.
	je    @DoneMTC0

	cmp   eax, C0REG_PREVID				; PRevID is read-only.
	je	  @DoneMTC0

	cmp   eax, C0REG_WIRED
	je    @MTC0_Wired

	cmp   eax, C0REG_INDEX
	je    @MTC0_Index

	cmp   eax, C0REG_PAGEMASK
	je    @MTC0_PageMask

	cmp   eax, C0REG_ENTRYLO0
	je    @MTC0_EntryLo0

	cmp   eax, C0REG_ENTRYLO1
	je    @MTC0_EntryLo1

	cmp   eax, C0REG_ENTRYHI
	je    @MTC0_EntryHi

	cmp   eax, C0REG_EXCEPTIONPC
	je    @MTC0_ExceptionPC

	cmp   eax, C0REG_ERROREPC
	je    @MTC0_ErrorEPC

	cmp   eax, C0REG_CAUSE
	je    @MTC0_Cause

	cmp   eax, C0REG_COUNT
	je    @MTC0_Count

	cmp   eax, C0REG_COMPARE
	je    @MTC0_Compare

	cmp   eax, C0REG_STATUS
	je    @MTC0_Status
	jmp   @DoneMTC0

align 4
@MTC0_Wired:
	mov   edx, (TLBENTRIES - 1)
	and   edx, 3Fh
	mov   DWORD PTR [_c0reg.Random], edx

	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	and   edx, 3Fh
	mov   DWORD PTR [_c0reg.Wired], edx
	jmp   @DoneMTC0

align 4
@MTC0_Index:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	and   edx, 3Fh
	mov   DWORD PTR [_c0reg.Index], edx
	jmp   @DoneMTC0

align 4
@MTC0_PageMask:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.PageMask], edx
	jmp   @DoneMTC0

align 4
@MTC0_EntryLo0:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.EntryLo0], edx
	jmp   @DoneMTC0

align 4
@MTC0_EntryLo1:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.EntryLo1], edx
	jmp   @DoneMTC0

align 4
@MTC0_EntryHi:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.EntryHi], edx
	jmp   @DoneMTC0

align 4
@MTC0_ExceptionPC:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.ExceptionPC], edx
	jmp   @DoneMTC0

align 4
@MTC0_ErrorEPC:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.ErrorEPC], edx
	jmp   @DoneMTC0

align 4
@MTC0_Cause:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   eax, DWORD PTR [_c0reg.Cause]
	and   edx, 30h								; Only interested in IP0 and IP1.
	and   eax, NOT 30h
	or    eax, edx
	mov   DWORD PTR [_c0reg.Cause], eax
	jmp   @DoneMTC0

align 4
@MTC0_Count:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.Count], edx
	jmp   @DoneMTC0

align 4
@MTC0_Compare:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.Compare], edx
	call  DWORD PTR [_EventFUNC_Compare]		; Add count/compare event.
	jmp   @DoneMTC0

align 4
@MTC0_Status:
	mov   edx, DWORD PTR [_r4reg.GPR + ebx]
	mov   DWORD PTR [_c0reg.Status], edx
    call  DWORD PTR [_FUNC_RegenIntr]           ; Check to see if an IM flag got changed so we can process a buffered interrupt.
	jmp   @DoneMTC0

align 4
@DoneMTC0:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@DMTC0:
	jmp   @Unh

;===================================================

align 4
@CTC0:
	jmp   @Unh

;----------------------------------------------------------------------------------------------------------------------
;												COP0 Functions
;----------------------------------------------------------------------------------------------------------------------

align 4
@TLBR:
	mov   esi, OFFSET [_tlb]
	mov   eax, DWORD PTR [_c0reg.Index]
	and   eax, TLB_INDEX_MASK
	shl   eax, TLB_ENTRY_SHIFT
	add   esi, eax

	mov   eax, DWORD PTR [esi + tlb_PageMask]
	mov   ebx, DWORD PTR [esi + tlb_EntryHi]
	mov   ecx, DWORD PTR [esi + tlb_EntryLo1]
	mov   edx, DWORD PTR [esi + tlb_EntryLo0]

	mov   DWORD PTR [_c0reg.PageMask], eax
	mov   DWORD PTR [_c0reg.EntryHi],  ebx
	mov   DWORD PTR [_c0reg.EntryLo1], ecx
	mov   DWORD PTR [_c0reg.EntryLo0], edx

	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

;    TLB_EntryDef *t;
;
;    t = &tlb[c0reg.Index];
;
;    // Load the entry into the TLB structure...
;    t->PageMask = c0reg.PageMask;
;    t->EntryHi  = c0reg.EntryHi & (!c0reg.PageMask);
;    t->EntryLo1 = c0reg.EntryLo1;
;    t->EntryLo0 = c0reg.EntryLo0;
;
;    // Set the G bit in the TLB entry if the G bits in *both* the EntryLo0 and EntryLo1 are true.
;    if ((t->EntryLo0 & 0x00000001) && (t->EntryLo1 & 0x00000001))
;        t->EntryHi |= 0x00001000;
;    else
;        t->EntryHi &= ~0x00001000;

align 4
@TLBWI:
	mov   esi, OFFSET [_tlb]
	mov   eax, DWORD PTR [_c0reg.Index]
	and   eax, TLB_INDEX_MASK
	shl   eax, TLB_ENTRY_SHIFT
	add   esi, eax

	mov   eax, DWORD PTR [_c0reg.PageMask]			; tlb->PageMask = c0reg.PageMask
	mov   ebx, DWORD PTR [_c0reg.EntryHi]			; tlb->EntryHi  = c0reg.EntryHi & (!c0reg.PageMask)
	mov   ecx, eax
	not   ecx
	and   ebx, ecx
	mov   ecx, DWORD PTR [_c0reg.EntryLo1]			; tlb->EntryLo1 = c0reg.EntryLo1
	mov   edx, DWORD PTR [_c0reg.EntryLo0]			; tlb->EntryLo0 = c0reg.EntryLo0

	mov   DWORD PTR [esi + tlb_PageMask], eax
	mov   DWORD PTR [esi + tlb_EntryHi],  ebx
	mov   DWORD PTR [esi + tlb_EntryLo1], ecx
	mov   DWORD PTR [esi + tlb_EntryLo0], edx

	and   ecx, 01h
	jz    @TLBWIAndMask

	and   edx, 01h
	jz    @TLBWIAndMask

@TLBWIOrMask:
	or    DWORD PTR [esi + tlb_EntryHi], 00001000h
	jmp   @TLBWIDone

align 4
@TLBWIAndMask:
	and   DWORD PTR [esi + tlb_EntryHi], NOT 00001000h

@TLBWIDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@TLBWR:
	jmp   @Unh

;===================================================

; for i in 0...TLBEntries–1
; if (TLB[i]95...77 = EntryHi 31...12) and (TLB[i]76 or
; (TLB[i]71...64 = EntryHi 7...0 )) then
; Index ¬ 0 26 || i 5...0
; endif
; endfor

align 4
@TLBP:
	or    DWORD PTR [_c0reg.Index], (1 SHL 26)			; Default to 'not found'
	mov   ebx, DWORD PTR [_c0reg.EntryHi]
	mov   esi, OFFSET [_tlb]
	mov   ecx, 0
	
@TLBPLoop:
	mov   eax, DWORD PTR [esi + tlb_EntryHi]
	shr   eax, 12
	mov   edx, ebx
	shr   edx, 12
	cmp   eax, edx
	jne   @TLBPNext

	mov   eax, DWORD PTR [esi + tlb_EntryHi]
	shr   eax, 11
	and   eax, 01h
	cmp   eax, 01h
	je    @TLBPFound

	mov   eax, DWORD PTR [esi + tlb_EntryHi]
	and   eax, 0FFh
	and   ebx, 0FFh
	jne   @TLBPNext

@TLBPFound:
	mov   eax, DWORD PTR [_c0reg.Index]
	and   eax, NOT (1 SHL 26)
	and   eax, NOT TLB_INDEX_MASK
	or    eax, ecx
	mov   DWORD PTR [_c0reg.Index], eax
	jmp   @TLBPDone

align 4
@TLBPNext:
	add   esi, TLB_ENTRY_SIZE
	inc   ecx
	cmp   ecx, TLBENTRIES
	jne   @TLBPLoop
	
@TLBPDone:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@ERET:
	mov   eax, DWORD PTR [_c0reg.Status]
	and   eax, C0STATUS_SR
	jz    @ERET_EXL

@ERET_ERL:
	and   DWORD PTR [_c0reg.Status], NOT C0STATUS_ERL
	mov   eax, DWORD PTR [_c0reg.ErrorEPC]
	mov   DWORD PTR [_r4reg.PC], eax
	jmp   @MainLoop

@ERET_EXL:
	and   DWORD PTR [_c0reg.Status], NOT C0STATUS_EXL
	mov   eax, DWORD PTR [_c0reg.ExceptionPC]
	mov   DWORD PTR [_r4reg.PC], eax
	jmp   @MainLoop

;----------------------------------------------------------------------------------------------------------------------
;												COP1 Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@MFC1:
	DECODE_RT ebx
	DECODE_FS edx

	mov   eax, DWORD PTR [_r4reg.FGR + (edx * 4)]
	mov   DWORD PTR [_r4reg.GPR + (ebx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@CFC1:
	DECODE_FS ebx
	DECODE_RT edx

	mov   ecx, DWORD PTR [c1reg_offsets + (ebx * 4)]		; Lookup offset to COP1 register.
	mov   eax, DWORD PTR [_c1reg + ecx]
	mov   DWORD PTR [_r4reg.GPR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@MTC1:
	DECODE_RT ebx
	DECODE_FS edx

	mov   eax, DWORD PTR [_r4reg.GPR + (ebx * 4)]
	mov   DWORD PTR [_r4reg.FGR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@CTC1:
	DECODE_FS ebx

	cmp   ebx, C1REG_FCSR							; If reg != C1REG_FCSR, then ignore opcode.
	jne   @CTC1Done

	DECODE_RT edx
	mov   eax, DWORD PTR [_r4reg.GPR + (edx * 4)]
	mov   DWORD PTR [_c1reg.FCSR], eax

@CTC1Done:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;----------------------------------------------------------------------------------------------------------------------
;												COP1 BC Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@BC1F:
	DECODE_OFFSET eax, ax

    mov   ebx, DWORD PTR [_c1reg.FCSR]
    and   ebx, C1FCSR_COMP
    jnz   @BC1F_Done

	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BC1F_Done:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BC1T:
	DECODE_OFFSET eax, ax

    mov   ebx, DWORD PTR [_c1reg.FCSR]
    and   ebx, C1FCSR_COMP
    jz    @BC1T_Done

	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2

@BC1T_Done:
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@BC1FL:
	DECODE_OFFSET eax, ax

    mov   ebx, DWORD PTR [_c1reg.FCSR]
    and   ebx, C1FCSR_COMP
    jnz   @BC1FL_Add8

	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
    add   DWORD PTR [_r4reg.PC], 4
    jmp   @MainLoop

@BC1FL_Add8:
	add   DWORD PTR [_r4reg.PC], 8
	jmp   @MainLoop

;===================================================

align 4
@BC1TL:
	DECODE_OFFSET eax, ax

    mov   ebx, DWORD PTR [_c1reg.FCSR]
    and   ebx, C1FCSR_COMP
    jz    @BC1TL_Add8

	mov   DWORD PTR [_R4PC_newPC], eax
	mov   BYTE PTR [_R4PC_count], 2
    add   DWORD PTR [_r4reg.PC], 4
    jmp   @MainLoop

@BC1TL_Add8:
	add   DWORD PTR [_r4reg.PC], 8
	jmp   @MainLoop

;----------------------------------------------------------------------------------------------------------------------
;												COP1 S Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@C1S_ADD:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fadd  DWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  DWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_SUB:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fsub  DWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  DWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_MUL:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fmul  DWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  DWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_DIV:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fdiv  DWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  DWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_SQRT:
	DECODE_FS ebx
	DECODE_FD edx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fsqrt
	fstp  DWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_ABS:
	DECODE_FS ebx
	DECODE_FD edx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fabs
	fstp  DWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_MOV:
	DECODE_FS ebx
	DECODE_FD edx

	mov   eax, DWORD PTR [_r4reg.FGR + (ebx * 4)]
	mov   DWORD PTR [_r4reg.FGR + (edx * 4)], eax
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_NEG:
	DECODE_FS ebx
	DECODE_FD edx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fchs
	fstp  DWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_ROUND_L:
	jmp   @Unh

;===================================================

align 4
@C1S_TRUNC_L:
	jmp   @Unh

;===================================================

align 4
@C1S_CEIL_L:
	jmp   @Unh

;===================================================

align 4
@C1S_FLOOR_L:
	jmp   @Unh

;===================================================

align 4
@C1S_ROUND_W:
	jmp   @Unh

;===================================================

align 4
@C1S_TRUNC_W:
	DECODE_FS ebx
	DECODE_FD edx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
    fldcw WORD  PTR [FPU_TRUNC]
	fistp DWORD PTR [_r4reg.FGR + (edx * 4)]
    fldcw WORD  PTR [FPU_CVT]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_CEIL_W:
	jmp   @Unh

;===================================================

align 4
@C1S_FLOOR_W:
	jmp   @Unh

;===================================================

align 4
@C1S_CVT_D:
	DECODE_FS ebx
	DECODE_FD edx

    fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
    fstp  QWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_CVT_W:
	DECODE_FS ebx
	DECODE_FD edx

	fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fistp DWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_CVT_L:
	jmp   @Unh

;===================================================

align 4
@C1S_C_F:
	jmp   @Unh

;===================================================

align 4
@C1S_C_UN:
	jmp   @Unh

;===================================================

align 4
@C1S_C_EQ:
    DECODE_FS ebx
    DECODE_FT edx

    and   DWORD PTR [_c1reg.FCSR], NOT C1FCSR_COMP
    fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
    fcomp DWORD PTR [_r4reg.FGR + (edx * 4)]
    fnstsw ax
    sahf
    jnz   @C1S_C_EQ_Done

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP

@C1S_C_EQ_Done:
    add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_C_UEQ:
	jmp   @Unh

;===================================================

align 4
@C1S_C_OLT:
	jmp   @Unh

;===================================================

align 4
@C1S_C_ULT:
	jmp   @Unh

;===================================================

align 4
@C1S_C_OLE:
	jmp   @Unh

;===================================================

align 4
@C1S_C_ULE:
	jmp   @Unh

;===================================================

align 4
@C1S_C_SF:
	jmp   @Unh

;===================================================

align 4
@C1S_C_NGLE:
	jmp   @Unh

;===================================================

align 4
@C1S_C_SEQ:
	jmp   @Unh

;===================================================

align 4
@C1S_C_NGL:
	jmp   @Unh

;===================================================

align 4
@C1S_C_LT:
    DECODE_FS ebx
    DECODE_FT edx

    and   DWORD PTR [_c1reg.FCSR], NOT C1FCSR_COMP
    fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
    fcomp DWORD PTR [_r4reg.FGR + (edx * 4)]
    fnstsw ax
    sahf
    jnc   @C1S_C_LT_Done

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP

@C1S_C_LT_Done:
    add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_C_NGE:
	jmp   @Unh

;===================================================

align 4
@C1S_C_LE:
    DECODE_FS ebx
    DECODE_FT edx

    and   DWORD PTR [_c1reg.FCSR], NOT C1FCSR_COMP
    fld   DWORD PTR [_r4reg.FGR + (ebx * 4)]
    fcomp DWORD PTR [_r4reg.FGR + (edx * 4)]
    fnstsw ax
    sahf
    jnc   @C1S_C_LE_CheckEqual

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP
    jmp   @C1S_C_LE_Done

align 4
@C1S_C_LE_CheckEqual:
    jnz   @C1S_C_LE_Done

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP

@C1S_C_LE_Done:
    add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1S_C_NGT:
	jmp   @Unh

;----------------------------------------------------------------------------------------------------------------------
;												COP1 D Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@C1D_ADD:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fadd  QWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  QWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_SUB:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fsub  QWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  QWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_MUL:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fmul  QWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  QWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_DIV:
	DECODE_FS ebx
	DECODE_FT edx
	DECODE_FD ecx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fdiv  QWORD PTR [_r4reg.FGR + (edx * 4)]
	fstp  QWORD PTR [_r4reg.FGR + (ecx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_SQRT:
	DECODE_FS ebx
	DECODE_FD edx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fsqrt
	fstp  QWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_ABS:
	DECODE_FS ebx
	DECODE_FD edx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fabs
	fstp  QWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_MOV:
	DECODE_FS ebx
	DECODE_FD edx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fstp  QWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_NEG:
	DECODE_FS ebx
	DECODE_FD edx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fchs
	fstp  QWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_ROUND_L:
	jmp   @Unh

;===================================================

align 4
@C1D_TRUNC_L:
	jmp   @Unh

;===================================================

align 4
@C1D_CEIL_L:
	jmp   @Unh

;===================================================

align 4
@C1D_FLOOR_L:
	jmp   @Unh

;===================================================

align 4
@C1D_ROUND_W:
	jmp   @Unh

;===================================================

align 4
@C1D_TRUNC_W:
	DECODE_FS ebx
	DECODE_FD edx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
    fldcw WORD  PTR [FPU_TRUNC]
	fistp DWORD PTR [_r4reg.FGR + (edx * 4)]
    fldcw WORD  PTR [FPU_CVT]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_CEIL_W:
	jmp   @Unh

;===================================================

align 4
@C1D_FLOOR_W:
	jmp   @Unh

;===================================================

align 4
@C1D_CVT_S:
	DECODE_FS ebx
	DECODE_FD edx

    fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
    fstp  DWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
    jmp   @MainLoop

;===================================================

align 4
@C1D_CVT_W:
	DECODE_FS ebx
	DECODE_FD edx

	fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
	fistp DWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_CVT_L:
	jmp   @Unh

;===================================================

align 4
@C1D_C_F:
	jmp   @Unh

;===================================================

align 4
@C1D_C_UN:
	jmp   @Unh

;===================================================

align 4
@C1D_C_EQ:
    DECODE_FS ebx
    DECODE_FT edx

    and   DWORD PTR [_c1reg.FCSR], NOT C1FCSR_COMP
    fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
    fcomp QWORD PTR [_r4reg.FGR + (edx * 4)]
    fnstsw ax
    sahf
    jnz   @C1D_C_EQ_Done

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP

@C1D_C_EQ_Done:
    add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_C_UEQ:
	jmp   @Unh

;===================================================

align 4
@C1D_C_OLT:
	jmp   @Unh

;===================================================

align 4
@C1D_C_ULT:
	jmp   @Unh

;===================================================

align 4
@C1D_C_OLE:
	jmp   @Unh

;===================================================

align 4
@C1D_C_ULE:
	jmp   @Unh

;===================================================

align 4
@C1D_C_SF:
	jmp   @Unh

;===================================================

align 4
@C1D_C_NGLE:
	jmp   @Unh

;===================================================

align 4
@C1D_C_SEQ:
	jmp   @Unh

;===================================================

align 4
@C1D_C_NGL:
	jmp   @Unh

;===================================================

align 4
@C1D_C_LT:
    DECODE_FS ebx
    DECODE_FT edx

    and   DWORD PTR [_c1reg.FCSR], NOT C1FCSR_COMP
    fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
    fcomp QWORD PTR [_r4reg.FGR + (edx * 4)]
    fnstsw ax
    sahf
    jnc   @C1D_C_LT_Done

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP

@C1D_C_LT_Done:
    add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_C_NGE:
	jmp   @Unh

;===================================================

align 4
@C1D_C_LE:
    DECODE_FS ebx
    DECODE_FT edx

    and   DWORD PTR [_c1reg.FCSR], NOT C1FCSR_COMP
    fld   QWORD PTR [_r4reg.FGR + (ebx * 4)]
    fcomp QWORD PTR [_r4reg.FGR + (edx * 4)]
    fnstsw ax
    sahf
    jnc   @C1D_C_LE_CheckEqual

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP
    jmp   @C1D_C_LE_Done

align 4
@C1D_C_LE_CheckEqual:
    jnz   @C1D_C_LE_Done

    or    DWORD PTR [_c1reg.FCSR], C1FCSR_COMP

@C1D_C_LE_Done:
    add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1D_C_NGT:
	jmp   @Unh

;----------------------------------------------------------------------------------------------------------------------
;												COP1 W Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@C1W_CVT_S:
	DECODE_FS ebx
	DECODE_FD edx

	fild  DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fstp  DWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;===================================================

align 4
@C1W_CVT_D:
	DECODE_FS ebx
	DECODE_FD edx

	fild  DWORD PTR [_r4reg.FGR + (ebx * 4)]
	fstp  QWORD PTR [_r4reg.FGR + (edx * 4)]
	add   DWORD PTR [_r4reg.PC], 4
	jmp   @MainLoop

;----------------------------------------------------------------------------------------------------------------------
;												COP1 L Opcodes
;----------------------------------------------------------------------------------------------------------------------

align 4
@C1L_CVT_S:
	jmp   @Unh

;===================================================

align 4
@C1L_CVT_D:
	jmp   @Unh

;===================================================

_TEXT ENDS

	END

