/********************************************************************
	filename: 	Asura_ExceptionHandler.cpp
	created:	28.9.2001
	author:		Mike Healey
	
	purpose:	displays a message on screen when the game crashes
*********************************************************************/
#include "Core/Asura.h"
#include "Core/Asura_PS2_OS.h"
#include "Debugging/Asura_ExceptionHandler.h"

#ifdef ASURA_PS2_USE_EXCEPTION_HANDLER

#include <libgraph.h>
#include <libdma.h>
#include <libpkt.h> 

#if ASURA_PS2_EE_ONLY_EH

// Stdlib platform-specific.
#include <devfont.h>

#include "Core/Asura_PlatformBasics.h"
#include "Core/Asura_PS2_DMA.h"
#include "Core/Asura_String.h"
#include "Rendering/Asura_PS2_GS.h"

class Asura_PS2_ExceptionData
{
public:
	u_int m_uStat;
	u_int m_uCause;
	u_int m_uEpc;
	u_int m_uBva;
	u_int m_uBpa;
	u_int m_auGpr[32];

	void Set( const u_int uStat, const u_int uCause, const u_int uEpc, const u_int uBva, const u_int uBpa, const u_long128* const pxGpr );
	u_int GetStack( void ) const;
	u_int GetReturnAddress( void ) const;
};

inline void Asura_PS2_ExceptionData::Set( const u_int uStat, const u_int uCause, const u_int uEpc, const u_int uBva, const u_int uBpa, const u_long128* const pxGpr )
{
	m_uStat	= uStat;
	m_uCause = uCause;
	m_uEpc	= uEpc;
	m_uBva	= uBva;
	m_uBpa	= uBpa;

	for ( u_int i = 0; i < 32; i++ )
	{
		m_auGpr[i] = pxGpr[i];
	}
}

inline u_int Asura_PS2_ExceptionData::GetStack(void) const
{
	return m_auGpr[29];
}

inline u_int Asura_PS2_ExceptionData::GetReturnAddress(void) const
{
	return m_auGpr[31];
}

volatile u_int uExceptionHandlerLevel = 0;
Asura_PS2_ExceptionData xExceptionData;

const int MAX_CALL_STACK_ENTRIES = 16;

u_int auCallStack[MAX_CALL_STACK_ENTRIES];
u_int uNumCallStackEntries = 0;

int ConvertCause( const int iCause );
const char* const GetCauseString( const int iConvertedCause );
void PrintAll();
void ConsolePrintAll( const int iConsoleID );
u_int GetCallStack( const u_int uRa, const u_int uSp, u_int* const puCallStack, const u_int uMaxNumCallStackEntries );
void SetGSRegisterSlow( const u_long uReg, const u_long uValue );
void SPrint( const char* const szFormat, ...);

