/* ================================================================================== */
/* Copyright (c) 1998-1999 3Com Corporation or its subsidiaries. All rights reserved. */
/* ================================================================================== */

#include "EmulatorCommon.h"
#include "DebugMgr.h"

#include "ATraps.h"				// ATrap
#include "Byteswapping.h"		// Canonical
#include "CPU_REG.h"			// LowMem_GetGlobal, LowMem_SetGlobal, LowMem::GetTrapAddress
#include "Crc.h"				// Crc16CalcBlock
#include "ErrorHandling.h"		// kError_NoError, ReportUnhandledException
#include "Logging.h"			// gErrLog
#include "MetaMemory.h"			// MetaMemory::MarkCPUBreak
#include "Platform.h"			// Platform::SendPacket
#include "RAM_ROM.h"			// CEnableFullAccess
#include "SessionFile.h"		// SessionFile
#include "SocketMessaging.h"	// Socket::Write
#include "StringData.h"			// kExceptionNames
#include "Strings.r.h"			// kStr_ values
#include "TrapPatches.h"		// RemoveAllTailpatches, InstallAllTailpatches
#include "UAE_Utils.h"			// uae_memcpy


#define kOpcode_ADD		0x0697	// ADD.L X, (A7)
#define kOpcode_LINK	0x4E50
#define kOpcode_RTE		0x4E73
#define kOpcode_RTD		0x4E74
#define kOpcode_RTS		0x4E75
#define kOpcode_JMP		0x4ED0


// Types

#pragma mark Types

struct SysPktEmptyRspType
{
	_sysPktBodyCommon;								// Common Body header
};


static const char* kPacketNames[256];

#define PacketName(command) (kPacketNames[(command) & 0x7F])

// Functions

#pragma mark Functions

static uae_u32		PrvGetAddressRegister	(int num);
static uae_u32		PrvGetDataRegister		(int num);
static Bool			PrvBPEquals				(uae_u32 a, uae_u32 b);
static Bool			PrvBPNotEquals			(uae_u32 a, uae_u32 b);
static Bool			PrvBPGreater			(uae_u32 a, uae_u32 b);
static Bool			PrvBPGreaterOrEqual		(uae_u32 a, uae_u32 b);
static Bool			PrvBPLess				(uae_u32 a, uae_u32 b);
static Bool			PrvBPLessOrEqual		(uae_u32 a, uae_u32 b);
static const char*	PrvSkipWhite			(const char* p);

static Bool			PrvParseDecimal			(const char **ps, int *i);
static Bool			PrvParseUnsigned		(const char **ps, uae_u32 *u);


struct NamedConditionType
{
	const char*		name;
	compareFun		function;
};

static NamedConditionType kConditions[] =
{
	{"==",	&PrvBPEquals},
	{"!=",	&PrvBPNotEquals},
	{">",	&PrvBPGreater},
	{">=",	&PrvBPGreaterOrEqual},
	{"<",	&PrvBPLess},
	{"<=",	&PrvBPLessOrEqual},
	{NULL,	NULL}
};



/*
	+- Called by Debugger
	|	+- Implemented
	|	|								Command
	|	|	Command name				Number
	-------	---------------------------	-----------
	*	*	sysPktStateCmd				0x00		SysPktStateCmdType				SysPktStateRspType
	*	*	sysPktReadMemCmd			0x01		SysPktReadMemCmdType			SysPktReadMemRspType
	*	*	sysPktWriteMemCmd			0x02		SysPktWriteMemCmdType			SysPktWriteMemRspType
	*	.	sysPktSingleStepCmd			0x03		
	*	*	sysPktGetRtnNameCmd			0x04		SysPktRtnNameCmdType			SysPktRtnNameRspType
	*	*	sysPktReadRegsCmd			0x05		SysPktReadRegsCmdType			SysPktReadRegsRspType
	*	*	sysPktWriteRegsCmd			0x06		SysPktWriteRegsCmdType			SysPktWriteRegsRspType
	*	*	sysPktContinueCmd			0x07		SysPktContinueCmdType			<no response>
	*	*	sysPktRPCCmd				0x0A		SysPktRPCType
		*	sysPktGetBreakpointsCmd		0x0B		SysPktGetBreakpointsCmdType		SysPktGetBreakpointsRspType
	*	*	sysPktSetBreakpointsCmd		0x0C		SysPktSetBreakpointsCmdType		SysPktSetBreakpointsRspType
		.	sysPktRemoteUIUpdCmd		0x0C		SysPktRemoteUIUpdCmdType
		.	sysPktRemoteEvtCmd			0x0D		SysPktRemoteEvtCmdType
	*	*	sysPktDbgBreakToggleCmd		0x0D		SysPktDbgBreakToggleCmdType		SysPktDbgBreakToggleRspType
	*	.	sysPktFlashCmd				0x0E		SysPktFlashWriteType
	*	.	sysPktCommCmd				0x0F		SysPktCommCmdType				SysPktCommRspType
	*	*	sysPktGetTrapBreaksCmd		0x10		SysPktGetTrapBreaksCmdType		SysPktGetTrapBreaksRspType
	*	*	sysPktSetTrapBreaksCmd		0x11		SysPktSetTrapBreaksCmdType		SysPktSetTrapBreaksRspType
		+	sysPktGremlinsCmd			0x12		SysPktGremlinsCmdType
	*	*	sysPktFindCmd				0x13		SysPktFindCmdType				SysPktFindRspType
		*	sysPktGetTrapConditionsCmd	0x14		SysPktGetTrapConditionsCmdType	SysPktGetTrapConditionsRspType
		*	sysPktSetTrapConditionsCmd	0x15		SysPktSetTrapConditionsCmdType	SysPktSetTrapConditionsRspType
		.	sysPktChecksumCmd			0x16		SysPktChecksumType
		.	sysPktExecFlashCmd			0x17		sysPktExecFlashType
	*	*	sysPktRemoteMsgCmd			0x7f		SysPktRemoteMsgCmdType
*/


static void PrvPrintHeader (const SlkPktHeaderType& header)
{
	LogAppendMsg ("	header.signature1	= 0x%04X",	header.signature1);
	LogAppendMsg ("	header.signature2	= 0x%02X",	header.signature2);
	LogAppendMsg ("	header.dest			= 0x%02X",	header.dest);
	LogAppendMsg ("	header.src			= 0x%02X",	header.src);
	LogAppendMsg ("	header.type			= 0x%02X",	header.type);
	LogAppendMsg ("	header.bodySize		= 0x%04X",	header.bodySize);
	LogAppendMsg ("	header.transId		= 0x%02X",	header.transId);
	LogAppendMsg ("	header.checksum		= 0x%02X",	header.checksum);
}

