microVU: Fixed linking on branch delays. There may still be some situations that don't work correctly, but i don't have any games which use it, have ammended console logging to inform us if the game is broken.

Fixes in this commit:
-Evil Dead - Fistful of Boomstick - Now goes ingame and works right (needs software mode on gsdx to fix lighting)
-Tony Hawk's Project 8 - Graphics are no correct. Like earlier TH games, you need Negative rounding to fix it all.
-Mark of Kri - The game now has collision detection! Apparently before you'd get stuck on objects, now you can walk freely.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@5561 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
refraction@gmail.com 2013-02-18 15:39:22 +00:00
parent a7c8da7578
commit 484065d0a8
4 changed files with 187 additions and 20 deletions

View File

@ -460,12 +460,21 @@ __ri int mVUbranchCheck(mV) {
mVUlow.badBranch = 1;
incPC(2);
mVUlow.evilBranch = 1;
mVUregs.blockType = 2;
if(mVUlow.branch == 2 || mVUlow.branch == 10) //Needs linking, we can only guess this if the next is not conditional
{
if(branchType <= 2 && branchType >= 9) //First branch is not conditional so we know what the link will be
{ //So we can let the existing evil block do its thing! We know where to get the addr :)
mVUregs.blockType = 2;
} //Else it is conditional, so we need to do some nasty processing later in microVU_Branch.inl
}
else mVUregs.blockType = 2; //Second branch doesn't need linking, so can let it run its evil block course (MGS2 for testing)
mVUregs.needExactMatch |= 7; // This might not be necessary, but w/e...
mVUregs.flagInfo = 0;
mVUregs.fullFlags0 = 0;
mVUregs.fullFlags1 = 0;
DevCon.Warning("microVU%d: %s in %s delay slot! [%04x]", mVU.index,
DevCon.Warning("microVU%d: %s in %s delay slot! [%04x] - If game broken report to PCSX2 Team", mVU.index,
branchSTR[mVUlow.branch&0xf], branchSTR[branchType&0xf], xPC);
return 1;
}

View File

@ -18,7 +18,7 @@
extern bool doEarlyExit (microVU& mVU);
extern void mVUincCycles(microVU& mVU, int x);
extern void* mVUcompile (microVU& mVU, u32 startPC, uptr pState);
extern void* mVUcompileSingleInstruction(microVU& mVU, u32 startPC, uptr pState, microFlagCycles& mFC);
__fi int getLastFlagInst(microRegInfo& pState, int* xFlag, int flagType, int isEbit) {
if (isEbit) return findFlagInst(xFlag, 0x7fffffff);
if (pState.needExactMatch & (1<<flagType)) return 3;
@ -126,18 +126,76 @@ void normJumpCompile(mV, microFlagCycles& mFC, bool isEvilJump) {
void normBranch(mV, microFlagCycles& mFC) {
// E-bit Branch
if (mVUup.eBit) { iPC = branchAddr/4; mVUendProgram(mVU, &mFC, 1); return; }
if (mVUup.eBit) { if(mVUlow.badBranch) DevCon.Warning("End on evil Unconditional branch! - Not implemented! - If game broken report to PCSX2 Team"); iPC = branchAddr/4; mVUendProgram(mVU, &mFC, 1); return; }
if(mVUlow.badBranch)
{
u32 badBranchAddr = branchAddr+8;
incPC(3);
if(mVUlow.branch == 2 || mVUlow.branch == 10) //Delay slot branch needs linking
{
DevCon.Warning("Found %s in delay slot, linking - If game broken report to PCSX2 Team", mVUlow.branch == 2 ? "BAL" : "JALR");
xMOV(gprT3, badBranchAddr);
xSHR(gprT3, 3);
mVUallocVIb(mVU, gprT3, _It_);
}
incPC(-3);
}
// Normal Branch
mVUsetupBranch(mVU, mFC);
normBranchCompile(mVU, branchAddr);
}
//Messy handler warning!!
//This handles JALR/BAL in the delay slot of a conditional branch. We do this because the normal handling
//Doesn't seem to work properly, even if the link is made to the correct address, so we do it manually instead.
//Normally EvilBlock handles all this stuff, but something to do with conditionals and links don't quite work right :/
void condJumpProcessingEvil(mV, microFlagCycles& mFC, int JMPcc) {
u32 bPC = iPC-1; // mVUcompile can modify iPC, mVUpBlock, and mVUregs so back them up
microBlock* pBlock = mVUpBlock;
u32 badBranchAddr;
iPC = bPC-2;
setCode();
badBranchAddr = branchAddr;
xCMP(ptr16[&mVU.branch], 0);
xForwardJump32 eJMP(xInvertCond((JccComparisonType)JMPcc));
mVUcompileSingleInstruction(mVU, badBranchAddr, (uptr)&mVUregs, mFC);
xMOV(gprT3, badBranchAddr+8);
iPC = bPC;
setCode();
xSHR(gprT3, 3);
mVUallocVIb(mVU, gprT3, _It_); //Link to branch addr + 8
normJumpCompile(mVU, mFC, true); //Compile evil branch, just in time!
eJMP.SetTarget();
incPC(2); // Point to delay slot of evil Branch (as the original branch isn't taken)
mVUcompileSingleInstruction(mVU, xPC, (uptr)&mVUregs, mFC);
xMOV(gprT3, xPC);
iPC = bPC;
setCode();
xSHR(gprT3, 3);
mVUallocVIb(mVU, gprT3, _It_);
normJumpCompile(mVU, mFC, true); //Compile evil branch, just in time!
}
void condBranch(mV, microFlagCycles& mFC, int JMPcc) {
mVUsetupBranch(mVU, mFC);
xCMP(ptr16[&mVU.branch], 0);
incPC(3);
if (mVUup.eBit) { // Conditional Branch With E-Bit Set
if(mVUlow.evilBranch) DevCon.Warning("End on evil branch! - Not implemented! - If game broken report to PCSX2 Team");
mVUendProgram(mVU, &mFC, 2);
xForwardJump8 eJMP((JccComparisonType)JMPcc);
incPC(1); // Set PC to First instruction of Non-Taken Side
@ -151,6 +209,17 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc) {
return;
}
else { // Normal Conditional Branch
if(mVUlow.evilBranch) //We are dealing with an evil evil block, so we need to process this slightly differently
{
if(mVUlow.branch == 10 || mVUlow.branch == 2) //Evil branch is a jump of some measure
{
//Because of how it is linked, we need to make sure the target is recompiled if taken
condJumpProcessingEvil(mVU, mFC, JMPcc);
return;
}
}
microBlock* bBlock;
incPC2(1); // Check if Branch Non-Taken Side has already been recompiled
blockCreate(iPC/2);
@ -190,7 +259,22 @@ void normJump(mV, microFlagCycles& mFC) {
normBranchCompile(mVU, jumpAddr);
return;
}
if(mVUlow.badBranch)
{
incPC(3);
if(mVUlow.branch == 2 || mVUlow.branch == 10) //Delay slot BAL needs linking, only need to do BAL here, JALR done earlier
{
DevCon.Warning("Found %x in delay slot, linking - If game broken report to PCSX2 Team", mVUlow.branch == 2 ? "BAL" : "JALR");
incPC(-2);
mVUallocVIa(mVU, gprT1, _Is_);
xADD(gprT1, 8);
xSHR(gprT1, 3);
incPC(2);
mVUallocVIb(mVU, gprT1, _It_);
}
incPC(-3);
}
if (mVUup.eBit) { // E-bit Jump
mVUendProgram(mVU, &mFC, 2);
xMOV(gprT1, ptr32[&mVU.branch]);

View File

@ -186,7 +186,8 @@ __ri void branchWarning(mV) {
mVUlow.isNOP = 1;
}
else incPC(2);
if (mVUinfo.isBdelay) { // Check if VI Reg Written to on Branch Delay Slot Instruction
if (mVUinfo.isBdelay && !mVUlow.evilBranch) { // Check if VI Reg Written to on Branch Delay Slot Instruction
if (mVUlow.VI_write.reg && mVUlow.VI_write.used && !mVUlow.readFlags) {
mVUlow.backupVI = 1;
mVUregs.viBackUp = mVUlow.VI_write.reg;
@ -420,6 +421,57 @@ __fi void mVUinitFirstPass(microVU& mVU, uptr pState, u8* thisPtr) {
// Recompiler
//------------------------------------------------------------------
//This bastardized function is used when a linked branch is in a conditional delay slot. It's messy, it's horrible, but it works.
//Unfortunately linking the reg manually and using the normal evil block method seems to suck at this :/
//If this is removed, test Evil Dead: Fistful of Boomstick (hangs going ingame), Mark of Kri (collision detection)
//and Tony Hawks Project 8 (graphics are half missing, requires Negative rounding when working)
void* mVUcompileSingleInstruction(microVU& mVU, u32 startPC, uptr pState, microFlagCycles& mFC) {
u8* thisPtr = x86Ptr;
// First Pass
iPC = startPC / 4;
mVUbranch = 0;
incPC(1);
startLoop(mVU);
mVUincCycles(mVU, 1);
mVUopU(mVU, 0);
mVUcheckBadOp(mVU);
if (curI & _Ebit_) { eBitPass1(mVU, branch); DevCon.Warning("E Bit on single instruction");}
if (curI & _DTbit_) { branch = 4; DevCon.Warning("D Bit on single instruction");}
if (curI & _Mbit_) { mVUup.mBit = 1; DevCon.Warning("M Bit on single instruction");}
if (curI & _Ibit_) { mVUlow.isNOP = 1; mVUup.iBit = 1; DevCon.Warning("I Bit on single instruction");}
else { incPC(-1); mVUopL(mVU, 0); incPC(1); }
mVUsetCycles(mVU);
mVUinfo.readQ = mVU.q;
mVUinfo.writeQ = !mVU.q;
mVUinfo.readP = mVU.p;
mVUinfo.writeP = !mVU.p;
mVUcount++;
mVUsetFlagInfo(mVU);
incPC(1);
mVUsetFlags(mVU, mFC); // Sets Up Flag instances
mVUoptimizePipeState(mVU); // Optimize the End Pipeline State for nicer Block Linking
mVUdebugPrintBlocks(mVU,0);// Prints Start/End PC of blocks executed, for debugging...
mVUtestCycles(mVU); // Update VU Cycles and Exit Early if Necessary
// Second Pass
iPC = startPC / 4;
setCode();
if (mVUup.mBit) { xOR(ptr32[&mVU.regs().flags], VUFLAG_MFLAGSET); }
mVUexecuteInstruction(mVU);
mVUincCycles(mVU, 1); //Just incase the is XGKick
if (mVUinfo.doXGKICK) { mVU_XGKICK_DELAY(mVU, 1); }
return thisPtr;
}
void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) {
microFlagCycles mFC;
@ -431,6 +483,7 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) {
mVUsetupRange(mVU, startPC, 1); // Setup Program Bounds/Range
mVU.regAlloc->reset(); // Reset regAlloc
mVUinitFirstPass(mVU, pState, thisPtr);
mVUbranch = 0;
for(int branch = 0; mVUcount < endCount; mVUcount++) {
incPC(1);
startLoop(mVU);
@ -478,6 +531,7 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) {
mVUsetupRange(mVU, xPC, 0);
mVUdebugPrintBlocks(mVU,1);
incPC(-3); // Go back to branch opcode
switch (mVUlow.branch) {
case 1: case 2: normBranch(mVU, mFC); return thisPtr; // B/BAL
case 9: case 10: normJump (mVU, mFC); return thisPtr; // JR/JALR
@ -502,7 +556,7 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) {
__fi void* mVUentryGet(microVU& mVU, microBlockManager* block, u32 startPC, uptr pState) {
microBlock* pBlock = block->search((microRegInfo*)pState);
if (pBlock) return pBlock->x86ptrStart;
else return mVUcompile(mVU, startPC, pState);
else { return mVUcompile(mVU, startPC, pState);}
}
// Search for Existing Compiled Block (if found, return x86ptr; else, compile and return x86ptr)

View File

@ -1245,7 +1245,8 @@ void setBranchA(mP, int x, int _x_) {
void condEvilBranch(mV, int JMPcc) {
if (mVUlow.badBranch) {
xMOV(ptr32[&mVU.branch], gprT1);
xMOV(ptr32[&mVU.badBranch], branchAddrN);
xMOV(ptr32[&mVU.badBranch], branchAddr);
xCMP(gprT1b, 0);
xForwardJump8 cJMP((JccComparisonType)JMPcc);
incPC(6); // Branch Not Taken Addr + 8
@ -1277,10 +1278,14 @@ mVUop(mVU_BAL) {
setBranchA(mX, 2, _It_);
pass1 { mVUanalyzeNormBranch(mVU, _It_, 1); }
pass2 {
xMOV(gprT1, bSaveAddr);
mVUallocVIb(mVU, gprT1, _It_);
if (mVUlow.badBranch) { xMOV(ptr32[&mVU.badBranch], branchAddrN); }
if (mVUlow.evilBranch) { xMOV(ptr32[&mVU.evilBranch], branchAddr); }
if(!mVUlow.evilBranch)
{
xMOV(gprT1, bSaveAddr);
mVUallocVIb(mVU, gprT1, _It_);
}
if (mVUlow.badBranch) { xMOV(ptr32[&mVU.badBranch], branchAddr); }
if (mVUlow.evilBranch) { xMOV(ptr32[&mVU.evilBranch], branchAddr);}
mVU.profiler.EmitOp(opBAL);
}
pass3 { mVUlog("BAL vi%02d [<a href=\"#addr%04x\">%04x</a>]", _Ft_, branchAddr, branchAddr); }
@ -1377,13 +1382,9 @@ void normJumpPass2(mV) {
mVUallocVIa(mVU, gprT1, _Is_);
xSHL(gprT1, 3);
xAND(gprT1, mVU.microMemSize - 8);
if (!mVUlow.evilBranch) xMOV(ptr32[&mVU.branch], gprT1);
else xMOV(ptr32[&mVU.evilBranch], gprT1);
if (mVUlow.badBranch) {
xADD(gprT1, 8);
xAND(gprT1, mVU.microMemSize - 8);
xMOV(ptr32[&mVU.badBranch], gprT1);
}
if (!mVUlow.evilBranch) xMOV(ptr32[&mVU.branch], gprT1);
else { xMOV(ptr32[&mVU.evilBranch], gprT1);}
}
}
@ -1399,8 +1400,27 @@ mVUop(mVU_JALR) {
pass1 { mVUanalyzeJump(mVU, _Is_, _It_, 1); }
pass2 {
normJumpPass2(mVU);
xMOV(gprT1, bSaveAddr);
mVUallocVIb(mVU, gprT1, _It_);
if(!mVUlow.evilBranch)
{
xMOV(gprT1, bSaveAddr);
mVUallocVIb(mVU, gprT1, _It_);
}
if(mVUlow.evilBranch)
{
incPC(-2);
if(mVUlow.branch >= 9) //Previous branch is a jump of some type so
//we need to take the branch address from the register it uses.
{
DevCon.Warning("Linking JALR from JALR/JR branch target! - If game broken report to PCSX2 Team");
mVUallocVIa(mVU, gprT1, _Is_);
xADD(gprT1, 8);
xSHR(gprT1, 3);
incPC(2);
mVUallocVIb(mVU, gprT1, _It_);
}
else incPC(2);
}
mVU.profiler.EmitOp(opJALR);
}
pass3 { mVUlog("JALR vi%02d, [vi%02d]", _Ft_, _Fs_); }