void Asura_ExceptionHandler( u_int uStat, u_int uCause, u_int uEpc, u_int uBva, u_int uBpa, u_long128* pxGpr )
{
    SPrint( "EXCEPTION\n" );

	uExceptionHandlerLevel++;
	switch ( uExceptionHandlerLevel )
	{
		case 1:
		{
			//only set the exception data on the first pass
			xExceptionData.Set( uStat, uCause, uEpc, uBva, uBpa, pxGpr );
			auCallStack[0] = uEpc;
			auCallStack[1] = xExceptionData.GetReturnAddress();
			uNumCallStackEntries = 2 + GetCallStack( xExceptionData.GetReturnAddress(), xExceptionData.GetStack(), &auCallStack[2], ( MAX_CALL_STACK_ENTRIES - 2 ) );
			break;
		}
		case 2:
		case 3:
		case 4:
		{
			break;
		}

		default:
		{
			::exit( -1 );
		}
	}

	// print all the info to the tty
	PrintAll();

	const int EX_SCRN_W = 512;
	const int EX_SCRN_H = 224;
	const int EX_REFRESH_RATE = 60;
	const int CONSOLE_OFFX = ( ( ( 4096 - EX_SCRN_W ) / 2 ) << 4 );
	const int CONSOLE_OFFY = ( ( ( 4096 - EX_SCRN_H ) / 2 ) << 4 );

	sceDevConsInit();

	int iConsoleID = ::sceDevConsOpen( (2048<<4)-((EX_SCRN_W/2-20)<<4), (2048<<4)-((EX_SCRN_H/2-10)<<4), 80, 64 );

	// A lot of these functions might cause more exceptions
	// ::scePcStop(); // need this if using libpc
	::sceGsResetPath();
	::sceGsSyncPath( 1, 0 );

	tGS_PMODE xPmode;
	tGS_SMODE2 xSmode2;
	tGS_DISPFB1 xDispfb1;
	tGS_DISPLAY1 xDisplay1;
	tGS_BGCOLOR	xBgcolor;
	tGS_DISPFB2	xDispfb2;
	tGS_DISPLAY2 xDisplay2;

	xBgcolor.R = 0;
	xBgcolor.G = 0x20;
	xBgcolor.B = 0;

	xDispfb1.DBX = 0;
	xDispfb1.DBY = 0;
	xDispfb1.FBP = 0;
	xDispfb1.FBW = ( EX_SCRN_W ) / 64;
	xDispfb1.PSM = SCE_GS_PSMCT32;

	xDisplay1.DH = EX_SCRN_H - 1;
	xDisplay1.DW = 2559;
	xDisplay1.DX = 0;
	xDisplay1.DY = 0;

	xDisplay1.MAGH = 4;
	xDisplay1.MAGV = 0;

	xSmode2.DPMS = 0;
	xSmode2.FFMD = SCE_GS_FRAME;
	xSmode2.INT = 0;

	xPmode.ALP = 0x80;
	xPmode.AMOD = 0;
	xPmode.EN1 = 1;
	xPmode.EN2 = 1;
	xPmode.MMOD = 1;
	xPmode.SLBG = 0;

	// For 60Hz
	xDisplay1.DX = 692;
	xDisplay1.DY = 30;

	// Setup the second display buffer settings for flicker filter 
	*(uint64*)&xDisplay2 = *(uint64*)&xDisplay1;
	xDisplay2.DH -= 0;

	*(uint64*)&xDispfb2 = *(uint64*)&xDispfb1;
	xDispfb2.DBY += 0;

	for ( int i = 0; i < 4; i++ )
    {
		DPUT_GS_PMODE( *(uint64*)(&xPmode) );
		DPUT_GS_SMODE2( *(uint64*)(&xSmode2) );
		DPUT_GS_DISPFB1( *(uint64*)(&xDispfb1) );
		DPUT_GS_DISPLAY1( *(uint64*)(&xDisplay1) );
		DPUT_GS_BGCOLOR( *(uint64*)(&xBgcolor) );

		DPUT_GS_DISPFB2( *(uint64*)(&xDispfb2) );
		DPUT_GS_DISPLAY2( *(uint64*)(&xDisplay2) );

		Asura_PS2_GS::ClearVRAMColour( 0x00002000 );

		SetGSRegisterSlow( SCE_GS_TEXA, SCE_GS_SET_TEXA( 0, 0, 0 ) );
		SetGSRegisterSlow( SCE_GS_FBA_1, SCE_GS_SET_FBA( 0 ) );
		SetGSRegisterSlow( SCE_GS_COLCLAMP, SCE_GS_SET_COLCLAMP( 1 ) );
		SetGSRegisterSlow( SCE_GS_PRMODECONT, SCE_GS_SET_PRMODECONT( 1 ) );
		SetGSRegisterSlow( SCE_GS_ALPHA_1, SCE_GS_SET_ALPHA( 0, 1, 0, 1, 0 ) );
		SetGSRegisterSlow( SCE_GS_FRAME_1, SCE_GS_SET_FRAME( 0, EX_SCRN_W/64, SCE_GS_PSMCT32, 0 ) );
		SetGSRegisterSlow( SCE_GS_SCISSOR_1, SCE_GS_SET_SCISSOR( 0, EX_SCRN_W - 1, 0, EX_SCRN_H -1 ) );
		SetGSRegisterSlow( SCE_GS_XYOFFSET_1, SCE_GS_SET_XYOFFSET( CONSOLE_OFFX, CONSOLE_OFFY ) );

		ConsolePrintAll( iConsoleID );
		::sceDevConsDrawS( iConsoleID );
		::sceDevConsClear( iConsoleID );

		::sceGsSyncPath( 1, 0 );
    }

	while ( 1 )
	{
		::sceGsSyncPath( 1, 0 );
	}
	::exit(-1);
} // End Function 'Asura_ExceptionHandler'


void SetGSRegisterSlow( const u_long uReg, const u_long uValue )
{
	const u_int uNLOOP = 1;
	const u_int uQWC   = 1 + uNLOOP;

	Asura_PS2_QW* const pxBase = Asura_PS2_SPR_Alloc( 0, uQWC );
	Asura_PS2_QW* pxOut = pxBase;

	// GIF tag.
	// Use PACKED format and A+D because REGLIST mode can't use all the GS registers
	(reinterpret_cast<Asura_PS2_GifTag*>(pxOut++))->Set( uNLOOP,
													     true,					// eop
													     false,					// pre
													     0x0,					// prim
													     SCE_GIF_PACKED,		// flag
													     1,						// nreg
													     SCE_GIF_PACKED_AD );	// Reg0
	pxOut->u64s[0] = uValue;
	pxOut->u64s[1] = uReg;
	pxOut++;
	
	Asura_Assert( static_cast<u_int>(pxOut - pxBase) == uQWC, "" );

	// Just to be paranoid, make sure channel 2 is idle.
	while( *D2_CHCR & D_CHCR_STR_M ) { }

	// Send.
	Asura_PS2_DMA::NormalTransferFromMem( Asura_PS2_DMA::CHANNEL_GIF, pxBase, uQWC, true, true, true );

	Asura_PS2_SPR_Dealloc( pxBase );
}

void SPrint(const char* const szFormat, ...)
{
	char szBuffer[512];
	va_list xArgs;
	va_start( xArgs, szFormat );
    u_int uLen = Asura_PrintfToBfr( szBuffer, szFormat, xArgs, 512, true );
	va_end( xArgs );

	scePrintf( szBuffer );
}
int ConvertCause( const int iCause )
{
	return ( ( iCause >> 2 ) & 0xF );
}

