mVU: Keep start PC, modify prog search to avoid recompilation

Also fix some M-Bit stuff
This commit is contained in:
refractionpcsx2 2020-12-04 13:03:32 +00:00
parent 378eccc6ea
commit 4595aae0de
11 changed files with 103 additions and 21 deletions

View File

@ -113,6 +113,7 @@ void VU_Thread::ExecuteRingBuffer()
vifRegs.itop = Read(); vifRegs.itop = Read();
if (addr != -1) vuRegs.VI[REG_TPC].UL = addr; if (addr != -1) vuRegs.VI[REG_TPC].UL = addr;
vuCPU->SetStartPC(vuRegs.VI[REG_TPC].UL << 3);
vuCPU->Execute(vu1RunCycles); vuCPU->Execute(vu1RunCycles);
gifUnit.gifPath[GIF_PATH_1].FinishGSPacketMTVU(); gifUnit.gifPath[GIF_PATH_1].FinishGSPacketMTVU();
semaXGkick.Post(); // Tell MTGS a path1 packet is complete semaXGkick.Post(); // Tell MTGS a path1 packet is complete

View File

@ -24,7 +24,7 @@
// the lower 16 bit value. IF the change is breaking of all compatibility with old // 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. // 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 // 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. :) // between the GS saving function and the MTGS's needs. :)

View File

@ -134,6 +134,7 @@ struct __aligned16 VURegs {
// Current opcode being interpreted or recompiled (this var is used by Interps // 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) // but not microVU. Would like to have it local to their respective classes... someday)
u32 code; u32 code;
u32 start_pc;
// branch/branchpc are used by interpreter only, but making them local to the interpreter // branch/branchpc are used by interpreter only, but making them local to the interpreter
// classes requires considerable code refactoring. Maybe later. >_< // classes requires considerable code refactoring. Maybe later. >_<

View File

@ -48,6 +48,8 @@ void __fastcall vu0ExecMicro(u32 addr) {
VU0.VI[REG_VPU_STAT].UL |= 0x01; VU0.VI[REG_VPU_STAT].UL |= 0x01;
VU0.cycle = cpuRegs.cycle; VU0.cycle = cpuRegs.cycle;
if ((s32)addr != -1) VU0.VI[REG_TPC].UL = addr; if ((s32)addr != -1) VU0.VI[REG_TPC].UL = addr;
CpuVU0->SetStartPC(VU0.VI[REG_TPC].UL << 3);
_vuExecMicroDebug(VU0); _vuExecMicroDebug(VU0);
CpuVU0->ExecuteBlock(1); CpuVU0->ExecuteBlock(1);
} }

View File

@ -198,6 +198,11 @@ InterpVU0::InterpVU0()
IsInterpreter = true; IsInterpreter = true;
} }
void InterpVU0::SetStartPC(u32 startPC)
{
VU0.start_pc = startPC;
}
void InterpVU0::Step() void InterpVU0::Step()
{ {
vu0Exec( &VU0 ); vu0Exec( &VU0 );

View File

@ -61,7 +61,8 @@ void __fastcall vu1ExecMicro(u32 addr)
VU0.VI[REG_VPU_STAT].UL &= ~0xFF00; VU0.VI[REG_VPU_STAT].UL &= ~0xFF00;
VU0.VI[REG_VPU_STAT].UL |= 0x0100; VU0.VI[REG_VPU_STAT].UL |= 0x0100;
if ((s32)addr != -1) VU1.VI[REG_TPC].UL = addr; 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); CpuVU1->Execute(vu1RunCycles);
} }

View File

