//
// 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: debugger.cpp $
// Revision 1.22  1998/05/25 20:56:42  BREAKPOINT
// Removed debugger code from release builds.  Only usable when _DEBUG is #defined (a debug build).
// Revision 1.21  1998/05/25 04:38:31  BREAKPOINT
// Added 'Call Stack' to debugger which shows the history of JAL[R]/JR RA instructions.
// Revision 1.20  1998/05/24 23:16:22  BREAKPOINT
// Added 'breakpoint on Count' to debugger.
// Revision 1.19  1998/05/24 20:47:43  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.18  1998/05/16 17:27:08  BREAKPOINT
// Fixed some debugging problems with the RSP... (debugger windows not being refreshed properly, etc).
// Revision 1.17  1998/05/03 21:49:07  BREAKPOINT
// Added 'Raw Dump' to debugger to dump out code/data.
// Revision 1.16  1998/05/02 13:33:07  BREAKPOINT
// Added RSP 'dump code'.
// 
// ./
// Revision 1.15  1998/05/01 07:44:43  BREAKPOINT
// Changed RSP debugger window to only have one edit box for entire vector register.
// Revision 1.14  1998/04/29 21:28:05  BREAKPOINT
// Added 'Dump Code' capability to R4300i debugger.  Writes code to output file... need to add for RSP debugger now.
// Revision 1.13  1998/04/21 21:23:33  BREAKPOINT
// RSP breakpoints done... a little quirky, but they work.  I guess I can't figure out how legerde's method of using the PC works.
// Revision 1.12  1998/04/20 23:11:29  BREAKPOINT
// Fixed up RSP debugger so vector regs would display right.
// Revision 1.11  1998/04/19 16:32:29  BREAKPOINT
// Finished RSP debugger disasm and register windows.
// Revision 1.10  1998/04/13 00:55:07  BREAKPOINT
// Completed RSP debugger / register dump window.
// Revision 1.9  1998/04/08 19:49:25  BREAKPOINT
// Sped up memory window in debugger...
// Revision 1.8  1998/04/07 23:27:59  BREAKPOINT
// Fixed up debugger... added 'Safe_Read*()' calls to access memory without causing crashes when reading from unmapped memory.
// Revision 1.7  1998/04/05 23:48:30  BREAKPOINT
// Added RSP register window, and added RSP debugger stepping...
// Revision 1.6  1998/04/05 19:46:04  BREAKPOINT
// Added RSP debugger *ONLY* when _DEBUG is defined!
// Revision 1.5  1998/03/22 17:51:06  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.4  1998/03/08 19:12:18  BREAKPOINT
// Version 0.3b.3 checkin... I need to really check this stuff in more often..
// Revision 1.3  1998/01/31 19:49:02  BREAKPOINT
// Fixed code to handle synchronization with new assembly core.
// Revision 1.2  1998/01/20 00:29:31  BREAKPOINT
// Fixed c1reg lookup
// Revision 1.1  1998/01/10 01:25:40  BREAKPOINT
// Initial revision
//

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

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

#include <windows.h>
#include <commctrl.h>
#include <types.h>
#include "unreal.h"
#include "emu.h"
#define DEBUGGER_NO_EXTERN
#include "debugger.h"
#include "resource.h"
#include "input.h"
#include "cod_map.h"
#include "rsp.h"
#include "heap.h"

#include "inline.cpp"


#ifdef _DEBUG

FARPROC debugproc, registerproc, memoryproc, callstackproc, rspdebugproc, rspregisterproc;
HWND debughWnd, registerhWnd, memoryhWnd, callstackhWnd, rspdebughWnd, rspregisterhWnd;
HWND hWnd_debugdisasm, hWnd_debugscroll, hWnd_memorydump, hWnd_callstack, hWnd_rspdebugdisasm, hWnd_rspdebugscroll;
ulong DebuggerPC = 0, RSPDebuggerPC = 0;
int PointerIdx = -1, RSPPointerIdx = -1;

#endif // _DEBUG

bool breakpoints_on_regv, mem_inupdate = FALSE;
ulong temp_new_PC, memory_addr;
BPNode *FirstBPNode = NULL, *LastBPNode = NULL;

ulong raw_opcode_ulong, raw_opcode_addr;
uchar raw_opcode[4];
bool  is_outside_memory;


#ifdef _DEBUG
int LDebugPointer[29] = {
    IDC_DEBUG_L1,  IDC_DEBUG_L2,  IDC_DEBUG_L3,  IDC_DEBUG_L4,  IDC_DEBUG_L5,  IDC_DEBUG_L6,  IDC_DEBUG_L7,  IDC_DEBUG_L8,
    IDC_DEBUG_L9,  IDC_DEBUG_L10, IDC_DEBUG_L11, IDC_DEBUG_L12, IDC_DEBUG_L13, IDC_DEBUG_L14, IDC_DEBUG_L15, IDC_DEBUG_L16,
    IDC_DEBUG_L17, IDC_DEBUG_L18, IDC_DEBUG_L19, IDC_DEBUG_L20, IDC_DEBUG_L21, IDC_DEBUG_L22, IDC_DEBUG_L23, IDC_DEBUG_L24,
    IDC_DEBUG_L25, IDC_DEBUG_L26, IDC_DEBUG_L27, IDC_DEBUG_L28, IDC_DEBUG_L29
};

int RDebugPointer[29] = {
    IDC_DEBUG_R1,  IDC_DEBUG_R2,  IDC_DEBUG_R3,  IDC_DEBUG_R4,  IDC_DEBUG_R5,  IDC_DEBUG_R6,  IDC_DEBUG_R7,  IDC_DEBUG_R8,
    IDC_DEBUG_R9,  IDC_DEBUG_R10, IDC_DEBUG_R11, IDC_DEBUG_R12, IDC_DEBUG_R13, IDC_DEBUG_R14, IDC_DEBUG_R15, IDC_DEBUG_R16,
    IDC_DEBUG_R17, IDC_DEBUG_R18, IDC_DEBUG_R19, IDC_DEBUG_R20, IDC_DEBUG_R21, IDC_DEBUG_R22, IDC_DEBUG_R23, IDC_DEBUG_R24,
    IDC_DEBUG_R25, IDC_DEBUG_R26, IDC_DEBUG_R27, IDC_DEBUG_R28, IDC_DEBUG_R29
};

int GPR_Regs[32] = {
    IDC_DEBUG_GPR0,  IDC_DEBUG_GPR1,  IDC_DEBUG_GPR2,  IDC_DEBUG_GPR3,  IDC_DEBUG_GPR4,  IDC_DEBUG_GPR5,  IDC_DEBUG_GPR6,
    IDC_DEBUG_GPR7,  IDC_DEBUG_GPR8,  IDC_DEBUG_GPR9,  IDC_DEBUG_GPR10, IDC_DEBUG_GPR11, IDC_DEBUG_GPR12, IDC_DEBUG_GPR13,
    IDC_DEBUG_GPR14, IDC_DEBUG_GPR15, IDC_DEBUG_GPR16, IDC_DEBUG_GPR17, IDC_DEBUG_GPR18, IDC_DEBUG_GPR19, IDC_DEBUG_GPR20,
    IDC_DEBUG_GPR21, IDC_DEBUG_GPR22, IDC_DEBUG_GPR23, IDC_DEBUG_GPR24, IDC_DEBUG_GPR25, IDC_DEBUG_GPR26, IDC_DEBUG_GPR27,
    IDC_DEBUG_GPR28, IDC_DEBUG_GPR29, IDC_DEBUG_GPR30, IDC_DEBUG_GPR31
};

