VIF/VU: Cleaned up VIF Stall behaviour, sync XGKick with Unpacks.

Also cleaned up a bunch of bad/old code
Fixed branches on E-Bit and M-Bit (VU0)
Fixed up VU Int behaviour with VU Instant on/off

Savestate bump
This commit is contained in:
refractionpcsx2 2021-09-10 22:03:20 +01:00
parent 7e29a7e5a4
commit 3f56414824
12 changed files with 120 additions and 105 deletions

View File

@ -31,7 +31,7 @@ enum class FreezeAction
// 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 = (0x9A23 << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A24 << 16) | 0x0000;
// the freezing data between submodules and core
// an interesting thing to note is that this dates back from before plugin

View File

@ -180,6 +180,7 @@ struct __aligned16 VURegs
u32 xgkickdiff;
u32 xgkicksizeremaining;
u32 xgkicklastcycle;
u32 xgkickcyclecount;
u8 VIBackupCycles;
u32 VIOldValue;

View File

@ -183,8 +183,7 @@ static void _vu0Exec(VURegs* VU)
{
VU->VI[REG_TPC].UL = VU->branchpc;
if (VU->blockhasmbit)
VU->blockhasmbit = false;
VU->blockhasmbit = false;
if(VU->takedelaybranch)
{
@ -205,8 +204,7 @@ static void _vu0Exec(VURegs* VU)
VU0.VI[REG_VPU_STAT].UL&= ~0x1; /* E flag */
vif0Regs.stat.VEW = false;
if (VU->blockhasmbit)
VU->blockhasmbit = false;
VU->blockhasmbit = false;
}
}
@ -267,12 +265,16 @@ void InterpVU0::Execute(u32 cycles)
VU0.flags &= ~VUFLAG_MFLAGSET;
u32 startcycles = VU0.cycle;
while((VU0.cycle - startcycles) < cycles) {
if (!(VU0.VI[REG_VPU_STAT].UL & 0x1) || (VU0.flags & VUFLAG_MFLAGSET)) {
if (VU0.branch || VU0.ebit) {
vu0Exec(&VU0); // run branch delay slot?
if (!(VU0.VI[REG_VPU_STAT].UL & 0x1)) {
// Branches advance the PC to the new location if there was a branch in the E-Bit delay slot
if (VU0.branch) {
VU0.VI[REG_TPC].UL = VU0.branchpc;
VU0.branch = 0;
}
break;
}
if (VU0.flags & VUFLAG_MFLAGSET)
break;
vu0Exec(&VU0);
}
VU0.VI[REG_TPC].UL >>= 3;

View File

@ -207,14 +207,13 @@ static void _vu1Exec(VURegs* VU)
_vuFlushAll(VU);
VU0.VI[REG_VPU_STAT].UL &= ~0x100;
vif1Regs.stat.VEW = false;
// In instant VU mode, VU1 goes WAY ahead of the CPU, making the XGKick fall way behind
// We also have some code to update it in VIF Unpacks too, since in some games (Aggressive Inline) overwrite the XGKick data
if (INSTANT_VU1)
VU1.xgkicklastcycle = cpuRegs.cycle;
}
}
if (VU->xgkickenable && (VU1.cycle - VU->xgkicklastcycle) >= 2)
{
_vuXGKICKTransfer(VU, (VU1.cycle - VU->xgkicklastcycle), false);
}
// Progress the write position of the FMAC pipeline by one place
if (uregs.pipe == VUPIPE_FMAC || lregs.pipe == VUPIPE_FMAC)
VU->fmacwritepos = ++VU->fmacwritepos & 3;
@ -278,13 +277,15 @@ void InterpVU1::Execute(u32 cycles)
VU1.VI[REG_TPC].UL <<= 3;
u32 startcycles = VU1.cycle;
while ((VU1.cycle - startcycles) < cycles)
{
if (!(VU0.VI[REG_VPU_STAT].UL & 0x100))
{
if (VU1.branch || VU1.ebit)
if (VU1.branch == 1)
{
Step(); // run branch delay slot?
VU1.VI[REG_TPC].UL = VU1.branchpc;
VU1.branch = 0;
}
break;
}

View File

@ -35,12 +35,9 @@ void BaseVUmicroCPU::ExecuteBlock(bool startUp)
if (!(stat & test))
{
if (m_Idx == 1)
if (m_Idx == 1 && VU1.xgkickenable)
{
if (VU1.xgkickenable && (cpuRegs.cycle - VU1.xgkicklastcycle) >= 2)
{
_vuXGKICKTransfer(&VU1, (cpuRegs.cycle - VU1.xgkicklastcycle), false);
}
_vuXGKICKTransfer(&VU1, (cpuRegs.cycle - VU1.xgkicklastcycle), false);
}
return;
}
@ -63,7 +60,9 @@ void BaseVUmicroCPU::ExecuteBlock(bool startUp)
u32 nextblockcycles = m_Idx ? VU1.nextBlockCycles : VU0.nextBlockCycles;
if((VU0.flags & VUFLAG_MFLAGSET) || VU0.blockhasmbit)
bool useNextBlocks = (VU0.flags & VUFLAG_MFLAGSET) || VU0.blockhasmbit || !EmuConfig.Cpu.Recompiler.EnableVU0;
if(useNextBlocks)
cpuSetNextEventDelta(nextblockcycles);
else if(s)
cpuSetNextEventDelta(s);
@ -86,7 +85,7 @@ void BaseVUmicroCPU::ExecuteBlock(bool startUp)
Execute(delta);
}
if ((stat & test) && !EmuConfig.Gamefixes.VUKickstartHack)
if ((stat & test) && (!EmuConfig.Gamefixes.VUKickstartHack || (!m_Idx && !EmuConfig.Cpu.Recompiler.EnableVU0)))
{
// Queue up next required time to run a block
nextblockcycles = m_Idx ? VU1.nextBlockCycles : VU0.nextBlockCycles;

View File

@ -207,6 +207,14 @@ __fi void _vuTestPipes(VURegs * VU)
flushed |= _vuEFUflush(VU);
flushed |= _vuIALUflush(VU);
} while (flushed == true);
if (VU == &VU1)
{
if (VU1.xgkickenable)
{
_vuXGKICKTransfer(&VU1, (VU1.cycle - VU1.xgkicklastcycle), false);
}
}
}
static void __fastcall _vuFMACTestStall(VURegs* VU, int reg, int xyzw)
@ -2322,7 +2330,11 @@ void _vuXGKICKTransfer(VURegs* VU, u32 cycles, bool flush)
if (!VU->xgkickenable)
return;
while (!VU->xgkickendpacket || VU->xgkicksizeremaining > 0)
VU->xgkickcyclecount += cycles;
VU->xgkicklastcycle += cycles;
VUM_LOG("Adding %d cycles, total XGKick cycles to run now %d", cycles, VU->xgkickcyclecount);
while ((!VU->xgkickendpacket || VU->xgkicksizeremaining > 0) && (flush || VU->xgkickcyclecount >= 2))
{
u32 transfersize = 0;
@ -2345,7 +2357,7 @@ void _vuXGKICKTransfer(VURegs* VU, u32 cycles, bool flush)
if (!flush)
{
transfersize = std::min(VU->xgkicksizeremaining / 0x10, cycles / 2);
transfersize = std::min(VU->xgkicksizeremaining / 0x10, VU->xgkickcyclecount / 2);
transfersize = std::min(transfersize, VU->xgkickdiff / 0x10);
}
else
@ -2366,12 +2378,11 @@ void _vuXGKICKTransfer(VURegs* VU, u32 cycles, bool flush)
if ((VU0.VI[REG_VPU_STAT].UL & 0x100) && flush)
VU->cycle += transfersize * 2;
cycles -= transfersize * 2;
VU->xgkickcyclecount -= transfersize * 2;
VU->xgkickaddr = (VU->xgkickaddr + (transfersize * 0x10)) & 0x3FFF;
VU->xgkicksizeremaining -= (transfersize * 0x10);
VU->xgkickdiff = 0x4000 - VU->xgkickaddr;
VU->xgkicklastcycle += std::max(transfersize * 2, 2U);
if (VU->xgkicksizeremaining || !VU->xgkickendpacket)
VUM_LOG("XGKICK next addr %x left size %x", VU->xgkickaddr, VU->xgkicksizeremaining);
@ -2386,13 +2397,13 @@ void _vuXGKICKTransfer(VURegs* VU, u32 cycles, bool flush)
CPU_INT(DMAC_VIF1, 8);
}
}
if (!flush && cycles < 2)
return;
}
VUM_LOG("Disabling XGKICK");
VU->xgkickenable = false;
_vuTestPipes(VU);
if (flush)
{
VUM_LOG("Disabling XGKICK");
VU->xgkickenable = false;
_vuTestPipes(VU);
}
}
static __ri void _vuXGKICK(VURegs * VU)
@ -2411,7 +2422,8 @@ static __ri void _vuXGKICK(VURegs * VU)
VU->xgkicksizeremaining = 0;
VU->xgkickendpacket = false;
VU->xgkicklastcycle = VU->cycle;
IPU_LOG("XGKICK size %x EOP %d addr %x", VU->xgkicksizeremaining, VU->xgkickendpacket, addr);
VU->xgkickcyclecount = 0;
VUM_LOG("XGKICK addr %x", addr);
}
static __ri void _vuXTOP(VURegs * VU) {

View File

@ -157,7 +157,6 @@ __fi void vif0VUFinish()
if(vif0.waitforvu)
{
vif0.waitforvu = false;
ExecuteVU(0);
//Make sure VIF0 isnt already scheduled to spin.
if(!(cpuRegs.interrupt & 0x1) && vif0ch.chcr.STR && !vif0Regs.stat.test(VIF0_STAT_VSS | VIF0_STAT_VIS | VIF0_STAT_VFS))
vif0Interrupt();
@ -180,6 +179,10 @@ __fi void vif0Interrupt()
CPU_INT(VIF_VU0_FINISH, 16);
return;
}
if (vif0Regs.stat.VGW)
{
DevCon.Warning("VIF0 waiting for path");
}
if (vif0.irq && vif0.vifstalled.enabled && vif0.vifstalled.value == VIF_IRQ_STALL)
{

View File

@ -252,27 +252,13 @@ __fi void vif1VUFinish()
vif1Regs.stat.VEW = false;
VIF_LOG("VU1 finished");
if( gifRegs.stat.APATH == 1 )
{
VIF_LOG("Clear APATH1");
gifRegs.stat.APATH = 0;
gifRegs.stat.OPH = 0;
vif1Regs.stat.VGW = false; //Let vif continue if it's stuck on a flush
if(!vif1.waitforvu)
{
if(gifUnit.checkPaths(0,1,1)) gifUnit.Execute(false, true);
}
}
if(vif1.waitforvu)
{
vif1.waitforvu = false;
ExecuteVU(1);
//Check if VIF is already scheduled to interrupt, if it's waiting, kick it :P
if((cpuRegs.interrupt & ((1<<DMAC_VIF1) | (1 << DMAC_MFIFO_VIF))) == 0 && vif1ch.chcr.STR && !vif1Regs.stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS))
if ((cpuRegs.interrupt & ((1 << DMAC_VIF1) | (1 << DMAC_MFIFO_VIF))) == 0 && vif1ch.chcr.STR && !vif1Regs.stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS))
{
if(dmacRegs.ctrl.MFD == MFD_VIF1)
if (dmacRegs.ctrl.MFD == MFD_VIF1)
vifMFIFOInterrupt();
else
vif1Interrupt();
@ -330,6 +316,10 @@ __fi void vif1Interrupt()
CPU_INT(VIF_VU1_FINISH, 16);
return;
}
if (vif1Regs.stat.VGW)
return;
if (!vif1ch.chcr.STR) Console.WriteLn("Vif1 running when CHCR == %x", vif1ch.chcr._u32);
if (vif1.irq && vif1.vifstalled.enabled && vif1.vifstalled.value == VIF_IRQ_STALL)

View File

@ -43,6 +43,12 @@ __ri void vifExecQueue(int idx)
if (!GetVifX.queued_program || (VU0.VI[REG_VPU_STAT].UL & 1 << (idx * 8)))
return;
if (GetVifX.queued_gif_wait)
{
if (gifUnit.checkPaths(1, 1, 0))
return;
}
GetVifX.queued_program = false;
if (!idx)
@ -71,7 +77,7 @@ static __fi void vifFlush(int idx)
vifExecQueue(idx);
}
static __fi void vuExecMicro(int idx, u32 addr)
static __fi void vuExecMicro(int idx, u32 addr, bool requires_wait)
{
VIFregisters& vifRegs = vifXRegs;
@ -114,29 +120,12 @@ static __fi void vuExecMicro(int idx, u32 addr)
GetVifX.queued_pc = addr & (idx ? 0x7ffu : 0x1ffu);
GetVifX.unpackcalls = 0;
GetVifX.queued_gif_wait = requires_wait;
if (!idx || (!THREAD_VU1 && !INSTANT_VU1))
vifExecQueue(idx);
}
void ExecuteVU(int idx)
{
vifStruct& vifX = GetVifX;
if ((vifX.cmd & 0x7f) == 0x17)
{
vuExecMicro(idx, -1);
vifX.cmd = 0;
vifX.pass = 0;
}
else if ((vifX.cmd & 0x7f) == 0x14 || (vifX.cmd & 0x7f) == 0x15)
{
vuExecMicro(idx, (u16)(vifXRegs.code));
vifX.cmd = 0;
vifX.pass = 0;
}
vifExecQueue(idx);
}
//------------------------------------------------------------------
// Vif0/Vif1 Code Implementations
//------------------------------------------------------------------
@ -228,10 +217,9 @@ vifOp(vifCode_Flush)
vif1Regs.stat.VGW = true;
vif1.vifstalled.enabled = VifStallEnable(vif1ch);
vif1.vifstalled.value = VIF_TIMING_BREAK;
return 0;
}
if (vif1.waitforvu)
if (vif1.waitforvu || vif1Regs.stat.VGW)
return 0;
vif1.cmd = 0;
@ -259,10 +247,9 @@ vifOp(vifCode_FlushA)
vif1Regs.stat.VGW = true;
vif1.vifstalled.enabled = VifStallEnable(vif1ch);
vif1.vifstalled.value = VIF_TIMING_BREAK;
return 0;
}
if (vif1.waitforvu)
if (vif1.waitforvu || vif1Regs.stat.VGW)
return 0;
vif1.cmd = 0;
@ -279,6 +266,10 @@ vifOp(vifCode_FlushE)
pass1
{
vifFlush(idx);
if (vifX.waitforvu)
return 0;
vifX.cmd = 0;
vifX.pass = 0;
}
@ -381,7 +372,7 @@ vifOp(vifCode_MPG)
vifX.tag.size = vifNum ? (vifNum * 2) : 512;
vifFlush(idx);
if (vifX.vifstalled.enabled)
if (vifX.waitforvu)
return 0;
else
{
@ -426,19 +417,20 @@ vifOp(vifCode_MSCAL)
{
vifFlush(idx);
if (!vifX.waitforvu)
if (vifX.waitforvu)
return 0;
vuExecMicro(idx, (u16)(vifXRegs.code), false);
vifX.cmd = 0;
vifX.pass = 0;
if (GetVifX.vifpacketsize > 1)
{
vuExecMicro(idx, (u16)(vifXRegs.code));
vifX.cmd = 0;
vifX.pass = 0;
if (GetVifX.vifpacketsize > 1)
//Warship Gunner 2 has a rather big dislike for the delays
if (((data[1] >> 24) & 0x60) == 0x60) // Immediate following Unpack
{
//Warship Gunner 2 has a rather big dislike for the delays
if (((data[1] >> 24) & 0x60) == 0x60) // Immediate following Unpack
{
//Snowblind games only use MSCAL, so other MS kicks force the program directly.
vifExecQueue(idx);
}
//Snowblind games only use MSCAL, so other MS kicks force the program directly.
vifExecQueue(idx);
}
}
}
@ -460,13 +452,14 @@ vifOp(vifCode_MSCALF)
vifX.vifstalled.enabled = VifStallEnable(vifXch);
vifX.vifstalled.value = VIF_TIMING_BREAK;
}
if (!vifX.waitforvu)
{
vuExecMicro(idx, (u16)(vifXRegs.code));
vifX.cmd = 0;
vifX.pass = 0;
vifExecQueue(idx);
}
if (vifX.waitforvu || vif1Regs.stat.VGW)
return 0;
vuExecMicro(idx, (u16)(vifXRegs.code), true);
vifX.cmd = 0;
vifX.pass = 0;
vifExecQueue(idx);
}
pass3 { VifCodeLog("MSCALF"); }
return 1;
@ -478,17 +471,18 @@ vifOp(vifCode_MSCNT)
pass1
{
vifFlush(idx);
if (!vifX.waitforvu)
if (vifX.waitforvu)
return 0;
vuExecMicro(idx, -1, false);
vifX.cmd = 0;
vifX.pass = 0;
if (GetVifX.vifpacketsize > 1)
{
vuExecMicro(idx, -1);
vifX.cmd = 0;
vifX.pass = 0;
if (GetVifX.vifpacketsize > 1)
if (((data[1] >> 24) & 0x60) == 0x60) // Immediate following Unpack
{
if (((data[1] >> 24) & 0x60) == 0x60) // Immediate following Unpack
{
vifExecQueue(idx);
}
vifExecQueue(idx);
}
}
}