const char* const GetCauseString( const int iConvertedCause )
{
	const char* const szCauses[] = 
	{
		"Interrupt",
		"TLB modification exception",
		"TLB exception (load or instruction fetch)",
		"Address error exception (load or instruction fetch)",
		"Address error exception (store)",
		"Bus error exception (instruction fetch)",
		"Bus error exception (data reference: load or store)",
		"Syscall exception",
		"Breakpoint exception",
		"Reserved instruction exception",
		"Co-processor Unusable exception",
		"Arithmetic overflow exception",
		"Trap exception"
		"Fatal_error",
		"UNKNOWN",
	};

	return ( szCauses[iConvertedCause & 0xF] );
}

void PrintAll()
{
	int iConvertedCause = ConvertCause( xExceptionData.m_uCause );
	const char* const szCauseDescription = GetCauseString( iConvertedCause );

	SPrint( "\n\n\n\n" );
	SPrint( "*********************************************\n" );
	SPrint( "************* Exception Occurred ************\n" );
	SPrint( "\n" );
	SPrint( "Please write this data down\n" );
	SPrint( "Exception '%s'(%d)\n",
		szCauseDescription, iConvertedCause );
	SPrint( "epc=0x%08X  ra=0x%08X stat=0x%08X cause=0x%08X\n",
		xExceptionData.m_uEpc, xExceptionData.GetReturnAddress(), xExceptionData.m_uStat, xExceptionData.m_uCause );
	SPrint( "bva=0x%08X bpa=0x%08X   sp=0x%08x\n",
		xExceptionData.m_uBva, xExceptionData.m_uBpa, xExceptionData.GetStack() );
	SPrint( "\n" );

	for ( u_int uCallStackEntry = 0; uCallStackEntry < uNumCallStackEntries; uCallStackEntry++ )
	{
		SPrint( "callStack[%2d] = 0x%08X\n", uCallStackEntry, auCallStack[uCallStackEntry] );
	}

	SPrint( "\nIt would be helpful to know this data\n" );
	SPrint( "\nat 0x%08X v0 0x%08X v1 0x%08X\n",
		xExceptionData.m_auGpr[01], xExceptionData.m_auGpr[02], xExceptionData.m_auGpr[03] );
	SPrint( "a0 0x%08X a1 0x%08X a2 0x%08X a3 0x%08X\n",
		xExceptionData.m_auGpr[04], xExceptionData.m_auGpr[05], xExceptionData.m_auGpr[06], xExceptionData.m_auGpr[07] );
	SPrint( "t0 0x%08X t1 0x%08X t2 0x%08X t3 0x%08X\n",
		xExceptionData.m_auGpr[ 8], xExceptionData.m_auGpr[ 9], xExceptionData.m_auGpr[10], xExceptionData.m_auGpr[11] );
	SPrint( "t4 0x%08X t5 0x%08X t6 0x%08X t7 0x%08X\n",
		xExceptionData.m_auGpr[12], xExceptionData.m_auGpr[13], xExceptionData.m_auGpr[14], xExceptionData.m_auGpr[15] );
	SPrint( "s0 0x%08X s1 0x%08X s2 0x%08X s3 0x%08X\n",
		xExceptionData.m_auGpr[16], xExceptionData.m_auGpr[17], xExceptionData.m_auGpr[18], xExceptionData.m_auGpr[19] );
	SPrint( "s4 0x%08X s5 0x%08X s6 0x%08X s7 0x%08X\n",
		xExceptionData.m_auGpr[20], xExceptionData.m_auGpr[21], xExceptionData.m_auGpr[22], xExceptionData.m_auGpr[23] );
	SPrint( "t8 0x%08X t9 0x%08X k0 0x%08X k1 0x%08X\n",
		xExceptionData.m_auGpr[24], xExceptionData.m_auGpr[25], xExceptionData.m_auGpr[26], xExceptionData.m_auGpr[27] );
	SPrint( "gp 0x%08X s8 0x%08X ra 0x%08X\n",
		xExceptionData.m_auGpr[28], xExceptionData.m_auGpr[30], xExceptionData.m_auGpr[31] );
	SPrint( "*********************************************\n" );
}

