From dc3d9f4bfcaab73af4c53983cd115158e2cabe94 Mon Sep 17 00:00:00 2001
From: "Jake.Stine" <Jake.Stine@96395faa-99c1-11dd-bbfe-3dabce05a288>
Date: Wed, 11 Mar 2009 07:40:23 +0000
Subject: [PATCH] Fixed the bug in r740 that broke speedhacks; and improved the
 PERF support a bit more using bitfields and more correct mode tests.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@745 96395faa-99c1-11dd-bbfe-3dabce05a288
---
 pcsx2/COP0.cpp                  | 106 ++++++++++++++++++++++----------
 pcsx2/COP0.h                    |   3 +-
 pcsx2/R5900.cpp                 |   3 +-
 pcsx2/R5900.h                   |  31 +++++++++-
 pcsx2/x86/iCOP0.cpp             |  45 ++------------
 pcsx2/x86/ix86-32/iR5900-32.cpp |   5 +-
 6 files changed, 115 insertions(+), 78 deletions(-)

diff --git a/pcsx2/COP0.cpp b/pcsx2/COP0.cpp
index 67f5b4847c..c13da57caf 100644
--- a/pcsx2/COP0.cpp
+++ b/pcsx2/COP0.cpp
@@ -145,59 +145,104 @@ void WriteTLB(int i)
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
+// Performance Counters Update Stuff!
+// 
 // Note regarding updates of PERF and TIMR registers: never allow increment to be 0.
 // That happens when a game loads the MFC0 twice in the same recompiled block (before the
 // cpuRegs.cycles update), and can cause games to lock up since it's an unexpected result.
-
+//
 // PERF Overflow exceptions:  The exception is raised when the MSB of the Performance
-// Counter Register is set (basic arithmetic overflow, which means it does *not* include
-// when the bit is later cleared).
+// Counter Register is set.  I'm assuming the exception continues to re-raise until the
+// app clears the bit manually (needs testing).
+//
+// PERF Events:
+//  * Event 0 on PCR 0 is unused (counter disable)
+//  * Event 15 is usable as a specific counter disable bit (since CTE affects both counters)
+//  * Events 16-31 are reserved (act as counter disable)
+//
+// Most event mode aren't supported, and issue a warning and do a standard instruction
+// count.  But only mode 1 (instruction counter) has been found to be used by games thus far.
+//
 
-__forceinline void COP0_UpdatePCR0()
+__forceinline void COP0_UpdatePCR()
 {
-	if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020)
+	if( cpuRegs.CP0.n.Status.b.ERL || !cpuRegs.PERF.n.pccr.b.CTE ) return;
+
+	// TODO : Implement memory mode checks here (kernel/super/user)
+	// For now we just assume user mode.
+	
+	if( cpuRegs.PERF.n.pccr.b.U0 && (cpuRegs.PERF.n.pccr.b.Event0 != 0 && cpuRegs.PERF.n.pccr.b.Event0 < 15) )
 	{
-		u32 incr = cpuRegs.cycle-s_iLastPERFCycle[0];
+		// ----------------------------------
+		//    Update Performance Counter 0
+		// ----------------------------------
+
+		if( cpuRegs.PERF.n.pccr.b.Event0 != 1 )
+			Console::Notice( "COP0 - PCR0 Unsupported Update Event Mode = 0x%x", params cpuRegs.PERF.n.pccr.b.Event0 );
+
+		u32 incr = cpuRegs.cycle - s_iLastPERFCycle[0];
 		if( incr == 0 ) incr++;
 
-		u32 prev = cpuRegs.PERF.n.pcr0;
+		// use prev/XOR method for one-time exceptions (but likely less correct)
+		//u32 prev = cpuRegs.PERF.n.pcr0;
 		cpuRegs.PERF.n.pcr0 += incr;
 		s_iLastPERFCycle[0] = cpuRegs.cycle;
 		
-		if( cpuRegs.PERF.n.pccr & (1UL<<31) )	// MSB is the overflow enable bit.
+		//prev ^= (1UL<<31);		// XOR is fun!
+		//if( (prev & cpuRegs.PERF.n.pcr0) & (1UL<<31) )
+		if( cpuRegs.PERF.n.pcr0 & 0x80000000 )
 		{
-			prev ^= (1UL<<31);		// XOR is fun!
-			if( (prev & cpuRegs.PERF.n.pcr0) & (1UL<<31) )
+			// TODO: Vector to the appropriate exception here.
+			// This code *should* be correct, but is untested (and other parts of the emu are
+			// not prepared to handle proper Level 2 exception vectors yet)
+			
+			/*if( delay_slot )
 			{
-				// TODO: Vector to the appropriate exception here.
+				cpuRegs.CP0.ErrorEPC = cpuRegs.pc - 4;
+				cpuRegs.CP0.Cause.BD2 = 1;
 			}
+			else
+			{
+				cpuRegs.CP0.ErrorEPC = cpuRegs.pc;
+				cpuRegs.CP0.Cause.BD2 = 0;
+			}
+			
+			if( cpuRegs.CP0.Status.DEV )
+			{
+				// Bootstrap vector
+				cpuRegs.pc = 0xbfc00280;
+			}
+			else
+			{
+				cpuRegs.pc = 0x80000080;
+			}
+			cpuRegs.CP0.Status.ERL = 1;
+			cpuRegs.CP0.Cause.EXC2 = 2;*/
 		}
 	}
