mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
476 lines
10 KiB
C++
476 lines
10 KiB
C++
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#pragma once
|
|
|
|
// Sets FDIV Flags at the proper time
|
|
__fi void mVUdivSet(mV)
|
|
{
|
|
if (mVUinfo.doDivFlag)
|
|
{
|
|
if (!sFLAG.doFlag)
|
|
xMOV(getFlagReg(sFLAG.write), getFlagReg(sFLAG.lastWrite));
|
|
xAND(getFlagReg(sFLAG.write), 0xfff3ffff);
|
|
xOR(getFlagReg(sFLAG.write), ptr32[&mVU.divFlag]);
|
|
}
|
|
}
|
|
|
|
// Optimizes out unneeded status flag updates
|
|
// This can safely be done when there is an FSSET opcode
|
|
__fi void mVUstatusFlagOp(mV)
|
|
{
|
|
int curPC = iPC;
|
|
int i = mVUcount;
|
|
bool runLoop = true;
|
|
|
|
if (sFLAG.doFlag)
|
|
{
|
|
sFLAG.doNonSticky = true;
|
|
}
|
|
else
|
|
{
|
|
for (; i > 0; i--)
|
|
{
|
|
incPC2(-2);
|
|
if (sFLAG.doNonSticky)
|
|
{
|
|
runLoop = false;
|
|
break;
|
|
}
|
|
else if (sFLAG.doFlag)
|
|
{
|
|
sFLAG.doNonSticky = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (runLoop)
|
|
{
|
|
for (; i > 0; i--)
|
|
{
|
|
incPC2(-2);
|
|
|
|
if (sFLAG.doNonSticky)
|
|
break;
|
|
|
|
sFLAG.doFlag = false;
|
|
}
|
|
}
|
|
iPC = curPC;
|
|
DevCon.WriteLn(Color_Green, "microVU%d: FSSET Optimization", getIndex);
|
|
}
|
|
|
|
int findFlagInst(int* fFlag, int cycles)
|
|
{
|
|
int j = 0, jValue = -1;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if ((fFlag[i] <= cycles) && (fFlag[i] > jValue))
|
|
{
|
|
j = i;
|
|
jValue = fFlag[i];
|
|
}
|
|
}
|
|
return j;
|
|
}
|
|
|
|
// Setup Last 4 instances of Status/Mac/Clip flags (needed for accurate block linking)
|
|
int sortFlag(int* fFlag, int* bFlag, int cycles)
|
|
{
|
|
int lFlag = -5;
|
|
int x = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
bFlag[i] = findFlagInst(fFlag, cycles);
|
|
if (lFlag != bFlag[i])
|
|
x++;
|
|
lFlag = bFlag[i];
|
|
cycles++;
|
|
}
|
|
return x; // Returns the number of Valid Flag Instances
|
|
}
|
|
|
|
void sortFullFlag(int* fFlag, int* bFlag)
|
|
{
|
|
int m = std::max(std::max(fFlag[0], fFlag[1]), std::max(fFlag[2], fFlag[3]));
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int t = 3 - (m - fFlag[i]);
|
|
bFlag[i] = (t < 0) ? 0 : t + 1;
|
|
}
|
|
}
|
|
|
|
#define sFlagCond (sFLAG.doFlag || mVUlow.isFSSET || mVUinfo.doDivFlag)
|
|
#define sHackCond (mVUsFlagHack && !sFLAG.doNonSticky)
|
|
|
|
// Note: Flag handling is 'very' complex, it requires full knowledge of how microVU recs work, so don't touch!
|
|
__fi void mVUsetFlags(mV, microFlagCycles& mFC)
|
|
{
|
|
int endPC = iPC;
|
|
u32 aCount = 0; // Amount of instructions needed to get valid mac flag instances for block linking
|
|
//bool writeProtect = false;
|
|
|
|
// Ensure last ~4+ instructions update mac/status flags (if next block's first 4 instructions will read them)
|
|
for (int i = mVUcount; i > 0; i--, aCount++)
|
|
{
|
|
if (sFLAG.doFlag)
|
|
{
|
|
|
|
if (__Mac)
|
|
{
|
|
mFLAG.doFlag = true;
|
|
//writeProtect = true;
|
|
}
|
|
|
|
if (__Status)
|
|
{
|
|
sFLAG.doNonSticky = true;
|
|
//writeProtect = true;
|
|
}
|
|
|
|
if (aCount >= 3)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
incPC2(-2);
|
|
}
|
|
|
|
// Status/Mac Flags Setup Code
|
|
int xS = 0, xM = 0, xC = 0;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
mFC.xStatus[i] = i;
|
|
mFC.xMac [i] = i;
|
|
mFC.xClip [i] = i;
|
|
}
|
|
|
|
if (!(mVUpBlock->pState.needExactMatch & 1))
|
|
{
|
|
xS = (mVUpBlock->pState.flagInfo >> 2) & 3;
|
|
mFC.xStatus[0] = -1;
|
|
mFC.xStatus[1] = -1;
|
|
mFC.xStatus[2] = -1;
|
|
mFC.xStatus[3] = -1;
|
|
mFC.xStatus[(xS - 1) & 3] = 0;
|
|
}
|
|
|
|
if (!(mVUpBlock->pState.needExactMatch & 2))
|
|
{
|
|
mFC.xMac[0] = -1;
|
|
mFC.xMac[1] = -1;
|
|
mFC.xMac[2] = -1;
|
|
mFC.xMac[3] = -1;
|
|
}
|
|
|
|
if (!(mVUpBlock->pState.needExactMatch & 4))
|
|
{
|
|
xC = (mVUpBlock->pState.flagInfo >> 6) & 3;
|
|
mFC.xClip[0] = -1;
|
|
mFC.xClip[1] = -1;
|
|
mFC.xClip[2] = -1;
|
|
mFC.xClip[3] = -1;
|
|
mFC.xClip[(xC - 1) & 3] = 0;
|
|
}
|
|
|
|
mFC.cycles = 0;
|
|
u32 xCount = mVUcount; // Backup count
|
|
iPC = mVUstartPC;
|
|
for (mVUcount = 0; mVUcount < xCount; mVUcount++)
|
|
{
|
|
if (mVUlow.isFSSET && !noFlagOpts)
|
|
{
|
|
if (__Status) // Don't Optimize out on the last ~4+ instructions
|
|
{
|
|
if ((xCount - mVUcount) > aCount)
|
|
mVUstatusFlagOp(mVU);
|
|
}
|
|
else
|
|
mVUstatusFlagOp(mVU);
|
|
}
|
|
mFC.cycles += mVUstall;
|
|
|
|
sFLAG.read = doSFlagInsts ? findFlagInst(mFC.xStatus, mFC.cycles) : 0;
|
|
mFLAG.read = doMFlagInsts ? findFlagInst(mFC.xMac, mFC.cycles) : 0;
|
|
cFLAG.read = doCFlagInsts ? findFlagInst(mFC.xClip, mFC.cycles) : 0;
|
|
|
|
sFLAG.write = doSFlagInsts ? xS : 0;
|
|
mFLAG.write = doMFlagInsts ? xM : 0;
|
|
cFLAG.write = doCFlagInsts ? xC : 0;
|
|
|
|
sFLAG.lastWrite = doSFlagInsts ? (xS - 1) & 3 : 0;
|
|
mFLAG.lastWrite = doMFlagInsts ? (xM - 1) & 3 : 0;
|
|
cFLAG.lastWrite = doCFlagInsts ? (xC - 1) & 3 : 0;
|
|
|
|
if (sHackCond)
|
|
{
|
|
sFLAG.doFlag = false;
|
|
}
|
|
|
|
if (sFLAG.doFlag)
|
|
{
|
|
if (noFlagOpts)
|
|
{
|
|
sFLAG.doNonSticky = true;
|
|
mFLAG.doFlag = true;
|
|
}
|
|
}
|
|
|
|
if (sFlagCond)
|
|
{
|
|
mFC.xStatus[xS] = mFC.cycles + 4;
|
|
xS = (xS + 1) & 3;
|
|
}
|
|
|
|
if (mFLAG.doFlag)
|
|
{
|
|
mFC.xMac[xM] = mFC.cycles + 4;
|
|
xM = (xM + 1) & 3;
|
|
}
|
|
|
|
if (cFLAG.doFlag)
|
|
{
|
|
mFC.xClip[xC] = mFC.cycles + 4;
|
|
xC = (xC + 1) & 3;
|
|
}
|
|
|
|
mFC.cycles++;
|
|
incPC2(2);
|
|
}
|
|
|
|
mVUregs.flagInfo |= ((__Status) ? 0 : (xS << 2));
|
|
mVUregs.flagInfo |= /*((__Mac||1) ? 0 :*/ (xM << 4)/*)*/; //TODO: Optimise this? Might help with number of blocks.
|
|
mVUregs.flagInfo |= ((__Clip) ? 0 : (xC << 6));
|
|
iPC = endPC;
|
|
}
|
|
|
|
#define getFlagReg2(x) ((bStatus[0] == x) ? getFlagReg(x) : gprT1)
|
|
#define getFlagReg3(x) ((gFlag == x) ? gprT1 : getFlagReg(x))
|
|
#define getFlagReg4(x) ((gFlag == x) ? gprT1 : gprT2)
|
|
#define shuffleMac ((bMac[3] << 6) | (bMac[2] << 4) | (bMac[1] << 2) | bMac[0])
|
|
#define shuffleClip ((bClip[3] << 6) | (bClip[2] << 4) | (bClip[1] << 2) | bClip[0])
|
|
|
|
// Recompiles Code for Proper Flags on Block Linkings
|
|
__fi void mVUsetupFlags(mV, microFlagCycles& mFC)
|
|
{
|
|
|
|
if (mVUregs.flagInfo & 1)
|
|
{
|
|
if (mVUregs.needExactMatch)
|
|
DevCon.Error("mVU ERROR!!!");
|
|
}
|
|
|
|
const bool pf = false; // Print Flag Info
|
|
if (pf)
|
|
DevCon.WriteLn("mVU%d - [#%d][sPC=%04x][bPC=%04x][mVUBranch=%d][branch=%d]",
|
|
mVU.index, mVU.prog.cur->idx, mVUstartPC / 2 * 8, xPC, mVUbranch, mVUlow.branch);
|
|
|
|
if (doSFlagInsts && __Status)
|
|
{
|
|
if (pf)
|
|
DevCon.WriteLn("mVU%d - Status Flag", mVU.index);
|
|
int bStatus[4];
|
|
int sortRegs = sortFlag(mFC.xStatus, bStatus, mFC.cycles);
|
|
// DevCon::Status("sortRegs = %d", params sortRegs);
|
|
// Note: Emitter will optimize out mov(reg1, reg1) cases...
|
|
if (sortRegs == 1)
|
|
{
|
|
xMOV(gprF0, getFlagReg(bStatus[0]));
|
|
xMOV(gprF1, getFlagReg(bStatus[1]));
|
|
xMOV(gprF2, getFlagReg(bStatus[2]));
|
|
xMOV(gprF3, getFlagReg(bStatus[3]));
|
|
}
|
|
else if (sortRegs == 2)
|
|
{
|
|
xMOV(gprT1, getFlagReg (bStatus[3]));
|
|
xMOV(gprF0, getFlagReg (bStatus[0]));
|
|
xMOV(gprF1, getFlagReg2(bStatus[1]));
|
|
xMOV(gprF2, getFlagReg2(bStatus[2]));
|
|
xMOV(gprF3, gprT1);
|
|
}
|
|
else if (sortRegs == 3)
|
|
{
|
|
int gFlag = (bStatus[0] == bStatus[1]) ? bStatus[2] : bStatus[1];
|
|
xMOV(gprT1, getFlagReg (gFlag));
|
|
xMOV(gprT2, getFlagReg (bStatus[3]));
|
|
xMOV(gprF0, getFlagReg (bStatus[0]));
|
|
xMOV(gprF1, getFlagReg3(bStatus[1]));
|
|
xMOV(gprF2, getFlagReg4(bStatus[2]));
|
|
xMOV(gprF3, gprT2);
|
|
}
|
|
else
|
|
{
|
|
const xRegister32& temp3 = mVU.regAlloc->allocGPR();
|
|
xMOV(gprT1, getFlagReg(bStatus[0]));
|
|
xMOV(gprT2, getFlagReg(bStatus[1]));
|
|
xMOV(temp3, getFlagReg(bStatus[2]));
|
|
xMOV(gprF3, getFlagReg(bStatus[3]));
|
|
xMOV(gprF0, gprT1);
|
|
xMOV(gprF1, gprT2);
|
|
xMOV(gprF2, temp3);
|
|
mVU.regAlloc->clearNeeded(temp3);
|
|
}
|
|
}
|
|
|
|
if (doMFlagInsts && __Mac)
|
|
{
|
|
if (pf)
|
|
DevCon.WriteLn("mVU%d - Mac Flag", mVU.index);
|
|
int bMac[4];
|
|
sortFlag(mFC.xMac, bMac, mFC.cycles);
|
|
xMOVAPS(xmmT1, ptr128[mVU.macFlag]);
|
|
xSHUF.PS(xmmT1, xmmT1, shuffleMac);
|
|
xMOVAPS(ptr128[mVU.macFlag], xmmT1);
|
|
}
|
|
|
|
if (doCFlagInsts && __Clip)
|
|
{
|
|
if (pf)
|
|
DevCon.WriteLn("mVU%d - Clip Flag", mVU.index);
|
|
int bClip[4];
|
|
sortFlag(mFC.xClip, bClip, mFC.cycles);
|
|
xMOVAPS(xmmT2, ptr128[mVU.clipFlag]);
|
|
xSHUF.PS(xmmT2, xmmT2, shuffleClip);
|
|
xMOVAPS(ptr128[mVU.clipFlag], xmmT2);
|
|
}
|
|
}
|
|
|
|
#define shortBranch() \
|
|
{ \
|
|
if ((branch == 3) || (branch == 4)) /*Branches*/ \
|
|
{ \
|
|
_mVUflagPass(mVU, aBranchAddr, sCount + found, found, v); \
|
|
if (branch == 3) /*Non-conditional Branch*/ \
|
|
break; \
|
|
branch = 0; \
|
|
} \
|
|
else if (branch == 5) /*JR/JARL*/ \
|
|
{ \
|
|
if (sCount + found < 4) \
|
|
mVUregs.needExactMatch |= 7; \
|
|
break; \
|
|
} \
|
|
else /*E-Bit End*/ \
|
|
break; \
|
|
}
|
|
|
|
// Scan through instructions and check if flags are read (FSxxx, FMxxx, FCxxx opcodes)
|
|
void _mVUflagPass(mV, u32 startPC, u32 sCount, u32 found, std::vector<u32>& v)
|
|
{
|
|
|
|
for (u32 i = 0; i < v.size(); i++)
|
|
{
|
|
if (v[i] == startPC)
|
|
return; // Prevent infinite recursion
|
|
}
|
|
v.push_back(startPC);
|
|
|
|
int oldPC = iPC;
|
|
int oldBranch = mVUbranch;
|
|
int aBranchAddr = 0;
|
|
iPC = startPC / 4;
|
|
mVUbranch = 0;
|
|
for (int branch = 0; sCount < 4; sCount += found)
|
|
{
|
|
mVUregs.needExactMatch &= 7;
|
|
incPC(1);
|
|
mVUopU(mVU, 3);
|
|
found |= (mVUregs.needExactMatch & 8) >> 3;
|
|
mVUregs.needExactMatch &= 7;
|
|
if (curI & _Ebit_)
|
|
{
|
|
branch = 1;
|
|
}
|
|
if (curI & _Tbit_)
|
|
{
|
|
branch = 6;
|
|
}
|
|
if ((curI & _Dbit_) && doDBitHandling)
|
|
{
|
|
branch = 6;
|
|
}
|
|
if (!(curI & _Ibit_))
|
|
{
|
|
incPC(-1);
|
|
mVUopL(mVU, 3);
|
|
incPC(1);
|
|
}
|
|
|
|
if (branch >= 2)
|
|
{
|
|
shortBranch();
|
|
}
|
|
else if (branch == 1)
|
|
{
|
|
branch = 2;
|
|
}
|
|
if (mVUbranch)
|
|
{
|
|
branch = ((mVUbranch > 8) ? (5) : ((mVUbranch < 3) ? 3 : 4));
|
|
incPC(-1);
|
|
aBranchAddr = branchAddr(mVU);
|
|
incPC(1);
|
|
mVUbranch = 0;
|
|
}
|
|
incPC(1);
|
|
if ((mVUregs.needExactMatch & 7) == 7)
|
|
break;
|
|
}
|
|
iPC = oldPC;
|
|
mVUbranch = oldBranch;
|
|
mVUregs.needExactMatch &= 7;
|
|
setCode();
|
|
}
|
|
|
|
void mVUflagPass(mV, u32 startPC, u32 sCount = 0, u32 found = 0)
|
|
{
|
|
std::vector<u32> v;
|
|
_mVUflagPass(mVU, startPC, sCount, found, v);
|
|
}
|
|
|
|
// Checks if the first ~4 instructions of a block will read flags
|
|
void mVUsetFlagInfo(mV)
|
|
{
|
|
if (noFlagOpts)
|
|
{
|
|
mVUregs.needExactMatch = 0x7;
|
|
mVUregs.flagInfo = 0x0;
|
|
return;
|
|
}
|
|
if (mVUbranch <= 2) // B/BAL
|
|
{
|
|
incPC(-1);
|
|
mVUflagPass(mVU, branchAddr(mVU));
|
|
incPC(1);
|
|
|
|
mVUregs.needExactMatch &= 0x7;
|
|
}
|
|
else if (mVUbranch <= 8) // Conditional Branch
|
|
{
|
|
incPC(-1); // Branch Taken
|
|
mVUflagPass(mVU, branchAddr(mVU));
|
|
int backupFlagInfo = mVUregs.needExactMatch;
|
|
mVUregs.needExactMatch = 0;
|
|
|
|
incPC(4); // Branch Not Taken
|
|
mVUflagPass(mVU, xPC);
|
|
incPC(-3);
|
|
|
|
mVUregs.needExactMatch |= backupFlagInfo;
|
|
mVUregs.needExactMatch &= 0x7;
|
|
}
|
|
else // JR/JALR
|
|
{
|
|
if (!doConstProp || !mVUlow.constJump.isValid)
|
|
{
|
|
mVUregs.needExactMatch |= 0x7;
|
|
}
|
|
else
|
|
{
|
|
mVUflagPass(mVU, (mVUlow.constJump.regValue * 8) & (mVU.microMemSize - 8));
|
|
}
|
|
mVUregs.needExactMatch &= 0x7;
|
|
}
|
|
}
|