void ConsolePrintAll( const int iConsoleID )
{
	int iConvertedCause = ConvertCause( xExceptionData.m_uCause );
	const char* const szCauseDescription = GetCauseString( iConvertedCause );

	::sceDevConsPrintf( iConsoleID, "******* Exception Occurred ******\n" );
	::sceDevConsPrintf( iConsoleID, "Exception '%s'(%d)\n",
						szCauseDescription, iConvertedCause );
	::sceDevConsPrintf( iConsoleID, "epc 0x%08X\n",
					    xExceptionData.m_uEpc );
	::sceDevConsPrintf( iConsoleID, " ra 0x%08X\n",
						xExceptionData.GetReturnAddress() );
	::sceDevConsPrintf( iConsoleID, "bva 0x%08X\n",
						xExceptionData.m_uBva );
	::sceDevConsPrintf( iConsoleID, "\n" );

	for ( u_int uCallStackEntry = 0; uCallStackEntry < uNumCallStackEntries; uCallStackEntry++ )
	{
		::sceDevConsPrintf( iConsoleID, "callStack[%2d] = 0x%08X\n", uCallStackEntry, auCallStack[uCallStackEntry] );
	}

	if ( 0 )
	{
		::sceDevConsPrintf( iConsoleID, "It would be helpful to know this data\n" );
		::sceDevConsPrintf( iConsoleID, "epc 0x%08X sp 0x%08X\nstat = 0x%08X cause = 0x%08X\n", xExceptionData.m_uEpc, xExceptionData.m_auGpr[29], xExceptionData.m_uStat, xExceptionData.m_uCause );
		::sceDevConsPrintf( iConsoleID, "bva = 0x%08X bpa = 0x%08X\n", xExceptionData.m_uBva, xExceptionData.m_uBpa );
		::sceDevConsPrintf( iConsoleID, "\n" );
		::sceDevConsPrintf( iConsoleID, "at 0x%08X v0 0x%08X\nv1 0x%08X ra 0x%08X\n", xExceptionData.m_auGpr[1], xExceptionData.m_auGpr[2], xExceptionData.m_auGpr[3], xExceptionData.m_auGpr[31] );
		::sceDevConsPrintf( iConsoleID, "a0 0x%08X a1 0x%08X\na2 0x%08X a3 0x%08X\n", xExceptionData.m_auGpr[4], xExceptionData.m_auGpr[5], xExceptionData.m_auGpr[6], xExceptionData.m_auGpr[7] );
		::sceDevConsPrintf( iConsoleID, "t0 0x%08X t1 0x%08X\nt2 0x%08X t3 0x%08X\n", xExceptionData.m_auGpr[8], xExceptionData.m_auGpr[9], xExceptionData.m_auGpr[10], xExceptionData.m_auGpr[11] );
		::sceDevConsPrintf( iConsoleID, "t4 0x%08X t5 0x%08X\nt6 0x%08X t7 0x%08X\n", xExceptionData.m_auGpr[12], xExceptionData.m_auGpr[13], xExceptionData.m_auGpr[14], xExceptionData.m_auGpr[15] );
		::sceDevConsPrintf( iConsoleID, "s0 0x%08X s1 0x%08X\ns2 0x%08X s3 0x%08X\n", xExceptionData.m_auGpr[16], xExceptionData.m_auGpr[17], xExceptionData.m_auGpr[18], xExceptionData.m_auGpr[19] );
		::sceDevConsPrintf( iConsoleID, "s4 0x%08X s5 0x%08X\ns6 0x%08X s7 0x%08X\n", xExceptionData.m_auGpr[20], xExceptionData.m_auGpr[21], xExceptionData.m_auGpr[22], xExceptionData.m_auGpr[23] );
		::sceDevConsPrintf( iConsoleID, "t8 0x%08X t9 0x%08X\nk0 0x%08X k1 0x%08X\n", xExceptionData.m_auGpr[24], xExceptionData.m_auGpr[25], xExceptionData.m_auGpr[26], xExceptionData.m_auGpr[27] );
		::sceDevConsPrintf( iConsoleID, "gp 0x%08X s8 0x%08X\n", xExceptionData.m_auGpr[28], xExceptionData.m_auGpr[30] );
	}
}

//---------------------------------------------------------------
// get call stack
//---------------------------------------------------------------
/*
* $XConsortium: getretmips.c,v 1.4 94/04/17 20:59:44 keith Exp $
*
Copyright (c) 1992  X Consortium

Permission is hereby granted, Free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of the X Consortium shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the X Consortium.
*
* Author:  Keith Packard, MIT X Consortium
*/