-}
-
-__forceinline void COP0_UpdatePCR1()
-{
-	if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000)
+	
+	if( cpuRegs.PERF.n.pccr.b.U1 && cpuRegs.PERF.n.pccr.b.Event1 < 15)
 	{
-		u32 incr = cpuRegs.cycle-s_iLastPERFCycle[1];
+		// ----------------------------------
+		//    Update Performance Counter 1
+		// ----------------------------------
+		
+		if( cpuRegs.PERF.n.pccr.b.Event1 != 1 )
+			Console::Notice( "COP0 - PCR1 Unsupported Update Event Mode = 0x%x", params cpuRegs.PERF.n.pccr.b.Event1 );
+
+		u32 incr = cpuRegs.cycle - s_iLastPERFCycle[1];
 		if( incr == 0 ) incr++;
 
-		u32 prev = cpuRegs.PERF.n.pcr1;
 		cpuRegs.PERF.n.pcr1 += incr;
 		s_iLastPERFCycle[1] = cpuRegs.cycle;
 
-		if( cpuRegs.PERF.n.pccr & (1UL<<31) )	// MSB is the overflow enable bit.
+		if( cpuRegs.PERF.n.pcr1 & 0x80000000 )
 		{
-			prev ^= (1UL<<31);		// XOR?  I don't even know OR!  Harhar!
-			if( (prev & cpuRegs.PERF.n.pcr1) & (1UL<<31) )
-			{
-				// TODO: Vector to the appropriate exception here.
-			}
+			// See PCR0 comments for notes on exceptions
 		}
 	}
 }
 
