//
// 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: emu.cpp $
// Revision 1.22  1998/05/25 20:56:46  BREAKPOINT
// Removed debugger code from release builds.  Only usable when _DEBUG is #defined (a debug build).
// Revision 1.21  1998/05/25 15:52:24  BREAKPOINT
// Reset call stack counter upon file->reset.
// Revision 1.20  1998/05/24 20:48:51  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/05/23 15:41:06  BREAKPOINT
// Implemented new (and correct) interrupt system..
// Revision 1.18  1998/05/16 02:50:01  BREAKPOINT
// Added extra debugging code to exception handlers to see if access violation was due to DMA or not.
// Revision 1.17  1998/05/14 01:26:39  BREAKPOINT
// Fixed problems with interrupt "hang" due to multiple actions being in the heap at the same time.  EventHeapDeleteAction() now
// handles this.
// Revision 1.16  1998/05/10 23:52:28  BREAKPOINT
// Added RSP reset (to zap vectors/accumulators/flags) upon emulator start.
// Revision 1.15  1998/04/19 16:32:45  BREAKPOINT
// Added Trap Exceptions and included TEQ opcode.
// Revision 1.14  1998/04/08 23:49:15  BREAKPOINT
// Added RDP interrupts (to be used at a later time).
// Revision 1.13  1998/04/05 01:32:50  BREAKPOINT
// Modified vertical retrace handler so it flushes the CFB each time through, regardless of interrupt mask state, and queues
// another interrupt.
// Revision 1.12  1998/04/04 16:39:33  BREAKPOINT
// Added debugging info for ALL COP1 opcodes.
// Revision 1.11  1998/04/03 00:01:32  BREAKPOINT
// Added disassembly output for all COP1 opcodes.
// Revision 1.10  1998/04/02 22:08:45  BREAKPOINT
// Changed 'Unhandled Opcode' to display the opcode that's unhandled...
// Revision 1.9  1998/04/02 21:18:17  BREAKPOINT
// Implemented new memory mapping routines and changed ROM loading code.
// Revision 1.8  1998/03/29 20:19:20  BREAKPOINT
// Added Arithmetic Overflow Exception to exception handler...
// Revision 1.7  1998/03/22 22:57:08  BREAKPOINT
// Added more COP1 *.D opcodes.
// Revision 1.6  1998/03/22 17:53:35  BREAKPOINT
// Added COD/MAP file support to internal debugger
// Revision 1.5  1998/03/08 19:12:20  BREAKPOINT
// Version 0.3b.3 checkin... I need to really check this stuff in more often..
// Revision 1.4  1998/01/31 19:49:39  BREAKPOINT
// Removed C emulation code.  Other fixes to speed up memory accesses.
// Revision 1.3  1998/01/20 00:30:41  BREAKPOINT
// Zapped structure declarations, new c1reg init, and new TLBWI and CFC1
// opcodes..
// Revision 1.2  1998/01/10 21:57:18  BREAKPOINT
// Initial changes to move to assembly version of R4300i assembly core
// Revision 1.1  1998/01/10 01:26:24  BREAKPOINT
// Initial revision
//

static char *rcsid = "$Id: emu.cpp 1.22 1998/05/25 20:56:46 BREAKPOINT Exp $";

//---------------------------------------------------------------------------------------------------------------------

#define NO_EXTERN
#include <types.h>
#include "unreal.h"
#include "cfb.h"
#include "emu.h"
#include "debugger.h"
#include "audio.h"
#include "mmio.h"
#include "r4300i.h"
#include "resource.h"
#include "heap.h"
#include "input.h"
#include "cod_map.h"
#include "rsp.h"
#include "rdp.h"

// Include inline functions...
#include "inline.cpp"

RSP_Processor *RSP;
ulong aireg_dram_hold, aireg_len_hold;
int aireg_dram_state = 0, aireg_len_state = 0;

bool in_MemCopy = FALSE, in_MemSet = FALSE;
ulong MemCopy_dest, MemSet_dest;

__int64 Last_Vert_Retrace_Count = 0;

// *******************************************************************************************************
// *******************************************************************************************************
//
//                                      INITIALIZATION AND EXECUTION
//
// *******************************************************************************************************
// *******************************************************************************************************

void InitMemoryMap(void)
{
    int t;

    for (t = 0; t < NUM_MEMMAPS; t++)
    {
        memmap[t].ptr        = NULL;
        memmap[t].start_addr = 0x00000000;
        memmap[t].end_addr   = 0x00000000;
        memmap[t].read_func  = NULL;
        memmap[t].write_func = NULL;
    }
}

void AddMemoryPtr(LPVOID ptr, ulong start, ulong end, void (*rd)(void), void (*wr)(void), bool zero)
{
    int t;
    ulong len;

    if (ptr == NULL)
    {
        MessageBox(MainhWnd, "Uhoh!  Tried to add a NULL pointer to the memory map!\n\nProbable cause: Out of memory!", NULL, MB_OK);
        return;
    }

    if (zero)
    {
        // Zero memory...
        len = (end - start) + 1;
        memset(ptr, 0, len);
    }

    // Fix addresses...
    start &= 0x1FFFFFFF;
    end   &= 0x1FFFFFFF;

    for (t = 0; t < NUM_MEMMAPS; t++)
    {
        if (memmap[t].ptr == NULL)
        {
            memmap[t].ptr        = ptr;
            memmap[t].start_addr = start;
            memmap[t].end_addr   = end;
            memmap[t].read_func  = rd;
            memmap[t].write_func = wr;
            return;
        }
    }

    MessageBox(MainhWnd, "Uhoh!  Ran out of room to store a pointer in the memory map!", NULL, MB_OK);
}

void AddMemoryPtrToTop(LPVOID ptr, ulong start, ulong end, void (*rd)(void), void (*wr)(void), bool zero)
{
    int t;
    ulong len;

    if (ptr == NULL)
    {
        MessageBox(MainhWnd, "Uhoh!  Tried to add a NULL pointer to the memory map!\n\nProbable cause: Out of memory!", NULL, MB_OK);
        return;
    }

    if (zero)
    {
        // Zero memory...
        len = (end - start) + 1;
        memset(ptr, 0, len);
    }

    // Fix addresses...
    start &= 0x1FFFFFFF;
    end   &= 0x1FFFFFFF;

    // First check if the top pointer is free...
    if (memmap[0].ptr == NULL)
    {
        // It is, so just add this pointer.
        memmap[0].ptr        = ptr;
        memmap[0].start_addr = start;
        memmap[0].end_addr   = end;
        memmap[0].read_func  = rd;
        memmap[0].write_func = wr;
        return;
    }

    // First one isn't free, so optimize the memory map and move all the other pointers down by one first.  Check to make sure
    // the last pointer isn't used, otherwise we can't add this pointer.
    OptimizeMemoryMap();

    if (memmap[NUM_MEMMAPS - 1].ptr != NULL)
    {
        MessageBox(MainhWnd, "Uhoh!  Ran out of room to store a pointer in the memory map!", NULL, MB_OK);
        return;
    }

    for (t = (NUM_MEMMAPS - 1); t >= 1; t--)
    {
        memmap[t].ptr        = memmap[t - 1].ptr;
        memmap[t].start_addr = memmap[t - 1].start_addr;
        memmap[t].end_addr   = memmap[t - 1].end_addr;
        memmap[t].read_func  = memmap[t - 1].read_func;
        memmap[t].write_func = memmap[t - 1].write_func;
    }

    // First one is available, just add it.
    memmap[0].ptr        = ptr;
    memmap[0].start_addr = start;
    memmap[0].end_addr   = end;
    memmap[0].read_func  = rd;
    memmap[0].write_func = wr;
}

void OptimizeMemoryMap(void)
{
    int curr, next, todo, currwalk;

    for (curr = 0; curr < NUM_MEMMAPS; curr++)
    {
        if (memmap[curr].ptr == NULL)
        {
            // Set marker to see if there's anything after this pointer to optimize...
            todo = -1;

            for (next = (curr + 1); next < NUM_MEMMAPS; next++)
            {
                if (memmap[next].ptr != NULL)
                {
                    todo = next;
                    next = NUM_MEMMAPS;
                }
            }

            if (todo == -1) return;             // All done!

            currwalk = curr;
            for (next = todo; next < NUM_MEMMAPS; next++)
            {
                memmap[currwalk].ptr        = memmap[next].ptr;
                memmap[currwalk].start_addr = memmap[next].start_addr;
                memmap[currwalk].end_addr   = memmap[next].end_addr;
                memmap[currwalk].read_func  = memmap[next].read_func;
                memmap[currwalk].write_func = memmap[next].write_func;
                currwalk++;
            }
        }
    }
}