u_int GetCallStack( const u_int uRa, const u_int uSp, u_int* const puCallStack, const u_int uMaxNumCallStackEntries )
{
	u_int* puRa = (u_int*)uRa;
	u_int* puSp = (u_int*)uSp;
	u_int* puResults = puCallStack;
	int iMax = (int)uMaxNumCallStackEntries;

	/* Return stack generation for MIPS processors
	* This is tricky as MIPS stack frames aren't
	* easily unrolled -- we look for pc restoration
	* and stack adjustment instructions beyond the return
	* address to discover the correct values
	*/

	const u_int MAX_MEMORY				= 128 * 1024 * 1024;

	/* lw $31,const($sp) is : 100 011 11101 11111 const */
	/*                        1000 1111 1011 1111       */

	const u_int RESTORE_RETURNVAL			= 0x8fbf0000;
	const u_int RESTORE_RETURNVAL_MASK	= 0xffff0000;

	/* ld $31,const($sp) is : 110 111 11101 11111 const */
	/*                        1101 1111 1011 1111       */

	const u_int RESTORE_RETURNVAL2		= 0xdfbf0000;

	/* lq $31,const($sp) is : 011110 11101 11111 const */
	/* EE-CORE SPECIFIC       0111 1011 1011 1111      */

	const u_int RESTORE_RETURNVAL3		= 0x7bbf0000;

	/* addiu $sp, $sp, const is 001 001 11101 11101 const */
	/*                          0010 0111 1011 1101 const */

	const u_int ADJUST_STACKP_C			= 0x27bd0000;
	const u_int ADJUST_STACKP_C_MASK		= 0xffff0000;

	/* addu $sp, $sp, $at is 000 000 11101 00001 11101 00000 100 001  */
	/*                       0000 0011 1010 0001 1110 1000 0010 0001 */

	const u_int ADJUST_STACKP_V			= 0x03a1e821;
	const u_int ADJUST_STACKP_V_MASK		= 0xffffffff;

	/* lui $at, const is 001 111 00000 00001 const */
	/*		     0011 1100 0000 0001 const */

	const u_int SET_UPPER_C				= 0x3c010000;
	const u_int SET_UPPER_C_MASK			= 0xffff0000;

	/* ori $at, $at, const is 001 101 00001 00001 const */
	/*                        0011 0100 0010 0001 const */

	const u_int OR_LOWER_C				= 0x34210000;
	const u_int OR_LOWER_C_MASK			= 0xffff0000;

	/* ori $at, $zero, const is 001 101 00000 00001 const */
	/*                          0011 0100 0000 0001 const */

	const u_int SET_LOWER_C				= 0x34010000;
	const u_int SET_LOWER_C_MASK			= 0xffff0000;

	/* jr $ra */
	const u_int RETURN	    			= 0x03e00008;

	/* nop */
	const u_int NOP						= 0x00000000;

	/*
	* This computation is expensive, so we cache the results;
	* a simple hash function and straight-forward replacement.
	*/

	typedef struct _returnCache
	{
		u_int* puReturnAddress;
		int iRaOffset;
		int iSpAdjust;
	} ReturnCacheRec, *ReturnCachePtr;

	const u_int HASH_SIZE = 256;
	ReturnCacheRec axReturnCache[HASH_SIZE];

#define ZST_HASH(ra)    ((((int) (ra)) >> 2) & (HASH_SIZE - 1))
#define ZST_CALL(f)	    (0x0c000000 | (((int) (f)) >> 2))
#define ZST_JUMP(f)	    (0x08000000 | (((int) (f)) >> 2))

	extern int main(int, char const **);
	extern int _start(void);
	extern void Exit(int);


	int iLevels = 0;
	u_int* puRa_limit;
	u_int uInst;
	u_int* puMainAddress;
	u_int* puThreadStartAddress;
	u_short usConst_upper;
	u_short usConst_lower;
	int iRa_offset;
	int iSp_adjust;
	int iNop_count;
	bool bFound_ra_offset, bFound_sp_adjust;
	bool bFound_const_upper, bFound_const_lower;
	ReturnCachePtr  pxRc;

	puMainAddress			= (u_int*) ZST_CALL(main);
	// Hack for PS2 ... the ExitThread address in CRT0 is preceeded by a "J Exit"
	puThreadStartAddress	= puMainAddress;	//(u_int *)ZST_JUMP(Exit);

	while (puRa && iMax)
	{
		pxRc = &axReturnCache[ZST_HASH(puRa)];
		if (pxRc->puReturnAddress != puRa)
		{
			bFound_ra_offset = false;
			bFound_sp_adjust = false;
			bFound_const_upper = false;
			bFound_const_lower = false;
			usConst_upper = 0;
			usConst_lower = 0;
			pxRc->puReturnAddress = puRa;
			puRa_limit = (u_int*)MAX_MEMORY;
			iRa_offset = 0;
			iSp_adjust = -1;
			iNop_count = 0;
			while ((!bFound_ra_offset || !bFound_sp_adjust) && (puRa < puRa_limit))
			{
				if (puRa == NULL)
				{
					//early exit!
					*puResults++ = 0;
					return((u_int)iLevels);
				}

				uInst = *puRa;
				if (uInst == NOP)
				{
					++iNop_count;
					if (iNop_count > 10)
					{
						break;
					}
				}
				else
				{
					iNop_count = 0;
					/* look for the offset of the PC in the stack frame */
					if ((uInst & RESTORE_RETURNVAL_MASK) == RESTORE_RETURNVAL)
					{
						iRa_offset = uInst & ~RESTORE_RETURNVAL_MASK;
						bFound_ra_offset = true;
					}
					else if ((uInst & RESTORE_RETURNVAL_MASK) == RESTORE_RETURNVAL2)
					{
						iRa_offset = uInst & ~RESTORE_RETURNVAL_MASK;
						bFound_ra_offset = true;
					}
					else if ((uInst & RESTORE_RETURNVAL_MASK) == RESTORE_RETURNVAL3)
					{
						iRa_offset = uInst & ~RESTORE_RETURNVAL_MASK;
						bFound_ra_offset = true;
					}
					else if ((uInst & ADJUST_STACKP_C_MASK) == ADJUST_STACKP_C)
					{
						iSp_adjust = (short)(uInst & ~ADJUST_STACKP_C_MASK);
						bFound_sp_adjust = true;
					}
					else if ((uInst & ADJUST_STACKP_V_MASK) == ADJUST_STACKP_V)
					{
						iSp_adjust = 0;
						bFound_sp_adjust = true;
					}
					else if ((uInst & SET_UPPER_C_MASK) == SET_UPPER_C)
					{
						usConst_upper = uInst & ~SET_UPPER_C_MASK;
						usConst_lower = 0;
						bFound_const_upper = true;
					}
					else if ((uInst & OR_LOWER_C_MASK) == OR_LOWER_C)
					{
						usConst_lower = uInst & ~OR_LOWER_C_MASK;
						bFound_const_lower = true;
					}
					else if ((uInst & SET_LOWER_C_MASK) == SET_LOWER_C)
					{
						usConst_lower = uInst & ~SET_LOWER_C_MASK;
						usConst_upper = 0;
						bFound_const_lower = true;
					}
					else if (uInst == RETURN)
					{
						puRa_limit = puRa + 2;
					}
				}
				puRa++;
			}
			if (iSp_adjust == 0 && (bFound_const_upper || bFound_const_lower))
			{
				iSp_adjust = (usConst_upper << 16) | usConst_lower;
			}
			pxRc->iRaOffset = iRa_offset;
			pxRc->iSpAdjust = iSp_adjust;
		}
		/* if something went wrong, punt */
		if (pxRc->iSpAdjust <= 0)
		{
			*puResults++ = 0;
			break;
		}
		puRa = (u_int *) puSp[pxRc->iRaOffset>>2];
		if(	(puRa == 0) ||
			(puRa >= (u_int *) MAX_MEMORY) ||
			(((u_int) puRa & 3) != 0) ||
			(puRa[-2] == (u_int) puMainAddress) ||
			(puRa[-2] == (u_int) puThreadStartAddress) )
		{
			*puResults++ = 0;
			break;
		}
		puSp += pxRc->iSpAdjust >> 2;
		*puResults++ = ((u_int) puRa) - 8;
		iMax--;
		iLevels++;
	}
	return((u_int)iLevels);
}