int FGR_Regs[32] = {
    IDC_DEBUG_FGR0,  IDC_DEBUG_FGR1,  IDC_DEBUG_FGR2,  IDC_DEBUG_FGR3,  IDC_DEBUG_FGR4,  IDC_DEBUG_FGR5,  IDC_DEBUG_FGR6,
    IDC_DEBUG_FGR7,  IDC_DEBUG_FGR8,  IDC_DEBUG_FGR9,  IDC_DEBUG_FGR10, IDC_DEBUG_FGR11, IDC_DEBUG_FGR12, IDC_DEBUG_FGR13,
    IDC_DEBUG_FGR14, IDC_DEBUG_FGR15, IDC_DEBUG_FGR16, IDC_DEBUG_FGR17, IDC_DEBUG_FGR18, IDC_DEBUG_FGR19, IDC_DEBUG_FGR20,
    IDC_DEBUG_FGR21, IDC_DEBUG_FGR22, IDC_DEBUG_FGR23, IDC_DEBUG_FGR24, IDC_DEBUG_FGR25, IDC_DEBUG_FGR26, IDC_DEBUG_FGR27,
    IDC_DEBUG_FGR28, IDC_DEBUG_FGR29, IDC_DEBUG_FGR30, IDC_DEBUG_FGR31
};

int RSP_GPR_Regs[32] = {
    IDC_RSP_GPR0,  IDC_RSP_GPR1,  IDC_RSP_GPR2,  IDC_RSP_GPR3,  IDC_RSP_GPR4,  IDC_RSP_GPR5,  IDC_RSP_GPR6,  IDC_RSP_GPR7,
	IDC_RSP_GPR8,  IDC_RSP_GPR9,  IDC_RSP_GPR10, IDC_RSP_GPR11, IDC_RSP_GPR12, IDC_RSP_GPR13, IDC_RSP_GPR14, IDC_RSP_GPR15,
	IDC_RSP_GPR16, IDC_RSP_GPR17, IDC_RSP_GPR18, IDC_RSP_GPR19, IDC_RSP_GPR20, IDC_RSP_GPR21, IDC_RSP_GPR22, IDC_RSP_GPR23,
	IDC_RSP_GPR24, IDC_RSP_GPR25, IDC_RSP_GPR26, IDC_RSP_GPR27, IDC_RSP_GPR28, IDC_RSP_GPR29, IDC_RSP_GPR30, IDC_RSP_GPR31
};

int RSP_Vect_Regs[32] = {
	IDC_RSP_V0,  IDC_RSP_V1,  IDC_RSP_V2,  IDC_RSP_V3,  IDC_RSP_V4,  IDC_RSP_V5,  IDC_RSP_V6,  IDC_RSP_V7,	IDC_RSP_V8,
	IDC_RSP_V9,  IDC_RSP_V10, IDC_RSP_V11, IDC_RSP_V12, IDC_RSP_V13, IDC_RSP_V14, IDC_RSP_V15, IDC_RSP_V16, IDC_RSP_V17,
	IDC_RSP_V18, IDC_RSP_V19, IDC_RSP_V20, IDC_RSP_V21, IDC_RSP_V22, IDC_RSP_V23, IDC_RSP_V24, IDC_RSP_V25, IDC_RSP_V26,
	IDC_RSP_V27, IDC_RSP_V28, IDC_RSP_V29, IDC_RSP_V30, IDC_RSP_V31
};
#endif // _DEBUG

BPNode *AddBPNode(void)
{
    if (FirstBPNode == NULL)
    {
        FirstBPNode = new BPNode;
        FirstBPNode->prev = NULL;
        FirstBPNode->next = NULL;

        LastBPNode = FirstBPNode;
    }
    else
    {
        FirstBPNode->prev = new BPNode;
        FirstBPNode->prev->prev = NULL;
        FirstBPNode->prev->next = FirstBPNode;

        FirstBPNode = FirstBPNode->prev;
    }

    return FirstBPNode;
}

BPNode *FindBPNode(ulong PC)
{
    BPNode *bp;

    bp = FirstBPNode;
    while (bp)
    {
        if (bp->PC == PC)
            return bp;

        bp = bp->next;
    }

    return NULL;
}

void RemoveBPNode(BPNode *bp)
{
    BPNode *Current;

    if (bp == NULL) return;

    Current = LastBPNode;
    while ((Current != NULL) && (Current->PC != bp->PC))
        Current = Current->prev;

    // Not found!
    if (Current == NULL) return;

    if ((Current->next == NULL) && (Current->prev == NULL))
    {
        // Only one in list...
        FirstBPNode = NULL;
        LastBPNode  = NULL;
    }
    else
    {
        if (Current->next == NULL)
        {
            // Last one in list...
            LastBPNode = Current->prev;
            LastBPNode->next = NULL;
        }
        else
        {
            if (Current->prev == NULL)
            {
                // First one in list...
                FirstBPNode = FirstBPNode->next;
                FirstBPNode->prev = NULL;
            }
            else
            {
                // Somewhere in the middle...
                Current->next->prev = Current->prev;
                Current->prev->next = Current->next;
            }
        }
    }

    delete Current;
}

#ifdef _DEBUG
BOOL APIENTRY JumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char buf[16];
    ulong temp;

    switch (message)
    {
        case WM_INITDIALOG:
            sprintf(buf, "%08X", r4reg.PC);
            SetDlgItemText(hDlg, IDC_JUMP_PC, buf);
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_JUMP_PC, buf, 9);

                buf[8] = 0;
                sscanf(buf, "%x", &temp);

                temp      &= 0xFFFFFFFC;
                DebuggerPC = temp - 0x00000038;

                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY DumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char start[16], end[16], fname[128], tmp[128], buf[128];
    ulong start_pc, end_pc, temp;
    CODMAPNode *cm;
    BPNode *bp;
	FILE *fp;

    switch (message)
    {
        case WM_INITDIALOG:
            sprintf(buf, "%08X", r4reg.PC);
            SetDlgItemText(hDlg, IDC_DUMP_START, buf);
            SetDlgItemText(hDlg, IDC_DUMP_END,   buf);
			SetDlgItemText(hDlg, IDC_DUMP_FNAME, "dump.txt");
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_DUMP_START, start, 9);
                start[8] = 0;
                sscanf(start, "%x", &start_pc);
                start_pc &= 0xFFFFFFFC;

                GetDlgItemText(hDlg, IDC_DUMP_END, end, 9);
                end[8] = 0;
                sscanf(end, "%x", &end_pc);
                end_pc &= 0xFFFFFFFC;

				GetDlgItemText(hDlg, IDC_DUMP_FNAME, fname, 128);
				fp = fopen(fname, "wt");
				if (fp == NULL)
					MessageBox(MainhWnd, "Can't open file for writing!", NULL, MB_OK);
				else
				{
					for (temp = start_pc; temp <= end_pc; temp += 4)
					{
						// If there is a breakpoint on this opcode, set it back to the original opcode.
						bp = FindBPNode(temp);
						if (bp) Write32(temp, bp->opcode);

						// Make the opcode.
						raw_opcode_addr = temp;
						MakeDebugOpcode();

						// If we changed the opcode, set it back to invalid.
						if (bp) Write32(temp, 0x7F7F7F7F);

						if (!is_outside_memory)
						{
							OpcodePrintTable[DECODE_OPCODE](tmp);

							if (HasBreakpoint(temp))
							{
								cm = FindCODMAPNode(temp);
								if (cm == NULL)
									sprintf(buf, "*%08X:\t%s", temp, tmp);
								else
									sprintf(buf, "*%s:\t%s", cm->symbol, tmp);
							}
							else
							{
								cm = FindCODMAPNode(temp);
								if (cm == NULL)
									sprintf(buf, "%08X:\t%s", temp, tmp);
								else
									sprintf(buf, "%s:\t%s", cm->symbol, tmp);
							}
						}
						else
						{
							if (HasBreakpoint(temp))
							{
								cm = FindCODMAPNode(temp);
								if (cm == NULL)
									sprintf(buf, "*%08X:\t***", temp);
								else
									sprintf(buf, "*%s:\t***", cm->symbol);
							}
							else
							{
								cm = FindCODMAPNode(temp);
								if (cm == NULL)
									sprintf(buf, "%08X:\t***", temp);
								else
									sprintf(buf, "%s:\t***", cm->symbol);
							}
						}

						fprintf(fp, "%s\n", buf);
					}

					fclose(fp);
				}

                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY RawDumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char start[16], end[16], fname[128], buf[128];
    ulong start_pc, end_pc, temp;
    BPNode *bp;
	FILE *fp;

    switch (message)
    {
        case WM_INITDIALOG:
            sprintf(buf, "%08X", r4reg.PC);
            SetDlgItemText(hDlg, IDC_DUMP_START, buf);
            SetDlgItemText(hDlg, IDC_DUMP_END,   buf);
			SetDlgItemText(hDlg, IDC_DUMP_FNAME, "dump.bin");
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_DUMP_START, start, 9);
                start[8] = 0;
                sscanf(start, "%x", &start_pc);
                start_pc &= 0xFFFFFFFC;

                GetDlgItemText(hDlg, IDC_DUMP_END, end, 9);
                end[8] = 0;
                sscanf(end, "%x", &end_pc);
                end_pc &= 0xFFFFFFFC;

				GetDlgItemText(hDlg, IDC_DUMP_FNAME, fname, 128);
				fp = fopen(fname, "wb");
				if (fp == NULL)
					MessageBox(MainhWnd, "Can't open file for writing!", NULL, MB_OK);
				else
				{
					for (temp = start_pc; temp <= end_pc; temp += 4)
					{
						// If there is a breakpoint on this opcode, set it back to the original opcode.
						bp = FindBPNode(temp);
						if (bp) Write32(temp, bp->opcode);

						// Make the opcode.
						raw_opcode_addr = temp;
						MakeDebugOpcode();

						// If we changed the opcode, set it back to invalid.
						if (bp) Write32(temp, 0x7F7F7F7F);

						fwrite(&raw_opcode[0], 1, 1, fp);
						fwrite(&raw_opcode[1], 1, 1, fp);
						fwrite(&raw_opcode[2], 1, 1, fp);
						fwrite(&raw_opcode[3], 1, 1, fp);
					}

					fclose(fp);
				}

                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY RSPJumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char buf[16];
    ulong temp;

    switch (message)
    {
        case WM_INITDIALOG:
            sprintf(buf, "%08X", sp_pc.PC);
            SetDlgItemText(hDlg, IDC_JUMP_PC, buf);
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_JUMP_PC, buf, 9);

                buf[8] = 0;
                sscanf(buf, "%x", &temp);

                temp         &= 0xFFFFFFFC;
                RSPDebuggerPC = temp - 0x00000038;

                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY RSPDumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char start[16], end[16], fname[128], tmp[128], buf[128];
    ulong start_pc, end_pc, temp;
    BPNode *bp;
	FILE *fp;

    switch (message)
    {
        case WM_INITDIALOG:
            sprintf(buf, "%08X", sp_pc.PC);
            SetDlgItemText(hDlg, IDC_DUMP_START, buf);
            SetDlgItemText(hDlg, IDC_DUMP_END,   buf);
			SetDlgItemText(hDlg, IDC_DUMP_FNAME, "rspdump.txt");
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_DUMP_START, start, 9);
                start[8] = 0;
                sscanf(start, "%x", &start_pc);
                start_pc &= 0xFFFFFFFC;

                GetDlgItemText(hDlg, IDC_DUMP_END, end, 9);
                end[8] = 0;
                sscanf(end, "%x", &end_pc);
                end_pc &= 0xFFFFFFFC;

				GetDlgItemText(hDlg, IDC_DUMP_FNAME, fname, 128);
				fp = fopen(fname, "wt");
				if (fp == NULL)
					MessageBox(MainhWnd, "Can't open file for writing!", NULL, MB_OK);
				else
				{
					for (temp = start_pc; temp <= end_pc; temp += 4)
					{
						// If there is a breakpoint on this opcode, set it back to the original opcode.
						bp = FindBPNode(temp);
						if (bp) Write32(temp, bp->opcode);

						// Make the opcode.
						raw_opcode_addr = temp;
						MakeDebugOpcode();

						// If we changed the opcode, set it back to invalid.
						if (bp) Write32(temp, 0x7F7F7F7F);

						if (!is_outside_memory)
						{
							RSP->RSP_OpcodePrint(tmp);

							if (HasBreakpoint(temp))
								sprintf(buf, "*%08X:\t%s", temp, tmp);
							else
								sprintf(buf, "%08X:\t%s", temp, tmp);
						}
						else
						{
							if (HasBreakpoint(temp))
								sprintf(buf, "*%08X:\t***", temp);
							else
								sprintf(buf, "%08X:\t***", temp);
						}

						fprintf(fp, "%s\n", buf);
					}

					fclose(fp);
				}

                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY RawRSPDumpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char start[16], end[16], fname[128], buf[128];
    ulong start_pc, end_pc, temp;
    BPNode *bp;
	FILE *fp;

    switch (message)
    {
        case WM_INITDIALOG:
            sprintf(buf, "%08X", sp_pc.PC);
            SetDlgItemText(hDlg, IDC_DUMP_START, buf);
            SetDlgItemText(hDlg, IDC_DUMP_END,   buf);
			SetDlgItemText(hDlg, IDC_DUMP_FNAME, "rspdump.bin");
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_DUMP_START, start, 9);
                start[8] = 0;
                sscanf(start, "%x", &start_pc);
                start_pc &= 0xFFFFFFFC;

                GetDlgItemText(hDlg, IDC_DUMP_END, end, 9);
                end[8] = 0;
                sscanf(end, "%x", &end_pc);
                end_pc &= 0xFFFFFFFC;

				GetDlgItemText(hDlg, IDC_DUMP_FNAME, fname, 128);
				fp = fopen(fname, "wb");
				if (fp == NULL)
					MessageBox(MainhWnd, "Can't open file for writing!", NULL, MB_OK);
				else
				{
					for (temp = start_pc; temp <= end_pc; temp += 4)
					{
						// If there is a breakpoint on this opcode, set it back to the original opcode.
						bp = FindBPNode(temp);
						if (bp) Write32(temp, bp->opcode);

						// Make the opcode.
						raw_opcode_addr = temp;
						MakeDebugOpcode();

						// If we changed the opcode, set it back to invalid.
						if (bp) Write32(temp, 0x7F7F7F7F);

						fwrite(&raw_opcode[0], 1, 1, fp);
						fwrite(&raw_opcode[1], 1, 1, fp);
						fwrite(&raw_opcode[2], 1, 1, fp);
						fwrite(&raw_opcode[3], 1, 1, fp);
					}

					fclose(fp);
				}

                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY RegVProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char buf[16];
    ulong regv;

    switch (message)
    {
        case WM_INITDIALOG:
            buf[0] = 0;
            SetDlgItemText(hDlg, IDC_BP_REGV, buf);
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_BP_REGV, buf, 9);

                buf[8] = 0;
                sscanf(buf, "%x", &regv);
                AddBPRegValue(regv);

                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY MemSearchProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char buf[16];
    ulong val;

    switch (message)
    {
        case WM_INITDIALOG:
            buf[0] = 0;
            SetDlgItemText(hDlg, IDC_MEM_VALUE, buf);
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_MEM_VALUE, buf, 9); buf[8] = 0;
                sscanf(buf, "%x", &val);

                SearchMemory(val);
                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY CountProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char buf[16];
    ulong temp;
	__int64 upper;

    switch (message)
    {
        case WM_INITDIALOG:
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                GetDlgItemText(hDlg, IDC_BP_COUNT, buf, 9);
                buf[8] = 0;
                sscanf(buf, "%x", &temp);

				upper = c0reg.Count & 0xFFFFFFFF00000000;

				// Account for Count rollover...
				if (temp < (ulong)c0reg.Count)
					EventHeapInsert((upper + 1) | temp, CountBreakpoint);
				else
					EventHeapInsert(upper | temp, CountBreakpoint);

				MessageBox(MainhWnd, "Ensure debugger is *NOT* open when count breakpoint occurs,\notherwise the breakpoint will be ignored!", "Notice", MB_OK);
                EndDialog(hDlg, TRUE);
            } else if (LOWORD(wParam) == IDCANCEL) {
                EndDialog(hDlg, TRUE);
            }
            return TRUE;
    }

    return FALSE;
}

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

	sprintf(buf, "Breakpoint on Count = %08X", c0reg.Count);
	MessageBox(NULL, buf, "Debug", MB_OK);
	SendMessage(MainhWnd, WM_COMMAND, ID_DEBUG_ENTERDEBUGGER, 0);
}

