mirror of https://github.com/PCSX2/pcsx2.git
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
This commit is contained in:
parent
cef77785cb
commit
15901551b4
132
pcsx2/COP0.cpp
132
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;
|
||||
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 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;
|
||||
case 12:
|
||||
cpuRegs.GPR.r[_Rt_].SD[0] = (s32)(cpuRegs.CP0.r[_Rd_] & 0xf0c79c1f);
|
||||
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;
|
||||
|
||||
case 25:
|
||||
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;
|
||||
|
||||
case 24:
|
||||
SysPrintf("MFC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF);
|
||||
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;
|
||||
|
||||
case 24:
|
||||
SysPrintf("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
pcsx2/COP0.h
14
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__ */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
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,17 +153,19 @@ 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;
|
||||
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);
|
||||
|
||||
|
@ -171,11 +178,12 @@ void recMFC0( void )
|
|||
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,11 +196,12 @@ 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;
|
||||
}
|
||||
|
||||
_deleteEEreg(_Rt_, 0);
|
||||
MOV32RtoM((uptr)&cpuRegs.GPR.r[_Rt_].UL[0],EAX);
|
||||
|
||||
if(EEINST_ISLIVE1(_Rt_)) {
|
||||
|
@ -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,36 +288,37 @@ 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;
|
||||
|
||||
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){
|
||||
switch(_Imm_ & 0x3F)
|
||||
{
|
||||
case 0:
|
||||
|
||||
updatePCCR();
|
||||
CALLFunc( (uptr)COP0_UpdatePCR0 );
|
||||
CALLFunc( (uptr)COP0_UpdatePCR1 );
|
||||
MOV32ItoM((uptr)&cpuRegs.PERF.n.pccr, g_cpuConstRegs[_Rt_].UL[0]);
|
||||
|
||||
// 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;
|
||||
|
||||
case 3:
|
||||
MOV32MtoR(EAX, (uptr)&cpuRegs.cycle);
|
||||
MOV32ItoM((uptr)&cpuRegs.PERF.n.pcr1, g_cpuConstRegs[_Rt_].UL[0]);
|
||||
|
@ -320,43 +326,47 @@ void recMTC0()
|
|||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 24:
|
||||
COP0_LOG("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF);
|
||||
break;
|
||||
|
||||
default:
|
||||
MOV32ItoM((uptr)&cpuRegs.CP0.r[_Rd_], g_cpuConstRegs[_Rt_].UL[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (_Rd_) {
|
||||
else
|
||||
{
|
||||
switch (_Rd_)
|
||||
{
|
||||
case 12:
|
||||
iFlushCall(FLUSH_NODESTROY);
|
||||
//_flushCachedRegs(); //NOTE: necessary?
|
||||
_callFunctionArg1((uptr)WriteCP0Status, MEM_GPRTAG|_Rt_, 0);
|
||||
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_);
|
||||
|
||||
// update the cycles
|
||||
MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX);
|
||||
MOV32RtoM((uptr)&s_iLastPERFCycle[1], ECX);
|
||||
case 25:
|
||||
switch(_Imm_ & 0x3F)
|
||||
{
|
||||
case 0:
|
||||
CALLFunc( (uptr)COP0_UpdatePCR0 );
|
||||
CALLFunc( (uptr)COP0_UpdatePCR1 );
|
||||
_eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pccr, _Rt_);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
MOV32MtoR(ECX, (uptr)&cpuRegs.cycle);
|
||||
_eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pcr0, _Rt_);
|
||||
MOV32RtoM((uptr)&s_iLastPERFCycle[0], ECX);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
MOV32MtoR(ECX, (uptr)&cpuRegs.cycle);
|
||||
_eeMoveGPRtoM((uptr)&cpuRegs.PERF.n.pcr1, _Rt_);
|
||||
|
@ -364,9 +374,11 @@ void recMTC0()
|
|||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 24:
|
||||
COP0_LOG("MTC0 Breakpoint debug Registers code = %x\n", cpuRegs.code & 0x3FF);
|
||||
break;
|
||||
|
||||
default:
|
||||
_eeMoveGPRtoM((uptr)&cpuRegs.CP0.r[_Rd_], _Rt_);
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue