From 501766d852c08dc26d1c80402af6d94df6c892c0 Mon Sep 17 00:00:00 2001
From: "refraction@gmail.com"
 <refraction@gmail.com@96395faa-99c1-11dd-bbfe-3dabce05a288>
Date: Tue, 12 Mar 2013 23:37:50 +0000
Subject: [PATCH] microVU: T/D Bit on branches fixed, kinda.  It's ugly,
 possibly wrong in places, but it works for known games that fall for this. 
 Fixes VP2.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@5590 96395faa-99c1-11dd-bbfe-3dabce05a288
---
 pcsx2/x86/microVU_Compile.inl | 107 ++++++++++++++++++++++++++++++----
 1 file changed, 97 insertions(+), 10 deletions(-)

diff --git a/pcsx2/x86/microVU_Compile.inl b/pcsx2/x86/microVU_Compile.inl
index b1fad803c..ad1e63819 100644
--- a/pcsx2/x86/microVU_Compile.inl
+++ b/pcsx2/x86/microVU_Compile.inl
@@ -480,26 +480,111 @@ void* mVUcompileSingleInstruction(microVU& mVU, u32 startPC, uptr pState, microF
 
 void mVUDoDBit(microVU& mVU, microFlagCycles* mFC)
 {
-	incPC(2);
+	bool isBranch = false;
+	JccComparisonType Jcc;
+
+	incPC(2); //Check next slot for branch delay, if not, that's where the VU will resume anyway.
+
+	if(mVUinfo.isBdelay) isBranch = true;
+	
 	xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
 	xForwardJump32 eJMP(Jcc_Zero);
 	xOR(ptr32[&VU0.VI[REG_VPU_STAT].UL], (isVU1 ? 0x200 : 0x2));
 	xOR(ptr32[&mVU.regs().flags], VUFLAG_INTCINTERRUPT);
-	mVUDTendProgram(mVU, mFC, 1);
+
+	if(isBranch) 
+	{		
+		incPC(-2); // Go back to branch opcode
+
+		DevCon.Warning("D Bit on branch");
+		mVUDTendProgram(mVU, mFC, 2);
+		xCMP(ptr16[&mVU.branch], 0);
+		switch (mVUlow.branch) {
+				case 1: case 2:  Jcc = Jcc_Unconditional; DevCon.Warning("D Bit on B/BAL, might be buggy");  break; // B/BAL
+				case 9: case 10: DevCon.Warning("JR/JALR probably not supported on D Bit!");		  break; // JR/JALR
+				case 3: Jcc = Jcc_Equal;		  break; // IBEQ
+				case 4: Jcc = Jcc_GreaterOrEqual; break; // IBGEZ
+				case 5: Jcc = Jcc_Greater;		  break; // IBGTZ
+				case 6: Jcc = Jcc_LessOrEqual;	  break; // IBLEQ
+				case 7: Jcc = Jcc_Less;			  break; // IBLTZ
+				case 8: Jcc = Jcc_NotEqual;		  break; // IBNEQ
+			}
+		if(mVUlow.branch < 9) 
+		{
+			incPC(1);
+			xForwardJump8 bJMP((JccComparisonType)Jcc);
+				incPC(1); // Set PC to First instruction of Non-Taken Side
+				xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
+				xJMP(mVU.exitFunct);
+			bJMP.SetTarget();
+			incPC(-4); // Go Back to Branch Opcode to get branchAddr
+			iPC = branchAddr/4;
+			xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
+		}
+		else
+		{
+			xMOV(gprT1, ptr32[&mVU.branch]);
+			xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], gprT1);
+		}
+		xJMP(mVU.exitFunct);
+	}
+	else
+		mVUDTendProgram(mVU, mFC, 1);
 	eJMP.SetTarget();
-	incPC(-2);
 }
 
 void mVUDoTBit(microVU& mVU, microFlagCycles* mFC)
 {
-	incPC(2);
+	bool isBranch = false;
+	JccComparisonType Jcc;
+
+	incPC(2); //Check next slot for branch delay, if not, that's where the VU will resume anyway.
+
+	if(mVUinfo.isBdelay) isBranch = true;
+
 	xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
 	xForwardJump32 eJMP(Jcc_Zero);
 	xOR(ptr32[&VU0.VI[REG_VPU_STAT].UL], (isVU1 ? 0x400 : 0x4));
 	xOR(ptr32[&mVU.regs().flags], VUFLAG_INTCINTERRUPT);
-	mVUDTendProgram(mVU, mFC, 1);
+	if(isBranch) 
+	{
+		incPC(-2); // Go back to branch opcode
+
+		DevCon.Warning("T Bit on branch");
+		mVUDTendProgram(mVU, mFC, 2);
+		xCMP(ptr16[&mVU.branch], 0);
+		switch (mVUlow.branch) {
+				case 1: case 2:  Jcc = Jcc_Unconditional; DevCon.Warning("T Bit on B/BAL, might be buggy");  break; // B/BAL
+				case 9: case 10: DevCon.Warning("JR/JALR probably not supported on T Bit!");		  break; // JR/JALR
+				case 3: Jcc = Jcc_Equal;		  break; // IBEQ
+				case 4: Jcc = Jcc_GreaterOrEqual; break; // IBGEZ
+				case 5: Jcc = Jcc_Greater;		  break; // IBGTZ
+				case 6: Jcc = Jcc_LessOrEqual;	  break; // IBLEQ
+				case 7: Jcc = Jcc_Less;			  break; // IBLTZ
+				case 8: Jcc = Jcc_NotEqual;		  break; // IBNEQ
+			}
+		if(mVUlow.branch < 9) 
+		{
+			incPC(1);
+			xForwardJump8 bJMP((JccComparisonType)Jcc);
+				incPC(1); // Set PC to First instruction of Non-Taken Side
+				xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
+				xJMP(mVU.exitFunct);
+			bJMP.SetTarget();
+			incPC(-4); // Go Back to Branch Opcode to get branchAddr
+			iPC = branchAddr/4;
+			xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
+		}
+		else
+		{
+			xMOV(gprT1, ptr32[&mVU.branch]);
+			xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], gprT1);
+		}
+		xJMP(mVU.exitFunct);
+	}
+	else
+		mVUDTendProgram(mVU, mFC, 1);
 	eJMP.SetTarget();
-	incPC(-2);	
 }
 
 void mVUSaveFlags(microVU& mVU,microFlagCycles &mFC, microFlagCycles &mFCBackup)
@@ -563,10 +648,6 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) {
 		if (mVUinfo.isEOB)			{ handleBadOp(mVU, x); x = 0xffff; }
 		if (mVUup.mBit)				{ xOR(ptr32[&mVU.regs().flags], VUFLAG_MFLAGSET); }
 		mVUexecuteInstruction(mVU);
-		incPC(-1);
-		if(mVUup.tBit) {mVUDoTBit(mVU, &mFC); }
-		else if(mVUup.dBit) { mVUDoDBit(mVU, &mFC);}
-		incPC(1);
 		if (mVUinfo.doXGKICK)		{ mVU_XGKICK_DELAY(mVU, 1); }
 		if (isEvilBlock)			{ mVUsetupRange(mVU, xPC, 0); normJumpCompile(mVU, mFC, 1); return thisPtr; }
 		else if (!mVUinfo.isBdelay)	{ incPC(1); }
@@ -586,6 +667,12 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) {
 				case 8: condBranch(mVU, mFC, Jcc_NotEqual);		  return thisPtr; // IBNEQ
 			}
 		}
+
+		incPC(-2);
+		if(mVUup.tBit) { mVUDoTBit(mVU, &mFC); }
+		else if(mVUup.dBit) { mVUDoDBit(mVU, &mFC); }
+		else incPC(2);
+
 	}
 	if ((x == endCount) && (x!=1)) { Console.Error("microVU%d: Possible infinite compiling loop!", mVU.index); }