@ -202,6 +202,11 @@ void InterpVU1::Shutdown() noexcept {
vu1Thread.WaitVU(); vu1Thread.WaitVU();
} }
void InterpVU1::SetStartPC(u32 startPC)
{
VU1.start_pc = startPC;
}
void InterpVU1::Step() void InterpVU1::Step()
{ {
VU1.VI[REG_TPC].UL &= VU1_PROGMASK; VU1.VI[REG_TPC].UL &= VU1_PROGMASK;

View File

@ -85,6 +85,7 @@ public:
virtual void Reserve()=0; virtual void Reserve()=0;
virtual void Shutdown()=0; virtual void Shutdown()=0;
virtual void Reset()=0; virtual void Reset()=0;
virtual void SetStartPC(u32 startPC)=0;
virtual void Execute(u32 cycles)=0; virtual void Execute(u32 cycles)=0;
virtual void ExecuteBlock(bool startUp)=0; virtual void ExecuteBlock(bool startUp)=0;
@ -172,6 +173,7 @@ public:
void Reset() { } void Reset() { }
void Step(); void Step();
void SetStartPC(u32 startPC);
void Execute(u32 cycles); void Execute(u32 cycles);
void Clear(u32 addr, u32 size) {} void Clear(u32 addr, u32 size) {}
@ -192,6 +194,7 @@ public:
void Shutdown() noexcept; void Shutdown() noexcept;
void Reset(); void Reset();
void SetStartPC(u32 startPC);
void Step(); void Step();
void Execute(u32 cycles); void Execute(u32 cycles);
void Clear(u32 addr, u32 size) {} void Clear(u32 addr, u32 size) {}
@ -217,6 +220,7 @@ public:
void Shutdown() noexcept; void Shutdown() noexcept;
void Reset(); void Reset();
void SetStartPC(u32 startPC);
void Execute(u32 cycles); void Execute(u32 cycles);
void Clear(u32 addr, u32 size); void Clear(u32 addr, u32 size);
void Vsync() noexcept; void Vsync() noexcept;
@ -237,6 +241,7 @@ public:
void Reserve(); void Reserve();
void Shutdown() noexcept; void Shutdown() noexcept;
void Reset(); void Reset();
void SetStartPC(u32 startPC);
void Execute(u32 cycles); void Execute(u32 cycles);
void Clear(u32 addr, u32 size); void Clear(u32 addr, u32 size);
void Vsync() noexcept; void Vsync() noexcept;

View File

@ -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) // Searches for Cached Micro Program and sets prog.cur to it (returns entry-point to program)
_mVUt __fi void* mVUsearchProg(u32 startPC, uptr pState) { _mVUt __fi void* mVUsearchProg(u32 startPC, uptr pState) {
microVU& mVU = mVUx; microVU& mVU = mVUx;
microProgramQuick& quick = mVU.prog.quick[startPC/8]; microProgramQuick& quick = mVU.prog.quick[mVU.regs().start_pc/8];
microProgramList* list = mVU.prog.prog [startPC/8]; microProgramList* list = mVU.prog.prog [mVU.regs().start_pc/8];
if(!quick.prog) { // If null, we need to search for new program if(!quick.prog) { // If null, we need to search for new program
std::deque<microProgram*>::iterator it(list->begin()); std::deque<microProgram*>::iterator it(list->begin());
for ( ; it != list->end(); ++it) { 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 // If cleared and program not found, make a new program instance
mVU.prog.cleared = 0; mVU.prog.cleared = 0;
mVU.prog.isSame = 1; 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); void* entryPoint = mVUblockFetch(mVU, startPC, pState);
quick.block = mVU.prog.cur->block[startPC/8]; quick.block = mVU.prog.cur->block[startPC/8];
quick.prog = mVU.prog.cur; quick.prog = mVU.prog.cur;
@ -301,9 +302,13 @@ _mVUt __fi void* mVUsearchProg(u32 startPC, uptr pState) {
//mVUprintUniqueRatio(mVU); //mVUprintUniqueRatio(mVU);
return entryPoint; return entryPoint;
} }
// If list.quick, then we've already found and recompiled the program ;) // If list.quick, then we've already found and recompiled the program ;)
mVU.prog.isSame = -1; mVU.prog.isSame = -1;
mVU.prog.cur = quick.prog; 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); return mVUentryGet(mVU, quick.block, startPC, pState);
} }
@ -347,6 +352,11 @@ void recMicroVU1::Reset() {
mVUreset(microVU1, true); mVUreset(microVU1, true);
} }
void recMicroVU0::SetStartPC(u32 startPC)
{
VU0.start_pc = startPC;
}
void recMicroVU0::Execute(u32 cycles) { void recMicroVU0::Execute(u32 cycles) {
pxAssert(m_Reserved); // please allocate me first! :| pxAssert(m_Reserved); // please allocate me first! :|
@ -366,6 +376,12 @@ void recMicroVU0::Execute(u32 cycles) {
hwIntcIrq(6); hwIntcIrq(6);
} }
} }
void recMicroVU1::SetStartPC(u32 startPC)
{
VU1.start_pc = startPC;
}
void recMicroVU1::Execute(u32 cycles) { void recMicroVU1::Execute(u32 cycles) {
pxAssert(m_Reserved); // please allocate me first! :| pxAssert(m_Reserved); // please allocate me first! :|

View File

@ -118,19 +118,19 @@ void mVUDTendProgram(mV, microFlagCycles* mFC, int isEbit) {
void mVUendProgram(mV, microFlagCycles* mFC, int isEbit) { void mVUendProgram(mV, microFlagCycles* mFC, int isEbit) {
int fStatus = getLastFlagInst(mVUpBlock->pState, mFC->xStatus, 0, isEbit); int fStatus = getLastFlagInst(mVUpBlock->pState, mFC->xStatus, 0, isEbit && isEbit != 3);
int fMac = getLastFlagInst(mVUpBlock->pState, mFC->xMac, 1, isEbit); int fMac = getLastFlagInst(mVUpBlock->pState, mFC->xMac, 1, isEbit && isEbit != 3);
int fClip = getLastFlagInst(mVUpBlock->pState, mFC->xClip, 2, isEbit); int fClip = getLastFlagInst(mVUpBlock->pState, mFC->xClip, 2, isEbit && isEbit != 3);
int qInst = 0; int qInst = 0;
int pInst = 0; int pInst = 0;
microBlock stateBackup; microBlock stateBackup;
memcpy(&stateBackup, &mVUregs, sizeof(mVUregs)); //backup the state, it's about to get screwed with. 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 mVU.regAlloc->TDwritebackAll(); //Writing back ok, invalidating early kills the rec, so don't do it :P
else else
mVU.regAlloc->flushAll(); mVU.regAlloc->flushAll();
if (isEbit) { if (isEbit && isEbit != 3) {
memzero(mVUinfo); memzero(mVUinfo);
memzero(mVUregsTemp); memzero(mVUregsTemp);
mVUincCycles(mVU, 100); // Ensures Valid P/Q instances (And sets all cycle data to 0) 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_MAC_FLAG].UL], gprT1);
xMOV(ptr32[&mVU.regs().VI[REG_CLIP_FLAG].UL], gprT2); 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(xmmT1, ptr128[mVU.macFlag]);
xMOVAPS(ptr128[&mVU.regs().micro_macflags], xmmT1); xMOVAPS(ptr128[&mVU.regs().micro_macflags], xmmT1);
xMOVAPS(xmmT1, ptr128[mVU.clipFlag]); 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) { if (!mVU.index || !THREAD_VU1) {
xAND(ptr32[&VU0.VI[REG_VPU_STAT].UL], (isVU1 ? ~0x100 : ~0x001)); // VBS0/VBS1 flag 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 //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); xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
xJMP(mVU.exitFunct); xJMP(mVU.exitFunct);
} }
@ -293,6 +293,21 @@ void normBranch(mV, microFlagCycles& mFC) {
eJMP.SetTarget(); eJMP.SetTarget();
iPC = tempPC; 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 (mVUup.eBit) {
if(mVUlow.badBranch) if(mVUlow.badBranch)
DevCon.Warning("End on evil Unconditional branch! - Not implemented! - If game broken report to PCSX2 Team"); 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(); eJMP.SetTarget();
iPC = tempPC; 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 (mVUup.eBit) { // Conditional Branch With E-Bit Set
if(mVUlow.evilBranch) if(mVUlow.evilBranch)
DevCon.Warning("End on evil branch! - Not implemented! - If game broken report to PCSX2 Team"); 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) { 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 (mVUlow.constJump.isValid) { // Jump Address is Constant
if (mVUup.eBit) { // E-bit Jump if (mVUup.eBit) { // E-bit Jump
iPC = (mVUlow.constJump.regValue*2) & (mVU.progMemMask); iPC = (mVUlow.constJump.regValue*2) & (mVU.progMemMask);

View File

@ -567,13 +567,18 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState)
} }
if ((curI & _Mbit_) && isVU0) { if ((curI & _Mbit_) && isVU0) {
incPC(-2); if (xPC > 0)
if (!(curI & _Mbit_)) { //If the last instruction was also M-Bit we don't need to sync again {
incPC(2); incPC(-2);
mVUup.mBit = true; 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 else
incPC(2); mVUup.mBit = true;
} }
if (curI & _Ibit_) { if (curI & _Ibit_) {
@ -677,6 +682,7 @@ void* mVUcompile(microVU& mVU, u32 startPC, uptr pState)
mVUsetupRange(mVU, xPC, false); mVUsetupRange(mVU, xPC, false);
incPC(2); incPC(2);
mVUendProgram(mVU, &mFC, 0); mVUendProgram(mVU, &mFC, 0);
normBranchCompile(mVU, xPC);
incPC(-2); incPC(-2);
goto perf_and_return; 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 if (doJumpAsSameProgram) { // Treat jump as part of same microProgram
return mVUblockFetch(mVUx, startPC, ptr); return mVUblockFetch(mVUx, startPC, ptr);
} }
mVUx.regs().start_pc = startPC;
if (doJumpCaching) { // When doJumpCaching, ptr is a microBlock pointer if (doJumpCaching) { // When doJumpCaching, ptr is a microBlock pointer
microVU& mVU = mVUx; microVU& mVU = mVUx;
microBlock* pBlock = (microBlock*)ptr; microBlock* pBlock = (microBlock*)ptr;