BOOL APIENTRY CallStackProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            hWnd_callstack = GetDlgItem(hDlg, IDC_CALLSTACK_DUMP);
            SendMessage(hWnd_callstack, LB_INITSTORAGE, 64, 1280);

            if (loaded) RefreshCallStack();
            return TRUE;

        case WM_COMMAND:
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY DebuggerProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    int t, idx;
    ulong temp_pc;
    FARPROC jmpproc, regvproc, dumpproc, countproc;
    BPNode *bp;

    switch (message)
    {
        case WM_INITDIALOG:
			SetWindowText(hDlg, "R4300i Debugger");

            // Clear all breakpoints.
            for (t = 0; t < NUM_BREAKPOINTS; t++)
            {
                bkpt_regv[t].active = FALSE;
                bkpt_regv[t].value  = 0x00000000;
            }

			breakpoints_on_regv = FALSE;

            for (t = 0; t < 29; t++)
            {
                SetDlgItemText(hDlg, LDebugPointer[t], "");
                SetDlgItemText(hDlg, RDebugPointer[t], "");
            }

            hWnd_debugdisasm = GetDlgItem(hDlg, IDC_DEBUG_DISASM);
            hWnd_debugscroll = GetDlgItem(hDlg, IDC_DEBUG_SCROLL);

            SendMessage(hWnd_debugdisasm, LB_INITSTORAGE, 29, 1131);
            SendMessage(hWnd_debugscroll, SBM_SETRANGE, 0, MAXLONG);
            SendMessage(hWnd_debugscroll, SBM_SETPOS, MAXLONG / 2, TRUE);

            if (loaded) RefreshDebugger();
            return TRUE;

        case WM_VSCROLL:
            switch ((int) LOWORD(wParam))
            {
                case SB_LINEDOWN: DebuggerPC += 0x00000004; RefreshDebugger(); break;
                case SB_LINEUP:   DebuggerPC -= 0x00000004; RefreshDebugger(); break;
                case SB_PAGEDOWN: DebuggerPC += 0x00000074; RefreshDebugger(); break;
                case SB_PAGEUP:   DebuggerPC -= 0x00000074; RefreshDebugger(); break;
            }

            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDC_DEBUG_STEP:
					if (running)
					{
						MessageBox(MainhWnd, "You cannot step while in 'run' mode.\nStop emulation by pressing [Break] first.", "Notice", MB_OK);
						return TRUE;
					}

                    // If there is a breakpoint on this opcode, set it back to the original opcode.
                    temp_pc = r4reg.PC;
                    bp = FindBPNode(temp_pc);
                    if (bp) Write32(temp_pc, bp->opcode);

					// Kick off the emulation thread for one opcode.
					ResetEvent(hDebugEvent);
                    SetEvent(hRunEvent);
					AcquireDevices(MainhWnd);

					// Now wait until it signals us that it's done.
					WaitForSingleObject(hDebugEvent, INFINITE);

                    // If we changed the opcode, set it back to invalid.
                    if (bp) Write32(temp_pc, 0x7F7F7F7F);

					UnAcquireDevices();
                    DebuggerPC = 0;
                    RefreshDebugAll();
                    return TRUE;

                case IDC_DEBUG_SKIP:
					if (running)
					{
						MessageBox(MainhWnd, "You cannot skip while in 'run' mode.\nStop emulation by pressing [Break] first.", "Notice", MB_OK);
						return TRUE;
					}

                    DebuggerPC = 0;
                    r4reg.PC  += 0x00000004;
                    RefreshDebugger();
                    RefreshRegisters();
                    return TRUE;

                case IDC_DEBUG_GO:
					if (running)
					{
						MessageBox(MainhWnd, "You are already in 'run' mode.", "Notice", MB_OK);
						return TRUE;
					}

                    running = TRUE;
                    SetEvent(hRunEvent);
					AcquireDevices(MainhWnd);
                    return TRUE;

                case IDC_DEBUG_BREAK:
					if (!running)
					{
						MessageBox(MainhWnd, "You are already in 'stop' mode.", "Notice", MB_OK);
						return TRUE;
					}

                    DebuggerPC = 0;
                    running = FALSE;
                    RefreshDebugAll();
                    return TRUE;

                case IDC_DEBUG_RESETTOPC:
                    DebuggerPC = 0;
                    RefreshDebugger();
                    return TRUE;

                case IDC_DEBUG_JUMP:
                    jmpproc = MakeProcInstance((FARPROC)JumpProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_JUMP), MainhWnd, jmpproc);
                    FreeProcInstance(jmpproc);
                    
                    RefreshDebugger();
                    RefreshRegisters();
                    return TRUE;

                case IDC_DEBUG_BP_EXEC:
					if (running)
					{
						MessageBox(MainhWnd, "You cannot set a breakpoint while in 'run' mode.\nStop emulation by pressing [Break] first.", "Notice", MB_OK);
						return TRUE;
					}

                    idx = SendMessage(hWnd_debugdisasm, LB_GETCURSEL, 0, 0);
                    ToggleBreakpoint(DebuggerPC + (idx << 2));
                    RefreshDebugger();
                    return TRUE;

                case IDC_DEBUG_BP_REGV:
					MessageBox(MainhWnd, "Not supported", NULL, MB_OK);
                    return TRUE;
/*
                    regvproc = MakeProcInstance((FARPROC)RegVProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_BP_REGV), MainhWnd, regvproc);
                    FreeProcInstance(regvproc);
                    
                    RefreshDebugger();
                    RefreshRegisters();
                    return TRUE;
*/

				case IDC_DEBUG_BP_COUNT:
					if (running)
					{
						MessageBox(MainhWnd, "You cannot set a breakpoint while in 'run' mode.\nStop emulation by pressing [Break] first.", "Notice", MB_OK);
						return TRUE;
					}

					countproc = MakeProcInstance((FARPROC)CountProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_COUNT), MainhWnd, countproc);
                    FreeProcInstance(countproc);
					break;

				case IDC_DEBUG_DUMP:
                    dumpproc = MakeProcInstance((FARPROC)DumpProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_DUMP), MainhWnd, dumpproc);
                    FreeProcInstance(dumpproc);
					break;

				case IDC_DEBUG_DUMPRAW:
                    dumpproc = MakeProcInstance((FARPROC)RawDumpProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_DUMP), MainhWnd, dumpproc);
                    FreeProcInstance(dumpproc);
					break;

                case IDC_DEBUG_CLOSE:
                    SendMessage(MainhWnd, WM_COMMAND, ID_DEBUG_CLOSEDEBUGGER, 0);
                    return TRUE;
            }
            break;
    }

    return FALSE;
}