LPVOID FindMemoryPtr(ulong addr)
{
    addr &= 0x1FFFFFFF;
	return (LPVOID)((char *)N64Memory + addr);
}

void MemoryCopy(ulong dest_addr, ulong src_addr, ulong len)
{
	LPVOID sptr, dptr;

	sptr = FindMemoryPtr(src_addr);
	dptr = FindMemoryPtr(dest_addr);
	if (sptr == NULL || dptr == NULL) return;

	in_MemCopy = TRUE;
	MemCopy_dest = dest_addr;
	memcpy(dptr, sptr, len);
	in_MemCopy = FALSE;
}

void MemorySet(ulong dest_addr, uchar val, ulong len)
{
	LPVOID dptr;

	dptr = FindMemoryPtr(dest_addr);
	if (dptr == NULL) return;

	in_MemSet = TRUE;
	MemSet_dest = dest_addr;
	memset(dptr, val, len);
	in_MemSet = FALSE;
}

void _cdecl MemCopy(void)
{
	MemoryCopy(DMA_dest_addr, DMA_src_addr, DMA_len);
}

void _cdecl MemReadFail(void)
{
	char buf[256];

    running = FALSE;

	sprintf(buf, "Attempt to read from unmapped memory!\n\nAddress = %08X\nPC = %08X", MEM_addr, r4reg.PC);
    MessageBox(NULL, buf, NULL, MB_OK);
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

void _cdecl MemWriteFail(void)
{
	char buf[256];

    running = FALSE;

	sprintf(buf, "Attempt to write to unmapped memory!\n\nAddress = %08X\nValue = %08X\nPC = %08X", MEM_addr, MEM_val, r4reg.PC);
    MessageBox(NULL, buf, NULL, MB_OK);
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

#define OS_TV_PAL               0
#define OS_TV_NTSC              1
#define OS_TV_MPAL              2

void InitEmulation(void)
{
    int t;
	ulong osTvType;

	// A (kinda) cheap hack to fix possible blits from the CFB updater.
	CFB_BitDepth = 8;

	// Set TV type to NTSC.
	osTvType = OS_TV_NTSC;

	// Terminate existing emulation thread.
	TerminateEmulationThread();

    // Initialize memory map.
    InitMemoryMap();

    // Initialize R4300i.
    for (t = 0; t < 32; t++)
    {
        r4reg.GPR[t] = 0x00000000;
        r4reg.FGR[t] = (float)0.0;
    }

	for (t = 0; t < 64; t++)
		r4reg.GPR64[t] = 0x00000000;

    // Setup stack, PC, and other regs...
	r4reg.GPR[19]	  = 0x00000000;							// osRomType: Cartridge
    r4reg.GPR[20]     = osTvType;                           // osTvType: NTSC mode
	r4reg.GPR[21]     = 0x00000000;							// osResetType: Cold
    r4reg.GPR[22]     = 0x0000003F;                         // Required for CRC check to succeed
    r4reg.GPR[23]     = 0x00000000;							// osVersion: TBD (?)
    r4reg.GPR[29]     = 0x00400000;							// Stack pointer

	r4reg.PC          = 0xA4000040;
    r4reg.MultHI      = 0x00000000;
    r4reg.MultLO      = 0x00000000;
    r4reg.LLBit       = FALSE;
    r4reg.MultHI64[0] = 0x00000000;
    r4reg.MultHI64[1] = 0x00000000;
    r4reg.MultLO64[0] = 0x00000000;
    r4reg.MultLO64[1] = 0x00000000;

    // Initialize COP0.
    c0reg.Index    = 0x00000000;
    c0reg.Random   = (TLBENTRIES - 1) & 0x0000003F;
    c0reg.PageMask = 0x00000000;
    c0reg.Wired    = 0x00000000;
    c0reg.Count    = 0x00000000;
    c0reg.Compare  = 0x00000000;
    c0reg.Status   = C0STATUS_IE | C0STATUS_ERL | C0STATUS_BEV | C0STATUS_COP0 | C0STATUS_COP1;
    c0reg.Cause    = 0x00000000;
    c0reg.PRevID   = 0x00000B00;
    c0reg.Config   = 0x00066463;
    c0reg.LLAddr   = 0x00000000;
    c0reg.WatchLo  = 0x00000000;
    c0reg.WatchHi  = 0x00000000;
    c0reg.PErr     = 0x00000000;
    c0reg.CacheErr = 0x00000000;
    c0reg.TagLo    = 0x00000000;
    c0reg.TagHi    = 0x00000000;
    c0reg.EntryHi  = 0x00000000;
    c0reg.EntryLo0 = 0x00000000;
    c0reg.EntryLo1 = 0x00000000;
    c0reg.Context  = 0x00000000;
    c0reg.ErrorEPC = 0x00000000;
    c0reg.ExceptionPC = 0x00000000;

    c0reg.BadVAddr[0] = 0x00000000;
    c0reg.BadVAddr[1] = 0x00000000;
    c0reg.XContext[0] = 0x00000000;
    c0reg.XContext[1] = 0x00000000;

    // Initialize COP1.
    c1reg.Impl_Rev  = 0x00000511;
    c1reg.Unused_1  = 0x00000000;
    c1reg.Unused_2  = 0x00000000;
    c1reg.Unused_3  = 0x00000000;
    c1reg.Unused_4  = 0x00000000;
    c1reg.Unused_5  = 0x00000000;
    c1reg.Unused_6  = 0x00000000;
    c1reg.Unused_7  = 0x00000000;
    c1reg.Unused_8  = 0x00000000;
    c1reg.Unused_9  = 0x00000000;
    c1reg.Unused_10 = 0x00000000;
    c1reg.Unused_11 = 0x00000000;
    c1reg.Unused_12 = 0x00000000;
    c1reg.Unused_13 = 0x00000000;
    c1reg.Unused_14 = 0x00000000;
    c1reg.Unused_15 = 0x00000000;
    c1reg.Unused_16 = 0x00000000;
    c1reg.Unused_17 = 0x00000000;
    c1reg.Unused_18 = 0x00000000;
    c1reg.Unused_19 = 0x00000000;
    c1reg.Unused_20 = 0x00000000;
    c1reg.Unused_21 = 0x00000000;
    c1reg.Unused_22 = 0x00000000;
    c1reg.Unused_23 = 0x00000000;
    c1reg.Unused_24 = 0x00000000;
    c1reg.Unused_25 = 0x00000000;
    c1reg.Unused_26 = 0x00000000;
    c1reg.Unused_27 = 0x00000000;
    c1reg.Unused_28 = 0x00000000;
    c1reg.Unused_29 = 0x00000000;
    c1reg.Unused_30 = 0x00000000;
    c1reg.FCSR	    = 0x00000000;

    // Initialize Peripherial Interface...
    pireg.dram_addr = 0x00000000;
    pireg.cart_addr = 0x00000000;
    pireg.rd_len    = 0x00000000;
    pireg.wr_len    = 0x00000000;
    pireg.status    = 0x00000000;

    pireg.dom1_lat         = 0x00000000;
    pireg.dom1_pulse_width = 0x00000000;
    pireg.dom1_page_size   = 0x00000000;
    pireg.dom1_release     = 0x00000000;
    pireg.dom2_lat         = 0x00000000;
    pireg.dom2_pulse_width = 0x00000000;
    pireg.dom2_page_size   = 0x00000000;
    pireg.dom2_release     = 0x00000000;

	// Initialize DMA...
	DMA_running       = FALSE;
	DMA_src_addr      = 0;
	DMA_dest_addr     = 0;
	DMA_bytes_to_copy = 0;
	DMA_len           = 0;

    // Initialize MIPS Interface...
    mireg.mode      = 0x00000000;
    mireg.version   = 0x01010101;           // Older N64s use this -- newer ones have 0x02020202
    mireg.intr      = 0x00000000;
    mireg.intr_mask = 0x00000000;

	// Initialize VI Interface...
	vireg.stat_ctrl  = 0x00000000;
    vireg.origin     = 0x00000000;
    vireg.width      = 0x00000000;
    vireg.intr       = 0x00000000;
    vireg.curr_vline = 0x00000000;
    vireg.vid_timing = 0x00000000;
    vireg.vsync      = 0x00000000;
    vireg.hsync      = 0x00000000;
    vireg.hsync_leap = 0x00000000;
    vireg.horiz_vid  = 0x00000000;
    vireg.vert_vid   = 0x00000000;
    vireg.vert_burst = 0x00000000;
    vireg.x_scale    = 0x00000000;
    vireg.y_scale    = 0x00000000;

	// Initialize SI Interface...
	sireg.dram_addr      = 0x00000000;
	sireg.pif_addr_rd64b = 0x00000000;
	sireg.pif_addr_wr64b = 0x00000000;
	sireg.status         = 0x00000000;

	// Initialize AI Interface...
	aireg.dram_addr = 0x00000000;
	aireg.len       = 0x00000000;
	aireg.control   = 0x00000000;
	aireg.status    = 0x00000000;
	aireg.dacrate   = 0x00000000;
	aireg.bitrate   = 0x00000000;

    // Initialize LEO (DD64)...
    leoreg.not_exist = 1;                      // LEO Exist = 0, Not exist = !0

	// Initialize RSP...
	RSP->Cold_Reset();

	// And initialize RDP...
	InitRDP();

    // Free any memory, if applicable...
    if (RDRAM           != NULL) { VirtualFree(RDRAM,           RDRAM_TOTAL_LENGTH,   MEM_DECOMMIT); RDRAM           = NULL; }
	if (SP_DATA_MEM     != NULL) { VirtualFree(SP_DATA_MEM,     SP_DMEM_SIZE,         MEM_DECOMMIT); SP_DATA_MEM     = NULL; }
	if (SP_INST_MEM     != NULL) { VirtualFree(SP_INST_MEM,     SP_IMEM_SIZE,         MEM_DECOMMIT); SP_INST_MEM     = NULL; }
    if (RDRAM_REGS      != NULL) { VirtualFree(RDRAM_REGS,      RDRAM_BASE_SIZE,      MEM_DECOMMIT); RDRAM_REGS      = NULL; }
	if (RDRAM_TEMP_MEM  != NULL) { VirtualFree(RDRAM_TEMP_MEM,  RDRAM_TEMP_BASE_SIZE, MEM_DECOMMIT); RDRAM_TEMP_MEM  = NULL; }
    if (SP_REGS         != NULL) { VirtualFree(SP_REGS,	        SP_BASE_SIZE,         MEM_DECOMMIT); SP_REGS         = NULL; }
    if (SP_PC           != NULL) { VirtualFree(SP_PC,           SP_PC_SIZE,           MEM_DECOMMIT); SP_PC           = NULL; }
    if (SP_IMEM_BIST    != NULL) { VirtualFree(SP_IMEM_BIST,    SP_IBIST_SIZE,        MEM_DECOMMIT); SP_IMEM_BIST    = NULL; }
    if (DP_CTRL_REGS    != NULL) { VirtualFree(DP_CTRL_REGS,    DPC_BASE_SIZE,        MEM_DECOMMIT); DP_CTRL_REGS    = NULL; }
    if (DP_SPAN_REGS    != NULL) { VirtualFree(DP_SPAN_REGS,    DPS_BASE_SIZE,        MEM_DECOMMIT); DP_SPAN_REGS    = NULL; }
    if (MIPS_REGS       != NULL) { VirtualFree(MIPS_REGS,       MI_BASE_SIZE,         MEM_DECOMMIT); MIPS_REGS       = NULL; }
	if (VIDEO_REGS      != NULL) { VirtualFree(VIDEO_REGS,      VI_BASE_SIZE,         MEM_DECOMMIT); VIDEO_REGS      = NULL; }
    if (AUDIO_REGS      != NULL) { VirtualFree(AUDIO_REGS,      AI_BASE_SIZE,         MEM_DECOMMIT); AUDIO_REGS      = NULL; }
    if (PERIF_REGS      != NULL) { VirtualFree(PERIF_REGS,      PI_BASE_SIZE,         MEM_DECOMMIT); PERIF_REGS      = NULL; }
    if (RDRAM_INTF_REGS != NULL) { VirtualFree(RDRAM_INTF_REGS, RI_BASE_SIZE,         MEM_DECOMMIT); RDRAM_INTF_REGS = NULL; }
    if (SERIAL_REGS     != NULL) { VirtualFree(SERIAL_REGS,     SI_BASE_SIZE,         MEM_DECOMMIT); SERIAL_REGS     = NULL; }
    if (PIF_ROM         != NULL) { VirtualFree(PIF_ROM,         PIF_ROM_BASE_SIZE,    MEM_DECOMMIT); PIF_ROM         = NULL; }
    if (PIF_RAM         != NULL) { VirtualFree(PIF_RAM,         PIF_RAM_BASE_SIZE,    MEM_DECOMMIT); PIF_RAM         = NULL; }
    if (LEO             != NULL) { VirtualFree(LEO,             LEO_BASE_SIZE,        MEM_DECOMMIT); LEO             = NULL; }

	// Now allocate RDRAM, other assorted memory spaces, and HW regs.
	RDRAM           = VirtualAlloc((char *)N64Memory + RDRAM_BASE_ADDR,     RDRAM_TOTAL_LENGTH,   MEM_COMMIT, PAGE_READWRITE);
	SP_DATA_MEM     = VirtualAlloc((char *)N64Memory + SP_DMEM_BASE,        SP_DMEM_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	SP_INST_MEM     = VirtualAlloc((char *)N64Memory + SP_IMEM_BASE,        SP_IMEM_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	RDRAM_REGS      = VirtualAlloc((char *)N64Memory + RDRAM_BASE_REG,      RDRAM_BASE_SIZE,      MEM_COMMIT, PAGE_READWRITE);
	RDRAM_TEMP_MEM  = VirtualAlloc((char *)N64Memory + RDRAM_TEMP_BASE_REG, RDRAM_TEMP_BASE_SIZE, MEM_COMMIT, PAGE_READWRITE);
	SP_REGS         = VirtualAlloc((char *)N64Memory + SP_BASE_REG,         SP_BASE_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	SP_PC           = VirtualAlloc((char *)N64Memory + SP_PC_REG,           SP_PC_SIZE,           MEM_COMMIT, PAGE_READWRITE);
	SP_IMEM_BIST    = VirtualAlloc((char *)N64Memory + SP_IBIST_REG,        SP_IBIST_SIZE,        MEM_COMMIT, PAGE_READWRITE);
	DP_CTRL_REGS    = VirtualAlloc((char *)N64Memory + DPC_BASE_REG,        DPC_BASE_SIZE,        MEM_COMMIT, PAGE_READWRITE);
	DP_SPAN_REGS    = VirtualAlloc((char *)N64Memory + DPS_BASE_REG,        DPS_BASE_SIZE,        MEM_COMMIT, PAGE_READWRITE);
	MIPS_REGS       = VirtualAlloc((char *)N64Memory + MI_BASE_REG,         MI_BASE_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	VIDEO_REGS      = VirtualAlloc((char *)N64Memory + VI_BASE_REG,         VI_BASE_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	AUDIO_REGS      = VirtualAlloc((char *)N64Memory + AI_BASE_REG,         AI_BASE_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	PERIF_REGS      = VirtualAlloc((char *)N64Memory + PI_BASE_REG,         PI_BASE_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	RDRAM_INTF_REGS = VirtualAlloc((char *)N64Memory + RI_BASE_REG,         RI_BASE_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	SERIAL_REGS     = VirtualAlloc((char *)N64Memory + SI_BASE_REG,         SI_BASE_SIZE,         MEM_COMMIT, PAGE_READWRITE);
	PIF_ROM         = VirtualAlloc((char *)N64Memory + PIF_ROM_BASE_REG,    PIF_ROM_BASE_SIZE,    MEM_COMMIT, PAGE_READWRITE);
	PIF_RAM         = VirtualAlloc((char *)N64Memory + PIF_RAM_BASE_REG,    PIF_RAM_BASE_SIZE,    MEM_COMMIT, PAGE_READWRITE);
	LEO             = VirtualAlloc((char *)N64Memory + LEO_BASE_REG,        LEO_BASE_SIZE,        MEM_COMMIT, PAGE_READWRITE);

    // Add all memory mapped registers to the memory list...
    AddMemoryPtr(RDRAM_REGS,      RDRAM_BASE_REG,   RDRAM_BASE_END,   RDRAM_Reg_Read,   RDRAM_Reg_Write,   TRUE);
    AddMemoryPtr(SP_REGS,		  SP_BASE_REG,      SP_BASE_END,      SP_Reg_Read,      SP_Reg_Write,      TRUE);
    AddMemoryPtr(SP_PC,           SP_PC_REG,        SP_PC_END,        SP_PC_Read,       SP_PC_Write,       TRUE);
    AddMemoryPtr(SP_IMEM_BIST,    SP_IBIST_REG,     SP_IBIST_END,     SP_IBIST_Read,    SP_IBIST_Write,    TRUE);
    AddMemoryPtr(DP_CTRL_REGS,    DPC_BASE_REG,     DPC_BASE_END,     DPC_Reg_Read,     DPC_Reg_Write,     TRUE);
    AddMemoryPtr(DP_SPAN_REGS,    DPS_BASE_REG,     DPS_BASE_END,     DPS_Reg_Read,     DPS_Reg_Write,     TRUE);
    AddMemoryPtr(MIPS_REGS,       MI_BASE_REG,      MI_BASE_END,      MI_Reg_Read,      MI_Reg_Write,      TRUE);
    AddMemoryPtr(VIDEO_REGS,      VI_BASE_REG,      VI_BASE_END,      VI_Reg_Read,      VI_Reg_Write,      TRUE);
    AddMemoryPtr(AUDIO_REGS,      AI_BASE_REG,      AI_BASE_END,      AI_Reg_Read,      AI_Reg_Write,      TRUE);
    AddMemoryPtr(PERIF_REGS,      PI_BASE_REG,      PI_BASE_END,      PI_Reg_Read,      PI_Reg_Write,      TRUE);
    AddMemoryPtr(RDRAM_INTF_REGS, RI_BASE_REG,      RI_BASE_END,      RI_Reg_Read,      RI_Reg_Write,      TRUE);
    AddMemoryPtr(SERIAL_REGS,     SI_BASE_REG,      SI_BASE_END,      SI_Reg_Read,      SI_Reg_Write,      TRUE);
    AddMemoryPtr(PIF_ROM,         PIF_ROM_BASE_REG, PIF_ROM_BASE_END, PIF_ROM_Reg_Read, PIF_ROM_Reg_Write, TRUE);
    AddMemoryPtr(PIF_RAM,         PIF_RAM_BASE_REG, PIF_RAM_BASE_END, PIF_RAM_Reg_Read, PIF_RAM_Reg_Write, TRUE);
    AddMemoryPtr(LEO,			  LEO_BASE_REG,     LEO_BASE_END,     LEO_Reg_Read,     LEO_Reg_Write,     TRUE);

	// Add a chunk of memory we're not sure about.  :)
	AddMemoryPtr(RDRAM_TEMP_MEM,  RDRAM_TEMP_BASE_REG, RDRAM_TEMP_BASE_END, RDRAM_Temp_Read, RDRAM_Temp_Write, TRUE);

	// Hmm... add the RSP DMEM and IMEM to the memory map, only because they're in the middle of MMIO stuff.
	AddMemoryPtr(SP_DATA_MEM, SP_DMEM_BASE, (SP_DMEM_BASE + SP_DMEM_SIZE) - 1, MMIO_DoNothing_Read, MMIO_DoNothing_Write, TRUE);
	AddMemoryPtr(SP_INST_MEM, SP_IMEM_BASE, (SP_IMEM_BASE + SP_IMEM_SIZE) - 1, MMIO_DoNothing_Read, MMIO_DoNothing_Write, TRUE);

    // Now, emulate the PIF-ROM and copy the first 0x1000 bytes to the RSP's data memory.
    MemoryCopy(0xA4000000, 0xB0000000, 0x1000);

    // Setup some one-time used variables...
    Write32(0x80000300, osTvType);                    // Set osTvType to NTSC

	// Zap important stuff.
	R4PC_newPC = 0x00000000;
	R4PC_count = 0;
	CSTACK_count = 0;

	// Fire up emulation thread!
	StartEmulationThread();
	ResumeThread(hEmuThread);
}

// *******************************************************************************************************
// *******************************************************************************************************
//
//                                              OPCODES
//
// *******************************************************************************************************
// *******************************************************************************************************

void _cdecl UnknownOpcode(void)
{
    char buf[64];

    // Check to see if this is just a breakpoint opcode.
    if (HasBreakpoint(r4reg.PC))
    {
        sprintf(buf, "Encountered breakpoint at %08X", r4reg.PC);
        MessageBox(NULL, buf, NULL, MB_OK);
    }
    else
    {
        if (!stop_on_unhandled) return;
	    raw_opcode_addr = r4reg.PC;
	    MakeDebugOpcode();

        sprintf(buf, "Unknown Opcode (%08X): %02X %02X %02X %02X", r4reg.PC, raw_opcode[0], raw_opcode[1], raw_opcode[2],
            raw_opcode[3]);
        MessageBox(NULL, buf, NULL, MB_OK);
    }

    running = FALSE;
	RSP->RSP_Stop();
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

void _cdecl COP_Unusable(void)
{
    char buf[64];

	raw_opcode_addr = r4reg.PC;
	MakeDebugOpcode();

    sprintf(buf, "Coprocessor Unusable Exception (%08X): %02X %02X %02X %02X", r4reg.PC, raw_opcode[0], raw_opcode[1],
		raw_opcode[2], raw_opcode[3]);
    MessageBox(NULL, buf, NULL, MB_OK);

    running = FALSE;
	RSP->RSP_Stop();
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

void _cdecl COP0_Unknown(void)
{
    char buf[64];

    if (!stop_on_unhandled) return;
	raw_opcode_addr = r4reg.PC;
	MakeDebugOpcode();

    sprintf(buf, "Unknown COP0 Opcode (%08X): %02X %02X %02X %02X", r4reg.PC, raw_opcode[0], raw_opcode[1], raw_opcode[2],
        raw_opcode[3]);
    MessageBox(NULL, buf, NULL, MB_OK);

    running = FALSE;
	RSP->RSP_Stop();
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

void _cdecl COP1_Unknown(void)
{
    char buf[64];

    if (!stop_on_unhandled) return;
	raw_opcode_addr = r4reg.PC;
	MakeDebugOpcode();

    sprintf(buf, "Unknown COP1 Opcode (%08X): %02X %02X %02X %02X", r4reg.PC, raw_opcode[0], raw_opcode[1], raw_opcode[2],
        raw_opcode[3]);
    MessageBox(NULL, buf, NULL, MB_OK);

    running = FALSE;
	RSP->RSP_Stop();
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

void _cdecl COP2_Unknown(void)
{
    char buf[64];

    if (!stop_on_unhandled) return;
	raw_opcode_addr = r4reg.PC;
	MakeDebugOpcode();

    sprintf(buf, "Unknown COP2 Opcode (%08X): %02X %02X %02X %02X", r4reg.PC, raw_opcode[0], raw_opcode[1], raw_opcode[2],
        raw_opcode[3]);
    MessageBox(NULL, buf, NULL, MB_OK);

    running = FALSE;
	RSP->RSP_Stop();
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

void _cdecl Unhandled_Opcode(void)
{
    char buf[128], tmp[64];

    if (!stop_on_unhandled) return;
	raw_opcode_addr = r4reg.PC;
	MakeDebugOpcode();

    OpcodePrintTable[DECODE_OPCODE](tmp);

    sprintf(buf, "Unhandled Opcode (%08X): %s (%02X %02X %02X %02X)", r4reg.PC, tmp, raw_opcode[0], raw_opcode[1], raw_opcode[2],
        raw_opcode[3]);
    MessageBox(NULL, buf, NULL, MB_OK);

    running = FALSE;
	RSP->RSP_Stop();
    if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
}

// *******************************************************************************************************
// *******************************************************************************************************
//
//                                           OPCODE PRINTING
//
// *******************************************************************************************************
// *******************************************************************************************************

void P_SpecialOpcode(char *buf)
{
    SpecialPrintTable[DECODE_FUNCTION](buf);
}

void P_REGIMMOpcode(char *buf)
{
    REGIMMPrintTable[DECODE_RT](buf);
}

void P_UnknownOpcode(char *buf)
{
    strcpy(buf, "???");
}

void P_Reserved(char *buf)
{
    strcpy(buf, "-RESERVED-");
}

void P_COP0(char *buf)
{
    COP0PrintTable[DECODE_RS](buf);
}

void P_COP0_Func(char *buf)
{
    COP0PrintFunc[DECODE_C0FUNC](buf);
}

void P_COP1(char *buf)
{
    COP1PrintTable[DECODE_C1FMT](buf);
}

void P_COP2(char *buf)
{
//    COP2PrintTable[DECODE_C2FMT](buf);
    P_COP2_Unknown(buf);
}

void P_COP0_Unknown(char *buf)
{
    strcpy(buf, "COP0 ???");
}

void P_COP1_Unknown(char *buf)
{
    strcpy(buf, "COP1 ???");
}

void P_COP2_Unknown(char *buf)
{
    strcpy(buf, "COP2 ???");
}

// -----------------------------------------------------------------------------------------------------

char *jump_decode(void)
{
    static char buf[256];
    ulong addr;
    CODMAPNode *cm;

    addr = DECODE_JUMP;
    cm = FindCODMAPNode(addr);

    if (cm != NULL)
        sprintf(buf, "%s", cm->symbol);
    else
        sprintf(buf, "0x%08X", addr);

    return buf;
}

char *offset_decode(void)
{
    static char buf[256];
    ulong addr;
    CODMAPNode *cm;

    addr = DECODE_OFFSET;
    cm = FindCODMAPNode(addr);

    if (cm != NULL)
        sprintf(buf, "%s", cm->symbol);
    else
        sprintf(buf, "0x%08X", addr);

    return buf;
}

void P_J(char *buf)      { sprintf(buf, "J\t%s",                  jump_decode()); }
void P_JAL(char *buf)    { sprintf(buf, "JAL\t%s",                jump_decode()); }
void P_BEQ(char *buf)    { sprintf(buf, "BEQ\t%s, %s, %s",        GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT], offset_decode()); }
void P_BNE(char *buf)    { sprintf(buf, "BNE\t%s, %s, %s",        GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT], offset_decode()); }
void P_BLEZ(char *buf)   { sprintf(buf, "BLEZ\t%s, %s",           GPR_LUT[DECODE_RS], offset_decode()); }
void P_BGTZ(char *buf)   { sprintf(buf, "BGTZ\t%s, %s",           GPR_LUT[DECODE_RS], offset_decode()); }
void P_ADDI(char *buf)   { sprintf(buf, "ADDI\t%s, %s, 0x%04X",   GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_ADDIU(char *buf)  { sprintf(buf, "ADDIU\t%s, %s, 0x%04X",  GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_SLTI(char *buf)   { sprintf(buf, "SLTI\t%s, %s, 0x%04X",   GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_SLTIU(char *buf)  { sprintf(buf, "SLTIU\t%s, %s, 0x%04X",  GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_ANDI(char *buf)   { sprintf(buf, "ANDI\t%s, %s, 0x%04X",   GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_ORI(char *buf)    { sprintf(buf, "ORI\t%s, %s, 0x%04X",    GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_XORI(char *buf)   { sprintf(buf, "XORI\t%s, %s, 0x%04X",   GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_LUI(char *buf)    { sprintf(buf, "LUI\t%s, 0x%04X",        GPR_LUT[DECODE_RT], DECODE_IMMED); }
void P_BEQL(char *buf)   { sprintf(buf, "BEQL\t%s, %s, %s",       GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], offset_decode()); }
void P_BNEL(char *buf)   { sprintf(buf, "BNEL\t%s, %s, %s",       GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], offset_decode()); }
void P_BLEZL(char *buf)  { sprintf(buf, "BLEZL\t%s, %s",          GPR_LUT[DECODE_RS], offset_decode()); }
void P_BGTZL(char *buf)  { sprintf(buf, "BGTZL\t%s, %s",          GPR_LUT[DECODE_RS], offset_decode()); }
void P_DADDI(char *buf)  { sprintf(buf, "DADDI\t%s, %s, 0x%04X",  GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_DADDIU(char *buf) { sprintf(buf, "DADDIU\t%s, %s, 0x%04X", GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS], DECODE_IMMED); }
void P_LDL(char *buf)    { sprintf(buf, "LDL\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LDR(char *buf)    { sprintf(buf, "LDR\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LB(char *buf)     { sprintf(buf, "LB\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LH(char *buf)     { sprintf(buf, "LH\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LWL(char *buf)    { sprintf(buf, "LWL\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LW(char *buf)     { sprintf(buf, "LW\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LBU(char *buf)    { sprintf(buf, "LBU\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LHU(char *buf)    { sprintf(buf, "LHU\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LWR(char *buf)    { sprintf(buf, "LWR\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LWU(char *buf)    { sprintf(buf, "LWU\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SB(char *buf)     { sprintf(buf, "SB\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SH(char *buf)     { sprintf(buf, "SH\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SWL(char *buf)    { sprintf(buf, "SWL\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SW(char *buf)     { sprintf(buf, "SW\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SDL(char *buf)    { sprintf(buf, "SDL\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SDR(char *buf)    { sprintf(buf, "SDR\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SWR(char *buf)    { sprintf(buf, "SWR\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_CACHE(char *buf)  { sprintf(buf, "CACHE\t%s, 0x%04X(%s)",  GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LL(char *buf)     { sprintf(buf, "LL\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LWC1(char *buf)   { sprintf(buf, "LWC1\t%s, 0x%04X(%s)",   FGR_LUT[DECODE_FT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LWC2(char *buf)   { sprintf(buf, "LWC2\t%s, 0x%04X(%s)",   GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_PREF(char *buf)   { sprintf(buf, "PREF\t%s, 0x%04X(%s)",   GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LLD(char *buf)    { sprintf(buf, "LLD\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LDC1(char *buf)   { sprintf(buf, "LDC1\t%s, 0x%04X(%s)",   FGR_LUT[DECODE_FT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LDC2(char *buf)   { sprintf(buf, "LDC2\t%s, 0x%04X(%s)",   GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_LD(char *buf)     { sprintf(buf, "LD\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SC(char *buf)     { sprintf(buf, "SC\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SWC1(char *buf)   { sprintf(buf, "SWC1\t%s, 0x%04X(%s)",   FGR_LUT[DECODE_FT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SWC2(char *buf)   { sprintf(buf, "SWC2\t%s, 0x%04X(%s)",   GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SCD(char *buf)    { sprintf(buf, "SCD\t%s, 0x%04X(%s)",    GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SDC1(char *buf)   { sprintf(buf, "SDC1\t%s, 0x%04X(%s)",   FGR_LUT[DECODE_FT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SDC2(char *buf)   { sprintf(buf, "SDC2\t%s, 0x%04X(%s)",   GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }
void P_SD(char *buf)     { sprintf(buf, "SD\t%s, 0x%04X(%s)",     GPR_LUT[DECODE_RT], DECODE_IMMED, GPR_LUT[DECODE_BASE]); }

// -------------------------------------------------------------------
//
//                       Special Opcodes
//
// -------------------------------------------------------------------

void P_SLL(char *buf)
{
    if (raw_opcode_ulong == 0x00000000)
        strcpy(buf, "NOP");
    else
        sprintf(buf, "SLL\t%s, %s, 0x%02X", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA);
}

void P_SRL(char *buf)    { sprintf(buf, "SRL\t%s, %s, 0x%02X", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }
void P_SRA(char *buf)    { sprintf(buf, "SRA\t%s, %s, 0x%02X", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }
void P_SLLV(char *buf)   { sprintf(buf, "SLLV\t%s, %s, %s",    GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS]); }
void P_SRLV(char *buf)   { sprintf(buf, "SRLV\t%s, %s, %s",    GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS]); }
void P_SRAV(char *buf)   { sprintf(buf, "SRAV\t%s, %s, %s",    GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS]); }
void P_JR(char *buf)     { sprintf(buf, "JR\t%s",              GPR_LUT[DECODE_RS]); }

void P_JALR(char *buf)
{
    int rd = DECODE_RD;

    if (rd == 31)
        sprintf(buf, "JALR\t%s", GPR_LUT[DECODE_RS]);
    else
        sprintf(buf, "JALR\t%s, %s", GPR_LUT[rd], GPR_LUT[DECODE_RS]);
}

void P_SYSCALL(char *buf) { sprintf(buf, "SYSCALL\t0x%05X",   DECODE_SYSCALL); }
void P_BREAK(char *buf)   { sprintf(buf, "BREAK\t0x%05X",     DECODE_BREAK); }
void P_SYNC(char *buf)    { strcpy(buf,  "SYNC"); }
void P_MFHI(char *buf)    { sprintf(buf, "MFHI\t%s",          GPR_LUT[DECODE_RD]); }
void P_MTHI(char *buf)    { sprintf(buf, "MTHI\t%s",          GPR_LUT[DECODE_RS]); }
void P_MFLO(char *buf)    { sprintf(buf, "MFLO\t%s",          GPR_LUT[DECODE_RD]); }
void P_MTLO(char *buf)    { sprintf(buf, "MTLO\t%s",          GPR_LUT[DECODE_RS]); }
void P_DSLLV(char *buf)   { sprintf(buf, "DSLLV\t%s, %s, %s", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS]); }
void P_DSRLV(char *buf)   { sprintf(buf, "DSLLV\t%s, %s, %s", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS]); }
void P_DSRAV(char *buf)   { sprintf(buf, "DSRAV\t%s, %s, %s", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], GPR_LUT[DECODE_RS]); }
void P_MULT(char *buf)    { sprintf(buf, "MULT\t%s, %s",      GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_MULTU(char *buf)   { sprintf(buf, "MULTU\t%s, %s",     GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DIV(char *buf)     { sprintf(buf, "DIV\t%s, %s",       GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DIVU(char *buf)    { sprintf(buf, "DIVU\t%s, %s",      GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DMULT(char *buf)   { sprintf(buf, "DMULT\t%s, %s",     GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DMULTU(char *buf)  { sprintf(buf, "DMULTU\t%s, %s",    GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DDIV(char *buf)    { sprintf(buf, "DDIV\t%s, %s",      GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DDIVU(char *buf)   { sprintf(buf, "DDIVU\t%s, %s",     GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_ADD(char *buf)     { sprintf(buf, "ADD\t%s, %s, %s",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_ADDU(char *buf)    { sprintf(buf, "ADDU\t%s, %s, %s",  GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_SUB(char *buf)     { sprintf(buf, "SUB\t%s, %s, %s",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_SUBU(char *buf)    { sprintf(buf, "SUBU\t%s, %s, %s",  GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_AND(char *buf)     { sprintf(buf, "AND\t%s, %s, %s",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_OR(char *buf)      { sprintf(buf, "OR\t%s, %s, %s",    GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_XOR(char *buf)     { sprintf(buf, "XOR\t%s, %s, %s",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_NOR(char *buf)     { sprintf(buf, "NOR\t%s, %s, %s",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_SLT(char *buf)     { sprintf(buf, "SLT\t%s, %s, %s",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_SLTU(char *buf)    { sprintf(buf, "SLTU\t%s, %s, %s",  GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DADD(char *buf)    { sprintf(buf, "DADD\t%s, %s, %s",  GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DADDU(char *buf)   { sprintf(buf, "DADDU\t%s, %s, %s", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DSUB(char *buf)    { sprintf(buf, "DSUB\t%s, %s, %s",  GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DSUBU(char *buf)   { sprintf(buf, "DSUBU\t%s, %s, %s", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_TGE(char *buf)     { sprintf(buf, "TGE\t%s, %s",       GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_TGEU(char *buf)    { sprintf(buf, "TGEU\t%s, %s",      GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_TLT(char *buf)     { sprintf(buf, "TLT\t%s, %s",       GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_TLTU(char *buf)    { sprintf(buf, "TLTU\t%s, %s",      GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); } 
void P_TEQ(char *buf)     { sprintf(buf, "TEQ\t%s, %s",       GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_TNE(char *buf)     { sprintf(buf, "TNE\t%s, %s",       GPR_LUT[DECODE_RS], GPR_LUT[DECODE_RT]); }
void P_DSLL(char *buf)    { sprintf(buf, "DSLL\t%s, %s, 0x%02X",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }
void P_DSRL(char *buf)    { sprintf(buf, "DSRL\t%s, %s, 0x%02X",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }
void P_DSRA(char *buf)    { sprintf(buf, "DSRA\t%s, %s, 0x%02X",   GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }
void P_DSLL32(char *buf)  { sprintf(buf, "DSLL32\t%s, %s, 0x%02X", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }
void P_DSRL32(char *buf)  { sprintf(buf, "DSRL32\t%s, %s, 0x%02X", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }
void P_DSRA32(char *buf)  { sprintf(buf, "DSRA32\t%s, %s, 0x%02X", GPR_LUT[DECODE_RD], GPR_LUT[DECODE_RT], DECODE_SA); }

// -------------------------------------------------------------------
//
//                       REGIMM Opcodes
//
// -------------------------------------------------------------------

void P_BLTZ(char *buf)    { sprintf(buf, "BLTZ\t%s, %s",    GPR_LUT[DECODE_RS], offset_decode()); }
void P_BGEZ(char *buf)    { sprintf(buf, "BGEZ\t%s, %s",    GPR_LUT[DECODE_RS], offset_decode()); }
void P_BLTZL(char *buf)   { sprintf(buf, "BLTZL\t%s, %s",   GPR_LUT[DECODE_RS], offset_decode()); }
void P_BGEZL(char *buf)   { sprintf(buf, "BGEZL\t%s, %s",   GPR_LUT[DECODE_RS], offset_decode()); }
void P_TGEI(char *buf)    { sprintf(buf, "TGEI\t%s, %s",    GPR_LUT[DECODE_RS], offset_decode()); }
void P_TGEIU(char *buf)   { sprintf(buf, "TGEIU\t%s, %s",   GPR_LUT[DECODE_RS], offset_decode()); }
void P_TLTI(char *buf)    { sprintf(buf, "TLTI\t%s, %s",    GPR_LUT[DECODE_RS], offset_decode()); }
void P_TLTIU(char *buf)   { sprintf(buf, "TLTIU\t%s, %s",   GPR_LUT[DECODE_RS], offset_decode()); }
void P_TEQI(char *buf)    { sprintf(buf, "TEQI\t%s, %s",    GPR_LUT[DECODE_RS], offset_decode()); }
void P_TNEI(char *buf)    { sprintf(buf, "TNEI\t%s, %s",    GPR_LUT[DECODE_RS], offset_decode()); }
void P_BLTZAL(char *buf)  { sprintf(buf, "BLTZAL\t%s, %s",  GPR_LUT[DECODE_RS], offset_decode()); }
void P_BGEZAL(char *buf)  { sprintf(buf, "BGEZAL\t%s, %s",  GPR_LUT[DECODE_RS], offset_decode()); }
void P_BLTZALL(char *buf) { sprintf(buf, "BLTZALL\t%s, %s", GPR_LUT[DECODE_RS], offset_decode()); }
void P_BGEZALL(char *buf) { sprintf(buf, "BGEZALL\t%s, %s", GPR_LUT[DECODE_RS], offset_decode()); }

// -------------------------------------------------------------------
//
//                          COP0 Opcodes
//
// -------------------------------------------------------------------

void P_MFC0(char *buf)  { sprintf(buf, "MFC0\t%s, %s",  GPR_LUT[DECODE_RT], COP0_LUT[DECODE_FS]); }
void P_DMFC0(char *buf) { sprintf(buf, "DMFC0\t%s, %s", GPR_LUT[DECODE_RT], COP0_LUT[DECODE_FS]); }
void P_CFC0(char *buf)  { sprintf(buf, "CFC0\t%s, %s",  GPR_LUT[DECODE_RT], COP0_LUT[DECODE_FS]); }
void P_MTC0(char *buf)  { sprintf(buf, "MTC0\t%s, %s",  GPR_LUT[DECODE_RT], COP0_LUT[DECODE_FS]); }
void P_DMTC0(char *buf) { sprintf(buf, "DMTC0\t%s, %s", GPR_LUT[DECODE_RT], COP0_LUT[DECODE_FS]); }
void P_CTC0(char *buf)  { sprintf(buf, "CTC0\t%s, %s",  GPR_LUT[DECODE_RT], COP0_LUT[DECODE_FS]); }
void P_TLBR(char *buf)  { strcpy(buf,  "TLBR"); }
void P_TLBWI(char *buf) { strcpy(buf,  "TLBWI"); }
void P_TLBWR(char *buf) { strcpy(buf,  "TLBWR"); }
void P_TLBP(char *buf)  { strcpy(buf,  "TLBP"); }
void P_ERET(char *buf)  { strcpy(buf,  "ERET"); }

// -------------------------------------------------------------------
//
//                          COP1 Opcodes
//
// -------------------------------------------------------------------

void P_COP1_BC(char *buf)       { COP1BCPrintTable[DECODE_C1BC](buf); }
void P_COP1_S(char *buf)        { COP1SPrintTable[DECODE_C1S](buf); }
void P_COP1_D(char *buf)        { COP1DPrintTable[DECODE_C1D](buf); }
void P_COP1_W(char *buf)        { COP1WPrintTable[DECODE_C1W](buf); }
void P_COP1_L(char *buf)        { COP1LPrintTable[DECODE_C1L](buf); }
void P_MFC1(char *buf)          { sprintf(buf, "MFC1\t%s, %s",      GPR_LUT[DECODE_RT], FGR_LUT[DECODE_FS]);  }
void P_CFC1(char *buf)          { sprintf(buf, "CFC1\t%s, %s",      GPR_LUT[DECODE_RT], COP1_LUT[DECODE_FS]); }
void P_MTC1(char *buf)          { sprintf(buf, "MTC1\t%s, %s",      GPR_LUT[DECODE_RT], FGR_LUT[DECODE_FS]);  }
void P_CTC1(char *buf)          { sprintf(buf, "CTC1\t%s, %s",      GPR_LUT[DECODE_RT], COP1_LUT[DECODE_FS]); }
void P_BC1F(char *buf)          { sprintf(buf, "BC1F\t%s",          offset_decode()); }
void P_BC1T(char *buf)          { sprintf(buf, "BC1T\t%s",          offset_decode()); }
void P_BC1FL(char *buf)         { sprintf(buf, "BC1FL\t%s",         offset_decode()); }
void P_BC1TL(char *buf)         { sprintf(buf, "BC1TL\t%s",         offset_decode()); }

void P_C1S_ADD(char *buf)       { sprintf(buf, "ADD.S\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_SUB(char *buf)       { sprintf(buf, "SUB.S\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_MUL(char *buf)       { sprintf(buf, "MUL.S\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_DIV(char *buf)       { sprintf(buf, "DIV.S\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_SQRT(char *buf)      { sprintf(buf, "SQRT.S\t%s, %s",    FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_ABS(char *buf)       { sprintf(buf, "ABS.S\t%s, %s",     FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_MOV(char *buf)       { sprintf(buf, "MOV.S\t%s, %s",     FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_NEG(char *buf)       { sprintf(buf, "NEG.S\t%s, %s",     FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_ROUND_L(char *buf)   { sprintf(buf, "ROUND.L.S\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_TRUNC_L(char *buf)   { sprintf(buf, "TRUNC.L.S\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_CEIL_L(char *buf)    { sprintf(buf, "CEIL.L.S\t%s, %s",  FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_FLOOR_L(char *buf)   { sprintf(buf, "FLOOR.L.S\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_ROUND_W(char *buf)   { sprintf(buf, "ROUND.W.S\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_TRUNC_W(char *buf)   { sprintf(buf, "TRUNC.W.S\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_CEIL_W(char *buf)    { sprintf(buf, "CEIL.W.S\t%s, %s",  FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_FLOOR_W(char *buf)   { sprintf(buf, "FLOOR.W.S\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_CVT_D(char *buf)     { sprintf(buf, "CVT.D.S\t%s, %s",   FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_CVT_W(char *buf)     { sprintf(buf, "CVT.W.S\t%s, %s",   FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_CVT_L(char *buf)     { sprintf(buf, "CVT.L.S\t%s, %s",   FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1S_C_F(char *buf)       { sprintf(buf, "C.F.S\t%s, %s",     FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_UN(char *buf)      { sprintf(buf, "C.UN.S\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_EQ(char *buf)      { sprintf(buf, "C.EQ.S\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_UEQ(char *buf)     { sprintf(buf, "C.UEQ.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_OLT(char *buf)     { sprintf(buf, "C.OLT.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_ULT(char *buf)     { sprintf(buf, "C.ULT.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_OLE(char *buf)     { sprintf(buf, "C.OLE.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_ULE(char *buf)     { sprintf(buf, "C.ULE.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_SF(char *buf)      { sprintf(buf, "C.SF.S\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_NGLE(char *buf)    { sprintf(buf, "C.NGLE.S\t%s, %s",  FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_SEQ(char *buf)     { sprintf(buf, "C.SEQ.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_NGL(char *buf)     { sprintf(buf, "C.NGL.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_LT(char *buf)      { sprintf(buf, "C.LT.S\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_NGE(char *buf)     { sprintf(buf, "C.NGE.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_LE(char *buf)      { sprintf(buf, "C.LE.S\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1S_C_NGT(char *buf)     { sprintf(buf, "C.NGT.S\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }

void P_C1D_ADD(char *buf)       { sprintf(buf, "ADD.D\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_SUB(char *buf)       { sprintf(buf, "SUB.D\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_MUL(char *buf)       { sprintf(buf, "MUL.D\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_DIV(char *buf)       { sprintf(buf, "DIV.D\t%s, %s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_SQRT(char *buf)      { sprintf(buf, "SQRT.D\t%s, %s",    FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_ABS(char *buf)       { sprintf(buf, "ABS.D\t%s, %s",     FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_MOV(char *buf)       { sprintf(buf, "MOV.D\t%s, %s",     FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_NEG(char *buf)       { sprintf(buf, "NEG.D\t%s, %s",     FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_ROUND_L(char *buf)   { sprintf(buf, "ROUND.L.D\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_TRUNC_L(char *buf)   { sprintf(buf, "TRUNC.L.D\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_CEIL_L(char *buf)    { sprintf(buf, "CEIL.L.D\t%s, %s",  FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_FLOOR_L(char *buf)   { sprintf(buf, "FLOOR.L.D\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_ROUND_W(char *buf)   { sprintf(buf, "ROUND.W.D\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_TRUNC_W(char *buf)   { sprintf(buf, "TRUNC.W.D\t%s,%s",  FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_CEIL_W(char *buf)    { sprintf(buf, "CEIL.W.D\t%s, %s",  FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_FLOOR_W(char *buf)   { sprintf(buf, "FLOOR.W.D\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_CVT_S(char *buf)     { sprintf(buf, "CVT.S.D\t%s, %s",   FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_CVT_W(char *buf)     { sprintf(buf, "CVT.W.D\t%s, %s",   FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_CVT_L(char *buf)     { sprintf(buf, "CVT.L.D\t%s, %s",   FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1D_C_F(char *buf)       { sprintf(buf, "C.F.D\t%s, %s",     FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_UN(char *buf)      { sprintf(buf, "C.UN.D\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_EQ(char *buf)      { sprintf(buf, "C.EQ.D\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_UEQ(char *buf)     { sprintf(buf, "C.UEQ.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_OLT(char *buf)     { sprintf(buf, "C.OLT.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_ULT(char *buf)     { sprintf(buf, "C.ULT.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_OLE(char *buf)     { sprintf(buf, "C.OLE.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_ULE(char *buf)     { sprintf(buf, "C.ULE.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_SF(char *buf)      { sprintf(buf, "C.SF.D\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_NGLE(char *buf)    { sprintf(buf, "C.NGLE.D\t%s, %s",  FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_SEQ(char *buf)     { sprintf(buf, "C.SEQ.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_NGL(char *buf)     { sprintf(buf, "C.NGL.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_LT(char *buf)      { sprintf(buf, "C.LT.D\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_NGE(char *buf)     { sprintf(buf, "C.NGE.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_LE(char *buf)      { sprintf(buf, "C.LE.D\t%s, %s",    FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }
void P_C1D_C_NGT(char *buf)     { sprintf(buf, "C.NGT.D\t%s, %s",   FGR_LUT[DECODE_FS], FGR_LUT[DECODE_FT]); }

void P_C1W_CVT_S(char *buf)     { sprintf(buf, "CVT.S.W\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1W_CVT_D(char *buf)     { sprintf(buf, "CVT.D.W\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1L_CVT_S(char *buf)     { sprintf(buf, "CVT.S.L\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }
void P_C1L_CVT_D(char *buf)     { sprintf(buf, "CVT.D.L\t%s, %s", FGR_LUT[DECODE_FD], FGR_LUT[DECODE_FS]); }

// *******************************************************************************************************
// *******************************************************************************************************
//
//                                        EXCEPTION PROCESSING
//
// *******************************************************************************************************
// *******************************************************************************************************

void GenerateException(int ex_ty)
{
	ulong temp_cause, vector;

	Log(TYPE_WRITE, "+++ Exeception Generated", ex_ty);

	// To keep code clean, let's check first if this is an interrupt exception and we're allowed to generate interrupts.
	if (ex_ty == EXCEPTION_INTERRUPT)
	{
		// If IE is off, EXL is on, ERL is on, or no interrupts are pending, then do *not* generate the exception.
		if (!((c0reg.Status & C0STATUS_IE) && ((!(c0reg.Status & C0STATUS_EXL)) && (!(c0reg.Status & C0STATUS_ERL))) && 
		   ((c0reg.Status & C0STATUS_IMASK) & (c0reg.Cause & C0CAUSE_IPMASK))))
		{
			// Nope, no-can-do an interrupt right now.
			return;
		}
	}

	switch (ex_ty)
	{
		case EXCEPTION_INTERRUPT:
		case EXCEPTION_ARITHMETIC_OVERFLOW:
		case EXCEPTION_TRAP:
		case EXCEPTION_TLB_LOAD:
		case EXCEPTION_TLB_STORE:
			// Save Cause register for use later.
			temp_cause = c0reg.Cause;
			c0reg.Cause = 0x00000000;

			if ((c0reg.Status & C0STATUS_EXL) == 0)
			{
				c0reg.Cause |= (temp_cause & 0x0000FF00) | (ex_ty << 2);

				// Check to see if we're in a delay slot, then modify EPC appropriately.
				if (R4PC_count != 0)
				{
					R4PC_count = 0;
					c0reg.Cause |= C0CAUSE_BD;
					c0reg.ExceptionPC = r4reg.PC - 4;
				}
				else
				{
					c0reg.ExceptionPC = r4reg.PC;
				}

				switch (ex_ty)
				{
//					case TLBrefill:  vector = 0x000; break;
//					case XTLBrefill: vector = 0x080; break;
					default:		 vector = 0x180; break;
				}
			}
			else
			{
				c0reg.Cause |= (temp_cause & 0x80000000) | (temp_cause & 0x0000FF00) | (ex_ty << 2);
				vector = 0x180;
			}

			// Turn on EXL.
			c0reg.Status |= C0STATUS_EXL;

			if (c0reg.Status & C0STATUS_BEV)
				r4reg.PC = 0xBFC00200 + vector;
			else
				r4reg.PC = 0x80000000 + vector;
			break;
	}
}

void _cdecl RegenInterrupts(void)
{
	ulong old_pc;

	// Save the PC.
	old_pc = r4reg.PC;

	// Blindly call GenerateException... if an interrupt can't be done at this time, then it will return.  Otherwise,
	// all flags have already been set, so it's safe to go on.
	GenerateException(EXCEPTION_INTERRUPT);

	// Check if we did an interrupt.  We need to have the PC be valid because MTC0 will +4 it at the end of it's opcode.
	if (r4reg.PC != old_pc)
	{
		// Fake a branch-delay slot...
		R4PC_count = 1;
		R4PC_newPC = r4reg.PC;
		r4reg.PC = old_pc;
	}
}

// *******************************************************************************************************

void _cdecl CountCompareInterrupt(void)
{
	char buf[64];

	// Remove this event from the heap.
	EventHeapDeleteMin();

	Log(TYPE_WRITE, "*** Count/Compare Interrupt", 0);
	sprintf(buf, "*** Count   = %08X", c0reg.Count);   Log(TYPE_WRITE, buf, 0);
	sprintf(buf, "*** Compare = %08X", c0reg.Compare); Log(TYPE_WRITE, buf, 0);

	c0reg.Cause |= C0CAUSE_IP7;
	GenerateException(EXCEPTION_INTERRUPT);
}

void _cdecl VerticalRetraceInterrupt(void)
{
	// Remove current event.
	EventHeapDeleteMin();
    Last_Vert_Retrace_Count = c0reg.Count;   //BWG

	// Flush the CFB.
	CFB_Flush();

	mireg.intr |= MI_INTR_VI;
	if (mireg.intr_mask & MI_INTR_MASK_VI)
	{
		Log(TYPE_WRITE, "*** Vertical Retrace Interrupt", 0);
		c0reg.Cause |= C0CAUSE_IP2;
		GenerateException(EXCEPTION_INTERRUPT);
	}

	// Add next VRT event to the heap.
	EventHeapInsert(c0reg.Count + VERTICAL_RETRACE_TIME, VerticalRetraceInterrupt);
}

void _cdecl SerialInterfaceInterrupt(void)
{
	// Remove current event.
	EventHeapDeleteMin();

	mireg.intr |= MI_INTR_SI;
	if (mireg.intr_mask & MI_INTR_MASK_SI)
	{
		Log(TYPE_WRITE, "*** Serial Interface Interrupt", 0);
		c0reg.Cause |= C0CAUSE_IP2;
		GenerateException(EXCEPTION_INTERRUPT);
	}
}

void _cdecl PeripherialInterfaceInterrupt(void)
{
	// Remove current event.
	EventHeapDeleteMin();

	mireg.intr |= MI_INTR_PI;
	if (mireg.intr_mask & MI_INTR_MASK_PI)
	{
		Log(TYPE_WRITE, "*** Peripherial Interface Interrupt", 0);
		c0reg.Cause |= C0CAUSE_IP2;
		GenerateException(EXCEPTION_INTERRUPT);
	}
}

void _cdecl AudioInterfaceInterrupt(void)
{
	// Remove current event.
	EventHeapDeleteMin();

	mireg.intr |= MI_INTR_AI;
	if (mireg.intr_mask & MI_INTR_MASK_AI)
	{
		Log(TYPE_WRITE, "*** Audio Interface Interrupt", 0);
		c0reg.Cause |= C0CAUSE_IP2;
		GenerateException(EXCEPTION_INTERRUPT);
	}
}

void _cdecl SignalProcessorInterrupt(void)
{
    // Remove current event.
    EventHeapDeleteMin();

    mireg.intr |= MI_INTR_SP;
    if (mireg.intr_mask & MI_INTR_MASK_SP)
    {
        Log(TYPE_WRITE, "*** Signal Processor Interrupt", 0);
        c0reg.Cause |= C0CAUSE_IP2;
        GenerateException(EXCEPTION_INTERRUPT);
    }
}

void _cdecl DisplayProcessorInterrupt(void)
{
    // Remove current event.
    EventHeapDeleteMin();

    mireg.intr |= MI_INTR_DP;
    if (mireg.intr_mask & MI_INTR_MASK_DP)
    {
        Log(TYPE_WRITE, "*** Display Processor Interrupt", 0);
        c0reg.Cause |= C0CAUSE_IP2;
        GenerateException(EXCEPTION_INTERRUPT);
    }
}

// *******************************************************************************************************

void _cdecl EventAdd_Compare(void)
{
    c0reg.Cause &= ~C0CAUSE_IP7;   //BWG (Clear Count/Compare Interrupt)

	if (c0reg.Compare == 0)
	{
		Log(TYPE_WRITE, "@@@ MTC0 Compare, 0 <-- IGNORING! @@@", 0);
		return;
	}

	Log(TYPE_WRITE, "@@@ Logging compare event @@@", c0reg.Compare);

	// Delete any old count/compare interrupt that might be pending.
	EventHeapDeleteAction(CountCompareInterrupt);

	// Handle wrap-around...
	if (c0reg.Compare < c0reg.Count)
		EventHeapInsert(c0reg.Compare + 0x0000000100000000, CountCompareInterrupt);
	else
		EventHeapInsert(c0reg.Compare, CountCompareInterrupt);
}

void _cdecl EventAdd_PI(void)
{
	Log(TYPE_WRITE, "@@@ Logging PI interrupt event @@@", 0);
	EventHeapInsert(c0reg.Count, PeripherialInterfaceInterrupt);
}

void _cdecl EventAdd_SP(void)
{
    Log(TYPE_WRITE, "@@@ Logging SP interrupt event @@@", 0);
    EventHeapInsert(c0reg.Count, SignalProcessorInterrupt);
}

void _cdecl EventAdd_DP(void)
{
    Log(TYPE_WRITE, "@@@ Logging DP interrupt event @@@", 0);
    EventHeapInsert(c0reg.Count, DisplayProcessorInterrupt);
}

// *******************************************************************************************************

void _cdecl GenerateArithmeticOverflow(void)
{
	Log(TYPE_WRITE, "*** Arithmetic Overflow Exception", 0);
	GenerateException(EXCEPTION_ARITHMETIC_OVERFLOW);
}

void _cdecl GenerateTrapException(void)
{
	Log(TYPE_WRITE, "*** Trap Exception", 0);
	GenerateException(EXCEPTION_TRAP);
}
