diff --git a/pcsx2/MTVU.cpp b/pcsx2/MTVU.cpp index 72a70cd1fc..bebd5f3a96 100644 --- a/pcsx2/MTVU.cpp +++ b/pcsx2/MTVU.cpp @@ -113,6 +113,7 @@ void VU_Thread::ExecuteRingBuffer() vifRegs.itop = Read(); if (addr != -1) vuRegs.VI[REG_TPC].UL = addr; + vuCPU->SetStartPC(vuRegs.VI[REG_TPC].UL << 3); vuCPU->Execute(vu1RunCycles); gifUnit.gifPath[GIF_PATH_1].FinishGSPacketMTVU(); semaXGkick.Post(); // Tell MTGS a path1 packet is complete diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index d817ff642d..8d993d0ac6 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -24,7 +24,7 @@ // the lower 16 bit value. IF the change is breaking of all compatibility with old // states, increment the upper 16 bit value, and clear the lower 16 bits to 0. -static const u32 g_SaveVersion = (0x9A11 << 16) | 0x0000; +static const u32 g_SaveVersion = (0x9A12 << 16) | 0x0000; // this function is meant to be used in the place of GSfreeze, and provides a safe layer // between the GS saving function and the MTGS's needs. :) diff --git a/pcsx2/VU.h b/pcsx2/VU.h index 117089e75e..dd67005d36 100644 --- a/pcsx2/VU.h +++ b/pcsx2/VU.h @@ -134,6 +134,7 @@ struct __aligned16 VURegs { // Current opcode being interpreted or recompiled (this var is used by Interps // but not microVU. Would like to have it local to their respective classes... someday) u32 code; + u32 start_pc; // branch/branchpc are used by interpreter only, but making them local to the interpreter // classes requires considerable code refactoring. Maybe later. >_< diff --git a/pcsx2/VU0micro.cpp b/pcsx2/VU0micro.cpp index 3be6664d0b..55de32c6f1 100644 --- a/pcsx2/VU0micro.cpp +++ b/pcsx2/VU0micro.cpp @@ -48,6 +48,8 @@ void __fastcall vu0ExecMicro(u32 addr) { VU0.VI[REG_VPU_STAT].UL |= 0x01; VU0.cycle = cpuRegs.cycle; if ((s32)addr != -1) VU0.VI[REG_TPC].UL = addr; + + CpuVU0->SetStartPC(VU0.VI[REG_TPC].UL << 3); _vuExecMicroDebug(VU0); CpuVU0->ExecuteBlock(1); } diff --git a/pcsx2/VU0microInterp.cpp b/pcsx2/VU0microInterp.cpp index acd0084adf..77ea99e13f 100644 --- a/pcsx2/VU0microInterp.cpp +++ b/pcsx2/VU0microInterp.cpp @@ -198,6 +198,11 @@ InterpVU0::InterpVU0() IsInterpreter = true; } +void InterpVU0::SetStartPC(u32 startPC) +{ + VU0.start_pc = startPC; +} + void InterpVU0::Step() { vu0Exec( &VU0 ); diff --git a/pcsx2/VU1micro.cpp b/pcsx2/VU1micro.cpp index 9a67898a75..f987269c67 100644 --- a/pcsx2/VU1micro.cpp +++ b/pcsx2/VU1micro.cpp @@ -61,7 +61,8 @@ void __fastcall vu1ExecMicro(u32 addr) VU0.VI[REG_VPU_STAT].UL &= ~0xFF00; VU0.VI[REG_VPU_STAT].UL |= 0x0100; if ((s32)addr != -1) VU1.VI[REG_TPC].UL = addr; - _vuExecMicroDebug(VU1); + CpuVU1->SetStartPC(VU1.VI[REG_TPC].UL << 3); + _vuExecMicroDebug(VU1); CpuVU1->Execute(vu1RunCycles); } diff --git a/pcsx2/VU1microInterp.cpp b/pcsx2/VU1microInterp.cpp index 9f158e6af4..efc731befa 100644 --- a/pcsx2/VU1microInterp.cpp +++ b/pcsx2/VU1microInterp.cpp @@ -202,6 +202,11 @@ void InterpVU1::Shutdown() noexcept { vu1Thread.WaitVU(); } +void InterpVU1::SetStartPC(u32 startPC) +{ + VU1.start_pc = startPC; +} + void InterpVU1::Step() { VU1.VI[REG_TPC].UL &= VU1_PROGMASK; diff --git a/pcsx2/VUmicro.h b/pcsx2/VUmicro.h index 86eb2acbb3..b0f6a8dd1d 100644 --- a/pcsx2/VUmicro.h +++ b/pcsx2/VUmicro.h @@ -85,6 +85,7 @@ public: virtual void Reserve()=0; virtual void Shutdown()=0; virtual void Reset()=0; + virtual void SetStartPC(u32 startPC)=0; virtual void Execute(u32 cycles)=0; virtual void ExecuteBlock(bool startUp)=0; @@ -172,6 +173,7 @@ public: void Reset() { } void Step(); + void SetStartPC(u32 startPC); void Execute(u32 cycles); void Clear(u32 addr, u32 size) {} @@ -192,6 +194,7 @@ public: void Shutdown() noexcept; void Reset(); + void SetStartPC(u32 startPC); void Step(); void Execute(u32 cycles); void Clear(u32 addr, u32 size) {} @@ -217,6 +220,7 @@ public: void Shutdown() noexcept; void Reset(); + void SetStartPC(u32 startPC); void Execute(u32 cycles); void Clear(u32 addr, u32 size); void Vsync() noexcept; @@ -237,6 +241,7 @@ public: void Reserve(); void Shutdown() noexcept; void Reset(); + void SetStartPC(u32 startPC); void Execute(u32 cycles); void Clear(u32 addr, u32 size); void Vsync() noexcept; diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp index 60e02baecf..d8ca0759f4 100644 --- a/pcsx2/x86/microVU.cpp +++ b/pcsx2/x86/microVU.cpp @@ -256,8 +256,9 @@ __fi bool mVUcmpProg(microVU& mVU, microProgram& prog, const bool cmpWholeProg) // Searches for Cached Micro Program and sets prog.cur to it (returns entry-point to program) _mVUt __fi void* mVUsearchProg(u32 startPC, uptr pState) { microVU& mVU = mVUx; - microProgramQuick& quick = mVU.prog.quick[startPC/8]; - microProgramList* list = mVU.prog.prog [startPC/8]; + microProgramQuick& quick = mVU.prog.quick[mVU.regs().start_pc/8]; + microProgramList* list = mVU.prog.prog [mVU.regs().start_pc/8]; + if(!quick.prog) { // If null, we need to search for new program std::deque::iterator it(list->begin()); for ( ; it != list->end(); ++it) { @@ -293,7 +294,7 @@ _mVUt __fi void* mVUsearchProg(u32 startPC, uptr pState) { // If cleared and program not found, make a new program instance mVU.prog.cleared = 0; mVU.prog.isSame = 1; - mVU.prog.cur = mVUcreateProg(mVU, startPC/8); + mVU.prog.cur = mVUcreateProg(mVU, mVU.regs().start_pc/8); void* entryPoint = mVUblockFetch(mVU, startPC, pState); quick.block = mVU.prog.cur->block[startPC/8]; quick.prog = mVU.prog.cur; @@ -301,9 +302,13 @@ _mVUt __fi void* mVUsearchProg(u32 startPC, uptr pState) { //mVUprintUniqueRatio(mVU); return entryPoint; } + // If list.quick, then we've already found and recompiled the program ;) - mVU.prog.isSame = -1; - mVU.prog.cur = quick.prog; + mVU.prog.isSame = -1; + mVU.prog.cur = quick.prog; + // Because the VU's can now run in sections and not whole programs at once + // we need to set the current block so it gets the right program back + quick.block = mVU.prog.cur->block[startPC / 8]; return mVUentryGet(mVU, quick.block, startPC, pState); } @@ -347,6 +352,11 @@ void recMicroVU1::Reset() { mVUreset(microVU1, true); } +void recMicroVU0::SetStartPC(u32 startPC) +{ + VU0.start_pc = startPC; +} + void recMicroVU0::Execute(u32 cycles) { pxAssert(m_Reserved); // please allocate me first! :| @@ -366,6 +376,12 @@ void recMicroVU0::Execute(u32 cycles) { hwIntcIrq(6); } } + +void recMicroVU1::SetStartPC(u32 startPC) +{ + VU1.start_pc = startPC; +} + void recMicroVU1::Execute(u32 cycles) { pxAssert(m_Reserved); // please allocate me first! :| diff --git a/pcsx2/x86/microVU_Branch.inl b/pcsx2/x86/microVU_Branch.inl index 58beb29f6a..83b4e6f064 100644 --- a/pcsx2/x86/microVU_Branch.inl +++ b/pcsx2/x86/microVU_Branch.inl @@ -118,19 +118,19 @@ void mVUDTendProgram(mV, microFlagCycles* mFC, int isEbit) { void mVUendProgram(mV, microFlagCycles* mFC, int isEbit) { - int fStatus = getLastFlagInst(mVUpBlock->pState, mFC->xStatus, 0, isEbit); - int fMac = getLastFlagInst(mVUpBlock->pState, mFC->xMac, 1, isEbit); - int fClip = getLastFlagInst(mVUpBlock->pState, mFC->xClip, 2, isEbit); + int fStatus = getLastFlagInst(mVUpBlock->pState, mFC->xStatus, 0, isEbit && isEbit != 3); + int fMac = getLastFlagInst(mVUpBlock->pState, mFC->xMac, 1, isEbit && isEbit != 3); + int fClip = getLastFlagInst(mVUpBlock->pState, mFC->xClip, 2, isEbit && isEbit != 3); int qInst = 0; int pInst = 0; microBlock stateBackup; memcpy(&stateBackup, &mVUregs, sizeof(mVUregs)); //backup the state, it's about to get screwed with. - if(!isEbit) + if(!isEbit || isEbit == 3) mVU.regAlloc->TDwritebackAll(); //Writing back ok, invalidating early kills the rec, so don't do it :P else mVU.regAlloc->flushAll(); - if (isEbit) { + if (isEbit && isEbit != 3) { memzero(mVUinfo); memzero(mVUregsTemp); mVUincCycles(mVU, 100); // Ensures Valid P/Q instances (And sets all cycle data to 0) @@ -178,7 +178,7 @@ void mVUendProgram(mV, microFlagCycles* mFC, int isEbit) { xMOV(ptr32[&mVU.regs().VI[REG_MAC_FLAG].UL], gprT1); xMOV(ptr32[&mVU.regs().VI[REG_CLIP_FLAG].UL], gprT2); - if (!isEbit) { // Backup flag instances + if (!isEbit || isEbit == 3) { // Backup flag instances xMOVAPS(xmmT1, ptr128[mVU.macFlag]); xMOVAPS(ptr128[&mVU.regs().micro_macflags], xmmT1); xMOVAPS(xmmT1, ptr128[mVU.clipFlag]); @@ -204,14 +204,14 @@ void mVUendProgram(mV, microFlagCycles* mFC, int isEbit) { } - if (isEbit || isVU1) { // Clear 'is busy' Flags + if ((isEbit && isEbit != 3) || isVU1) { // Clear 'is busy' Flags if (!mVU.index || !THREAD_VU1) { xAND(ptr32[&VU0.VI[REG_VPU_STAT].UL], (isVU1 ? ~0x100 : ~0x001)); // VBS0/VBS1 flag //xAND(ptr32[&mVU.getVifRegs().stat], ~VIF1_STAT_VEW); // Clear VU 'is busy' signal for vif } } - if (isEbit != 2) { // Save PC, and Jump to Exit Point + if (isEbit != 2 && isEbit != 3) { // Save PC, and Jump to Exit Point xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC); xJMP(mVU.exitFunct); } @@ -293,6 +293,21 @@ void normBranch(mV, microFlagCycles& mFC) { eJMP.SetTarget(); iPC = tempPC; } + if (mVUup.mBit) + { + DevCon.Warning("M-Bit on normal branch, report if broken"); + u32 tempPC = iPC; + u32* cpS = (u32*)&mVUregs; + u32* lpS = (u32*)&mVU.prog.lpState; + for (size_t i = 0; i < (sizeof(microRegInfo) - 4) / 4; i++, lpS++, cpS++) { + xMOV(ptr32[lpS], cpS[0]); + } + mVUendProgram(mVU, &mFC, 3); + iPC = branchAddr(mVU) / 4; + xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC); + xJMP(mVU.exitFunct); + iPC = tempPC; + } if (mVUup.eBit) { if(mVUlow.badBranch) DevCon.Warning("End on evil Unconditional branch! - Not implemented! - If game broken report to PCSX2 Team"); @@ -408,7 +423,27 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc) { eJMP.SetTarget(); iPC = tempPC; } - + if (mVUup.mBit) + { + u32 tempPC = iPC; + u32* cpS = (u32*)&mVUregs; + u32* lpS = (u32*)&mVU.prog.lpState; + for (size_t i = 0; i < (sizeof(microRegInfo) - 4) / 4; i++, lpS++, cpS++) { + xMOV(ptr32[lpS], cpS[0]); + } + mVUendProgram(mVU, &mFC, 3); + xCMP(ptr16[&mVU.branch], 0); + xForwardJump32 dJMP((JccComparisonType)JMPcc); + incPC(4); // Set PC to First instruction of Non-Taken Side + xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC); + xJMP(mVU.exitFunct); + dJMP.SetTarget(); + incPC(-4); // Go Back to Branch Opcode to get branchAddr + iPC = branchAddr(mVU) / 4; + xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC); + xJMP(mVU.exitFunct); + iPC = tempPC; + } 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"); @@ -471,6 +506,10 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc) { } void normJump(mV, microFlagCycles& mFC) { + if (mVUup.mBit) + { + DevCon.Warning("M-Bit on Jump! Please report if broken"); + } if (mVUlow.constJump.isValid) { // Jump Address is Constant if (mVUup.eBit) { // E-bit Jump iPC = (mVUlow.constJump.regValue*2) & (mVU.progMemMask); diff --git a/pcsx2/x86/microVU_Compile.inl b/pcsx2/x86/microVU_Compile.inl index a3b1195e90..a0617cac57 100644 --- a/pcsx2/x86/microVU_Compile.inl +++ b/pcsx2/x86/microVU_Compile.inl @@ -567,13 +567,18 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) } if ((curI & _Mbit_) && isVU0) { - incPC(-2); - if (!(curI & _Mbit_)) { //If the last instruction was also M-Bit we don't need to sync again - incPC(2); - mVUup.mBit = true; + if (xPC > 0) + { + incPC(-2); + if (!(curI & _Mbit_)) { //If the last instruction was also M-Bit we don't need to sync again + incPC(2); + mVUup.mBit = true; + } + else + incPC(2); } else - incPC(2); + mVUup.mBit = true; } if (curI & _Ibit_) { @@ -677,6 +682,7 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState) mVUsetupRange(mVU, xPC, false); incPC(2); mVUendProgram(mVU, &mFC, 0); + normBranchCompile(mVU, xPC); incPC(-2); goto perf_and_return; } @@ -773,6 +779,7 @@ _mVUt void* __fastcall mVUcompileJIT(u32 startPC, uptr ptr) { if (doJumpAsSameProgram) { // Treat jump as part of same microProgram return mVUblockFetch(mVUx, startPC, ptr); } + mVUx.regs().start_pc = startPC; if (doJumpCaching) { // When doJumpCaching, ptr is a microBlock pointer microVU& mVU = mVUx; microBlock* pBlock = (microBlock*)ptr;