static void PrvPrintBody (const SysPktBodyType& body)
{
	static Word	gLastNumReadBytes;

	if (PacketName (body.command))
	{
		LogAppendMsg ("	body.command		= 0x%02X / %s%s.",
			body.command,
			PacketName (body.command),
			(body.command & 0x80) == 0 ? "Cmd" : "Rsp");
	}
	else
	{
		LogAppendMsg ("	body.command		= 0x%02X / <unknown command>.",
			body.command);
	}

	switch (body.command)
	{
		case sysPktStateCmd:
			break;
		case sysPktStateRsp:
		{
			SysPktStateRspType&	body2 = (SysPktStateRspType&) body;
			int	what = body2.exceptionId / 4;

			LogAppendMsg ("	body.resetted		= %s",		body2.resetted ? "true" : "false");
			LogAppendMsg ("	body.exceptionId	= 0x%02X / %s",	what, kExceptionNames [what]);

			LogAppendMsg ("	body.reg.d[0]		= 0x%08X",	body2.reg.d[0]);
			LogAppendMsg ("	body.reg.d[1]		= 0x%08X",	body2.reg.d[1]);
			LogAppendMsg ("	body.reg.d[2]		= 0x%08X",	body2.reg.d[2]);
			LogAppendMsg ("	body.reg.d[3]		= 0x%08X",	body2.reg.d[3]);
			LogAppendMsg ("	body.reg.d[4]		= 0x%08X",	body2.reg.d[4]);
			LogAppendMsg ("	body.reg.d[5]		= 0x%08X",	body2.reg.d[5]);
			LogAppendMsg ("	body.reg.d[6]		= 0x%08X",	body2.reg.d[6]);
			LogAppendMsg ("	body.reg.d[7]		= 0x%08X",	body2.reg.d[7]);

			LogAppendMsg ("	body.reg.a[0]		= 0x%08X",	body2.reg.a[0]);
			LogAppendMsg ("	body.reg.a[1]		= 0x%08X",	body2.reg.a[1]);
			LogAppendMsg ("	body.reg.a[2]		= 0x%08X",	body2.reg.a[2]);
			LogAppendMsg ("	body.reg.a[3]		= 0x%08X",	body2.reg.a[3]);
			LogAppendMsg ("	body.reg.a[4]		= 0x%08X",	body2.reg.a[4]);
			LogAppendMsg ("	body.reg.a[5]		= 0x%08X",	body2.reg.a[5]);
			LogAppendMsg ("	body.reg.a[6]		= 0x%08X",	body2.reg.a[6]);

			LogAppendMsg ("	body.reg.usp		= 0x%08X",	body2.reg.usp);
			LogAppendMsg ("	body.reg.ssp		= 0x%08X",	body2.reg.ssp);
			LogAppendMsg ("	body.reg.pc			= 0x%08X",	body2.reg.pc);
			LogAppendMsg ("	body.reg.sr			= 0x%04X",	body2.reg.sr);

			LogAppendMsg ("	body.bp[0]			= 0x%08X",	body2.bp[0]);
			LogAppendMsg ("	body.bp[1]			= 0x%08X",	body2.bp[1]);
			LogAppendMsg ("	body.bp[2]			= 0x%08X",	body2.bp[2]);
			LogAppendMsg ("	body.bp[3]			= 0x%08X",	body2.bp[3]);
			LogAppendMsg ("	body.bp[4]			= 0x%08X",	body2.bp[4]);
			LogAppendMsg ("	body.bp[5]			= 0x%08X",	body2.bp[5]);

			LogAppendMsg ("	body.startAddr		= 0x%08X",	body2.startAddr);
			LogAppendMsg ("	body.endAddr		= 0x%08X",	body2.endAddr);
			LogAppendMsg ("	body.name			= %s",		body2.name);
			LogAppendMsg ("	body.trapTableRev	= %d",		body2.trapTableRev);
			break;
		}

		case sysPktReadMemCmd:
		{
			SysPktReadMemCmdType&	body2 = (SysPktReadMemCmdType&) body;
			LogAppendMsg ("	body.address		= 0x%08X",	body2.address);
			LogAppendMsg ("	body.numBytes		= 0x%04X",	body2.numBytes);

			gLastNumReadBytes = body2.numBytes;
			break;
		}
		case sysPktReadMemRsp:
		{
			SysPktReadMemRspType&	body2 = (SysPktReadMemRspType&) body;
			LogAppendData (&body2 + 1, gLastNumReadBytes, "	body.data			= ");
			break;
		}

		case sysPktWriteMemCmd:
		{
			SysPktWriteMemCmdType&	body2 = (SysPktWriteMemCmdType&) body;
			LogAppendMsg ("	body.address		= 0x%08X",	body2.address);
			LogAppendMsg ("	body.numBytes		= 0x%04X",	body2.numBytes);
			LogAppendData (&body2 + 1, body2.numBytes, "	body.data			= ");
			break;
		}
		case sysPktWriteMemRsp:
			break;

		case sysPktSingleStepCmd:
			break;
//		case sysPktSingleStepRsp:
//			break;

		case sysPktGetRtnNameCmd:
			break;
		case sysPktGetRtnNameRsp:
		{
			SysPktRtnNameRspType&	body2 = (SysPktRtnNameRspType&) body;
			LogAppendMsg ("	body.address		= 0x%08X",	body2.address);
			LogAppendMsg ("	body.startAddr		= 0x%08X",	body2.startAddr);
			LogAppendMsg ("	body.endAddr		= 0x%08X",	body2.endAddr);
			LogAppendMsg ("	body.name			= %s",		body2.name);
			break;
		}

		case sysPktReadRegsCmd:
			break;
		case sysPktReadRegsRsp:
			break;

		case sysPktWriteRegsCmd:
			break;
		case sysPktWriteRegsRsp:
			break;

		case sysPktContinueCmd:
			break;
//		case sysPktContinueRsp:
//			break;

		case sysPktRPCCmd:
		case sysPktRPCRsp:
		{
			SysPktRPCType&	body2 = (SysPktRPCType&) body;
			LogAppendMsg ("	body.trapWord		= 0x%04X / %s.",	body2.trapWord,
																	::GetTrapName (body2.trapWord));
			if (body.command == sysPktRPCRsp)
			{
				// There's a bug when talking with MWDebug over the MMF transport.
				// resultD0 and resultA0 were accidentally not byteswapped, something
				// that MWDebug accounts for and swaps itself.  When the byteswap
				// bug was fixed in Poser, MWDebug no longer worked.  Therefore,
				// we have to keep the bug when talking over that transport.  Anyone
				// using a sockets port should expect resultD0 and resultA0 to be
				// in the correct (host) format.

#if defined (_WINDOWS)
				if (!Socket::CheckConnection ())
				{
					Canonical (body2.resultD0);
					Canonical (body2.resultA0);
				}
#endif

				LogAppendMsg ("	body.resultD0		= 0x%08X.",	body2.resultD0);
				LogAppendMsg ("	body.resultA0		= 0x%08X.",	body2.resultA0);

#if defined (_WINDOWS)
				if (!Socket::CheckConnection ())
				{
					Canonical (body2.resultD0);
					Canonical (body2.resultA0);
				}
#endif
			}
			LogAppendMsg ("	body.numParams		= 0x%04X.",	body2.numParams);

			SysPktRPCParamInfo*	param = body2.param;

			for (Word ii = 0; ii < body2.numParams; ++ii)
			{
				if (param->byRef)
				{
					LogAppendData (&param->data, param->size,
						"	body.param%d			= %d bytes of data.", ii, param->size);
				}
				else if (body.command == sysPktRPCCmd)
				{
					if (param->size == 1)
					{
						uae_u8	value = *(uae_u8*) param->data;
						Canonical (value);
						LogAppendMsg ("	body.param%d			= 0x%02X.", ii, value);
					}
					else if (param->size == 2)
					{
						uae_u16	value = *(uae_u16*) param->data;
						Canonical (value);
						LogAppendMsg ("	body.param%d			= 0x%04X.", ii, value);
					}
					else if (param->size == 4)
					{
						uae_u32	value = *(uae_u32*) param->data;
						Canonical (value);
						LogAppendMsg ("	body.param%d			= 0x%08X.", ii, value);
					}
				}

				param = (SysPktRPCParamInfo*) (((char*) param) + offsetof (SysPktRPCParamInfo, data) +
												((param->size + 1) & ~1));
			}
			break;
		}

		case sysPktGetBreakpointsCmd:
			break;
		case sysPktGetBreakpointsRsp:
			break;

//		case sysPktSetBreakpointsCmd:
//			break;
		case sysPktSetBreakpointsRsp:
			break;

		case sysPktRemoteUIUpdCmd:
			break;
//		case sysPktRemoteUIUpdRsp:
//			break;

		case sysPktRemoteEvtCmd:
			break;
//		case sysPktRemoteEvtRsp:
//			break;

//		case sysPktDbgBreakToggleCmd:
//			break;
		case sysPktDbgBreakToggleRsp:
			break;

		case sysPktFlashCmd:
			break;
		case sysPktFlashRsp:
			break;

		case sysPktCommCmd:
			break;
		case sysPktCommRsp:
			break;

		case sysPktGetTrapBreaksCmd:
			break;
		case sysPktGetTrapBreaksRsp:
			break;

		case sysPktSetTrapBreaksCmd:
			break;
		case sysPktSetTrapBreaksRsp:
			break;

		case sysPktGremlinsCmd:
			break;
//		case sysPktGremlinsRsp:
//			break;

		case sysPktFindCmd:
			break;
		case sysPktFindRsp:
			break;

		case sysPktGetTrapConditionsCmd:
			break;
		case sysPktGetTrapConditionsRsp:
			break;

		case sysPktSetTrapConditionsCmd:
			break;
		case sysPktSetTrapConditionsRsp:
			break;

		case sysPktChecksumCmd:
			break;
		case sysPktChecksumRsp:
			break;

		case sysPktExecFlashCmd:
			break;
		case sysPktExecFlashRsp:
			break;

		case sysPktRemoteMsgCmd:
			break;
//		case sysPktRemoteMsgRsp:
//			break;

		default:
			break;
	}
}

static void PrvPrintFooter (const SlkPktFooterType& footer)
{
	LogAppendMsg ("	footer.crc16		= 0x%02X",	footer.crc16);
}


static SlkPktHeaderChecksum
PrvCalcHdrChecksum (SlkPktHeaderChecksum start, BytePtr bufP, Long count);

#define PRINTF	if (!LogHLDebugger ()) ; else LogAppendMsg

#pragma mark -


// ===========================================================================
//		¥ Debug
// ===========================================================================

DebugGlobalsType			gDebuggerGlobals;
Bool						gFullDebuggerPackets;

static SlkPktHeaderType*	gLatestHeader;


/***********************************************************************
 *
 * FUNCTION:	Debug::Initialize
 *
 * DESCRIPTION:	Initialize our debugging hooks.  Sets flags to tell the
 *				CPU loop to exit if any of the exceptions that we're
 *				interested in are encountered.  If an exception is
 *				encountered, the emulator calls Debug::EnterDebugger.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

/*-----------------------------------------------------------------------------------*\

	Notes on side-stepping the ROM debugger:

		The debugger manipulates the following low-memory globals:
		
		Byte	dbgWasEntered;		// set true the first time debugger is entered
		Byte	dbgInDebugger;		// true if in debugger
		Byte	dbgTracing;			// tracing in debugger
		Ptr		dbgGlobalsP;		// pointer to dbgGlobals
		Ptr		dbgSerGlobalsP;		// pointer to Debugger Serial globals

		We set dbgWasEntered and dbgInDebugger in EnterDebugger.  We clear
		dbgInDebugger in ExitDebugger.  Both of these values are examined by
		other parts of the ROM.
		
		dbgTracing is set when the ROM debugger needs to single step.  It needs
		to single-step if there are breakpoints in the ROM, or if we need to
		execute over an installed breakpoint.  We have other ways to do these
		(we can actually install breakpoints into the ROM, and our CPU loop has
		a mechanism for fetching the opcode that is currently overwritten by
		a breakpoint), so we never have to set dbgTracing to true.

		dbgGlobalsP appears to be accessed only by the ROM debugger code.  Since
		we should intercept everything that would cause that code to be accessed,
		we shouldn't have to do anything with that block.

		dbgSerGlobalsP is initialized by DbgInit.  None of the ROM debugger code
		looks at it.  Only the serial code does.

	May want to patch the following traps:

		ConGetS
		ConPutS
		
		sysTrapDbgSrcMessage
		sysTrapDbgMessage
		sysTrapDbgGetMessage
		sysTrapDbgCommSettings

\*-----------------------------------------------------------------------------------*/

