From 15901551b4f10d5a5f976a5945141d6b03bd9a0d Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Tue, 10 Mar 2009 19:21:54 +0000 Subject: [PATCH] Improved PCR/TIMR support -- fixed a case where games would try and read from the PCR multiple times in the same recompiled block, reuslting in bogus values, and fixed some sign extension errors on the interpreted version of MFC0. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@738 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/COP0.cpp | 146 +++++++++++++++++++++++++++++++------------- pcsx2/COP0.h | 14 +++-- pcsx2/R5900.cpp | 18 ++---- pcsx2/x86/iCOP0.cpp | 142 ++++++++++++++++++++++-------------------- 4 files changed, 194 insertions(+), 126 deletions(-) diff --git a/pcsx2/COP0.cpp b/pcsx2/COP0.cpp index ae6540f9ee..80c23137f9 100644 --- a/pcsx2/COP0.cpp +++ b/pcsx2/COP0.cpp @@ -144,79 +144,137 @@ void WriteTLB(int i) MapTLB(i); } +////////////////////////////////////////////////////////////////////////////////////////// +// 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. + +__forceinline void COP0_UpdatePCR0() +{ + if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) + { + u32 incr = cpuRegs.cycle-s_iLastPERFCycle[0]; + if( incr == 0 ) incr++; + + cpuRegs.PERF.n.pcr0 += incr; + s_iLastPERFCycle[0] = cpuRegs.cycle; + } +} + +__forceinline void COP0_UpdatePCR1() +{ + if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) + { + u32 incr = cpuRegs.cycle-s_iLastPERFCycle[1]; + if( incr == 0 ) incr++; + + cpuRegs.PERF.n.pcr1 += incr; + s_iLastPERFCycle[1] = cpuRegs.cycle; + } +} + + namespace R5900 { namespace Interpreter { namespace OpcodeImpl { namespace COP0 { -void MFC0() { - if (!_Rt_) return; - if (_Rd_ != 9) { COP0_LOG("%s\n", disR5900Current.getCString() ); } +void MFC0() +{ + // Note on _Rd_ Condition 9: CP0.Count should be updated even if _Rt_ is 0. + if( (_Rd_ != 9) && !_Rt_ ) return; + if(_Rd_ != 9) { COP0_LOG("%s\n", disR5900Current.getCString() ); } //if(bExecBIOS == FALSE && _Rd_ == 25) SysPrintf("MFC0 _Rd_ %x = %x\n", _Rd_, cpuRegs.CP0.r[_Rd_]); - switch (_Rd_) { + switch (_Rd_) + { - case 12: cpuRegs.GPR.r[_Rt_].UD[0] = (s64)(cpuRegs.CP0.r[_Rd_] & 0xf0c79c1f); break; + case 12: + cpuRegs.GPR.r[_Rt_].SD[0] = (s32)(cpuRegs.CP0.r[_Rd_] & 0xf0c79c1f); + break; + case 25: - switch(_Imm_ & 0x3F){ - case 0: cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.PERF.n.pccr; break; - case 1: - if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) { - cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0]; - s_iLastPERFCycle[0] = cpuRegs.cycle; - } - - cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.PERF.n.pcr0; - break; - case 3: - if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) { - cpuRegs.PERF.n.pcr1 += cpuRegs.cycle-s_iLastPERFCycle[1]; - s_iLastPERFCycle[1] = cpuRegs.cycle; - } - cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.PERF.n.pcr1; - break; + switch(_Imm_ & 0x3F) + { + case 0: // MFPS [LSB is clear] + cpuRegs.GPR.r[_Rt_].SD[0] = (s32)cpuRegs.PERF.n.pccr; + break; + + case 1: // MFPC [LSB is set] - read PCR0 + COP0_UpdatePCR0(); + cpuRegs.GPR.r[_Rt_].SD[0] = (s32)cpuRegs.PERF.n.pcr0; + break; + + case 3: // MFPC [LSB is set] - read PCR1 + COP0_UpdatePCR1(); + cpuRegs.GPR.r[_Rt_].SD[0] = (s32)cpuRegs.PERF.n.pcr1; + break; } /*SysPrintf("MFC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F);*/ - break; + break; + case 24: - SysPrintf("MFC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); - break; + Console::WriteLn("MFC0 Breakpoint debug Registers code = %x", params cpuRegs.code & 0x3FF); + break; + case 9: - // update - cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle; + { + u32 incr = cpuRegs.cycle-s_iLastCOP0Cycle; + if( incr == 0 ) incr++; + cpuRegs.CP0.n.Count += incr; s_iLastCOP0Cycle = cpuRegs.cycle; - default: cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.CP0.r[_Rd_]; + if( !_Rt_ ) break; + } + + default: + cpuRegs.GPR.r[_Rt_].UD[0] = (s64)cpuRegs.CP0.r[_Rd_]; } } -void MTC0() { +void MTC0() +{ COP0_LOG("%s\n", disR5900Current.getCString()); //if(bExecBIOS == FALSE && _Rd_ == 25) SysPrintf("MTC0 _Rd_ %x = %x\n", _Rd_, cpuRegs.CP0.r[_Rd_]); - switch (_Rd_) { + switch (_Rd_) + { case 25: /*if(bExecBIOS == FALSE && _Rd_ == 25) SysPrintf("MTC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F);*/ - switch(_Imm_ & 0x3F){ - case 0: - if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) - cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0]; - if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) - cpuRegs.PERF.n.pcr1 += cpuRegs.cycle-s_iLastPERFCycle[1]; + switch(_Imm_ & 0x3F) + { + 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]; + break; + + case 1: // MTPC [LSB is set] - set PCR0 + cpuRegs.PERF.n.pcr0 = cpuRegs.GPR.r[_Rt_].UL[0]; s_iLastPERFCycle[0] = cpuRegs.cycle; + break; + + case 3: // MTPC [LSB is set] - set PCR0 + cpuRegs.PERF.n.pcr1 = cpuRegs.GPR.r[_Rt_].UL[0]; s_iLastPERFCycle[1] = cpuRegs.cycle; - break; - case 1: cpuRegs.PERF.n.pcr0 = cpuRegs.GPR.r[_Rt_].UL[0]; s_iLastPERFCycle[0] = cpuRegs.cycle; break; - case 3: cpuRegs.PERF.n.pcr1 = cpuRegs.GPR.r[_Rt_].UL[0]; s_iLastPERFCycle[1] = cpuRegs.cycle; break; + break; } - break; + break; + case 24: - SysPrintf("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); - break; + Console::WriteLn("MTC0 Breakpoint debug Registers code = %x", params cpuRegs.code & 0x3FF); + break; + case 12: WriteCP0Status(cpuRegs.GPR.r[_Rt_].UL[0]); break; - case 9: s_iLastCOP0Cycle = cpuRegs.cycle; cpuRegs.CP0.r[9] = cpuRegs.GPR.r[_Rt_].UL[0]; break; - default: cpuRegs.CP0.r[_Rd_] = cpuRegs.GPR.r[_Rt_].UL[0]; break; + case 9: + s_iLastCOP0Cycle = cpuRegs.cycle; + cpuRegs.CP0.r[9] = cpuRegs.GPR.r[_Rt_].UL[0]; + break; + + default: + cpuRegs.CP0.r[_Rd_] = cpuRegs.GPR.r[_Rt_].UL[0]; + break; } } diff --git a/pcsx2/COP0.h b/pcsx2/COP0.h index 6fb2a200d0..f8b0e4e6e1 100644 --- a/pcsx2/COP0.h +++ b/pcsx2/COP0.h @@ -19,10 +19,14 @@ #ifndef __COP0_H__ #define __COP0_H__ -void WriteCP0Status(u32 value); -void UpdateCP0Status(); -void WriteTLB(int i); -void UnmapTLB(int i); -void MapTLB(int i); +extern void WriteCP0Status(u32 value); +extern void UpdateCP0Status(); +extern void WriteTLB(int i); +extern void UnmapTLB(int i); +extern void MapTLB(int i); + +extern void COP0_UpdatePCR0(); +extern void COP0_UpdatePCR1(); + #endif /* __COP0_H__ */ diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index fffa793c05..82dcd8b6b8 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -387,19 +387,13 @@ static __forceinline void _cpuTestTIMR() static __forceinline void _cpuTestPERF() { - // fixme - The interpreter and recompiler both re-calculate these values - // whenever they are read, so updating them at regular intervals *should be* - // merely a common courtesy. But when I set them up to be called less - // frequently some games would crash. I'd like to figure out why someday. [Air] + // Perfs are updated when read by games (COP0's MFC0/MTC0 instructions), so we need + // only update them at semi-regular intervals to keep cpuRegs.cycle from wrapping + // around twice on us btween updates. Hence this function is called from the cpu's + // Counters update. - if((cpuRegs.PERF.n.pccr & 0x800003E0) == 0x80000020) { - cpuRegs.PERF.n.pcr0 += cpuRegs.cycle-s_iLastPERFCycle[0]; - s_iLastPERFCycle[0] = cpuRegs.cycle; - } - if((cpuRegs.PERF.n.pccr & 0x800F8000) == 0x80008000) { - cpuRegs.PERF.n.pcr1 += cpuRegs.cycle-s_iLastPERFCycle[1]; - s_iLastPERFCycle[1] = cpuRegs.cycle; - } + COP0_UpdatePCR0(); + COP0_UpdatePCR1(); } // Checks the COP0.Status for exception enablings. diff --git a/pcsx2/x86/iCOP0.cpp b/pcsx2/x86/iCOP0.cpp index f53c527fbc..7bb3eca220 100644 --- a/pcsx2/x86/iCOP0.cpp +++ b/pcsx2/x86/iCOP0.cpp @@ -128,16 +128,21 @@ void recMFC0( void ) { int mmreg; - if ( ! _Rt_ ) return; - - if( _Rd_ == 9 ) { + if( _Rd_ == 9 ) + { + // This case needs to be handled even if the write-back is ignored (_Rt_ == 0 ) MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); - MOV32RtoR(EAX,ECX); + MOV32RtoR(EAX, ECX); SUB32MtoR(EAX, (uptr)&s_iLastCOP0Cycle); + u8* skipInc = JNZ8( 0 ); + INC32R(EAX); + x86SetJ8( skipInc ); ADD32RtoM((uptr)&cpuRegs.CP0.n.Count, EAX); MOV32RtoM((uptr)&s_iLastCOP0Cycle, ECX); MOV32MtoR( EAX, (uptr)&cpuRegs.CP0.r[ _Rd_ ] ); - + + if( !_Rt_ ) return; + _deleteEEreg(_Rt_, 0); MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); @@ -148,34 +153,37 @@ void recMFC0( void ) else EEINST_RESETHASLIVE1(_Rt_); return; } - if( _Rd_ == 25 ) { - - _deleteEEreg(_Rt_, 0); - switch(_Imm_ & 0x3F){ + + if ( !_Rt_ ) return; + + if( _Rd_ == 25 ) + { + switch(_Imm_ & 0x3F) + { case 0: MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pccr); - break; + break; case 1: - // check if needs to be incremented - MOV32MtoR(ECX, (uptr)&cpuRegs.PERF.n.pccr); + /*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]); + x86SetJ8(j8Ptr[0]);*/ + + CALLFunc( (uptr)COP0_UpdatePCR0 ); break; case 3: - // check if needs to be incremented - MOV32MtoR(ECX, (uptr)&cpuRegs.PERF.n.pccr); + /*MOV32MtoR(ECX, (uptr)&cpuRegs.PERF.n.pccr); MOV32MtoR(EAX, (uptr)&cpuRegs.PERF.n.pcr1); AND32ItoR(ECX, 0x800F8000); @@ -188,12 +196,13 @@ void recMFC0( void ) MOV32RtoM((uptr)&s_iLastPERFCycle[1], EDX); MOV32RtoM((uptr)&cpuRegs.PERF.n.pcr1, EAX); - x86SetJ8(j8Ptr[0]); - + x86SetJ8(j8Ptr[0]);*/ + + CALLFunc( (uptr)COP0_UpdatePCR1 ); break; } - - MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); + _deleteEEreg(_Rt_, 0); + MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX); if(EEINST_ISLIVE1(_Rt_)) { CDQ(); @@ -201,10 +210,6 @@ void recMFC0( void ) } else EEINST_RESETHASLIVE1(_Rt_); -#ifdef PCSX2_DEVBUILD - COP0_LOG("MFC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", - cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F); -#endif return; } else if( _Rd_ == 24){ @@ -283,93 +288,100 @@ void updatePCCR() void recMTC0() { - if( GPR_IS_CONST1(_Rt_) ) { - switch (_Rd_) { + if( GPR_IS_CONST1(_Rt_) ) + { + switch (_Rd_) + { case 12: iFlushCall(FLUSH_NODESTROY); //_flushCachedRegs(); //NOTE: necessary? _callFunctionArg1((uptr)WriteCP0Status, MEM_CONSTTAG, g_cpuConstRegs[_Rt_].UL[0]); - break; + break; + case 9: MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); MOV32RtoM((uptr)&s_iLastCOP0Cycle, ECX); MOV32ItoM((uptr)&cpuRegs.CP0.r[9], g_cpuConstRegs[_Rt_].UL[0]); - break; - case 25: - COP0_LOG("MTC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", - cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F); - switch(_Imm_ & 0x3F){ - case 0: - - updatePCCR(); - MOV32ItoM((uptr)&cpuRegs.PERF.n.pccr, g_cpuConstRegs[_Rt_].UL[0]); + break; + + case 25: + switch(_Imm_ & 0x3F) + { + case 0: + CALLFunc( (uptr)COP0_UpdatePCR0 ); + CALLFunc( (uptr)COP0_UpdatePCR1 ); + MOV32ItoM((uptr)&cpuRegs.PERF.n.pccr, g_cpuConstRegs[_Rt_].UL[0]); + break; - // update the cycles - MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX); - MOV32RtoM((uptr)&s_iLastPERFCycle[1], ECX); - break; case 1: MOV32MtoR(EAX, (uptr)&cpuRegs.cycle); MOV32ItoM((uptr)&cpuRegs.PERF.n.pcr0, g_cpuConstRegs[_Rt_].UL[0]); MOV32RtoM((uptr)&s_iLastPERFCycle[0], EAX); - break; + break; + case 3: MOV32MtoR(EAX, (uptr)&cpuRegs.cycle); MOV32ItoM((uptr)&cpuRegs.PERF.n.pcr1, g_cpuConstRegs[_Rt_].UL[0]); MOV32RtoM((uptr)&s_iLastPERFCycle[1], EAX); - break; + break; } - break; + break; + case 24: COP0_LOG("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); - break; + break; + default: MOV32ItoM((uptr)&cpuRegs.CP0.r[_Rd_], g_cpuConstRegs[_Rt_].UL[0]); - break; + break; } } - else { - switch (_Rd_) { + else + { + switch (_Rd_) + { case 12: iFlushCall(FLUSH_NODESTROY); //_flushCachedRegs(); //NOTE: necessary? _callFunctionArg1((uptr)WriteCP0Status, MEM_GPRTAG|_Rt_, 0); - break; + break; + case 9: MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); _eeMoveGPRtoM((uptr)&cpuRegs.CP0.r[9], _Rt_); MOV32RtoM((uptr)&s_iLastCOP0Cycle, ECX); - break; - case 25: - COP0_LOG("MTC0 PCCR = %x PCR0 = %x PCR1 = %x IMM= %x\n", - cpuRegs.PERF.n.pccr, cpuRegs.PERF.n.pcr0, cpuRegs.PERF.n.pcr1, _Imm_ & 0x3F); - switch(_Imm_ & 0x3F){ - case 0: - updatePCCR(); - _eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pccr, _Rt_); + break; + + case 25: + switch(_Imm_ & 0x3F) + { + case 0: + CALLFunc( (uptr)COP0_UpdatePCR0 ); + CALLFunc( (uptr)COP0_UpdatePCR1 ); + _eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pccr, _Rt_); + break; - // update the cycles - MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX); - MOV32RtoM((uptr)&s_iLastPERFCycle[1], ECX); - break; case 1: MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); _eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pcr0, _Rt_); MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX); - break; + break; + case 3: MOV32MtoR(ECX, (uptr)&cpuRegs.cycle); _eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pcr1, _Rt_); MOV32RtoM((uptr)&s_iLastPERFCycle[1], ECX); - break; + break; } - break; + break; + case 24: COP0_LOG("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF); - break; + break; + default: _eeMoveGPRtoM((uptr)&cpuRegs.CP0.r[_Rd_], _Rt_); - break; + break; } } }