#else // not ASURA_PS2_EE_ONLY_EH

/* SCEI CONFIDENTIAL
 "PlayStation 2" Programmer Tool Runtime Library libexcep Version 1.01
*/
/*
 *               Emotion Engine Library Sample Program
 *
 *                           - <excep> -
 *
 *                          Version <1.01>
 *                               SJIS
 *
 *      Copyright (C) 1998-2000 Sony Computer Entertainment Inc.
 *                       All Rights Reserved.
 *
 *                            <excep.c>
 *                    <check system configuration>
 *
 *       Version        Date            Design      Log
 *  --------------------------------------------------------------------
 *      1.00            Aug,18,2000    kumagae     first version
 *      1.01            Dec,04,2000    kumagae     ExceptionCodeを修正
 */


#include <eekernel.h>
#include <stdio.h>
#include <libgraph.h>
#include <libdma.h>
#include <libpkt.h>
#include <libexcep.h>

#define UNCACHED         (0x20000000)
#define SCREEN_WIDTH	640
#define SCREEN_HEIGHT	224
#define OFFX		(((4096-SCREEN_WIDTH)/2)<<4)
#define OFFY		(((4096-SCREEN_HEIGHT)/2)<<4)

#define MAXWIDTH	(77)
#define MAXHEIGHT	(27)


/*----------------------------------------------------*/
//void loadIopModule(void);
void InitGS(void);
/*----------------------------------------------------*/

sceGsDBuff db;
sceExcepIOPExceptionData iop;

const char *ExceptionCode[]={
  "Interrupt",
  "TLB modification exception",
  "TLB exception (load or instruction fetch)",
  "TLB exception (store)",
  "Address error exception (load or instruction fetch)",
  "Address error exception (store)",
  "Bus error exception (instruction fetch)",
  "Bus error exception (data reference: load or store)",
  "Syscall exception",
  "Breakpint exception",
  "Reserved instruction exception",
  "Coprocessor Unusable exception",
  "Arithmetic overflow exception",
  "Trap exception"
};
/*----------------------------------------------------*/

/* InitGS
   GSを初期化する。
 */

void InitGS( void )
{
	sceVif1Packet packet;	/* DMAパケットハンドラ */
	sceDmaEnv env;			/* DMA 共通環境 */
	sceDmaChan *p1;
	u_long giftagAD[2] = { SCE_GIF_SET_TAG(0, 1, 0, 0, 0, 1),
		0x000000000000000eL };	/* A＋D用GIFタグ */
	long128 buff[10];
	sceGsResetPath();
	sceDmaReset(1);

	/*非キャッシュアクセスでパケットを作成する。*/
	sceVif1PkInit(&packet, (u_long128 *)((u_int)buff | UNCACHED) );

	sceDmaGetEnv(&env);		/* DMA共通環境取得 */
	env.notify = 1<<SCE_DMA_VIF1; /* notify channel */
	sceDmaPutEnv(&env);

	/* DMA VIF１チャンネルデスクリプタ取得 */
	p1 = sceDmaGetChan(SCE_DMA_VIF1);
	p1->chcr.TTE = 1;

	sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);
	sceGsSetDefDBuff(&db, SCE_GS_PSMCT32, SCREEN_WIDTH, SCREEN_HEIGHT,
		SCE_GS_ZGEQUAL, SCE_GS_PSMZ24, SCE_GS_CLEAR);
	sceVif1PkReset(&packet);
	sceVif1PkCnt(&packet, 0);
	sceVif1PkOpenDirectCode(&packet, 0);
	sceVif1PkOpenGifTag(&packet, *(u_long128*)&giftagAD);

	/* add alpha environment packet */
	sceVif1PkReserve(&packet,
		sceGsSetDefAlphaEnv((sceGsAlphaEnv *)packet.pCurrent,0)
		* 4);
	sceVif1PkCloseGifTag(&packet);
	sceVif1PkCloseDirectCode(&packet);
	sceVif1PkEnd(&packet, 0);
	sceVif1PkTerminate(&packet);

	/* kick Gs initialize packet */
	/*DMAの転送アドレスは物理アドレスを指定する*/
	sceDmaSend(p1,(long128*)(((u_int)(packet.pBase)) & 0x8fffffff));
	/* wait for Gs initialize packet end */
	sceGsSyncPath(0,0);
	while(!sceGsSyncV(0)) { }	/* display next in odd field when interlace*/
}