void Debug::Initialize (void)
{
	memset (&gDebuggerGlobals, 0, sizeof (gDebuggerGlobals));

	// Install functions that will cause the CPU loop to exit if we're
	// connected to an external debugger.

	Software::InstallExceptionHandler (kException_BusErr, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_AddressErr, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_IllegalInstr, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_DivideByZero, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_Chk, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_Trap, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_Privilege, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_Trace, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_ATrap, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_FTrap, Debug::BreakIfConnected);
	Software::InstallExceptionHandler (kException_AutoVec7, Debug::BreakIfConnected);

	Software::InstallExceptionHandler (kException_Trap0 + sysDbgBreakpointTrapNum, Debug::BreakIfConnected);

	// This one's a little different.  We want to exit the CPU loop
	// on a TRAP 8, but we want to do it conditionally.  If desired,
	// HandleTrap8 will cause the CPU loop to exit (leaving the PC just
	// after the TRAP instruction).  Otherwise, it will treat the TRAP
	// like a NOP.

	Software::InstallExceptionHandler (kException_Trap0 + sysDbgTrapNum,
										Debug::HandleTrap8);

#if 0	// !!! TBD

	// Install Debugger Traps into trap table
	dispatchTableP[ SysTrapIndex (sysTrapDbgSrcMessage) ] = (Ptr)DbgMessage;
	dispatchTableP[ SysTrapIndex (sysTrapDbgMessage) ] = (Ptr)DbgMessage;
	dispatchTableP[ SysTrapIndex (sysTrapDbgGetMessage) ] = (Ptr)DbgGetMessage;
	dispatchTableP[ SysTrapIndex (sysTrapDbgCommSettings) ] = (Ptr)DbgCommSettings;

#endif

	// Open a line to the external debugger (if any).

	Platform::ConnectToDebugger ();

	kPacketNames [sysPktStateCmd]				= "sysPktState";
	kPacketNames [sysPktReadMemCmd]				= "sysPktReadMem";
	kPacketNames [sysPktWriteMemCmd]			= "sysPktWriteMem";
	kPacketNames [sysPktSingleStepCmd]			= "sysPktSingleStep";
	kPacketNames [sysPktGetRtnNameCmd]			= "sysPktGetRtnName";
	kPacketNames [sysPktReadRegsCmd]			= "sysPktReadRegs";
	kPacketNames [sysPktWriteRegsCmd]			= "sysPktWriteRegs";
	kPacketNames [sysPktContinueCmd]			= "sysPktContinue";
	kPacketNames [sysPktRPCCmd]					= "sysPktRPC";
	kPacketNames [sysPktGetBreakpointsCmd]		= "sysPktGetBreakpoints";
	kPacketNames [sysPktSetBreakpointsCmd]		= "sysPktSetBreakpoints";
	kPacketNames [sysPktRemoteUIUpdCmd]			= "sysPktRemoteUIUpd";
	kPacketNames [sysPktRemoteEvtCmd]			= "sysPktRemoteEvt";
	kPacketNames [sysPktDbgBreakToggleCmd]		= "sysPktDbgBreakToggle";
	kPacketNames [sysPktFlashCmd]				= "sysPktFlash";
	kPacketNames [sysPktCommCmd]				= "sysPktComm";
	kPacketNames [sysPktGetTrapBreaksCmd]		= "sysPktGetTrapBreaks";
	kPacketNames [sysPktSetTrapBreaksCmd]		= "sysPktSetTrapBreaks";
	kPacketNames [sysPktGremlinsCmd]			= "sysPktGremlins";
	kPacketNames [sysPktFindCmd]				= "sysPktFind";
	kPacketNames [sysPktGetTrapConditionsCmd]	= "sysPktGetTrapConditions";
	kPacketNames [sysPktSetTrapConditionsCmd]	= "sysPktSetTrapConditions";
	kPacketNames [sysPktChecksumCmd]			= "sysPktChecksum";
	kPacketNames [sysPktExecFlashCmd]			= "sysPktExecFlash";
	kPacketNames [sysPktRemoteMsgCmd]			= "sysPktRemoteMsg";
}