BOOL APIENTRY RSPDebuggerProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    int t, idx;
    ulong temp_pc;
    FARPROC jmpproc, dumpproc;
    BPNode *bp;

    switch (message)
    {
        case WM_INITDIALOG:
			SetWindowText(hDlg, "RSP Debugger");

            for (t = 0; t < 29; t++)
            {
                SetDlgItemText(hDlg, LDebugPointer[t], "");
                SetDlgItemText(hDlg, RDebugPointer[t], "");
            }

            hWnd_rspdebugdisasm = GetDlgItem(hDlg, IDC_DEBUG_DISASM);
            hWnd_rspdebugscroll = GetDlgItem(hDlg, IDC_DEBUG_SCROLL);

            SendMessage(hWnd_rspdebugdisasm, LB_INITSTORAGE, 29, 1131);
            SendMessage(hWnd_rspdebugscroll, SBM_SETRANGE, 0, MAXLONG);
            SendMessage(hWnd_rspdebugscroll, SBM_SETPOS, MAXLONG / 2, TRUE);

            if (loaded) RefreshDebugger();
            return TRUE;

        case WM_VSCROLL:
            switch ((int) LOWORD(wParam))
            {
                case SB_LINEDOWN: RSPDebuggerPC += 0x00000004; RefreshRSPDebugger(); break;
                case SB_LINEUP:   RSPDebuggerPC -= 0x00000004; RefreshRSPDebugger(); break;
                case SB_PAGEDOWN: RSPDebuggerPC += 0x00000074; RefreshRSPDebugger(); break;
                case SB_PAGEUP:   RSPDebuggerPC -= 0x00000074; RefreshRSPDebugger(); break;
            }

            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDC_DEBUG_STEP:
					if (RSP_running)
					{
						MessageBox(MainhWnd, "You cannot step while the RSP is in 'run' mode.\nStop emulation by pressing [Break] first.", "Notice", MB_OK);
						return TRUE;
					}

                    // If there is a breakpoint on this opcode, set it back to the original opcode.
                    temp_pc = sp_pc.PC;
                    bp = FindBPNode(temp_pc);
                    if (bp) Write32(temp_pc, bp->opcode);

					// Kick off the emulation thread for one opcode.
					RSP_running = FALSE;
					ResetEvent(hRSPDebugEvent);
					SetEvent(hRSPRunEvent);
					AcquireDevices(MainhWnd);

					// Wait for the RSP to finish it's current opcode.
					WaitForSingleObject(hRSPDebugEvent, INFINITE);

                    // If we changed the opcode, set it back to invalid.
                    if (bp) Write32(temp_pc, 0x7F7F7F7F);

					UnAcquireDevices();
					RSPDebuggerPC = 0;
                    RefreshDebugAll();
                    return TRUE;

                case IDC_DEBUG_SKIP:
					if (RSP_running)
					{
						MessageBox(MainhWnd, "You cannot skip while the RSP is in 'run' mode.\nStop emulation by pressing [Break] first.", "Notice", MB_OK);
						return TRUE;
					}

                    RSPDebuggerPC = 0;
                    sp_pc.PC     += 0x00000004;
                    RefreshRSPDebugger();
                    RefreshRegisters();
                    return TRUE;

                case IDC_DEBUG_GO:
					if (RSP_running)
					{
						MessageBox(MainhWnd, "The RSP is already in 'run' mode.", "Notice", MB_OK);
						return TRUE;
					}

                    RSP_running = TRUE;
                    SetEvent(hRSPRunEvent);
					AcquireDevices(MainhWnd);
                    return TRUE;

                case IDC_DEBUG_BREAK:
					if (!RSP_running)
					{
						MessageBox(MainhWnd, "The RSP is already in 'stop' mode.", "Notice", MB_OK);
						return TRUE;
					}

                    RSPDebuggerPC = 0;
                    RSP_running = FALSE;
                    RefreshDebugAll();
                    return TRUE;

                case IDC_DEBUG_RESETTOPC:
                    RSPDebuggerPC = 0;
                    RefreshRSPDebugger();
                    return TRUE;

                case IDC_DEBUG_JUMP:
                    jmpproc = MakeProcInstance((FARPROC)RSPJumpProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_JUMP), MainhWnd, jmpproc);
                    FreeProcInstance(jmpproc);
                    
                    RefreshRSPDebugger();
                    RefreshRegisters();
                    return TRUE;

                case IDC_DEBUG_BP_EXEC:
					if (RSP_running)
					{
						MessageBox(MainhWnd, "You cannot set breakpoints while the RSP is in 'run' mode.\nStop emulation by pressing [Break] first.", "Notice", MB_OK);
						return TRUE;
					}

                    idx = SendMessage(hWnd_rspdebugdisasm, LB_GETCURSEL, 0, 0);
                    ToggleBreakpoint(RSPDebuggerPC + (idx << 2));
                    RefreshRSPDebugger();
                    return TRUE;

                case IDC_DEBUG_BP_REGV:
					MessageBox(MainhWnd, "Not supported", NULL, MB_OK);
                    return TRUE;

				case IDC_DEBUG_DUMP:
                    dumpproc = MakeProcInstance((FARPROC)RSPDumpProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_DUMP), MainhWnd, dumpproc);
                    FreeProcInstance(dumpproc);
					break;

				case IDC_DEBUG_DUMPRAW:
                    dumpproc = MakeProcInstance((FARPROC)RawRSPDumpProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_DUMP), MainhWnd, dumpproc);
                    FreeProcInstance(dumpproc);
					break;

                case IDC_DEBUG_CLOSE:
                    SendMessage(MainhWnd, WM_COMMAND, ID_DEBUG_CLOSEDEBUGGER, 0);
                    return TRUE;
            }
            break;
	}

    return FALSE;
}

BOOL APIENTRY RSPRegisterProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            if (loaded) RefreshRSPRegisters();
            return TRUE;

        case WM_COMMAND:
            if (HIWORD(wParam) == EN_UPDATE)
            {
/*
				switch (LOWORD(wParam))
				{
					case IDC_MEMORY_SEARCH:
						mem_inupdate = TRUE;
						GetDlgItemText(hDlg, IDC_MEMORY_ADDR, buf, 9);
						buf[8] = 0;

						sscanf(buf, "%x", &memory_addr);
						RefreshMemory();
						mem_inupdate = FALSE;
						return TRUE;
				}
*/
            }

            switch (LOWORD(wParam))
            {
                case IDC_REGISTERS_CLOSE:
                    SendMessage(MainhWnd, WM_COMMAND, ID_DEBUG_CLOSEDEBUGGER, 0);
                    return TRUE;
            }
            break;
    }

    return FALSE;
}

BOOL APIENTRY RegisterProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            if (loaded) RefreshRegisters();
            return TRUE;

        case WM_COMMAND:
            if (HIWORD(wParam) == EN_UPDATE)
            {
/*
				switch (LOWORD(wParam))
				{
					case IDC_MEMORY_SEARCH:
						mem_inupdate = TRUE;
						GetDlgItemText(hDlg, IDC_MEMORY_ADDR, buf, 9);
						buf[8] = 0;

						sscanf(buf, "%x", &memory_addr);
						RefreshMemory();
						mem_inupdate = FALSE;
						return TRUE;
				}
*/
            }

            switch (LOWORD(wParam))
            {
                case IDC_REGISTERS_CLOSE:
                    SendMessage(MainhWnd, WM_COMMAND, ID_DEBUG_CLOSEDEBUGGER, 0);
                    return TRUE;
            }
            break;
    }

    return FALSE;
}

BOOL APIENTRY MemoryProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    FARPROC searchproc;
    char buf[16];

    switch (message)
    {
        case WM_INITDIALOG:
            memory_addr = r4reg.PC;
            sprintf(buf, "%08X", memory_addr);
            SetDlgItemText(hDlg, IDC_MEMORY_ADDR, buf);

            hWnd_memorydump = GetDlgItem(hDlg, IDC_MEMORY_DUMP);
            SendMessage(hWnd_debugdisasm, LB_INITSTORAGE, 11, 1280);

            if (loaded) RefreshMemory();
            return TRUE;

        case WM_COMMAND:
            if (HIWORD(wParam) == EN_UPDATE)
            {
                mem_inupdate = TRUE;
                GetDlgItemText(hDlg, IDC_MEMORY_ADDR, buf, 9);
                buf[8] = 0;

                sscanf(buf, "%x", &memory_addr);
                RefreshMemory();
                mem_inupdate = FALSE;
                return TRUE;
            }

            switch (LOWORD(wParam))
            {
                case IDC_MEMORY_SEARCH:
                    searchproc = MakeProcInstance((FARPROC)MemSearchProc, MainhInst);
                    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_MEM_SEARCH), MainhWnd, searchproc);
                    FreeProcInstance(searchproc);

                    RefreshMemory();
                    return TRUE;

                case IDC_MEMORY_CLOSE:
                    SendMessage(MainhWnd, WM_COMMAND, ID_DEBUG_CLOSEDEBUGGER, 0);
                    return TRUE;
            }
            break;
    }

    return FALSE;
}