View File

@ -94,15 +94,13 @@ struct vifStruct {
bool queued_program;
u32 queued_pc;
bool queued_gif_wait;
};
extern __aligned16 vifStruct vif0, vif1;
_vifT extern u32 vifRead32(u32 mem);
_vifT extern bool vifWrite32(u32 mem, u32 value);
void ExecuteVU(int idx);
extern void vif0Interrupt();
extern void vif0VUFinish();
extern void vif0Reset();

View File

@ -59,6 +59,12 @@ _vifT void vifTransferLoop(u32* &data) {
ret = vifCmdHandler[idx][vifX.cmd & 0x7f](vifX.pass, data);
data += ret;
pSize -= ret;
if (vifX.vifstalled.enabled)
{
int current_STR = idx ? vif1ch.chcr.STR : vif0ch.chcr.STR;
if (!current_STR)
DevCon.Warning("Warning! VIF%d stalled during FIFO transfer!", idx);
}
}
}

View File

@ -113,6 +113,15 @@ _vifT int nVifUnpack(const u8* data)
const bool isFill = (vifRegs.cycle.cl < wl);
s32 size = ret << 2;
// This is for use when XGKick is synced as VIF can overwrite XG Kick data as it's transferring out
// Test with Aggressive Inline Skating, or K-1 Premium 2005 Dynamite!
if (idx == 1 && VU1.xgkickenable && !(VU0.VI[REG_TPC].UL & 0x100))
{
// Catch up first, then the unpack cycles
_vuXGKICKTransfer(&VU1, cpuRegs.cycle - VU1.xgkicklastcycle, false);
_vuXGKICKTransfer(&VU1, ret/2, false);
}
if (ret == vif.tag.size) // Full Transfer
{
if (v.bSize) // Last transfer was partial