/***********************************************************************
 *
 * FUNCTION:	Debug::Reset
 *
 * DESCRIPTION:	Standard reset function.  Sets the sub-system to a
 *				default state.  This occurs not only on a Reset (as
 *				from the menu item), but also when the sub-system
 *				is first initialized (Reset is called after Initialize)
 *				as well as when the system is re-loaded from an
 *				insufficient session file.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::Reset (void)
{
	gDebuggerGlobals.firstEntrance	= true;
	gDebuggerGlobals.inDebugger = false;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::Save
 *
 * DESCRIPTION:	Standard save function.  Saves any sub-system state to
 *				the given session file.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::Save (SessionFile& f)
{
	const long	kCurrentVersion = 1;

	Chunk		chunk;
	ChunkStream	s (chunk);

	s << kCurrentVersion;

	s << gDebuggerGlobals.inDebugger;
	s << gDebuggerGlobals.ignoreDbgBreaks;
	s << gDebuggerGlobals.reEntered;
	s << gDebuggerGlobals.firstEntrance;
	s << gDebuggerGlobals.stepSpy;
	s << gDebuggerGlobals.breakingOnATrap;
	s << gDebuggerGlobals.continueOverATrap;
	s << gDebuggerGlobals.continueOverBP;

	s << gDebuggerGlobals.checkTrapWordOnExit;
	s << gDebuggerGlobals.trapWord;
	s << gDebuggerGlobals.refNum;

	int ii;
	for (ii = 0; ii < dbgTotalBreakpoints; ++ii)
	{
		s << (uaecptr) gDebuggerGlobals.bp[ii].addr;
		s << gDebuggerGlobals.bp[ii].enabled;
		s << gDebuggerGlobals.bp[ii].installed;

		s << gDebuggerGlobals.bpOpcode[ii];

		if (gDebuggerGlobals.bpCondition[ii])
		{
			// Save only the source string; we can reconstruct
			// the rest of the fields from that.

			s << gDebuggerGlobals.bpCondition[ii]->source;
		}
		else
		{
			s << "";
		}
	}

	for (ii = 0; ii < dbgTotalTrapBreaks; ++ii)
	{
		s << gDebuggerGlobals.trapBreak[ii];
		s << gDebuggerGlobals.trapParam[ii];
	}

	s << gDebuggerGlobals.ssAddr;
	s << gDebuggerGlobals.ssValue;

	s << gDebuggerGlobals.excType;

	s << gDebuggerGlobals.watchEnabled;
	s << gDebuggerGlobals.watchAddr;
	s << gDebuggerGlobals.watchBytes;

	f.WriteDebugInfo (chunk);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::Load
 *
 * DESCRIPTION:	Standard load function.  Loads any sub-system state
 *				from the given session file.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::Load (SessionFile& f)
{
	Chunk	chunk;
	if (f.ReadDebugInfo (chunk))
	{
		long		version;
		ChunkStream	s (chunk);

		s >> version;

		if (version >= 1)
		{
			s >> gDebuggerGlobals.inDebugger;
			s >> gDebuggerGlobals.ignoreDbgBreaks;
			s >> gDebuggerGlobals.reEntered;
			s >> gDebuggerGlobals.firstEntrance;
			s >> gDebuggerGlobals.stepSpy;
			s >> gDebuggerGlobals.breakingOnATrap;
			s >> gDebuggerGlobals.continueOverATrap;
			s >> gDebuggerGlobals.continueOverBP;

			s >> gDebuggerGlobals.checkTrapWordOnExit;
			s >> gDebuggerGlobals.trapWord;
			s >> gDebuggerGlobals.refNum;

			int ii;
			for (ii = 0; ii < dbgTotalBreakpoints; ++ii)
			{
				uaecptr	temp;
				s >> temp; gDebuggerGlobals.bp[ii].addr = (Ptr) temp;
				s >> gDebuggerGlobals.bp[ii].enabled;
				s >> gDebuggerGlobals.bp[ii].installed;

				s >> gDebuggerGlobals.bpOpcode[ii];

				string	source;
				s >> source;

				if (source.size () > 0)
				{
					BreakpointCondition*	bc = NewBreakpointCondition (source.c_str ());
					gDebuggerGlobals.bpCondition[ii] = bc;
				}
			}

			for (ii = 0; ii < dbgTotalTrapBreaks; ++ii)
			{
				s >> gDebuggerGlobals.trapBreak[ii];
				s >> gDebuggerGlobals.trapParam[ii];
			}

			s >> gDebuggerGlobals.ssAddr;
			s >> gDebuggerGlobals.ssValue;

			s >> gDebuggerGlobals.excType;

			s >> gDebuggerGlobals.watchEnabled;
			s >> gDebuggerGlobals.watchAddr;
			s >> gDebuggerGlobals.watchBytes;
		}
	}
	else
	{
		f.SetCanReload (false);
	}
}


/***********************************************************************
 *
 * FUNCTION:	Debug::Dispose
 *
 * DESCRIPTION:	Standard dispose function.  Completely release any
 *				resources acquired or allocated in Initialize and/or
 *				Load.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::Dispose (void)
{
	Platform::DisconnectFromDebugger ();
}


/***********************************************************************
 *
 * FUNCTION:	Debug::HandleNewPacket
 *
 * DESCRIPTION:	Completely handle a packet sent from an external
 *				debugger, setting any state and sending a reply if
 *				necessary.
 *
 * PARAMETERS:	pkt - a reference to a generic packet buffer.  This
 *					buffer is large enough to hold any currently-
 *					defined message.  The contents of the packet are
 *					determined by the first byte of the packet, which
 *					contains a command number.  We switch on that
 *					command number, cast the packet to the right data
 *					type, and call a handler function.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::HandleNewPacket (const SlkPktHeaderType& hdr,
								const SysPktBodyType& pkt)
{
	ErrCode	result = kError_NoError;

	PRINTF ("Entering Debug::HandleNewPacket.");

	// Make a copy of everything (in case we change it in place, we
	// don't upset the caller).

	SlkPktHeaderType	header = hdr;
	SysPktBodyType		packet = pkt;

	// Byteswap the data (if necessary).

	Canonical (header);
	Canonical (packet);

	// Keep a reference to the header, so that we can use it when forming replies.

	gLatestHeader = &header;

	if (LogHLDebuggerData ())
	{
		PrvPrintHeader (header);
		PrvPrintBody (packet);
//		PrvPrintFooter (footer);
	}
	else
	{
		if (PacketName (packet.command))
			PRINTF ("	Received %sCmd packet.", PacketName (packet.command));
		else
			PRINTF ("	Received unknown (0x%02X) packet.", packet.command);
	}

	// If this isn't for the debugger, just return.  Yeah, this just drops
	// the packet on the floor; we might want to form a better response
	// than that sometime later.

	if ((header.src != slkSocketDebugger) && (header.src != slkSocketConsole))
	{
		PRINTF ("Packet not for us; leaving Debug::HandleNewPacket.");

		goto Exit;
	}

	// If the packet is for the debugger socket, then we pretty much need
	// to have the CPU stopped.  If not, when the PalmDebugger user executes
	// "g", the restored registers may not be what the machine expects.
	//
	// As a courtesy, we stop the CPU if we get a "state" packet.  That way,
	// the user can enter the debugger by typing "att" in the PalmDebugger and
	// doesn't have to fuss with shortcut-.1.
	//
	// Otherwise, we ignore the packet.

	if (header.src == slkSocketDebugger)
	{
		if (!Debug::InDebugger ())
		{
			if (packet.command == sysPktStateCmd)
			{
				// This will send the state packet as well as setting our
				// "in the debugger" state.

				result = Debug::EnterDebugger (sysDbgTrapNum, m68k_getpc ());
				goto Exit;
			}

			// This packet is for the debugger socket, we're not in the
			// debugger, and the packet is not a "state" command, so just
			// ignore this request.

			goto Exit;
		}
	}

	{
		// Give us full access to RAM, ROM, Dragonball registers, low-mem, etc.
	
		CEnableFullAccess	munge;	// Remove blocks on memory access.

		// Goose the ROM, so it doesn't go to sleep while we're exchanging packets.
		// Do this by hand; we can't call ROM functions as subroutines while in
		// the debugger (we don't know what state the ROM is in).

	//	EvtResetAutoOffTimer ();
		LowMem_SetGlobal (sysAutoOffEvtTicks, LowMem_GetGlobal (hwrCurTicks));

		switch (packet.command)
		{
			case sysPktStateCmd:
				result = Debug::SendState ();
				break;

			case sysPktReadMemCmd:
				result = Debug::ReadMem ((SysPktReadMemCmdType&) packet);
				break;

			case sysPktWriteMemCmd:
				result = Debug::WriteMem ((SysPktWriteMemCmdType&) packet);
				break;

			case sysPktSingleStepCmd:
				// I don't think there's anything to do here.  I think that
				// single-stepping is performed by the debugger setting the
				// trace bit in the SR and sending a sysPktContinueCmd.
				PRINTF ("	Not supported in this release.");
				break;

			case sysPktGetRtnNameCmd:
				result = Debug::SendRoutineName ((SysPktRtnNameCmdType&) packet);
				break;

			case sysPktReadRegsCmd:
				result = Debug::ReadRegs ((SysPktReadRegsCmdType&) packet);
				break;

			case sysPktWriteRegsCmd:
				result = Debug::WriteRegs ((SysPktWriteRegsCmdType&) packet);
				break;

			case sysPktContinueCmd:
				result = Debug::Continue ((SysPktContinueCmdType&) packet);
				break;

			case sysPktRPCCmd:
				result = Debug::RPC ((SysPktRPCType&) packet);
				break;

			case sysPktGetBreakpointsCmd:
				result = Debug::GetBreakpoints ((SysPktGetBreakpointsCmdType&) packet);
				break;

			case sysPktSetBreakpointsCmd:
				result = Debug::SetBreakpoints ((SysPktSetBreakpointsCmdType2&) packet);
				break;

	//		case sysPktRemoteUIUpdCmd:
	//			// Sent TO debugger; never received FROM debugger.
	//			break;

	//		case sysPktRemoteEvtCmd:
	//			// Sent TO debugger; never received FROM debugger.
	//			break;

			case sysPktDbgBreakToggleCmd:
				result = Debug::ToggleBreak ((SysPktDbgBreakToggleCmdType&) packet);
				break;

			case sysPktFlashCmd:
				// Not supported in this release.
				PRINTF ("	Not supported in this release.");
				break;

			case sysPktCommCmd:
				// Not supported in this release.
				PRINTF ("	Not supported in this release.");
				break;

			case sysPktGetTrapBreaksCmd:
				result = Debug::GetTrapBreaks ((SysPktGetTrapBreaksCmdType&) packet);
				break;

			case sysPktSetTrapBreaksCmd:
				result = Debug::SetTrapBreaks ((SysPktSetTrapBreaksCmdType2&) packet);
				break;

			case sysPktGremlinsCmd:
				// Not supported in this release.
				PRINTF ("	Not supported in this release.");
				break;

			case sysPktFindCmd:
				result = Debug::Find ((SysPktFindCmdType&) packet);
				break;

			case sysPktGetTrapConditionsCmd:
				result = Debug::GetTrapConditions ((SysPktGetTrapConditionsCmdType&) packet);
				break;

			case sysPktSetTrapConditionsCmd:
				result = Debug::SetTrapConditions ((SysPktSetTrapConditionsCmdType2&) packet);
				break;

			case sysPktChecksumCmd:
				// Not supported in this release.
				PRINTF ("	Not supported in this release.");
				break;

			case sysPktExecFlashCmd:
				// Not supported in this release.
				PRINTF ("	Not supported in this release.");
				break;

			case sysPktRemoteMsgCmd:
				// Nothing for us to do.  With a serial link,
				// these are sent to clear the input port.
				PRINTF ("	Should not be *receiving these!");
				break;

			case 0xFF:
			{
				// This is a special comand only for Poser

				char fn[256];
				strcpy (fn, (char*) &packet.data[14]);

				LoadableList	fileList;
				fileList.push_back (LoadFilePB (fn));

				::LoadPalmFileList (fileList);
				break;
			}

			default:
				break;
		}
	}

Exit:
	// Zap our reference to the latest header now that we're done with it.

	gLatestHeader = NULL;

	PRINTF ("Exiting Debug::HandleNewPacket.");

	return result;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SendPacket
 *
 * DESCRIPTION:	Sends the given packet to the external debugger.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SendPacket (const void* pkt, long size)
{
	PRINTF ("Entering Debug::SendPacket.");

	// Fold all the parts into a buffer: header, body, footer.

	char	buffer[	sizeof (SlkPktHeaderType) +
					sizeof (SysPktBodyType) +
					sizeof (SlkPktFooterType)];

	SlkPktHeaderPtr	headerP	= (SlkPktHeaderPtr)	&buffer[0];
	SysPktBodyPtr	bodyP	= (SysPktBodyPtr)	&buffer[sizeof (SlkPktHeaderType)];
	SlkPktFooterPtr	footerP	= (SlkPktFooterPtr)	&buffer[sizeof (SlkPktHeaderType) + size];


	// Gen up a header.  If we're replying to a packet from the external
	// debugger, use the information from it to form a reply (socket
	// numbers and transaction ID).  Otherwise, stuff in some of our
	// own values.

	if (gLatestHeader)
	{
		headerP->signature1	= slkPktHeaderSignature1;
		headerP->signature2	= slkPktHeaderSignature2;
		headerP->dest		= gLatestHeader->src;
		headerP->src		= gLatestHeader->dest;
		headerP->type		= slkPktTypeSystem;
		headerP->bodySize	= size;
		headerP->transId	= gLatestHeader->transId;
		headerP->checksum	= 0;
	}
	else
	{
		headerP->signature1	= slkPktHeaderSignature1;
		headerP->signature2	= slkPktHeaderSignature2;
		headerP->dest		= slkSocketDebugger;
		headerP->src		= slkSocketDebugger;
		headerP->type		= slkPktTypeSystem;
		headerP->bodySize	= size;
		headerP->transId	= 0;
		headerP->checksum	= 0;
	}

	// Compute and stuff the header checksum

	Canonical (*headerP);
	headerP->checksum =  PrvCalcHdrChecksum (0, (BytePtr) headerP,
					sizeof (SlkPktHeaderType) - sizeof (SlkPktHeaderChecksum));
	Canonical (headerP->checksum);

	if (LogHLDebuggerData ())
	{
		Canonical (*headerP);
		PrvPrintHeader (*headerP);
		Canonical (*headerP);
	}


	// Copy in the packet body and byteswap it.

	memcpy (bodyP, pkt, size);
	if (LogHLDebuggerData ())
	{
		PrvPrintBody (*bodyP);
	}
	else
	{
		if (PacketName (bodyP->command))
			PRINTF ("	Sending %sRsp packet.", PacketName (bodyP->command));
		else
			PRINTF ("	Sending unknown (0x%02X) packet.", bodyP->command);
	}
	Canonical (*bodyP);

	// Calculate the footer checksum

	footerP->crc16 = Crc16CalcBlock (headerP, sizeof (SlkPktHeaderType), 0);
	footerP->crc16 = Crc16CalcBlock (bodyP, size, footerP->crc16);
	if (LogHLDebuggerData ())
		PrvPrintFooter (*footerP);
	Canonical (*footerP);

	// Send it to the external debugger.

	long	totalSize = sizeof (SlkPktHeaderType) + size + sizeof (SlkPktFooterType);
	
	ErrCode	result;

#if defined (_WINDOWS)
	if (Socket::CheckConnection ())
	{
		Socket::Write (buffer, totalSize);

		result = kError_NoError;
	}
	else
#endif
	{
		if (!gFullDebuggerPackets)
		{
			totalSize -= sizeof (SlkPktFooterType);
		}

		result = Platform::SendPacket (buffer, totalSize);
	}

	PRINTF ("Exiting Debug::SendPacket.");

	return result;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SendResponse
 *
 * DESCRIPTION:	Sends a simple null-body message to the external debugger.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SendResponse (int rsp)
{
	SysPktEmptyRspType	response;

	response.command = rsp;
	response._filler = 0;

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SendMessage
 *
 * DESCRIPTION:	Sends a text string to the external debugger.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SendMessage (const char* msg)
{
	SysPktBodyType	response;

	response.command = sysPktRemoteMsgCmd;
	response._filler = 0;

	size_t	len = strlen (msg) + 1;
	strcpy ((char*) &response.data[0], msg);

	return Debug::SendPacket (&response, sizeof (SysPktRemoteMsgCmdType) + len);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::BreakIfConnected
 *
 * DESCRIPTION:	Handle the given exception.  If we're connected to a
 *				debugger, we want to let it handle it, so change the CPU
 *				state and return TRUE (to say that we handled the
 *				execption).  Otherwise return false to perform default
 *				handling (which means handing it off to the emulated ROM).
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

Bool Debug::BreakIfConnected (uae_s32 exceptionNumber, uaecptr oldpc)
{
	// Dump any logged information to a file.

	LogDump ();

	// Attempt to enter the debugger.  If we succeed, set the break-reason
	// so that the CPU loop exits.  Then return true to say that we
	// handled the exception.

	if (Debug::EnterDebugger (exceptionNumber, oldpc) == kError_NoError)
	{
		Emulator::SetBreakReason (exceptionNumber);

		return true;
	}

	// We were unable to enter the debugger (perhaps there was no external
	// debugger running).  Return false to cause normal processing to occur
	// (which is probably to let the emulated ROM handle the exception,
	// either by contacting a debugger over the serial port, or by showing
	// a fatal exception error).

//	return false;

	// Ohhh...let's not do that.  Letting the ROM debug stubs take over isn't
	// really a good idea; they tend to hang the ROM, and the user wouldn't
	// like that.  Instead, let's show a dialog that lets them Reset.

	int button;
	uae_u16	opcode = get_word (oldpc);
	if (exceptionNumber == kException_ATrap &&
		(opcode == 0xA9EB || opcode == 0xA9EC || opcode == 0xA9EE))
	{
		button = Errors::ReportSANEUsage ();
	}
	else
	{
		button = Errors::ReportUnhandledException (exceptionNumber);
	}

	// (Exception is thrown if button == kDebug or kReset).

	Emulator::HandleDlgButton (button, oldpc);

	return true;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::HandleTrap8
 *
 * DESCRIPTION:	The CPU just encountered a TRAP 8, which is what gets
 *				compiled into the developer's code when he "calls" the
 *				DbgBreak or DbgSrcBreak "function".  HandleTrap8
 *				determines whether or not this TRAP 8 should be handled
 *				as an exception (in which case we'd enter the debugger)
 *				or should be skipped like a NOP. If "ignoreDbgBreaks" is
 *				true, then merely return TRUE to say that we completely
 *				handled the exception.  Otherwise, change the CPU state
 *				to non-running before we return TRUE.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

Bool Debug::HandleTrap8 (uae_s32 exceptionNumber, uaecptr oldpc)
{
	if (gDebuggerGlobals.ignoreDbgBreaks)
	{
		return true;
	}

	return Debug::BreakIfConnected (exceptionNumber, oldpc);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::HandleSystemCall
 *
 * DESCRIPTION:	An A-Trap is about to be executed.  This function is
 *				called to determine if we want to break on this A-Trap.
 *				If there are A-Trap breaks registered with the debugger,
 *				and if the "continueOverATrap" flag is not set (if it were set,
 *				that would mean that we'd just exited the debugger, and
 *				don't need to re-break on the A-Trap that caused us to
 *				enter the debugger), then scan the break table to see if
 *				the A-Trap we're about to execute is one we want to
 *				break on.
 *
 *				If it is, set the CPU break reason to kException_Trace,
 *				which will cause the CPU to break at the end of this
 *				opcode.  Also return TRUE, which tells the normal A-Trap
 *				handler that we completely handled this A-Trap, and that
 *				it doesn't need to do any trap dispatcher stuff.  Finally,
 *				back-up the PC so that it points to the A-Trap again so
 *				that when we resume execution we'll start at that A-Trap.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

Bool Debug::HandleSystemCall (const SystemCallContext& context)
{
	Bool	doBreak = false;

	if (!gDebuggerGlobals.continueOverATrap && gDebuggerGlobals.breakingOnATrap)
	{
		doBreak = Debug::MustBreakOnTrapSystemCall (context.fTrapWord, context.fExtra);

		// If we're supposed to break here and we can successfully contact an
		// external debugger, set the CPU state so that the CPU loop breaks at
		// the end of the current instruction.  Also back up the PC to point to
		// the TRAP instruction instead of the $Axxx selector.
		//
		// Ooops...gotta make sure we fudge the PC _before_ sending the state
		// to the debugger...

		if (doBreak)
		{
			uaecptr	curpc = m68k_getpc ();
			m68k_setpc (context.fPC);

			if (Debug::EnterDebugger (kException_Trace, context.fPC) == kError_NoError)
			{
				Emulator::SetBreakReason (kException_Trace);

				// Check again on the way to to see if there's still
				// a breakpoint at this location.  If so, we need to
				// set *another* flag to make sure we skip over the
				// breakpoint.  Otherwise, we'd just break here over
				// and over again.

				gDebuggerGlobals.checkTrapWordOnExit = true;
				gDebuggerGlobals.trapWord = context.fTrapWord;
				gDebuggerGlobals.refNum = context.fExtra;
			}
			else
			{
				m68k_setpc (curpc);
			}
		}
	}

	// Clear the flag telling us to inhibit the ATrapBreak check.

	gDebuggerGlobals.continueOverATrap = false;

	return doBreak;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::EnterDebugger
 *
 * DESCRIPTION:	Put the emulator into "debug mode".  This pretty much
 *				consists of setting up a bunch of flags, figuring out
 *				why we entered the debugger, and telling the external
 *				debugger that we've entered debug mode and what the
 *				current CPU state is.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

//
//	Reasons for entering the debugger:
//
//		Single step:
//			reason == kException_Trace
//
//			Normal processing.
//
//		Step spy:
//			reason == kException_Trace
//
//			Normal processing.
//
//		User breakpoint (temporary or otherwise):
//			reason == kException_Trap0 + sysDbgBreakpointTrapNum
//
//			Normal processing.
//
//		Compiled breakpoint (DbgBreak, DbgSrcBreak):
//			reason == kException_Trap0 + sysDbgTrapNum
//
//			Ignore if DbgGlobalsType.ignoreDbgBreaks is true.
//			
//			Actually, now we catch this one a little earlier.  In
//			Software_ProcessException, we call an installed exception
//			handler that leads to calling Debug::HandleTrap8.  That
//			function will return TRUE if ignoreDbgBreaks is true, which
//			will effectively turn the TRAP 8 into a NOP.  Otherwise, it
//			returns false, which will lead to EnterDebugger being called
//			to handle the exception.
//
//		"A-Trap" break:
//			reason == kException_Trace
//
//			When looking for an A-Trap to break on, the debugger uses
//			a special trap dispatcher.  Called on every TRAP F, when it
//			finds a trap to break on, it sets DbgGlobalsType.breakingOnATrap,
//			sets the tracing SR bit, and then calls the original trap
//			dispatcher.
//
//			Because the trap dispatcher is run in supervisor mode, the
//			tracing bit doesn't do anything until it RTE's.
//
//			Normal processing.
//
//		Exception (bus error, address error, etc):
//			reason == exception code
//
//			Normal processing.
//
//	The debugger is silently entered if reason == kException_Trace.
//	Otherwise, it displays a message saying what exception occured.

ErrCode Debug::EnterDebugger (uae_s32 reason, uaecptr startPC)
{
	PRINTF ("Entering Debug::EnterDebugger.");

	CEnableFullAccess	munge;	// Remove blocks on memory access.

	// If we entered due to a breakpoint (TRAP 0), backup the PC.

	uaecptr	curpc = m68k_getpc ();
	if (reason == (kException_Trap0 + sysDbgBreakpointTrapNum))
	{
		m68k_setpc (startPC);
	}

	// Save the reason for our entering the debugger.

	gDebuggerGlobals.excType = (Word) (reason * 4);

	// If we're re-entering the debugger, note that fact.

	gDebuggerGlobals.reEntered = LowMem_GetGlobal (dbgInDebugger) != 0;

	// Turn off sound in case it was on when the debugger was entered.

	HWRegisters::TurnSoundOff ();

	// Send a state message packet notifying the host that we've entered.
	// If we can't send the packet (perhaps because there is no external
	// debugger listening) return false saying that we failed to enter
	// the debugger.  When we return false, we indicate that perhaps the
	// emulated ROM debugger should take over.
	//
	// Concurrency note: in the multi-threaded version of the emulator,
	// EnterDebugger will be called from one thread (the CPU thread),
	// while the UI thread will be listening for packets from the
	// external debugger.  When SendState sends the packet from this
	// thread, the external debugger could receive it and send us back
	// a message immediately.  The question is, is this OK?  Are there
	// problems with the UI thread trying to debug us before the CPU
	// thread has had a chance to stop itself? Let's see: the UI thread
	// will receive the message, and start to handle it by telling the
	// CPU thread to stop.  That means that we should have a chance to
	// clean up (set our flags, remove breakpoints, etc.) and stop before
	// the UI thread tries to debug us.

	ErrCode	result = Debug::SendState ();

	if (result == kError_NoError)
	{
		// Flag to the ROM that we're in the debugger. "dbgInDebugger" gets
		// cleared in ExitDebugger. "dbgWasDebugger" stays set to true.

		LowMem_SetGlobal (dbgWasEntered, true);
		LowMem_SetGlobal (dbgInDebugger, true);
		gDebuggerGlobals.inDebugger = true;

		PRINTF ("Entered debug mode.");
	}
	else
	{
		// Reverse the pc-munging that we did earlier.

		m68k_setpc (curpc);

		PRINTF ("Failed to enter debug mode.");
	}

	PRINTF ("Exiting Debug::EnterDebugger.");

	return result;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::ExitDebugger
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::ExitDebugger (void)
{
	CEnableFullAccess	munge;	// Remove blocks on memory access.

	// If we're continuing, but we're on a breakpoint, set a boolean
	// that causes us to ignore the breakpoint when we hit it.

	for (int ii = 0; ii < dbgTotalBreakpoints; ++ii)
	{
		if (gDebuggerGlobals.bp[ii].enabled == false)
			continue;

		uaecptr	addr = (uaecptr) gDebuggerGlobals.bp[ii].addr;
		if (!addr)
			continue;

		if (addr == m68k_getpc ())
			gDebuggerGlobals.continueOverBP = true;
	}

	// Check to see if we (a) stopped here because of a request
	// to break on a particular system call and (b) that we're
	// still requested to break on this call.  If so, set a flag
	// to skip over the next break-on-system-call.  Otherwise,
	// we'd just break at this location over and over again.

	if (gDebuggerGlobals.checkTrapWordOnExit)
	{
		gDebuggerGlobals.checkTrapWordOnExit = false;

		if (MustBreakOnTrapSystemCall (gDebuggerGlobals.trapWord,
										gDebuggerGlobals.refNum))
		{
			// Set the flag that tells this function to not break
			// the next time it's entered.

			gDebuggerGlobals.continueOverATrap = true;
		}
	}

	// Set flags that we're no longer in the debugger.

	gDebuggerGlobals.excType = 0;
	LowMem_SetGlobal (dbgInDebugger, false);
	gDebuggerGlobals.inDebugger = false;

	return kError_NoError;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::InDebugger
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

Bool Debug::InDebugger (void)
{
	return gDebuggerGlobals.inDebugger;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::HandleCPUBreak
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::HandleCPUBreak (void)
{
	// Don't break on soft breakpoints if we're calling the ROM internally.

	if (ATrap::DoingCall ())
		return;

	// Don't break on soft breakpoints if we're exiting the debugger.

	if (gDebuggerGlobals.continueOverBP)
	{
		gDebuggerGlobals.continueOverBP = false;
		return;
	}

	ConditionalBreak (m68k_getpc ());
}


/***********************************************************************
 *
 * FUNCTION:	Debug::InstallCPUBreaks
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::InstallCPUBreaks (void)
{
	// Install the breakpoints.
	
	for (int ii = 0; ii < dbgTotalBreakpoints; ++ii)
	{
		if (gDebuggerGlobals.bp[ii].enabled)
		{
			MetaMemory::MarkCPUBreak ((uaecptr) gDebuggerGlobals.bp[ii].addr);
		}
	}
}


/***********************************************************************
 *
 * FUNCTION:	Debug::RemoveCPUBreaks
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::RemoveCPUBreaks (void)
{
	// Remove the breakpoints.
	
	for (int ii = 0; ii < dbgTotalBreakpoints; ++ii)
	{
		if (gDebuggerGlobals.bp[ii].enabled)
		{
			MetaMemory::UnmarkCPUBreak ((uaecptr) gDebuggerGlobals.bp[ii].addr);
		}
	}
}


/***********************************************************************
 *
 * FUNCTION:	Debug::NewBreakpointCondition
 *
 * DESCRIPTION:	Create a new breakpoint condition by parsing the given
 *				source string.  Returns NULL on parse error.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

BreakpointCondition* Debug::NewBreakpointCondition (const char* sourceString)
{
	const char*	source = sourceString;
	registerFun	regType;
	int			regNum;
	Bool		indirect = false;
	uae_u32		indirectOffset;
	int			size = 4;

	compareFun	condition;
	int			value;

	source = PrvSkipWhite (source);
	if (!source)
		return NULL;

	if (isdigit (*source))
	{
		indirect = true;
		if (!PrvParseUnsigned (&source, &indirectOffset))
			return NULL;

		source = PrvSkipWhite (source);

		if (*source != '(')
			return NULL;

		++source;
		source = PrvSkipWhite (source);
	}

	switch (tolower (*source++))
	{
		case 'd':
			regType = PrvGetDataRegister;
			break;

		case 'a':
			regType = PrvGetAddressRegister;
			break;

		default:
			return NULL;
	}

	source = PrvSkipWhite (source);

	if (!PrvParseDecimal (&source, &regNum))
		return NULL;

	source = PrvSkipWhite (source);

	if (indirect)
	{
		if (*source != ')')
			return NULL;

		++source;
		source = PrvSkipWhite (source);
	}

	if (*source == '.')
	{
		++source;

		switch (*source)
		{
			case 'b': size = 1; break;
			case 'w': size = 2; break;
			case 'l': size = 4; break;
			default: return NULL;
		}

		++source;
		source = PrvSkipWhite (source);
	}

	condition = NULL;
	for (NamedConditionType* c = kConditions; c->name; ++c)
	{
		if (!strncmp (source, c->name, strlen (c->name)))
		{
			condition = c->function;
			source += strlen (c->name);
			break;
		}
	}

	if (!condition)
		return NULL;

	if (sscanf (source, "%i", &value) != 1)
		return NULL;

	BreakpointCondition* bc = new BreakpointCondition;

	bc->regType			= regType;
	bc->regNum			= regNum;
	bc->indirect		= indirect;
	bc->indirectOffset	= indirectOffset;
	bc->size			= size;

	bc->condition		= condition;
	bc->value			= value;
	bc->source			= _strdup (sourceString);

	return bc;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SetBreakpoint
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::SetBreakpoint (int index, uaecptr addr, BreakpointCondition* c)
{
	ClearBreakpoint (index);

	gDebuggerGlobals.bp[index].enabled		= true;
	gDebuggerGlobals.bp[index].installed	= false;
	gDebuggerGlobals.bp[index].addr			= (Ptr) addr;
	gDebuggerGlobals.bpCondition[index]		= c;

	Emulator::InstallCPUBreaks ();
}


/***********************************************************************
 *
 * FUNCTION:	Debug::ClearBreakpoint
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::ClearBreakpoint (int index)
{
	gDebuggerGlobals.bp[index].enabled = false;

	if (gDebuggerGlobals.bpCondition[index])
	{
		if (gDebuggerGlobals.bpCondition[index]->source)
			free (gDebuggerGlobals.bpCondition[index]->source);

		delete gDebuggerGlobals.bpCondition[index];
		gDebuggerGlobals.bpCondition[index] = NULL;
	}

	Emulator::InstallCPUBreaks ();
}


#pragma mark -


/***********************************************************************
 *
 * FUNCTION:	Debug::SendState
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SendState ()
{
	PRINTF ("Entering Debug::SendState.");

	SysPktStateRspType	response;

	// Fill in the response.

	// Command.

	response.command		= sysPktStateRsp;
	response._filler		= 0;

	// Get the resetted boolean.  This flag is true if the "device" has
	// been resetted since the last time we sent a packet.  The debugger
	// needs to know this in order to update it's notion of the device's
	// state (like, for instance, if breakpoints are installed).

	response.resetted		= gDebuggerGlobals.firstEntrance;
	gDebuggerGlobals.firstEntrance = false;

	// Get the exception id.

	response.exceptionId	= gDebuggerGlobals.excType;

	// Get the registers.

	GetRegs (response.reg);

	// Get the breakpoints.

	for (int i = 0; i < dbgTotalBreakpoints; i++)
	{
		response.bp[i] = gDebuggerGlobals.bp[i];
	}

	// Get some code beginning at PC.

	for (int ii = 0; ii < sysPktStateRspInstWords; ++ii)
	{
		response.inst[ii] = get_word (m68k_getpc () + ii * 2);
	}

	// Get the routine name and address range info.

	::FindFunctionName (	m68k_getpc (),
							response.name,
							(uaecptr*) &response.startAddr,
							(uaecptr*) &response.endAddr);

	// Send the rev of the trap table.

	response.trapTableRev	= LowMem_GetGlobal (sysDispatchTableRev);

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::ReadMem
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::ReadMem (SysPktReadMemCmdType& packet)
{
	// Use generic system packet body.
	// Allows for command, _filler, and (256 + 16 - 2) bytes of data.

//	SysPktReadMemRspType	response;
	SysPktBodyType			response;

	response.command	= sysPktReadMemRsp;
	response._filler	= 0;

	uae_memcpy ((void*) (&response.data), (uaecptr) packet.address,
				packet.numBytes);

	return Debug::SendPacket (&response, sizeof (SysPktReadMemRspType) + packet.numBytes);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::WriteMem
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::WriteMem (SysPktWriteMemCmdType& packet)
{
	uae_memcpy ((uaecptr) packet.address, (const void*) ((&packet) + 1),
				packet.numBytes);

	// If we just altered low memory, recalculate the low-memory checksum.
	// Make sure we're on a ROM that has this field!  Determine this by
	// seeing that the address of sysLowMemChecksum is below the memCardInfo
	// fields that come after the FixedGlobals.

	if (LowMem_Location (sysLowMemChecksum) < LowMem_GetGlobal (memCardInfoP))
	{
		if (packet.address < (void*) 0x100)
		{
			DWord		checksum	= 0;
			uaecptr		csP			= UAE_NULL;

			// First, calculate the checksum

			while (csP < (uaecptr) 0x100)
			{
				DWord	data = get_long (csP);

				// Don't do these trap vectors since they change whenever the
				// debugger is set to break on any a-trap or breakpoint.

				if (csP == offsetof (M68KExcTableType, trapN[sysDispatchTrapNum]))
					data = 0;

				if (csP == offsetof (M68KExcTableType, trapN[sysDbgBreakpointTrapNum]))
					data = 0;

				if (csP == offsetof (M68KExcTableType, trace))
					data = 0;

				checksum += data;
				csP += 4;
			}

			// Save new checksum

			LowMem_SetGlobal (sysLowMemChecksum, checksum);
		}
	}

	return Debug::SendResponse (sysPktWriteMemRsp);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SendRoutineName
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SendRoutineName (SysPktRtnNameCmdType& packet)
{
	SysPktRtnNameRspType	response;

	response.command	= sysPktGetRtnNameRsp;
	response._filler	= 0;
	response.address	= (void*) (((uae_u32) packet.address) & ~1);	// Protect against odd address.
	response.startAddr	= NULL;
	response.endAddr	= NULL;
	response.name[0]	= 0;

	::FindFunctionName(	(uaecptr) response.address,
						response.name,
						(uaecptr*) &response.startAddr,
						(uaecptr*) &response.endAddr);

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::ReadRegs
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::ReadRegs (SysPktReadRegsCmdType&)
{
	SysPktReadRegsRspType2	response;

	response.command	= sysPktReadRegsRsp;
	response._filler	= 0;

	Debug::GetRegs (response.reg);


#if defined (_WINDOWS)
	// There's a bug when talking with MWDebug over the MMF transport.
	// resultD0 and resultA0 were accidentally not byteswapped, something
	// that MWDebug accounts for and swaps itself.  When the byteswap
	// bug was fixed in Poser, MWDebug no longer worked.  Therefore,
	// we have to keep the bug when talking over that transport.  Anyone
	// using a sockets port should expect resultD0 and resultA0 to be
	// in the correct (host) format.

	if (!Socket::CheckConnection ())
	{
		Canonical ((SysPktReadRegsRspType&) response);
	}
#endif

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::WriteRegs
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::WriteRegs (SysPktWriteRegsCmdType& packet)
{
	Debug::SetRegs (packet.reg);

	return Debug::SendResponse (sysPktWriteRegsRsp);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::Continue
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::Continue (SysPktContinueCmdType& packet)
{
	Debug::SetRegs (packet.regs);

	gDebuggerGlobals.stepSpy = packet.stepSpy != 0;
	gDebuggerGlobals.ssAddr = packet.ssAddr;
	// ssCount is ignored?
	gDebuggerGlobals.ssValue = packet.ssCheckSum;

	ErrCode	err = Debug::ExitDebugger ();

	// Perform any platform-specific actions.

	if (!err)
	{
		Platform::ExitDebugger ();
	}

	return err;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::RPC
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::RPC (SysPktRPCType& packet)
{
	SysPktRPCParamInfo*	param = packet.param;

	ATrap	trap;
	for (Word ii = 0; ii < packet.numParams; ++ii)
	{
		if (param->byRef)
		{
			trap.PushLong ((uae_u32) (&param->data));
		}
		else
		{
			if (param->size == 1)
			{
				uae_u8	value = *(uae_u8*) param->data;
				Canonical (value);
				trap.PushByte (value);
			}
			else if (param->size == 2)
			{
				uae_u16	value = *(uae_u16*) param->data;
				Canonical (value);
				trap.PushWord (value);
			}
			else if (param->size == 4)
			{
				uae_u32	value = *(uae_u32*) param->data;
				Canonical (value);
				trap.PushLong (value);
			}
		}

		param = (SysPktRPCParamInfo*) (((char*) param) + offsetof (SysPktRPCParamInfo, data) +
										((param->size + 1) & ~1));
	}

	// Call the trap.  Special case the call to SysReset, as merely calling
	// that function will not return from it.

	if (packet.trapWord == sysTrapSysReset)
	{
		// Set flags that we're no longer in the debugger.  This is the same
		// as the last few steps of ExitDebugger.

		gDebuggerGlobals.excType = 0;
		LowMem_SetGlobal (dbgInDebugger, false);
		gDebuggerGlobals.inDebugger = false;

		// Reset the emulator (causes it to "reboot")

		Emulator::Reset();

		// That's it!  No reply!

		return kError_NoError;
	}

	trap.Call (packet.trapWord);

	packet.resultD0 = trap.GetD0 ();
	packet.resultA0 = trap.GetA0 ();

#if defined (_WINDOWS)
	// There's a bug when talking with MWDebug over the MMF transport.
	// resultD0 and resultA0 were accidentally not byteswapped, something
	// that MWDebug accounts for and swaps itself.  When the byteswap
	// bug was fixed in Poser, MWDebug no longer worked.  Therefore,
	// we have to keep the bug when talking over that transport.  Anyone
	// using a sockets port should expect resultD0 and resultA0 to be
	// in the correct (host) format.

	if (!Socket::CheckConnection ())
	{
		Canonical (packet.resultD0);
		Canonical (packet.resultA0);
	}
#endif

	SysPktRPCType&	response = packet;
	long			numBytes = ((char*) param) - ((char*) &packet);

	response.command	= sysPktRPCRsp;
	response._filler	= 0;

	return Debug::SendPacket (&response, numBytes);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::GetBreakpoints
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::GetBreakpoints (SysPktGetBreakpointsCmdType&)
{
	SysPktGetBreakpointsRspType2	response;

	response.command	= sysPktGetBreakpointsRsp;
	response._filler	= 0;

	memcpy (response.bp, gDebuggerGlobals.bp, sizeof (gDebuggerGlobals.bp));

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SetBreakpoints
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SetBreakpoints (SysPktSetBreakpointsCmdType2& packet)
{
	for (int ii = 0; ii < dbgTotalBreakpoints; ++ii)
	{
		if (packet.bp[ii].enabled)
		{
			SetBreakpoint (ii, (uaecptr) packet.bp[ii].addr, NULL);
		}
		else
		{
			ClearBreakpoint (ii);
		}
	}

	return Debug::SendResponse (sysPktSetBreakpointsRsp);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::ToggleBreak
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::ToggleBreak (SysPktDbgBreakToggleCmdType&)
{
	gDebuggerGlobals.ignoreDbgBreaks = !gDebuggerGlobals.ignoreDbgBreaks;

	SysPktDbgBreakToggleRspType	response;

	response.command	= sysPktDbgBreakToggleRsp;
	response._filler	= 0;

	response.newState	= gDebuggerGlobals.ignoreDbgBreaks;

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::GetTrapBreaks
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::GetTrapBreaks (SysPktGetTrapBreaksCmdType&)
{
	SysPktGetTrapBreaksRspType2	response;

	response.command	= sysPktGetTrapBreaksRsp;
	response._filler	= 0;

	memcpy (response.trapBP, gDebuggerGlobals.trapBreak,
			sizeof (gDebuggerGlobals.trapBreak));

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SetTrapBreaks
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SetTrapBreaks (SysPktSetTrapBreaksCmdType2& packet)
{
	memcpy (gDebuggerGlobals.trapBreak, packet.trapBP,
				sizeof (gDebuggerGlobals.trapBreak));

	gDebuggerGlobals.breakingOnATrap = false;
	for (int ii = 0; ii < dbgTotalTrapBreaks; ++ii)
	{
		if (gDebuggerGlobals.trapBreak[ii])
		{
			gDebuggerGlobals.breakingOnATrap = true;
			break;
		}
	}

	return Debug::SendResponse (sysPktSetTrapBreaksRsp);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::Find
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::Find (SysPktFindCmdType& packet)
{
	SysPktFindRspType	response;

	response.command	= sysPktFindRsp;
	response._filler	= 0;

	response.addr		= (DWord) NULL;
	response.found		= false;
	
	{
		BytePtr	dataP = (BytePtr) (&packet + 1);
		DWord	startP = packet.firstAddr;

		while (startP <= packet.lastAddr)
		{
			Word	ii;

			for (ii = 0; ii < packet.numBytes; ++ii)
			{
				Byte	b1 = get_byte(startP + ii);
				Byte	b2 = dataP[ii];

				// Handle caseless conversion

				if (packet.caseInsensitive)
				{
					if (isalpha (b1))
						b1 = tolower (b1);

					if (isalpha (b2))
						b2 = tolower (b2);
				}

				if (b1 != b2)			// if different -->
					break;
			}

			// Check if matched

			if (ii >= packet.numBytes)
			{
				response.addr = startP;
				response.found = true;
				break;
			}

			startP++;					// advance start of search pointer
		}
	}

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::GetTrapConditions
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::GetTrapConditions (SysPktGetTrapConditionsCmdType&)
{
	SysPktGetTrapConditionsRspType2	response;

	response.command	= sysPktGetTrapConditionsRsp;
	response._filler	= 0;

	memcpy (response.trapParam, gDebuggerGlobals.trapParam,
			sizeof (gDebuggerGlobals.trapParam));

	return Debug::SendPacket (&response, sizeof (response));
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SetTrapConditions
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

ErrCode Debug::SetTrapConditions (SysPktSetTrapConditionsCmdType2& packet)
{
	memcpy (gDebuggerGlobals.trapParam, packet.trapParam,
			sizeof (gDebuggerGlobals.trapParam));

	return Debug::SendResponse (sysPktSetTrapConditionsRsp);
}


#pragma mark -

/***********************************************************************
 *
 * FUNCTION:	Debug::GetRegs
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::GetRegs (M68KRegsType& debuggerRegs)
{
	debuggerRegs.d[0]	= m68k_dreg (regs, 0);
	debuggerRegs.d[1]	= m68k_dreg (regs, 1);
	debuggerRegs.d[2]	= m68k_dreg (regs, 2);
	debuggerRegs.d[3]	= m68k_dreg (regs, 3);
	debuggerRegs.d[4]	= m68k_dreg (regs, 4);
	debuggerRegs.d[5]	= m68k_dreg (regs, 5);
	debuggerRegs.d[6]	= m68k_dreg (regs, 6);
	debuggerRegs.d[7]	= m68k_dreg (regs, 7);

	debuggerRegs.a[0]	= m68k_areg (regs, 0);
	debuggerRegs.a[1]	= m68k_areg (regs, 1);
	debuggerRegs.a[2]	= m68k_areg (regs, 2);
	debuggerRegs.a[3]	= m68k_areg (regs, 3);
	debuggerRegs.a[4]	= m68k_areg (regs, 4);
	debuggerRegs.a[5]	= m68k_areg (regs, 5);
	debuggerRegs.a[6]	= m68k_areg (regs, 6);
//	debuggerRegs.a[7]	= m68k_areg (regs, 7);

	// Update the usp, isp, and/or msp before returning it.
	// (from m68k_dumpstate() in newcpu.c)
	
	if (regs.s == 0)
		regs.usp = m68k_areg (regs, 7);
	else
		regs.isp = m68k_areg (regs, 7);

	// Make sure the Status Register is up-to-date before moving it
	// into the debugger register set.

	Registers::UpdateSRFromRegisters ();

	// Copy the remaining registers from the UAE register set to the
	// debugger's register set.

	debuggerRegs.usp	= regs.usp;
	debuggerRegs.ssp	= regs.isp;
	debuggerRegs.sr		= regs.sr;
	debuggerRegs.pc		= m68k_getpc ();
}


/***********************************************************************
 *
 * FUNCTION:	Debug::SetRegs
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::SetRegs (const M68KRegsType& debuggerRegs)
{
	m68k_dreg (regs, 0)	= debuggerRegs.d[0];
	m68k_dreg (regs, 1)	= debuggerRegs.d[1];
	m68k_dreg (regs, 2)	= debuggerRegs.d[2];
	m68k_dreg (regs, 3)	= debuggerRegs.d[3];
	m68k_dreg (regs, 4)	= debuggerRegs.d[4];
	m68k_dreg (regs, 5)	= debuggerRegs.d[5];
	m68k_dreg (regs, 6)	= debuggerRegs.d[6];
	m68k_dreg (regs, 7)	= debuggerRegs.d[7];

	m68k_areg (regs, 0)	= debuggerRegs.a[0];
	m68k_areg (regs, 1)	= debuggerRegs.a[1];
	m68k_areg (regs, 2)	= debuggerRegs.a[2];
	m68k_areg (regs, 3)	= debuggerRegs.a[3];
	m68k_areg (regs, 4)	= debuggerRegs.a[4];
	m68k_areg (regs, 5)	= debuggerRegs.a[5];
	m68k_areg (regs, 6)	= debuggerRegs.a[6];
//	m68k_areg (regs, 7)	= debuggerRegs.a[7];

	regs.usp	= debuggerRegs.usp;
	regs.isp	= debuggerRegs.ssp;
	regs.sr		= debuggerRegs.sr;
	m68k_setpc (debuggerRegs.pc);

	// Make sure 's' bit is correct.  This will also set SPCFLAG_INT, and
	// if either the t0 or t1 bits are set, SPCFLAG_TRACE, too.

	Registers::UpdateRegistersFromSR ();

	// I think we need to do a little extra handling here of the TRACE
	// mode.  If the TRACE mode bit is set, UpdateRegistersFromSR will
	// set its SPCFLAG_TRACE flag, which tells the CPU loop to finish
	// executing the current instruction, and then set the SPCFLAG_DOTRACE
	// flag afterwards, which will then cause execution to stop after
	// the NEXT instruction.  We have a bit of an edge condition here
	// when we set the SPCFLAG_TRACE flag BETWEEN instructions.  Doing it
	// this way will cause the next instruction to be treated as the
	// "current" instruction, and so execution will stop after executing
	// the second opcode.  We want execution to stop with the first
	// opcode, so fix up the flags here.  This code is the same as in
	// Emulator::ExecuteSpecial for managing this stuff.

	if (regs.spcflags & SPCFLAG_TRACE)
	{
		regs.spcflags &= ~SPCFLAG_TRACE;
		regs.spcflags |= SPCFLAG_DOTRACE;
	}

	// Finally, update the stack pointer from the usp or ssp,
	// as appropriate.

	if (regs.s == 0)
		m68k_areg (regs, 7) = debuggerRegs.usp;
	else
		m68k_areg (regs, 7) = debuggerRegs.ssp;
}


/***********************************************************************
 *
 * FUNCTION:	Debug::MustBreakOnTrapSystemCall
 *
 * DESCRIPTION:	Test the given trapWord (and optional refNum) to see
 *				if this is a combination that we expect to break on.
 *
 * PARAMETERS:	trapWord - the system function dispatch number (Axxx)
 *					to test.
 *
 *				refNum - the library reference number to test.  This
 *					value is used only if trapWord is in the range for
 *					library function calls.
 *
 * RETURNED:	True if we should break on this combination.
 *
 ***********************************************************************/

Bool Debug::MustBreakOnTrapSystemCall (uae_u16 trapWord, uae_u16 refNum)
{
	Bool	doBreak = false;
	uae_u16	trapIndex = SysTrapIndex (trapWord);

	// Do different compares for system traps and library traps.

	if (IsSystemTrap (trapWord))
	{
		for (int ii = 0; ii < dbgTotalTrapBreaks; ++ii)
		{
			if (trapIndex == gDebuggerGlobals.trapBreak[ii])
			{
				doBreak = true;
				break;
			}
		}
	}
	else
	{
		for (int ii = 0; ii < dbgTotalTrapBreaks; ++ii)
		{
			if (trapIndex == gDebuggerGlobals.trapBreak[ii] &&
				refNum == gDebuggerGlobals.trapParam[ii])
			{
				doBreak = true;
				break;
			}
		}
	}

	return doBreak;
}


/*********************************************************************************
 * PRIVATE
 *-------------------------------------------------------------------------------
 * SlkChecksum	PrvCalcHdrChecksum(Checksum SlkChecksum, BytePtr bufP,
 *															Long count);
 *
 * Computes the 16-bit checksum of bytes in a buffer.
 *
 * Arguments:
 *		SlkChecksum start		-- starting checksum value
 *		BytePtr bufP			-- ptr to the data buffer
 *		Long count				-- number of bytes in buffer
 *
 * Returns:
 *		16-bit checksum of the data
 *
 *********************************************************************************/

SlkPktHeaderChecksum
PrvCalcHdrChecksum(SlkPktHeaderChecksum start, BytePtr bufP, Long count)
{
	do {
		start += *bufP++;
	} while (--count);		
		
	return start;
}



////// breakpoint conditions

uae_u32 PrvGetAddressRegister (int num)
{
	return m68k_areg (regs, num);
}

uae_u32 PrvGetDataRegister (int num)
{
	return m68k_dreg (regs, num);
}

Bool PrvBPEquals (uae_u32 a, uae_u32 b)
{
	return a == b;
}

Bool PrvBPNotEquals (uae_u32 a, uae_u32 b)
{
	return a != b;
}

// Comparisons are unsigned for now.  Would it be more useful if they were signed?

Bool PrvBPGreater (uae_u32 a, uae_u32 b)
{
	return a > b;
}

Bool PrvBPGreaterOrEqual (uae_u32 a, uae_u32 b)
{
	return a >= b;
}

Bool PrvBPLess (uae_u32 a, uae_u32 b)
{
	return a < b;
}

Bool PrvBPLessOrEqual (uae_u32 a, uae_u32 b)
{
	return a <= b;
}

const char* PrvSkipWhite (const char* p)
{
	while (p && isspace (*p))
		++p;

	return p;
}