void RefreshRSPDebugger(void)
{
    ulong t;
    char buf[64], tmp[64];
    int cnt;
    BPNode *bp;

    if (!loaded) return;

    if (RSPPointerIdx >= 0 && RSPPointerIdx <= 28)
    {
        SetDlgItemText(rspdebughWnd, LDebugPointer[RSPPointerIdx], "");
        SetDlgItemText(rspdebughWnd, RDebugPointer[RSPPointerIdx], "");
    }

    RSPPointerIdx = -1;

    if (RSPDebuggerPC == 0)
        RSPDebuggerPC = sp_pc.PC - 0x00000038;

    SendMessage(hWnd_rspdebugdisasm, LB_RESETCONTENT, 0, 0);

    for (t = RSPDebuggerPC, cnt = 0; t < (RSPDebuggerPC + 0x00000074); t += 0x00000004, cnt++)
    {
		// If there is a breakpoint on this opcode, set it back to the original opcode.
		bp = FindBPNode(t);
		if (bp) Write32(t, bp->opcode);

		// Make the opcode.
		raw_opcode_addr = t;
		MakeDebugOpcode();

		// If we changed the opcode, set it back to invalid.
		if (bp) Write32(t, 0x7F7F7F7F);

		if (!is_outside_memory)
        {
            RSP->RSP_OpcodePrint(tmp);

			if (HasBreakpoint(t))
	            sprintf(buf, "*%08X:\t%s", t, tmp);
			else
	            sprintf(buf, "%08X:\t%s", t, tmp);
        }
        else
        {
			if (HasBreakpoint(t))
	            sprintf(buf, "*%08X:\t***", t);
			else
	            sprintf(buf, "%08X:\t***", t);
        }

        SendMessage(hWnd_rspdebugdisasm, LB_ADDSTRING, 0, (LPARAM)buf);

        if (t == sp_pc.PC)
            RSPPointerIdx = cnt;
    }
    
    if (RSPPointerIdx >= 0 && RSPPointerIdx <= 28)
    {
        SendMessage(hWnd_rspdebugdisasm, LB_SETCURSEL, RSPPointerIdx, 0);
        SetDlgItemText(rspdebughWnd, LDebugPointer[RSPPointerIdx], ">");
        SetDlgItemText(rspdebughWnd, RDebugPointer[RSPPointerIdx], "<");
    }
}

void RefreshRSPRegisters(void)
{
    int t;
    char temp[128], buf[32];

    if (!loaded) return;

    for (t = 0; t < 32; t++)
	{
        sprintf(buf, "%08X", RSP->RSP_GPR[t]);
		SetDlgItemText(rspregisterhWnd, RSP_GPR_Regs[t], buf);

		sprintf(buf, "%04X      ", (ushort)RSP->Get_VectS(t, 0)); strcpy(temp, buf);
		sprintf(buf, "%04X      ", (ushort)RSP->Get_VectS(t, 1)); strcat(temp, buf);
		sprintf(buf, "%04X      ", (ushort)RSP->Get_VectS(t, 2)); strcat(temp, buf);
		sprintf(buf, "%04X      ", (ushort)RSP->Get_VectS(t, 3)); strcat(temp, buf);
		sprintf(buf, "%04X      ", (ushort)RSP->Get_VectS(t, 4)); strcat(temp, buf);
		sprintf(buf, "%04X      ", (ushort)RSP->Get_VectS(t, 5)); strcat(temp, buf);
		sprintf(buf, "%04X      ", (ushort)RSP->Get_VectS(t, 6)); strcat(temp, buf);
		sprintf(buf, "%04X",       (ushort)RSP->Get_VectS(t, 7)); strcat(temp, buf);

		SetDlgItemText(rspregisterhWnd, RSP_Vect_Regs[t], temp);
	}

    sprintf(buf, "%08X", sp_pc.PC);        SetDlgItemText(rspregisterhWnd, IDC_RSP_PC,        buf);
	sprintf(buf, "%08X", spreg.mem_addr);  SetDlgItemText(rspregisterhWnd, IDC_RSP_MEMADDR,   buf);
	sprintf(buf, "%08X", spreg.dram_addr); SetDlgItemText(rspregisterhWnd, IDC_RSP_DRAMADDR,  buf);
	sprintf(buf, "%08X", spreg.read_len);  SetDlgItemText(rspregisterhWnd, IDC_RSP_RDLEN,     buf);
	sprintf(buf, "%08X", spreg.write_len); SetDlgItemText(rspregisterhWnd, IDC_RSP_WRLEN,     buf);
	sprintf(buf, "%08X", spreg.status);    SetDlgItemText(rspregisterhWnd, IDC_RSP_STATUS,    buf);
	sprintf(buf, "%08X", spreg.dma_full);  SetDlgItemText(rspregisterhWnd, IDC_RSP_DMAFULL,   buf);
	sprintf(buf, "%08X", spreg.dma_busy);  SetDlgItemText(rspregisterhWnd, IDC_RSP_DMABUSY,   buf);
	sprintf(buf, "%08X", spreg.semaphore); SetDlgItemText(rspregisterhWnd, IDC_RSP_SEMAPHORE, buf);

	sprintf(buf, "%08X", dpcreg.start);    SetDlgItemText(rspregisterhWnd, IDC_RDP_DMASTART,   buf);
	sprintf(buf, "%08X", dpcreg.end);      SetDlgItemText(rspregisterhWnd, IDC_RDP_DMAEND,     buf);
	sprintf(buf, "%08X", dpcreg.current);  SetDlgItemText(rspregisterhWnd, IDC_RDP_DMACURRENT, buf);
	sprintf(buf, "%08X", dpcreg.status);   SetDlgItemText(rspregisterhWnd, IDC_RDP_STATUS,     buf);
	sprintf(buf, "%08X", dpcreg.clock);    SetDlgItemText(rspregisterhWnd, IDC_RDP_CLOCK,      buf);
	sprintf(buf, "%08X", dpcreg.bufbusy);  SetDlgItemText(rspregisterhWnd, IDC_RDP_BUFBUSY,    buf);
	sprintf(buf, "%08X", dpcreg.pipebusy); SetDlgItemText(rspregisterhWnd, IDC_RDP_PIPEBUSY,   buf);
	sprintf(buf, "%08X", dpcreg.tmem);     SetDlgItemText(rspregisterhWnd, IDC_RDP_TMEMLOAD,   buf);
}

void RefreshDebugger(void)
{
    ulong t;
    char buf[64], tmp[64];
    int cnt;
    CODMAPNode *cm;
    BPNode *bp;

    if (!loaded) return;

    if (PointerIdx >= 0 && PointerIdx <= 28)
    {
        SetDlgItemText(debughWnd, LDebugPointer[PointerIdx], "");
        SetDlgItemText(debughWnd, RDebugPointer[PointerIdx], "");
    }

    PointerIdx = -1;

    if (DebuggerPC == 0) 
        DebuggerPC = r4reg.PC - 0x00000038;

    SendMessage(hWnd_debugdisasm, LB_RESETCONTENT, 0, 0);

    for (t = DebuggerPC, cnt = 0; t < (DebuggerPC + 0x00000074); t += 0x00000004, cnt++)
    {
		// If there is a breakpoint on this opcode, set it back to the original opcode.
		bp = FindBPNode(t);
		if (bp) Write32(t, bp->opcode);

		// Make the opcode.
		raw_opcode_addr = t;
		MakeDebugOpcode();

		// If we changed the opcode, set it back to invalid.
		if (bp) Write32(t, 0x7F7F7F7F);

        if (!is_outside_memory)
        {
            OpcodePrintTable[DECODE_OPCODE](tmp);

            if (HasBreakpoint(t))
            {
                cm = FindCODMAPNode(t);
                if (cm == NULL)
                    sprintf(buf, "*%08X:\t%s", t, tmp);
                else
                    sprintf(buf, "*%s:\t%s", cm->symbol, tmp);
            }
            else
            {
                cm = FindCODMAPNode(t);
                if (cm == NULL)
                    sprintf(buf, "%08X:\t%s", t, tmp);
                else
                    sprintf(buf, "%s:\t%s", cm->symbol, tmp);
            }
        }
        else
        {
            if (HasBreakpoint(t))
            {
                cm = FindCODMAPNode(t);
                if (cm == NULL)
                    sprintf(buf, "*%08X:\t***", t);
                else
                    sprintf(buf, "*%s:\t***", cm->symbol);
            }
            else
            {
                cm = FindCODMAPNode(t);
                if (cm == NULL)
                    sprintf(buf, "%08X:\t***", t);
                else
                    sprintf(buf, "%s:\t***", cm->symbol);
            }
        }

        SendMessage(hWnd_debugdisasm, LB_ADDSTRING, 0, (LPARAM)buf);

        if (t == r4reg.PC)
            PointerIdx = cnt;
    }
    
    if (PointerIdx >= 0 && PointerIdx <= 28)
    {
        SendMessage(hWnd_debugdisasm, LB_SETCURSEL, PointerIdx, 0);
        SetDlgItemText(debughWnd, LDebugPointer[PointerIdx], ">");
        SetDlgItemText(debughWnd, RDebugPointer[PointerIdx], "<");
    }
}

