- Properly fixed problems from r1254, a lot of games should be fixed.

pcsx2/microVU:
- Added some debug code that runs sVU and mVU, and compares their results. If they differ, then the game is halted, and debug info is given. (this can be enabled in iVU1micro.cpp with the DEBUG_COMPARE2 macro)... the code is a bit messy, but it gets the job done for now.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1265 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
cottonvibes 2009-05-26 01:00:38 +00:00
parent 0900ef5420
commit 22535ad886
7 changed files with 237 additions and 107 deletions

View File

@ -30,7 +30,11 @@
extern u32 vudump;
#endif
//#define DEBUG_COMPARE
int mVUdebugNow = 0;
//#define DEBUG_COMPARE // Run sVU or mVU and print results
//#define DEBUG_COMPARE2 // Runs both VU recs and breaks when results differ
#ifdef DEBUG_COMPARE
#include <windows.h>
@ -39,7 +43,7 @@ static int runAmount = 0;
void VUtestPause() {
runAmount++;
if (runAmount < 100) return;
if (runAmount < 654) return;
#ifndef PCSX2_MICROVU_
SysPrintf("Super VU - Pass %d\n", runAmount);
@ -48,13 +52,27 @@ void VUtestPause() {
#endif
for (int i = 0; i < 32; i++) {
SysPrintf("VF%02d = {%f, %f, %f, %f}\n", i, VU1.VF[i].F[0], VU1.VF[i].F[1], VU1.VF[i].F[2], VU1.VF[i].F[3]);
SysPrintf("VF%02d = {%f, %f, %f, %f}\n", i, VU1.VF[i].F[0], VU1.VF[i].F[1], VU1.VF[i].F[2], VU1.VF[i].F[3]);
}
SysPrintf("ACC = {%f, %f, %f, %f}\n", VU1.ACC.F[0], VU1.ACC.F[1], VU1.ACC.F[2], VU1.ACC.F[3]);
for (int i = 0; i < 16; i++) {
SysPrintf("VI%02d = % 8d ($%08x)\n", i, (s16)VU1.VI[i].UL, (s16)VU1.VI[i].UL);
SysPrintf("VI%02d = % 8d ($%08x)\n", i, (s16)VU1.VI[i].UL, (s16)VU1.VI[i].UL);
}
SysPrintf("Stat = % 8d ($%08x)\n", (s16)VU1.VI[REG_STATUS_FLAG].UL, (s16)VU1.VI[REG_STATUS_FLAG].UL);
SysPrintf("MAC = % 8d ($%08x)\n", (s16)VU1.VI[REG_MAC_FLAG].UL, (s16)VU1.VI[REG_MAC_FLAG].UL);
SysPrintf("CLIP = % 8d ($%08x)\n", (s16)VU1.VI[REG_CLIP_FLAG].UL, (s16)VU1.VI[REG_CLIP_FLAG].UL);
SysPrintf("Q-reg = %f ($%08x)\n", VU1.VI[REG_Q].F, (s32)VU1.VI[REG_Q].UL);
SysPrintf("P-reg = %f ($%08x)\n", VU1.VI[REG_P].F, (s32)VU1.VI[REG_P].UL);
SysPrintf("I-reg = %f ($%08x)\n", VU1.VI[REG_I].F, (s32)VU1.VI[REG_I].UL);
SysPrintf("_Stat = % 8d ($%08x)\n", (s16)VU1.statusflag, (s16)VU1.statusflag);
SysPrintf("_MAC = % 8d ($%08x)\n", (s16)VU1.macflag, (s16)VU1.macflag);
SysPrintf("_CLIP = % 8d ($%08x)\n", (s16)VU1.clipflag, (s16)VU1.clipflag);
u32 j = 0;
for (int i = 0; i < (0x4000 / 4); i++) {
j ^= ((u32*)(VU1.Mem))[i];
@ -70,112 +88,178 @@ void VUtestPause() {
void VUtestPause() {}
#endif
#ifndef PCSX2_MICROVU_
namespace VU1micro
{
void recAlloc()
{
SuperVUAlloc(1);
}
#ifdef DEBUG_COMPARE2
void __fastcall recClear( u32 Addr, u32 Size )
{
assert( (Addr&7) == 0 );
SuperVUClear(Addr, Size, 1); // Size should be a multiple of 8 bytes!
}
void recShutdown()
{
SuperVUDestroy( 1 );
}
// commented out because I'm not sure it actually works anymore with SuperVU (air)
/*static void iVU1DumpBlock()
{
FILE *f;
char filename[ g_MaxPath ];
u32 *mem;
u32 i;
#ifdef _WIN32
CreateDirectory("dumps", NULL);
sprintf_s( filename, g_MaxPath, "dumps\\vu%.4X.txt", VU1.VI[ REG_TPC ].UL );
#else
mkdir("dumps", 0755);
sprintf( filename, "dumps/vu%.4X.txt", VU1.VI[ REG_TPC ].UL );
#endif
Console::WriteLn( "dump1 %x => %x (%s)", params VU1.VI[ REG_TPC ].UL, pc, filename );
f = fopen( filename, "wb" );
for ( i = VU1.VI[REG_TPC].UL; i < pc; i += 8 ) {
char* pstr;
mem = (u32*)&VU1.Micro[i];
pstr = disVU1MicroUF( mem[1], i+4 );
fprintf(f, "%x: %-40s ", i, pstr);
pstr = disVU1MicroLF( mem[0], i );
fprintf(f, "%s\n", pstr);
}
fclose( f );
}*/
static void recReset()
{
SuperVUReset(1);
// these shouldn't be needed, but shouldn't hurt anything either.
x86FpuState = FPU_STATE;
}
static void recStep()
{
}
static void recExecuteBlock(void)
{
#ifdef _DEBUG
static u32 vuprogcount = 0;
vuprogcount++;
if( vudump & 8 ) __Log("start vu1: %x %x", VU1.VI[ REG_TPC ].UL, vuprogcount);
#endif
if((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0){
//Console::WriteLn("Execute block VU1, VU1 not busy");
return;
}
if (VU1.VI[REG_TPC].UL >= VU1.maxmicro)
{
Console::Error("VU1 memory overflow!!: %x", params VU1.VI[REG_TPC].UL);
/*VU0.VI[REG_VPU_STAT].UL&= ~0x100;
VU1.cycle++;
return;*/
}
assert( (VU1.VI[ REG_TPC ].UL&7) == 0 );
#ifdef DEBUG_COMPARE
SysPrintf("StartPC = 0x%04x\n", VU1.VI[REG_TPC].UL);
#ifndef DEBUG_COMPARE
#include <windows.h>
#endif
FreezeXMMRegs(1);
do { // while loop needed since not always will return finished
SuperVUExecuteProgram(VU1.VI[ REG_TPC ].UL & 0x3fff, 1);
} while( VU0.VI[ REG_VPU_STAT ].UL&0x100 );
FreezeXMMRegs(0);
VUtestPause();
}
}
#else
extern void initVUrec(VURegs* vuRegs, const int vuIndex);
extern void closeVUrec(const int vuIndex);
extern void resetVUrec(const int vuIndex);
extern void clearVUrec(u32 addr, u32 size, const int vuIndex);
extern void runVUrec(u32 startPC, u32 cycles, const int vuIndex);
PCSX2_ALIGNED16(u8 backVUregs[sizeof(VURegs)]);
PCSX2_ALIGNED16(u8 cmpVUregs [sizeof(VURegs)]);
PCSX2_ALIGNED16(u8 backVUmem [0x4000]);
PCSX2_ALIGNED16(u8 cmpVUmem [0x4000]);
static u32 runCount = 0;
#define VU3 ((VURegs)*((VURegs*)cmpVUregs))
#define cmpA Console::Error
#define cmpB Console::WriteLn
#define cmpPrint(cond) { \
if (cond) { \
cmpA("%s", params str1); \
cmpA("%s", params str2); \
} \
else { \
cmpB("%s", params str1); \
cmpB("%s", params str2); \
} \
}
namespace VU1micro
{
void recAlloc() { SuperVUAlloc(1); initVUrec(&VU1, 1); }
void __fastcall recClear(u32 Addr, u32 Size) { SuperVUClear(Addr, Size, 1); clearVUrec(Addr, Size, 1); }
void recShutdown() { SuperVUDestroy(1); closeVUrec(1); }
static void recReset() { SuperVUReset(1); resetVUrec(1); x86FpuState = FPU_STATE; }
static void recStep() {}
static void recExecuteBlock(void)
{
if((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return;
assert((VU1.VI[ REG_TPC ].UL&7) == 0);
if (VU1.VI[REG_TPC].UL >= VU1.maxmicro) { Console::Error("VU1 memory overflow!!: %x", params VU1.VI[REG_TPC].UL); }
#ifdef DEBUG_COMPARE
SysPrintf("(%08d) StartPC = 0x%04x\n", runAmount, VU1.VI[REG_TPC].UL);
#endif
FreezeXMMRegs(1);
runCount++;
memcpy_fast((u8*)backVUregs, (u8*)&VU1, sizeof(VURegs));
memcpy_fast((u8*)backVUmem, (u8*)VU1.Mem, 0x4000);
do { // while loop needed since not always will return finished
SuperVUExecuteProgram(VU1.VI[ REG_TPC ].UL & 0x3fff, 1);
} while( VU0.VI[ REG_VPU_STAT ].UL&0x100 );
memcpy_fast((u8*)cmpVUregs, (u8*)&VU1, sizeof(VURegs));
memcpy_fast((u8*)cmpVUmem, (u8*)VU1.Mem, 0x4000);
memcpy_fast((u8*)&VU1, (u8*)backVUregs, sizeof(VURegs));
memcpy_fast((u8*)VU1.Mem, (u8*)backVUmem, 0x4000);
runVUrec(VU1.VI[REG_TPC].UL, 300000 /*0x7fffffff*/, 1);
if ((memcmp((u8*)cmpVUregs, (u8*)&VU1, (16*32) + (16*16))) || (memcmp((u8*)cmpVUmem, (u8*)VU1.Mem, 0x4000))) {
char str1[150];
char str2[150];
SysPrintf("\n\n");
SysPrintf("-----------------------------------------------\n");
Console::Notice("Problem Occurred!");
SysPrintf("-----------------------------------------------\n");
SysPrintf("runCount = %d\n", runCount);
SysPrintf("StartPC [%04x]\n", ((VURegs*)backVUregs)->VI[REG_TPC].UL);
SysPrintf("-----------------------------------------------\n\n");
SysPrintf("-----------------------------------------------\n");
Console::Notice("Super VU / Micro VU");
SysPrintf("-----------------------------------------------\n");
for (int i = 0; i < 32; i++) {
sprintf(str1, "VF%02d = {%f, %f, %f, %f}", i, VU3.VF[i].F[0], VU3.VF[i].F[1], VU3.VF[i].F[2], VU3.VF[i].F[3]);
sprintf(str2, "VF%02d = {%f, %f, %f, %f}", i, VU1.VF[i].F[0], VU1.VF[i].F[1], VU1.VF[i].F[2], VU1.VF[i].F[3]);
cmpPrint(((VU1.VF[i].UL[0] != VU3.VF[i].UL[0]) || (VU1.VF[i].UL[1] != VU3.VF[i].UL[1]) || (VU1.VF[i].UL[2] != VU3.VF[i].UL[2]) || (VU1.VF[i].UL[3] != VU3.VF[i].UL[3])));
}
sprintf(str1, "ACC = {%f, %f, %f, %f}", VU3.ACC.F[0], VU3.ACC.F[1], VU3.ACC.F[2], VU3.ACC.F[3]);
sprintf(str2, "ACC = {%f, %f, %f, %f}", VU1.ACC.F[0], VU1.ACC.F[1], VU1.ACC.F[2], VU1.ACC.F[3]);
cmpPrint(((VU1.ACC.F[0] != VU3.ACC.F[0]) || (VU1.ACC.F[1] != VU3.ACC.F[1]) || (VU1.ACC.F[2] != VU3.ACC.F[2]) || (VU1.ACC.F[3] != VU3.ACC.F[3])));
for (int i = 0; i < 16; i++) {
sprintf(str1, "VI%02d = % 8d ($%08x)", i, (s16)VU3.VI[i].UL, VU3.VI[i].UL);
sprintf(str2, "VI%02d = % 8d ($%08x)", i, (s16)VU1.VI[i].UL, VU1.VI[i].UL);
cmpPrint((VU1.VI[i].UL != VU3.VI[i].UL));
}
sprintf(str1, "Stat = % 8d ($%08x)", (s16)VU3.VI[REG_STATUS_FLAG].UL, VU3.VI[REG_STATUS_FLAG].UL);
sprintf(str2, "Stat = % 8d ($%08x)", (s16)VU1.VI[REG_STATUS_FLAG].UL, VU1.VI[REG_STATUS_FLAG].UL);
cmpPrint((VU1.VI[REG_STATUS_FLAG].UL != VU3.VI[REG_STATUS_FLAG].UL));
sprintf(str1, "MAC = % 8d ($%08x)", (s16)VU3.VI[REG_MAC_FLAG].UL, VU3.VI[REG_MAC_FLAG].UL);
sprintf(str2, "MAC = % 8d ($%08x)", (s16)VU1.VI[REG_MAC_FLAG].UL, VU1.VI[REG_MAC_FLAG].UL);
cmpPrint((VU1.VI[REG_MAC_FLAG].UL != VU3.VI[REG_MAC_FLAG].UL));
sprintf(str1, "CLIP = % 8d ($%08x)", (s16)VU3.VI[REG_CLIP_FLAG].UL, VU3.VI[REG_CLIP_FLAG].UL);
sprintf(str2, "CLIP = % 8d ($%08x)", (s16)VU1.VI[REG_CLIP_FLAG].UL, VU1.VI[REG_CLIP_FLAG].UL);
cmpPrint((VU1.VI[REG_CLIP_FLAG].UL != VU3.VI[REG_CLIP_FLAG].UL));
sprintf(str1, "Q-reg = %f ($%08x)", VU3.VI[REG_Q].F, VU3.VI[REG_Q].UL);
sprintf(str2, "Q-reg = %f ($%08x)", VU1.VI[REG_Q].F, VU1.VI[REG_Q].UL);
cmpPrint((VU1.VI[REG_Q].UL != VU3.VI[REG_Q].UL));
sprintf(str1, "P-reg = %f ($%08x)", VU3.VI[REG_P].F, VU3.VI[REG_P].UL);
sprintf(str2, "P-reg = %f ($%08x)", VU1.VI[REG_P].F, VU1.VI[REG_P].UL);
cmpPrint((VU1.VI[REG_P].UL != VU3.VI[REG_P].UL));
sprintf(str1, "I-reg = %f ($%08x)", VU3.VI[REG_I].F, VU3.VI[REG_I].UL);
sprintf(str2, "I-reg = %f ($%08x)", VU1.VI[REG_I].F, VU1.VI[REG_I].UL);
cmpPrint((VU1.VI[REG_I].UL != VU3.VI[REG_I].UL));
sprintf(str1, "_Stat = % 8d ($%08x)", (s16)VU3.statusflag, VU3.statusflag);
sprintf(str2, "_Stat = % 8d ($%08x)", (s16)VU1.statusflag, VU1.statusflag);
cmpPrint((VU1.statusflag != VU3.statusflag));
sprintf(str1, "_MAC = % 8d ($%08x)", (s16)VU3.macflag, VU3.macflag);
sprintf(str2, "_MAC = % 8d ($%08x)", (s16)VU1.macflag, VU1.macflag);
cmpPrint((VU1.macflag != VU3.macflag));
sprintf(str1, "_CLIP = % 8d ($%08x)", (s16)VU3.clipflag, VU3.clipflag);
sprintf(str2, "_CLIP = % 8d ($%08x)", (s16)VU1.clipflag, VU1.clipflag);
cmpPrint((VU1.clipflag != VU3.clipflag));
u32 j = 0;
u32 z = 0;
for (int i = 0; i < (0x4000 / 4); i++) {
j ^= ((u32*)(cmpVUmem))[i];
z ^= ((u32*)(VU1.Mem)) [i];
}
sprintf(str1, "VU Mem CRC = 0x%08x", j);
sprintf(str2, "VU Mem CRC = 0x%08x", z);
cmpPrint((j != z));
sprintf(str1, "EndPC = 0x%04x", VU3.VI[REG_TPC].UL);
sprintf(str2, "EndPC = 0x%04x", VU1.VI[REG_TPC].UL);
cmpPrint((VU1.VI[REG_TPC].UL != VU3.VI[REG_TPC].UL));
SysPrintf("-----------------------------------------------\n\n");
mVUdebugNow = 1;
resetVUrec(1);
memcpy_fast((u8*)&VU1, (u8*)backVUregs, sizeof(VURegs));
memcpy_fast((u8*)VU1.Mem, (u8*)backVUmem, 0x4000);
runVUrec(VU1.VI[REG_TPC].UL, 300000 /*0x7fffffff*/, 1);
for (int i = 0; i < 10000000; i++) {
Sleep(1000);
}
}
VUtestPause();
FreezeXMMRegs(0);
}
}
#else
#ifdef PCSX2_MICROVU_
extern void initVUrec(VURegs* vuRegs, const int vuIndex);
extern void closeVUrec(const int vuIndex);
extern void resetVUrec(const int vuIndex);
extern void clearVUrec(u32 addr, u32 size, const int vuIndex);
extern void runVUrec(u32 startPC, u32 cycles, const int vuIndex);
namespace VU1micro
{
@ -190,7 +274,7 @@ namespace VU1micro
assert( (VU1.VI[REG_TPC].UL&7) == 0 );
#ifdef DEBUG_COMPARE
SysPrintf("StartPC = 0x%04x\n", VU1.VI[REG_TPC].UL);
SysPrintf("(%08d) StartPC = 0x%04x\n", runAmount, VU1.VI[REG_TPC].UL);
#endif
FreezeXMMRegs(1);
@ -202,6 +286,34 @@ namespace VU1micro
VUtestPause();
}
}
#else
namespace VU1micro
{
void recAlloc() { SuperVUAlloc(1); }
void __fastcall recClear(u32 Addr, u32 Size) { SuperVUClear(Addr, Size, 1); }
void recShutdown() { SuperVUDestroy(1); }
static void recReset() { SuperVUReset(1); x86FpuState = FPU_STATE; }
static void recStep() {}
static void recExecuteBlock(void)
{
if((VU0.VI[REG_VPU_STAT].UL & 0x100) == 0) return;
if (VU1.VI[REG_TPC].UL >= VU1.maxmicro) { Console::Error("VU1 memory overflow!!: %x", params VU1.VI[REG_TPC].UL); }
assert((VU1.VI[ REG_TPC ].UL&7) == 0);
#ifdef DEBUG_COMPARE
SysPrintf("(%08d) StartPC = 0x%04x\n", runAmount, VU1.VI[REG_TPC].UL);
#endif
FreezeXMMRegs(1);
do { // while loop needed since not always will return finished
SuperVUExecuteProgram(VU1.VI[ REG_TPC ].UL & 0x3fff, 1);
} while( VU0.VI[ REG_VPU_STAT ].UL&0x100 );
FreezeXMMRegs(0);
}
}
#endif
#endif
using namespace VU1micro;

View File

@ -132,6 +132,9 @@ extern PCSX2_ALIGNED16(microVU microVU1);
extern void (*mVU_UPPER_OPCODE[64])( VURegs* VU, s32 info );
extern void (*mVU_LOWER_OPCODE[128])( VURegs* VU, s32 info );
// Debug Helper
extern int mVUdebugNow;
// Main Functions
microVUt(void) mVUinit(VURegs*);
microVUx(void) mVUreset();

View File

@ -731,7 +731,7 @@ microVUt(void) mVUallocVIb(int GPRreg, int _reg_) {
}
if (_reg_ == 0) { return; }
else if (isMMX(_reg_)) { MOVD32RtoMMX(mmVI(_reg_), GPRreg); }
else if (_reg_ < 16) { MOV32RtoRm(gprR, GPRreg, (_reg_ - 9) * 16); }
else if (_reg_ < 16) { MOV16RtoRm(gprR, GPRreg, (_reg_ - 9) * 16); }
}
//------------------------------------------------------------------

View File

@ -184,10 +184,13 @@ microVUt(void) mVUendProgram(int qInst, int pInst, int fStatus, int fMac, int fC
void __fastcall mVUwarning0(u32 PC) { Console::Error("microVU0 Warning: Exiting from Possible Infinite Loop [%04x]", params PC); }
void __fastcall mVUwarning1(u32 PC) { Console::Error("microVU1 Warning: Exiting from Possible Infinite Loop [%04x]", params PC); }
void __fastcall mVUprintPC1(u32 PC) { Console::WriteLn("Block startPC [%04x]", params PC); }
void __fastcall mVUprintPC2(u32 PC) { Console::WriteLn("Block endPC [%04x]\n", params PC); }
microVUt(void) mVUtestCycles() {
microVU* mVU = mVUx;
iPC = mVUstartPC;
mVUdebugNOW(0);
CMP32ItoM((uptr)&mVU->cycles, 0);
u8* jmp8 = JG8(0);
MOV32ItoR(gprT2, xPC);
@ -209,7 +212,7 @@ microVUt(void*) __fastcall mVUcompile(u32 startPC, uptr pState) {
if (startPC > ((vuIndex) ? 0x3fff : 0xfff)) { Console::Error("microVU%d: invalid startPC", params vuIndex); }
startPC &= (vuIndex ? 0x3ff8 : 0xff8);
if (mVUblocks[startPC/8] == NULL) {
mVUblocks[startPC/8] = new microBlockManager();
}
@ -280,6 +283,7 @@ microVUt(void*) __fastcall mVUcompile(u32 startPC, uptr pState) {
microBlock* bBlock = NULL;
u32* ajmp = 0;
mVUsetupRange<vuIndex>(xPC);
mVUdebugNOW(1);
switch (mVUbranch) {
case 3: branchCase(JE32, JNE32); // IBEQ

View File

@ -203,7 +203,7 @@ microVUt(void) mVUflagPass(int startPC) {
if (mVUbranch) { branch = 3; mVUbranch = 0; }
incPC(1);
}
//if (mVUcount < 4) { mVUflagInfo |= 0xfff; } // Is this Too Slow? 99% of games probably don't need this.
if (mVUcount < 4) { mVUflagInfo |= 0xfff; }
iPC = oldPC;
mVUcount = oldCount;
mVUbranch = oldBranch;

View File

@ -1131,8 +1131,10 @@ microVUf(void) mVU_XGKICK() {
//------------------------------------------------------------------
#define setBranchA(x, _x_) { \
mVUbranch = x; \
pass1 { if (_Imm11_ == 1 && !_x_) { mVUinfo |= _isNOP; mVUbranch = 0; return; } } \
pass1 { if (_Imm11_ == 1 && !_x_) { mVUinfo |= _isNOP; return; } mVUbranch = x; } \
pass2 { if (_Imm11_ == 1 && !_x_) { return; } mVUbranch = x; } \
pass3 { mVUbranch = x; } \
pass4 { if (_Imm11_ == 1 && !_x_) { return; } mVUbranch = x; } \
}
microVUf(void) mVU_B() {

View File

@ -303,3 +303,12 @@ declareAllVariables
mVUreset<vuIndex>(); \
} \
}
#define mVUdebugNOW(isEndPC) { \
if (mVUdebugNow) { \
MOV32ItoR(gprT2, xPC); \
if (isEndPC) { CALLFunc((uptr)mVUprintPC2); } \
else { CALLFunc((uptr)mVUprintPC1); } \
MOV32ItoR(gprR, Roffset); \
} \
}