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); }