VU0: added a special case to the CFC2 instruction if it copies the value

from the TPC register (fixes Street Fighter EX3 #954 and R Racing
Evolution the invisible cars issue)
This commit is contained in:
Volodymyr Kutsenko 2017-02-23 02:37:33 +02:00
parent 726f9d5312
commit 6862106dee
2 changed files with 42 additions and 1 deletions

View File

@ -122,7 +122,15 @@ void CFC2() {
_vu0WaitMicro();
}
if (_Rt_ == 0) return;
cpuRegs.GPR.r[_Rt_].UL[0] = VU0.VI[_Fs_].UL;
if (_Fs_ == REG_TPC) {
// For explanation why this is needed here please refer to
// recCFC2() definded in microVU_Macro.inl
cpuRegs.GPR.r[_Rt_].UL[0] = VU0.VI[_Fs_].UL >> 3;
} else {
cpuRegs.GPR.r[_Rt_].UL[0] = VU0.VI[_Fs_].UL;
}
if(VU0.VI[_Fs_].UL & 0x80000000)
cpuRegs.GPR.r[_Rt_].UL[1] = 0xffffffff;
else

View File

@ -276,6 +276,39 @@ static void recCFC2() {
}
else xMOV(eax, ptr32[&vu0Regs.VI[_Rd_].UL]);
if (_Rd_ == REG_TPC) { // Divide TPC register value by 8 during copying
// Ok, this deserves an explanation.
// Accoring to the official PS2 VU0 coding manual there are 3 ways to execute a micro subroutine on VU0
// one of which is using the VCALLMSR intruction.
// The manual requires putting the address of the micro subroutine
// into the CMSAR0 register divided by 8 using the CTC2 command before executing VCALLMSR.
// Many games (for instance, 24: The Game, GTA LCS, GTA VCS and FFXII) do in fact use this way,
// they diligently put the address of the micro subroutine into a separate register (v0, v1 etc), divide it by 8
// and move it to CMSAR0 by calling the CTC2 command.
// However, there are also at least 2 confirmed games (R Racing Evolution, Street Fighter EX3)
// that execute a piece of code to run a micro subroutine on VU0 like this:
//
// ...
// cfc2 t4, TPC
// ctc2 t4, CMSAR0
// callmsr
// ...
//
// Interestingly enough there is no division by 8 but it works fine in these 2 mentioned games.
// It means the division operation is implicit.
// Homebrew tests for the real PS2 have shown that in fact the instruction "cfc2 t4, TPC" ends up with values that are not always divisible by 8.
// There are 2 possibilities: either the CFC2 instruction divides the value of the TPC (which is the Program Counter register
// for micro subroutines) by 8 itself during copying or the TPC register always works with addresses already divided by 8.
// The latter seems less possible because the Program Counter register by definition holds the memory address of the instruction.
// In addition, PCSX2 already implements TPC as an instruction pointer so we'll assume that division by 8
// is done by CFC2 while working with the TPC register.
// (fixes R Racing Evolution and Street Fighter EX3)
xSHR(eax, 3);
}
// FixMe: Should R-Reg have upper 9 bits 0?
xMOV(ptr32[&cpuRegs.GPR.r[_Rt_].UL[0]], eax);