void RefreshRegisters(void)
{
    int t;
    char buf[32];
    float a;
    ulong *l = (ulong *)&a;

    if (!loaded) return;

    for (t = 0; t < 32; t++)
    {
        a = (float)r4reg.FGR[t];
        sprintf(buf, "%08X", r4reg.GPR[t]); SetDlgItemText(registerhWnd, GPR_Regs[t], buf);
        sprintf(buf, "%08X", *l);           SetDlgItemText(registerhWnd, FGR_Regs[t], buf);
    }

    sprintf(buf, "%08X", r4reg.PC);     SetDlgItemText(registerhWnd, IDC_DEBUG_PC,     buf);
    sprintf(buf, "%d",   r4reg.LLBit);  SetDlgItemText(registerhWnd, IDC_DEBUG_LLBIT,  buf);
    sprintf(buf, "%08X", r4reg.MultHI); SetDlgItemText(registerhWnd, IDC_DEBUG_MULTHI, buf);
    sprintf(buf, "%08X", r4reg.MultLO); SetDlgItemText(registerhWnd, IDC_DEBUG_MULTLO, buf);

    sprintf(buf, "%08X", c0reg.Index);    SetDlgItemText(registerhWnd, IDC_DEBUG_INDEX,    buf);
    sprintf(buf, "%08X", c0reg.Random);   SetDlgItemText(registerhWnd, IDC_DEBUG_RANDOM,   buf);
    sprintf(buf, "%08X", c0reg.EntryLo0); SetDlgItemText(registerhWnd, IDC_DEBUG_ENTRYLO0, buf);
    sprintf(buf, "%08X", c0reg.EntryLo1); SetDlgItemText(registerhWnd, IDC_DEBUG_ENTRYLO1, buf);
    sprintf(buf, "%08X", c0reg.Context);  SetDlgItemText(registerhWnd, IDC_DEBUG_CONTEXT,  buf);
    sprintf(buf, "%08X", c0reg.PageMask); SetDlgItemText(registerhWnd, IDC_DEBUG_PAGEMASK, buf);
    sprintf(buf, "%08X", c0reg.Wired);    SetDlgItemText(registerhWnd, IDC_DEBUG_WIRED,    buf);
    sprintf64(buf, c0reg.BadVAddr);       SetDlgItemText(registerhWnd, IDC_DEBUG_BADVADDR, buf);
    sprintf(buf, "%08X", c0reg.Count);    SetDlgItemText(registerhWnd, IDC_DEBUG_COUNT,    buf);
    sprintf(buf, "%08X", c0reg.EntryHi);  SetDlgItemText(registerhWnd, IDC_DEBUG_ENTRYHI,  buf);
    sprintf(buf, "%08X", c0reg.Compare);  SetDlgItemText(registerhWnd, IDC_DEBUG_COMPARE,  buf);
    sprintf(buf, "%08X", c0reg.Status);   SetDlgItemText(registerhWnd, IDC_DEBUG_STATUS,   buf);
    sprintf(buf, "%08X", c0reg.Cause);    SetDlgItemText(registerhWnd, IDC_DEBUG_CAUSE,    buf);
    sprintf(buf, "%08X", c0reg.ExceptionPC); SetDlgItemText(registerhWnd, IDC_DEBUG_EXCEPTPC, buf);
    sprintf(buf, "%08X", c0reg.PRevID);   SetDlgItemText(registerhWnd, IDC_DEBUG_PREVID,   buf);
    sprintf(buf, "%08X", c0reg.Config);   SetDlgItemText(registerhWnd, IDC_DEBUG_CONFIG,   buf);
    sprintf(buf, "%08X", c0reg.LLAddr);   SetDlgItemText(registerhWnd, IDC_DEBUG_LLADDR,   buf);
    sprintf64(buf, c0reg.XContext);       SetDlgItemText(registerhWnd, IDC_DEBUG_XCONTEXT, buf);
    sprintf(buf, "%08X", c0reg.WatchLo);  SetDlgItemText(registerhWnd, IDC_DEBUG_WATCHLO,  buf);
    sprintf(buf, "%08X", c0reg.WatchHi);  SetDlgItemText(registerhWnd, IDC_DEBUG_WATCHHI,  buf);
    sprintf(buf, "%08X", c0reg.PErr);     SetDlgItemText(registerhWnd, IDC_DEBUG_PERR,     buf);
    sprintf(buf, "%08X", c0reg.CacheErr); SetDlgItemText(registerhWnd, IDC_DEBUG_CACHEERR, buf);
    sprintf(buf, "%08X", c0reg.TagLo);    SetDlgItemText(registerhWnd, IDC_DEBUG_TAGLO,    buf);
    sprintf(buf, "%08X", c0reg.TagHi);    SetDlgItemText(registerhWnd, IDC_DEBUG_TAGHI,    buf);
    sprintf(buf, "%08X", c0reg.ErrorEPC); SetDlgItemText(registerhWnd, IDC_DEBUG_ERROREPC, buf);

    sprintf(buf, "%08X", pireg.dram_addr); SetDlgItemText(registerhWnd, IDC_DEBUG_PI_DRAM, buf);
    sprintf(buf, "%08X", pireg.cart_addr); SetDlgItemText(registerhWnd, IDC_DEBUG_PI_CART, buf);
    sprintf(buf, "%08X", pireg.rd_len);    SetDlgItemText(registerhWnd, IDC_DEBUG_PI_RDLEN, buf);
    sprintf(buf, "%08X", pireg.wr_len);    SetDlgItemText(registerhWnd, IDC_DEBUG_PI_WRLEN, buf);
    sprintf(buf, "%08X", pireg.status);    SetDlgItemText(registerhWnd, IDC_DEBUG_PI_STATUS, buf);

    sprintf(buf, "%08X", pireg.dom1_lat);          SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D1LATN, buf);
    sprintf(buf, "%08X", pireg.dom1_pulse_width);  SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D1PULSE, buf);
    sprintf(buf, "%08X", pireg.dom1_page_size);    SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D1PGSZ, buf);
    sprintf(buf, "%08X", pireg.dom1_release);      SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D1REL, buf);

    sprintf(buf, "%08X", pireg.dom2_lat);          SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D2LATN, buf);
    sprintf(buf, "%08X", pireg.dom2_pulse_width);  SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D2PULSE, buf);
    sprintf(buf, "%08X", pireg.dom2_page_size);    SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D2PGSZ, buf);
    sprintf(buf, "%08X", pireg.dom2_release);      SetDlgItemText(registerhWnd, IDC_DEBUG_PI_D2REL, buf);

    sprintf(buf, "%08X", c1reg.Impl_Rev); SetDlgItemText(registerhWnd, IDC_DEBUG_FCR0,  buf);
    sprintf(buf, "%08X", c1reg.FCSR);     SetDlgItemText(registerhWnd, IDC_DEBUG_FCR31, buf);

    sprintf(buf, "%08X", mireg.mode);      SetDlgItemText(registerhWnd, IDC_DEBUG_MI_MODE, buf);
    sprintf(buf, "%08X", mireg.version);   SetDlgItemText(registerhWnd, IDC_DEBUG_MI_VERSION, buf);
    sprintf(buf, "%08X", mireg.intr);      SetDlgItemText(registerhWnd, IDC_DEBUG_MI_INTERRUPT, buf);
    sprintf(buf, "%08X", mireg.intr_mask); SetDlgItemText(registerhWnd, IDC_DEBUG_MI_INTRMASK, buf);
}