/* Parse a signed decimal integer */

Bool PrvParseDecimal (const char **ps, int *i)
{
	const char *s = *ps;

	if (sscanf (s, "%d", i) != 1)
		return false;

	while (isdigit (*s))
		++s;

	*ps = s;

	return true;
}

/* Parse an unsigned integer which may be either decimal or hex (e.g. "0xabc")
 */
Bool PrvParseUnsigned (const char **ps, uae_u32 *u)
{
	const char *s = *ps;

	if (sscanf (s, "%li", u) != 1)
		return false;

	if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))	/* hex */
	{
		s += 2;
		while (isxdigit (*s))
			++s;
	}
	else /* decimal */
	{
		while (isdigit (*s))
			++s;
	}

	*ps = s;

	return true;
}

Bool BreakpointCondition::Evaluate (void)
{
	uae_u32 r = regType (regNum);

	if (indirect)
	{
		if (size == 4)
			r = get_long (r + indirectOffset);
		else if (size == 2)
			r = get_word (r + indirectOffset);
		else if (size == 1)
			r = get_byte (r + indirectOffset);
		else
			return false;
	}
	else
	{
		if (size == 2)
			r = (uae_u16) r;
		else if (size == 1)
			r = (uae_u8) r;
	}

	return condition (r, value);
}