/*  EEExceptionHandler
    EE例外ハンドラ
    例外が発生した時呼び出され,EEのレジスタを表示する
*/
void   EEExceptionHandler(u_int stat, u_int cause, u_int epc, u_int bva, u_int bpa, u_long128 *gpr)
{
  int frame=0;
  InitGS();
  sceExcepConsOpen(OFFX + (16<<4), OFFY + (16<<3), MAXWIDTH, MAXHEIGHT );
  db.clear0.rgbaq.R = 0x0;	/* clear color for dbuf0 */
  db.clear0.rgbaq.G = 0x40;
  db.clear0.rgbaq.B = 0x80;
  
  db.clear1.rgbaq.R = 0x0;	/* clear color for dbuf1 */
  db.clear1.rgbaq.G = 0x40;
  db.clear1.rgbaq.B = 0x80;
  scePrintf("*** EE Exception [%s] ***\n",ExceptionCode[(cause>>2)&31]);
    
  scePrintf("stat=0x%08x cause=0x%08x epc=0x%08x bva=0x%08x bpa=0x%08x\n",stat,cause,epc,bva,bpa);
  scePrintf("at  =0x%08x     v0-1=0x%08x,0x%08x \n",gpr[GPR_at],gpr[GPR_v0],gpr[GPR_v1]);
  scePrintf("a0-3=0x%08x,0x%08x,0x%08x,0x%08x\n",gpr[GPR_a0],gpr[GPR_a1],gpr[GPR_a2],gpr[GPR_a3]);
  scePrintf("t0-7=0x%08x,0x%08x,0x%08x,0x%08x,\n",gpr[GPR_t0],gpr[GPR_t1],gpr[GPR_t2],gpr[GPR_t3]);
  scePrintf("     0x%08x,0x%08x,0x%08x,0x%08x\n",gpr[GPR_t4],gpr[GPR_t5],gpr[GPR_t6],gpr[GPR_t7]);
  scePrintf("s0-7=0x%08x,0x%08x,0x%08x,0x%08x,\n",gpr[GPR_s0],gpr[GPR_s1],gpr[GPR_s2],gpr[GPR_s3]);
  scePrintf("     0x%08x,0x%08x,0x%08x,0x%08x\n",gpr[GPR_s4],gpr[GPR_s5],gpr[GPR_s6],gpr[GPR_s7]);
  scePrintf("t8-9=0x%08x,0x%08x     ",gpr[GPR_t8],gpr[GPR_t9]);
  scePrintf("k0-1=0x%08x,0x%08x\n",gpr[GPR_k0],gpr[GPR_k1]);
  scePrintf("gp=0x%08x sp=0x%08x fp=0x%08x ra=0x%08x\n",gpr[GPR_gp],gpr[GPR_sp],gpr[GPR_fp],gpr[GPR_ra]);


  while(1){
    sceExcepConsLocate( 0, 0 );
    
    sceExcepConsPrintf("*** EE Exception [%s] ***\n",ExceptionCode[(cause>>2)&31]);
    
    sceExcepConsPrintf("stat=0x%08x cause=0x%08x epc=0x%08x bva=0x%08x bpa=0x%08x\n",stat,cause,epc,bva,bpa);
    sceExcepConsPrintf("at  =0x%08x     v0-1=0x%08x,0x%08x \n",gpr[GPR_at],gpr[GPR_v0],gpr[GPR_v1]);
    sceExcepConsPrintf("a0-3=0x%08x,0x%08x,0x%08x,0x%08x\n",gpr[GPR_a0],gpr[GPR_a1],gpr[GPR_a2],gpr[GPR_a3]);
    sceExcepConsPrintf("t0-7=0x%08x,0x%08x,0x%08x,0x%08x,\n",gpr[GPR_t0],gpr[GPR_t1],gpr[GPR_t2],gpr[GPR_t3]);
    sceExcepConsPrintf("     0x%08x,0x%08x,0x%08x,0x%08x\n",gpr[GPR_t4],gpr[GPR_t5],gpr[GPR_t6],gpr[GPR_t7]);
    sceExcepConsPrintf("s0-7=0x%08x,0x%08x,0x%08x,0x%08x,\n",gpr[GPR_s0],gpr[GPR_s1],gpr[GPR_s2],gpr[GPR_s3]);
    sceExcepConsPrintf("     0x%08x,0x%08x,0x%08x,0x%08x\n",gpr[GPR_s4],gpr[GPR_s5],gpr[GPR_s6],gpr[GPR_s7]);
    sceExcepConsPrintf("t8-9=0x%08x,0x%08x     ",gpr[GPR_t8],gpr[GPR_t9]);
    sceExcepConsPrintf("k0-1=0x%08x,0x%08x\n",gpr[GPR_k0],gpr[GPR_k1]);
    sceExcepConsPrintf("gp=0x%08x sp=0x%08x fp=0x%08x ra=0x%08x\n",gpr[GPR_gp],gpr[GPR_sp],gpr[GPR_fp],gpr[GPR_ra]);
    
    if( frame & 1 )
    {	/* interrace half pixcel adjust */
	    sceGsSetHalfOffset( &db.draw1, 2048, 2048, sceGsSyncV( 0 ) ^ 0x01 );
    }
    else
    {
	    sceGsSetHalfOffset( &db.draw0, 2048, 2048, sceGsSyncV( 0 ) ^ 0x01 );
    }
    iFlushCache( 0 );
    sceGsSyncPath( 0, 0 );
    sceGsSwapDBuff( &db, frame++ );
  }
}