-
 namespace R5900 {
 namespace Interpreter {
 namespace OpcodeImpl {
@@ -221,16 +266,16 @@ void MFC0()
 		    switch(_Imm_ & 0x3F)
 		    {
 			    case 0:		// MFPS  [LSB is clear]
-					cpuRegs.GPR.r[_Rt_].SD[0] = (s32)cpuRegs.PERF.n.pccr;
+					cpuRegs.GPR.r[_Rt_].SD[0] = (s32)cpuRegs.PERF.n.pccr.val;
 				break;
 
 			    case 1:		// MFPC [LSB is set] - read PCR0
-					COP0_UpdatePCR0();
+					COP0_UpdatePCR();
                     cpuRegs.GPR.r[_Rt_].SD[0] = (s32)cpuRegs.PERF.n.pcr0;
 				break;
 
 			    case 3:		// MFPC [LSB is set] - read PCR1
-					COP0_UpdatePCR1();
+					COP0_UpdatePCR();
 					cpuRegs.GPR.r[_Rt_].SD[0] = (s32)cpuRegs.PERF.n.pcr1;
 				break;
 		    }
@@ -269,9 +314,8 @@ void MTC0()
 			{
 				case 0:		// MTPS  [LSB is clear]
 					// Updates PCRs and sets the PCCR.
-					COP0_UpdatePCR0();
-					COP0_UpdatePCR1();
-					cpuRegs.PERF.n.pccr = cpuRegs.GPR.r[_Rt_].UL[0];
+					COP0_UpdatePCR();
+					cpuRegs.PERF.n.pccr.val = cpuRegs.GPR.r[_Rt_].UL[0];
 				break;
 				
 				case 1:		// MTPC [LSB is set] - set PCR0
diff --git a/pcsx2/COP0.h b/pcsx2/COP0.h
index f8b0e4e6e1..d669a49a73 100644
--- a/pcsx2/COP0.h
+++ b/pcsx2/COP0.h
@@ -25,8 +25,7 @@ extern void WriteTLB(int i);
 extern void UnmapTLB(int i);
 extern void MapTLB(int i);
 
-extern void COP0_UpdatePCR0();
-extern void COP0_UpdatePCR1();
+extern void COP0_UpdatePCR();
 
 
 #endif /* __COP0_H__ */
diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp
index 82dcd8b6b8..06fc4b1976 100644
--- a/pcsx2/R5900.cpp
+++ b/pcsx2/R5900.cpp
@@ -392,8 +392,7 @@ static __forceinline void _cpuTestPERF()
 	// around twice on us btween updates.  Hence this function is called from the cpu's
 	// Counters update.
 
-	COP0_UpdatePCR0();
-	COP0_UpdatePCR1();
+	COP0_UpdatePCR();
 }
 
 // Checks the COP0.Status for exception enablings.
diff --git a/pcsx2/R5900.h b/pcsx2/R5900.h
index 0117b1d8af..900142b2ea 100644
--- a/pcsx2/R5900.h
+++ b/pcsx2/R5900.h
@@ -52,8 +52,35 @@ union GPRregs {
 };
 
 union PERFregs {
-	struct {
-		u32 pccr, pcr0, pcr1, pad;
+	struct
+	{
+		union
+		{
+			struct  
+			{
+				u32 pad0:1;			// LSB should always be zero (or undefined)
+				u32 EXL0:1;			// enable PCR0 during Level 1 exception handling
+				u32 K0:1;			// enable PCR0 during Kernel Mode execution
+				u32 S0:1;			// enable PCR0 during Supervisor mode execution
+				u32 U0:1;			// enable PCR0 during User-mode execution
+				u32 Event0:5;		// PCR0 event counter (all values except 1 ignored at this time)
+
+				u32 pad1:1;			// more zero/undefined padding [bit 10]
+
+				u32 EXL1:1;			// enable PCR1 during Level 1 exception handling
+				u32 K1:1;			// enable PCR1 during Kernel Mode execution
+				u32 S1:1;			// enable PCR1 during Supervisor mode execution
+				u32 U1:1;			// enable PCR1 during User-mode execution
+				u32 Event1:5;		// PCR1 event counter (all values except 1 ignored at this time)
+				
+				u32 Reserved:11;
+				u32 CTE:1;			// Counter enable bit, no counting if set to zero.
+			} b;
+			
+			u32 val;
+		} pccr;
+
+		u32 pcr0, pcr1, pad;
 	} n;
 	u32 r[4];
 };
diff --git a/pcsx2/x86/iCOP0.cpp b/pcsx2/x86/iCOP0.cpp
index 7bb3eca220..10b2df00f3 100644
--- a/pcsx2/x86/iCOP0.cpp
+++ b/pcsx2/x86/iCOP0.cpp
@@ -162,44 +162,13 @@ void recMFC0( void )
 		{
 			case 0:
 				MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pccr);
+			break;
 
-				break;
 			case 1:
-				/*MOV32MtoR(ECX, (uptr)&cpuRegs.PERF.n.pccr);
-				MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pcr0);
-				AND32ItoR(ECX, 0x800003E0);
-
-				CMP32ItoR(ECX, 0x80000020);
-				j8Ptr[0] = JNE8(0);
-
-				MOV32MtoR(EDX, (uptr)&cpuRegs.cycle);
-				SUB32MtoR(EAX, (uptr)&s_iLastPERFCycle[0]);
-				ADD32RtoR(EAX, EDX);
-				MOV32RtoM((uptr)&s_iLastPERFCycle[0], EDX);
-				MOV32RtoM((uptr)&cpuRegs.PERF.n.pcr0, EAX);
-
-				x86SetJ8(j8Ptr[0]);*/
-				
-				CALLFunc( (uptr)COP0_UpdatePCR0 );
-				break;
 			case 3:
-				/*MOV32MtoR(ECX, (uptr)&cpuRegs.PERF.n.pccr);
-				MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pcr1);
-				AND32ItoR(ECX, 0x800F8000);
-
-				CMP32ItoR(ECX, 0x80008000);
-				j8Ptr[0] = JNE8(0);
-				
-				MOV32MtoR(EDX, (uptr)&cpuRegs.cycle);
-				SUB32MtoR(EAX, (uptr)&s_iLastPERFCycle[1]);
-				ADD32RtoR(EAX, EDX);
-				MOV32RtoM((uptr)&s_iLastPERFCycle[1], EDX);
-				MOV32RtoM((uptr)&cpuRegs.PERF.n.pcr1, EAX);
-
-				x86SetJ8(j8Ptr[0]);*/
-
-				CALLFunc( (uptr)COP0_UpdatePCR1 );
-				break;
+				CALLFunc( (uptr)COP0_UpdatePCR );
+				CALLFunc( (uptr)COP0_UpdatePCR );
+			break;
 		}
 		_deleteEEreg(_Rt_, 0);
 		MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX);
@@ -308,8 +277,7 @@ void recMTC0()
 				switch(_Imm_ & 0x3F)
 				{
 					case 0:
-						CALLFunc( (uptr)COP0_UpdatePCR0 );
-						CALLFunc( (uptr)COP0_UpdatePCR1 );
+						CALLFunc( (uptr)COP0_UpdatePCR );
 						MOV32ItoM((uptr)&cpuRegs.PERF.n.pccr, g_cpuConstRegs[_Rt_].UL[0]);
 					break;
 
@@ -356,8 +324,7 @@ void recMTC0()
 				switch(_Imm_ & 0x3F)
 				{
 					case 0:
-						CALLFunc( (uptr)COP0_UpdatePCR0 );
-						CALLFunc( (uptr)COP0_UpdatePCR1 );
+						CALLFunc( (uptr)COP0_UpdatePCR );
 						_eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pccr, _Rt_);
 					break;
 
diff --git a/pcsx2/x86/ix86-32/iR5900-32.cpp b/pcsx2/x86/ix86-32/iR5900-32.cpp
index c19a468c19..95931a0061 100644
--- a/pcsx2/x86/ix86-32/iR5900-32.cpp
+++ b/pcsx2/x86/ix86-32/iR5900-32.cpp
@@ -1198,9 +1198,10 @@ u32 eeScaleBlockCycles()
 		jNO_DEFAULT
 	}
 
-	const u32 temp = s_nBlockCycles * 
+	const u32 temp = s_nBlockCycles * (
 		(s_nBlockCycles <= (10<<3)) ? scalarLow :
-		((s_nBlockCycles > (21<<3)) ? scalarHigh : scalarMid );
+		((s_nBlockCycles > (21<<3)) ? scalarHigh : scalarMid )
+	);
 
 	return temp >> (3+2);
 }