/***********************************************************************
 *
 * FUNCTION:	Debug::ConditionalBreak
 *
 * DESCRIPTION:	.
 *
 * PARAMETERS:	None.
 *
 * RETURNED:	Nothing.
 *
 ***********************************************************************/

void Debug::ConditionalBreak (uaecptr startPC)
{
	Ptr	bpAddress = (Ptr) startPC;

	// Loop over the breakpoints to see if we've hit one.

	for (int ii = 0; ii < dbgTotalBreakpoints; ++ii)
	{
		// This breakpoint is not enabled, so just skip over it.

		if (!gDebuggerGlobals.bp[ii].enabled)
			continue;

		// This breakpoint is not for this PC, so just skip over it.

		if (gDebuggerGlobals.bp[ii].addr != bpAddress)
			continue;

		// If there is a condition for this breakpoint but it evaluates
		// to false, just skip over the breakpoint.

		BreakpointCondition*	bc = gDebuggerGlobals.bpCondition[ii];

		if (bc && bc->Evaluate () == false)
			continue;

		// Clear temporary breakpoint if we hit it.

		if (bpAddress == gDebuggerGlobals.bp[dbgTempBPIndex].addr)
		{
			gDebuggerGlobals.bp[dbgTempBPIndex].enabled = false;
		}

		// Break into the debugger.
		BreakIfConnected (kException_Trap0, startPC);
		break;
	}
}
