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