void RefreshMemory(void)
{
    int x, y;
    ulong addr;
    uchar b;
    char buf[128], text[32], temp[8];

    if (!loaded) return;

    addr = memory_addr;

    if (!mem_inupdate)
    {
        sprintf(buf, "%08X", addr);
        SetDlgItemText(memoryhWnd, IDC_MEMORY_ADDR, buf);
    }

    SendMessage(hWnd_memorydump, LB_RESETCONTENT, 0, 0);

    for (y = 0; y < 11; y++)
    {
		memset(text, 0, 32);
        sprintf(buf, "%08X:  ", addr);

        for (x = 0; x < 16; x++)
        {
			b = Debug_Read8(addr++);

            if (!is_outside_memory)
			{
                sprintf(temp, "%02X ", b);
				strcat(buf, temp);

	            if (b < 32 || b > 127) b = 32;
				sprintf(temp, "%c", b);
				strcat(text, temp);
			}
            else
			{
                sprintf(temp, "** ");
				strcat(buf, temp);

                sprintf(temp, "*");
				strcat(text, temp);
			}

            if (x == 7) strcat(buf, " ");
        }

        strcat(buf, " ");
		strcat(buf, text);

        SendMessage(hWnd_memorydump, LB_ADDSTRING, 0, (LPARAM)buf);
    }
}

void RefreshCallStack(void)
{
	CODMAPNode *cm_pc, *cm_call;
	char buf[256];
	int t, x;

    SendMessage(hWnd_callstack, LB_RESETCONTENT, 0, 0);

	for (t = CSTACK_count; t > 0; t--)
	{
		x = t - 1;

		cm_pc   = FindCODMAPNode(CSTACK_data[x].PC);
		cm_call = FindCODMAPNode(CSTACK_data[x].call);

		if (cm_pc == NULL)
		{
			if (cm_call == NULL)
				sprintf(buf, "%08X:\t%08X", CSTACK_data[x].PC, CSTACK_data[x].call);
			else
				sprintf(buf, "%08X:\t%s", CSTACK_data[x].PC, cm_call->symbol);
		}
		else
		{
			if (cm_call == NULL)
				sprintf(buf, "%s:\t%08X", cm_pc->symbol, CSTACK_data[x].call);
			else
				sprintf(buf, "%s:\t%s", cm_pc->symbol, cm_call->symbol);
		}

        SendMessage(hWnd_callstack, LB_ADDSTRING, 0, (LPARAM)buf);
    }
}
#endif // _DEBUG

void RefreshDebugAll(void)
{
#ifdef _DEBUG
	RSPDebuggerPC = 0;
    DebuggerPC = 0;

	RefreshRSPDebugger();
	RefreshRSPRegisters();
    RefreshDebugger();
    RefreshRegisters();
    RefreshMemory();
	RefreshCallStack();
#endif // _DEBUG
}

void ToggleBreakpoint(ulong addr)
{
    BPNode *bp;

    // Check and see if one already exists at this address.
    bp = FirstBPNode;
    while (bp)
    {
        if (bp->PC == addr)
        {
            // Write the opcode back and remove this node from the list.
            Write32(bp->PC, bp->opcode);
            RemoveBPNode(bp);
            return;
        }

        bp = bp->next;
    }

    // Setup a new one.
    bp = AddBPNode();
    bp->PC = addr;
    bp->opcode = Read32(bp->PC);
    Write32(bp->PC, 0x7F7F7F7F);
}

bool HasBreakpoint(ulong addr)
{
    BPNode *bp;

    bp = FirstBPNode;
    while (bp)
    {
        if (bp->PC == addr) return TRUE;
        bp = bp->next;
    }

    return FALSE;
}

void AddBPRegValue(ulong val)
{
    int t;

    for (t = 0; t < NUM_BREAKPOINTS; t++)
    {
        if (!bkpt_regv[t].active)
        {
            bkpt_regv[t].active = TRUE;
            bkpt_regv[t].value  = val;
            return;
        }
    }

    MessageBox(MainhWnd, "Couldn't add breakpoint on register value -- table full", NULL, MB_OK);
}

bool SearchSection(ulong beg_addr, ulong end_addr, ulong val)
{
    int t, i;
    MSG msg;
    char buf[128];
    FARPROC memproc;
    HWND memdlg, hWnd_loadfname, hWnd_loadpct, hWnd_loadprog;
    ulong to_search;

    // Open 'Loading ROM' modeless dialog...
    memproc = MakeProcInstance((FARPROC)LoadProc, MainhInst);
    memdlg  = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_LOADING), MainhWnd, memproc);
    
    hWnd_loadfname = GetDlgItem(memdlg, IDC_LOAD_NAME);
    hWnd_loadpct   = GetDlgItem(memdlg, IDC_LOAD_PCT);
    hWnd_loadprog  = GetDlgItem(memdlg, IDC_LOAD_PROGRESS);

    to_search = (end_addr + 1) - beg_addr;
    SendMessage(hWnd_loadprog, PBM_SETRANGE, 0, MAKELPARAM(0, to_search / 16384));
    SendMessage(hWnd_loadprog, PBM_SETSTEP, (WPARAM)1, 0);

    t = to_search / 16384;
    while (t--)
    {
        sprintf(buf, "Searching Memory [%08X]...", beg_addr);
        SetDlgItemText(memdlg, IDC_LOAD_NAME, buf);
        SendMessage(hWnd_loadprog, PBM_STEPIT, 0, 0);

        i = 4096;
        while (i--)
        {
            if (Read32(beg_addr) == val)
            {
                sprintf(buf, "Found [%08X] at address [%08X]", val, beg_addr);
                MessageBox(MainhWnd, buf, NULL, MB_OK);

                memory_addr = beg_addr;
                DestroyWindow(memdlg);
                FreeProcInstance(memproc);
                return TRUE;
            }

            beg_addr += 4;
        }

        if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            GetMessage(&msg, NULL, 0, 0);
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    DestroyWindow(memdlg);
    FreeProcInstance(memproc);
    return FALSE;
}

void SearchMemory(ulong val)
{
    int t;

    for (t = 0; t < NUM_MEMMAPS; t++)
    {
        if (memmap[t].ptr)
        {
            if (SearchSection(memmap[t].start_addr, memmap[t].end_addr, val) == TRUE)
                return;
        }
    }
}

void CheckBreakpoints(void)
{
	int t, x;
	char buf[256];

    // Search breakpoints on register value for any matches...
	if (breakpoints_on_regv)
	{
		for (x = 0; x < NUM_BREAKPOINTS; x++)
		{
			if (bkpt_regv[x].active)
			{
				for (t = 0; t < 32; t++)
				{
					if (r4reg.GPR[t] == bkpt_regv[x].value)
					{
						bkpt_regv[x].active = FALSE;

						sprintf(buf, "Encountered breakpoint on register value at %08X\nRegister [%s] == Value [%08X]", 
							r4reg.PC - 0x00000004, GPR_LUT[t], bkpt_regv[x].value);
						MessageBox(NULL, buf, NULL, MB_OK);

						running = FALSE;
						if (inDebugger) PostMessage(MainhWnd, WM_COMMAND, ID_DEBUG_REFRESH, 0);
						return;
					}
				}
			}
		}
	}
}