/*  IOPExceptionHandler
    IOP例外ハンドラ
    例外が発生した時呼び出され,IOPから受けとったIOPのレジスタとIOPモジュール名を表示する
*/
void IOPExceptionHandler(void *p,void *d)
{
  int frame=0;
  /*この関数の内部ではscePrintfすら使ってはいけない。
    IOPが死んでいるのでscePrintfを使ってしまうと,
    その場で停止してしまう。*/
  InitGS();
  
  db.clear0.rgbaq.R = 0x00;
  db.clear0.rgbaq.G = 0x80;
  db.clear0.rgbaq.B = 0x00;

  db.clear1.rgbaq.R = 0x00;
  db.clear1.rgbaq.G = 0x80;
  db.clear1.rgbaq.B = 0x00;
  sceExcepConsOpen(OFFX + (16<<4), OFFY + (16<<3),MAXWIDTH ,MAXHEIGHT );
  while(1){
    sceExcepConsLocate( 0, 0 );
    sceExcepConsPrintf("*** IOP Exception [%s] ***\n",ExceptionCode[(iop.reg[IOP_CAUSE]>>2)&31]);
    sceExcepConsPrintf("Module [%s] Version [%02x.%02x] Offset [0x%08x]\n",iop.module ,(iop.version)>>8 ,(iop.version)&0xf,iop.offset); 
    sceExcepConsPrintf("stat=0x%08x cause=0x%08x epc=0x%08x\n",iop.reg[IOP_SR] ,iop.reg[IOP_CAUSE], iop.reg[IOP_EPC]);
    sceExcepConsPrintf("at  =0x%08x     v0-1=0x%08x,0x%08x \n",iop.reg[GPR_at],iop.reg[GPR_v0], iop.reg[GPR_v1]);
    sceExcepConsPrintf("a0-3=0x%08x,0x%08x,0x%08x,0x%08x\n",iop.reg[GPR_a0], iop.reg[GPR_a1], iop.reg[GPR_a2], iop.reg[GPR_a3]);
    sceExcepConsPrintf("t0-7=0x%08x,0x%08x,0x%08x,0x%08x,\n",iop.reg[GPR_t0], iop.reg[GPR_t1], iop.reg[GPR_t2], iop.reg[GPR_t3]);
    sceExcepConsPrintf("     0x%08x,0x%08x,0x%08x,0x%08x\n",iop.reg[GPR_t4], iop.reg[GPR_t5], iop.reg[GPR_t6], iop.reg[GPR_t7]);
    sceExcepConsPrintf("s0-7=0x%08x,0x%08x,0x%08x,0x%08x,\n",iop.reg[GPR_s0], iop.reg[GPR_s1], iop.reg[GPR_s2], iop.reg[GPR_s3]);
    sceExcepConsPrintf("     0x%08x,0x%08x,0x%08x,0x%08x\n",iop.reg[GPR_s4], iop.reg[GPR_s5], iop.reg[GPR_s6], iop.reg[GPR_s7]);
    sceExcepConsPrintf("t8-9=0x%08x,0x%08x     ",iop.reg[GPR_t8], iop.reg[GPR_t9]);
    sceExcepConsPrintf("k0-1=0x%08x,0x%08x\n",iop.reg[GPR_k0], iop.reg[GPR_k1]);
    sceExcepConsPrintf("gp=0x%08x sp=0x%08x fp=0x%08x ra=0x%08x\n",iop.reg[GPR_gp], iop.reg[GPR_sp], iop.reg[GPR_fp], iop.reg[GPR_ra]);
    sceExcepConsPrintf("hi=0x%08x lo=0x%08x sr=0x%08x epc =0x%08x\n",iop.reg[IOP_HI], iop.reg[IOP_LO], iop.reg[IOP_SR], iop.reg[IOP_EPC]);
    sceExcepConsPrintf("cause=0x%08x tar=0x%08x badadr=0x%08x\n", iop.reg[IOP_CAUSE], iop.reg[IOP_TAR], iop.reg[IOP_BADVADDR]);
    sceExcepConsPrintf("dcic=0x%08x bpc=0x%08x bpcm=0x%08x bda=0x%08x bpam=0x%08x\n",iop.reg[IOP_DCIC], iop.reg[IOP_BPC], iop.reg[IOP_BPCM], iop.reg[IOP_BDA], iop.reg[IOP_BDAM]);
   
    if( frame & 1 )
    {	/* interrace half pixcel adjust */
        sceGsSetHalfOffset( &db.draw1, 2048, 2048, sceGsSyncV( 0 ) ^ 0x01 );
    }
    else
    {
        sceGsSetHalfOffset( &db.draw0, 2048, 2048, sceGsSyncV( 0 ) ^ 0x01 );
    }
    iFlushCache( 0 );
    sceGsSyncPath( 0, 0 );
    sceGsSwapDBuff( &db, frame++ );
  }
}

#endif //#else // not ASURA_PS2_EE_ONLY_EH


#endif //#ifdef ASURA_PS2_USE_EXCEPTION_HANDLER
