Files
archived-pcsx2/pcsx2/x86/iR3000Atables.cpp

2367 lines
49 KiB
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include <ctime>
#include "iR3000A.h"
#include "IopMem.h"
#include "IopDma.h"
#include "IopGte.h"
#include "common/Console.h"
using namespace x86Emitter;
extern int g_psxWriteOk;
extern u32 g_psxMaxRecMem;
// R3000A instruction implementation
#define REC_FUNC(f) \
static void rpsx##f() \
{ \
xMOV(ptr32[&psxRegs.code], (u32)psxRegs.code); \
_psxFlushCall(FLUSH_EVERYTHING); \
xFastCall((void*)(uptr)psx##f); \
PSX_DEL_CONST(_Rt_); \
/* branch = 2; */ \
}
// Same as above but with a different naming convension (to avoid various rename)
#define REC_GTE_FUNC(f) \
static void rgte##f() \
{ \
xMOV(ptr32[&psxRegs.code], (u32)psxRegs.code); \
_psxFlushCall(FLUSH_EVERYTHING); \
xFastCall((void*)(uptr)gte##f); \
PSX_DEL_CONST(_Rt_); \
/* branch = 2; */ \
}
extern void psxLWL();
extern void psxLWR();
extern void psxSWL();
extern void psxSWR();
// TODO(Stenzek): Operate directly on mem when destination register is not live.
// Do we want aligned targets? Seems wasteful...
#ifdef PCSX2_DEBUG
#define x86SetJ32A x86SetJ32
#endif
static int rpsxAllocRegIfUsed(int reg, int mode)
{
if (EEINST_USEDTEST(reg))
return _allocX86reg(X86TYPE_PSX, reg, mode);
else
return _checkX86reg(X86TYPE_PSX, reg, mode);
}
static void rpsxMoveStoT(int info)
{
if (EEREC_T == EEREC_S)
return;
if (info & PROCESS_EE_S)
xMOV(xRegister32(EEREC_T), xRegister32(EEREC_S));
else
xMOV(xRegister32(EEREC_T), ptr32[&psxRegs.GPR.r[_Rs_]]);
}
static void rpsxMoveStoD(int info)
{
if (EEREC_D == EEREC_S)
return;
if (info & PROCESS_EE_S)
xMOV(xRegister32(EEREC_D), xRegister32(EEREC_S));
else
xMOV(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rs_]]);
}
static void rpsxMoveTtoD(int info)
{
if (EEREC_D == EEREC_T)
return;
if (info & PROCESS_EE_T)
xMOV(xRegister32(EEREC_D), xRegister32(EEREC_T));
else
xMOV(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rt_]]);
}
static void rpsxMoveSToECX(int info)
{
if (info & PROCESS_EE_S)
xMOV(ecx, xRegister32(EEREC_S));
else
xMOV(ecx, ptr32[&psxRegs.GPR.r[_Rs_]]);
}
static void rpsxCopyReg(int dest, int src)
{
// try a simple rename first...
const int roldsrc = _checkX86reg(X86TYPE_PSX, src, MODE_READ);
if (roldsrc >= 0 && psxTryRenameReg(dest, src, roldsrc, 0, 0) >= 0)
return;
const int rdest = rpsxAllocRegIfUsed(dest, MODE_WRITE);
if (PSX_IS_CONST1(src))
{
if (dest < 32)
{
g_psxConstRegs[dest] = g_psxConstRegs[src];
PSX_SET_CONST(dest);
}
else
{
if (rdest >= 0)
xMOV(xRegister32(rdest), g_psxConstRegs[src]);
else
xMOV(ptr32[&psxRegs.GPR.r[dest]], g_psxConstRegs[src]);
}
return;
}
if (dest < 32)
PSX_DEL_CONST(dest);
const int rsrc = rpsxAllocRegIfUsed(src, MODE_READ);
if (rsrc >= 0 && rdest >= 0)
{
xMOV(xRegister32(rdest), xRegister32(rsrc));
}
else if (rdest >= 0)
{
xMOV(xRegister32(rdest), ptr32[&psxRegs.GPR.r[src]]);
}
else if (rsrc >= 0)
{
xMOV(ptr32[&psxRegs.GPR.r[dest]], xRegister32(rsrc));
}
else
{
xMOV(eax, ptr32[&psxRegs.GPR.r[src]]);
xMOV(ptr32[&psxRegs.GPR.r[dest]], eax);
}
}
////
static void rpsxADDIU_const()
{
g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] + _Imm_;
}
static void rpsxADDIU_(int info)
{
// Rt = Rs + Im
rpsxMoveStoT(info);
if (_Imm_ != 0)
xADD(xRegister32(EEREC_T), _Imm_);
}
PSXRECOMPILE_CONSTCODE1(ADDIU, XMMINFO_WRITET | XMMINFO_READS);
void rpsxADDI() { rpsxADDIU(); }
//// SLTI
static void rpsxSLTI_const()
{
g_psxConstRegs[_Rt_] = *(int*)&g_psxConstRegs[_Rs_] < _Imm_;
}
static void rpsxSLTI_(int info)
{
const xRegister32 dreg((_Rt_ == _Rs_) ? _allocX86reg(X86TYPE_TEMP, 0, 0) : EEREC_T);
xXOR(dreg, dreg);
if (info & PROCESS_EE_S)
xCMP(xRegister32(EEREC_S), _Imm_);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], _Imm_);
xSETL(xRegister8(dreg));
if (dreg.GetId() != EEREC_T)
{
std::swap(x86regs[dreg.GetId()], x86regs[EEREC_T]);
_freeX86reg(EEREC_T);
}
}
PSXRECOMPILE_CONSTCODE1(SLTI, XMMINFO_WRITET | XMMINFO_READS | XMMINFO_NORENAME);
//// SLTIU
static void rpsxSLTIU_const()
{
g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] < (u32)_Imm_;
}
static void rpsxSLTIU_(int info)
{
const xRegister32 dreg((_Rt_ == _Rs_) ? _allocX86reg(X86TYPE_TEMP, 0, 0) : EEREC_T);
xXOR(dreg, dreg);
if (info & PROCESS_EE_S)
xCMP(xRegister32(EEREC_S), _Imm_);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], _Imm_);
xSETB(xRegister8(dreg));
if (dreg.GetId() != EEREC_T)
{
std::swap(x86regs[dreg.GetId()], x86regs[EEREC_T]);
_freeX86reg(EEREC_T);
}
}
PSXRECOMPILE_CONSTCODE1(SLTIU, XMMINFO_WRITET | XMMINFO_READS | XMMINFO_NORENAME);
static void rpsxLogicalOpI(u64 info, int op)
{
if (_ImmU_ != 0)
{
rpsxMoveStoT(info);
switch (op)
{
case 0:
xAND(xRegister32(EEREC_T), _ImmU_);
break;
case 1:
xOR(xRegister32(EEREC_T), _ImmU_);
break;
case 2:
xXOR(xRegister32(EEREC_T), _ImmU_);
break;
jNO_DEFAULT
}
}
else
{
if (op == 0)
{
xXOR(xRegister32(EEREC_T), xRegister32(EEREC_T));
}
else if (EEREC_T != EEREC_S)
{
rpsxMoveStoT(info);
}
}
}
//// ANDI
static void rpsxANDI_const()
{
g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] & _ImmU_;
}
static void rpsxANDI_(int info)
{
rpsxLogicalOpI(info, 0);
}
PSXRECOMPILE_CONSTCODE1(ANDI, XMMINFO_WRITET | XMMINFO_READS);
//// ORI
static void rpsxORI_const()
{
g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] | _ImmU_;
}
static void rpsxORI_(int info)
{
rpsxLogicalOpI(info, 1);
}
PSXRECOMPILE_CONSTCODE1(ORI, XMMINFO_WRITET | XMMINFO_READS);
static void rpsxXORI_const()
{
g_psxConstRegs[_Rt_] = g_psxConstRegs[_Rs_] ^ _ImmU_;
}
static void rpsxXORI_(int info)
{
rpsxLogicalOpI(info, 2);
}
PSXRECOMPILE_CONSTCODE1(XORI, XMMINFO_WRITET | XMMINFO_READS);
void rpsxLUI()
{
if (!_Rt_)
return;
_psxOnWriteReg(_Rt_);
_psxDeleteReg(_Rt_, 0);
PSX_SET_CONST(_Rt_);
g_psxConstRegs[_Rt_] = psxRegs.code << 16;
}
static void rpsxADDU_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] + g_psxConstRegs[_Rt_];
}
static void rpsxADDU_consts(int info)
{
const s32 cval = static_cast<s32>(g_psxConstRegs[_Rs_]);
rpsxMoveTtoD(info);
if (cval != 0)
xADD(xRegister32(EEREC_D), cval);
}
static void rpsxADDU_constt(int info)
{
const s32 cval = static_cast<s32>(g_psxConstRegs[_Rt_]);
rpsxMoveStoD(info);
if (cval != 0)
xADD(xRegister32(EEREC_D), cval);
}
void rpsxADDU_(int info)
{
if ((info & PROCESS_EE_S) && (info & PROCESS_EE_T))
{
if (EEREC_D == EEREC_S)
{
xADD(xRegister32(EEREC_D), xRegister32(EEREC_T));
}
else if (EEREC_D == EEREC_T)
{
xADD(xRegister32(EEREC_D), xRegister32(EEREC_S));
}
else
{
xMOV(xRegister32(EEREC_D), xRegister32(EEREC_S));
xADD(xRegister32(EEREC_D), xRegister32(EEREC_T));
}
}
else if (info & PROCESS_EE_S)
{
xMOV(xRegister32(EEREC_D), xRegister32(EEREC_S));
xADD(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rt_]]);
}
else if (info & PROCESS_EE_T)
{
xMOV(xRegister32(EEREC_D), xRegister32(EEREC_T));
xADD(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rs_]]);
}
else
{
xMOV(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rs_]]);
xADD(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rt_]]);
}
}
PSXRECOMPILE_CONSTCODE0(ADDU, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT);
void rpsxADD() { rpsxADDU(); }
static void rpsxSUBU_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] - g_psxConstRegs[_Rt_];
}
static void rpsxSUBU_consts(int info)
{
// more complex because Rt can be Rd, and we're reversing the op
const s32 sval = g_psxConstRegs[_Rs_];
const xRegister32 dreg((_Rt_ == _Rd_) ? eax.GetId() : EEREC_D);
xMOV(dreg, sval);
if (info & PROCESS_EE_T)
xSUB(dreg, xRegister32(EEREC_T));
else
xSUB(dreg, ptr32[&psxRegs.GPR.r[_Rt_]]);
xMOV(xRegister32(EEREC_D), dreg);
}
static void rpsxSUBU_constt(int info)
{
const s32 tval = g_psxConstRegs[_Rt_];
rpsxMoveStoD(info);
if (tval != 0)
xSUB(xRegister32(EEREC_D), tval);
}
static void rpsxSUBU_(int info)
{
// Rd = Rs - Rt
if (_Rs_ == _Rt_)
{
xXOR(xRegister32(EEREC_D), xRegister32(EEREC_D));
return;
}
// a bit messier here because it's not commutative..
if ((info & PROCESS_EE_S) && (info & PROCESS_EE_T))
{
if (EEREC_D == EEREC_S)
{
xSUB(xRegister32(EEREC_D), xRegister32(EEREC_T));
}
else if (EEREC_D == EEREC_T)
{
// D might equal T
const xRegister32 dreg((_Rt_ == _Rd_) ? eax.GetId() : EEREC_D);
xMOV(dreg, xRegister32(EEREC_S));
xSUB(dreg, xRegister32(EEREC_T));
xMOV(xRegister32(EEREC_D), dreg);
}
else
{
xMOV(xRegister32(EEREC_D), xRegister32(EEREC_S));
xSUB(xRegister32(EEREC_D), xRegister32(EEREC_T));
}
}
else if (info & PROCESS_EE_S)
{
xMOV(xRegister32(EEREC_D), xRegister32(EEREC_S));
xSUB(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rt_]]);
}
else if (info & PROCESS_EE_T)
{
// D might equal T
const xRegister32 dreg((_Rt_ == _Rd_) ? eax.GetId() : EEREC_D);
xMOV(dreg, ptr32[&psxRegs.GPR.r[_Rs_]]);
xSUB(dreg, xRegister32(EEREC_T));
xMOV(xRegister32(EEREC_D), dreg);
}
else
{
xMOV(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rs_]]);
xSUB(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[_Rt_]]);
}
}
PSXRECOMPILE_CONSTCODE0(SUBU, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT);
void rpsxSUB() { rpsxSUBU(); }
namespace
{
enum class LogicalOp
{
AND,
OR,
XOR,
NOR
};
} // namespace
static void rpsxLogicalOp_constv(LogicalOp op, int info, int creg, u32 vreg, int regv)
{
xImpl_G1Logic* bad = nullptr;
const xImpl_G1Logic& xOP = op == LogicalOp::AND ? xAND
: op == LogicalOp::OR ? xOR
: op == LogicalOp::XOR ? xXOR
: op == LogicalOp::NOR ? xOR
: *bad;
s32 fixedInput, fixedOutput, identityInput;
bool hasFixed = true;
switch (op)
{
case LogicalOp::AND:
fixedInput = 0;
fixedOutput = 0;
identityInput = -1;
break;
case LogicalOp::OR:
fixedInput = -1;
fixedOutput = -1;
identityInput = 0;
break;
case LogicalOp::XOR:
hasFixed = false;
identityInput = 0;
break;
case LogicalOp::NOR:
fixedInput = -1;
fixedOutput = 0;
identityInput = 0;
break;
default:
pxAssert(0);
}
const s32 cval = static_cast<s32>(g_psxConstRegs[creg]);
if (hasFixed && cval == fixedInput)
{
xMOV(xRegister32(EEREC_D), fixedOutput);
}
else
{
if (regv >= 0)
xMOV(xRegister32(EEREC_D), xRegister32(regv));
else
xMOV(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[vreg]]);
if (cval != identityInput)
xOP(xRegister32(EEREC_D), cval);
if (op == LogicalOp::NOR)
xNOT(xRegister32(EEREC_D));
}
}
static void rpsxLogicalOp(LogicalOp op, int info)
{
pxAssert(!(info & PROCESS_EE_XMM));
xImpl_G1Logic* bad = nullptr;
const xImpl_G1Logic& xOP = op == LogicalOp::AND ? xAND
: op == LogicalOp::OR ? xOR
: op == LogicalOp::XOR ? xXOR
: op == LogicalOp::NOR ? xOR
: *bad;
pxAssert(&xOP != bad);
// swap because it's commutative and Rd might be Rt
u32 rs = _Rs_, rt = _Rt_;
int regs = (info & PROCESS_EE_S) ? EEREC_S : -1, regt = (info & PROCESS_EE_T) ? EEREC_T : -1;
if (_Rd_ == _Rt_)
{
std::swap(rs, rt);
std::swap(regs, regt);
}
if (op == LogicalOp::XOR && rs == rt)
{
xXOR(xRegister32(EEREC_D), xRegister32(EEREC_D));
}
else
{
if (regs >= 0)
xMOV(xRegister32(EEREC_D), xRegister32(regs));
else
xMOV(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[rs]]);
if (regt >= 0)
xOP(xRegister32(EEREC_D), xRegister32(regt));
else
xOP(xRegister32(EEREC_D), ptr32[&psxRegs.GPR.r[rt]]);
if (op == LogicalOp::NOR)
xNOT(xRegister32(EEREC_D));
}
}
static void rpsxAND_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] & g_psxConstRegs[_Rt_];
}
static void rpsxAND_consts(int info)
{
rpsxLogicalOp_constv(LogicalOp::AND, info, _Rs_, _Rt_, (info & PROCESS_EE_T) ? EEREC_T : -1);
}
static void rpsxAND_constt(int info)
{
rpsxLogicalOp_constv(LogicalOp::AND, info, _Rt_, _Rs_, (info & PROCESS_EE_S) ? EEREC_S : -1);
}
static void rpsxAND_(int info)
{
rpsxLogicalOp(LogicalOp::AND, info);
}
PSXRECOMPILE_CONSTCODE0(AND, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT);
static void rpsxOR_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] | g_psxConstRegs[_Rt_];
}
static void rpsxOR_consts(int info)
{
rpsxLogicalOp_constv(LogicalOp::OR, info, _Rs_, _Rt_, (info & PROCESS_EE_T) ? EEREC_T : -1);
}
static void rpsxOR_constt(int info)
{
rpsxLogicalOp_constv(LogicalOp::OR, info, _Rt_, _Rs_, (info & PROCESS_EE_S) ? EEREC_S : -1);
}
static void rpsxOR_(int info)
{
rpsxLogicalOp(LogicalOp::OR, info);
}
PSXRECOMPILE_CONSTCODE0(OR, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT);
//// XOR
static void rpsxXOR_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] ^ g_psxConstRegs[_Rt_];
}
static void rpsxXOR_consts(int info)
{
rpsxLogicalOp_constv(LogicalOp::XOR, info, _Rs_, _Rt_, (info & PROCESS_EE_T) ? EEREC_T : -1);
}
static void rpsxXOR_constt(int info)
{
rpsxLogicalOp_constv(LogicalOp::XOR, info, _Rt_, _Rs_, (info & PROCESS_EE_S) ? EEREC_S : -1);
}
static void rpsxXOR_(int info)
{
rpsxLogicalOp(LogicalOp::XOR, info);
}
PSXRECOMPILE_CONSTCODE0(XOR, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT);
//// NOR
static void rpsxNOR_const()
{
g_psxConstRegs[_Rd_] = ~(g_psxConstRegs[_Rs_] | g_psxConstRegs[_Rt_]);
}
static void rpsxNOR_consts(int info)
{
rpsxLogicalOp_constv(LogicalOp::NOR, info, _Rs_, _Rt_, (info & PROCESS_EE_T) ? EEREC_T : -1);
}
static void rpsxNOR_constt(int info)
{
rpsxLogicalOp_constv(LogicalOp::NOR, info, _Rt_, _Rs_, (info & PROCESS_EE_S) ? EEREC_S : -1);
}
static void rpsxNOR_(int info)
{
rpsxLogicalOp(LogicalOp::NOR, info);
}
PSXRECOMPILE_CONSTCODE0(NOR, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT);
//// SLT
static void rpsxSLT_const()
{
g_psxConstRegs[_Rd_] = *(int*)&g_psxConstRegs[_Rs_] < *(int*)&g_psxConstRegs[_Rt_];
}
static void rpsxSLTs_const(int info, int sign, int st)
{
const s32 cval = g_psxConstRegs[st ? _Rt_ : _Rs_];
const xImpl_Set& SET = st ? (sign ? xSETL : xSETB) : (sign ? xSETG : xSETA);
const xRegister32 dreg((_Rd_ == (st ? _Rs_ : _Rt_)) ? _allocX86reg(X86TYPE_TEMP, 0, 0) : EEREC_D);
const int regs = st ? ((info & PROCESS_EE_S) ? EEREC_S : -1) : ((info & PROCESS_EE_T) ? EEREC_T : -1);
xXOR(dreg, dreg);
if (regs >= 0)
xCMP(xRegister32(regs), cval);
else
xCMP(ptr32[&psxRegs.GPR.r[st ? _Rs_ : _Rt_]], cval);
SET(xRegister8(dreg));
if (dreg.GetId() != EEREC_D)
{
std::swap(x86regs[dreg.GetId()], x86regs[EEREC_D]);
_freeX86reg(EEREC_D);
}
}
static void rpsxSLTs_(int info, int sign)
{
const xImpl_Set& SET = sign ? xSETL : xSETB;
// need to keep Rs/Rt around.
const xRegister32 dreg((_Rd_ == _Rt_ || _Rd_ == _Rs_) ? _allocX86reg(X86TYPE_TEMP, 0, 0) : EEREC_D);
// force Rs into a register, may as well cache it since we're loading anyway.
const int regs = (info & PROCESS_EE_S) ? EEREC_S : _allocX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
xXOR(dreg, dreg);
if (info & PROCESS_EE_T)
xCMP(xRegister32(regs), xRegister32(EEREC_T));
else
xCMP(xRegister32(regs), ptr32[&psxRegs.GPR.r[_Rt_]]);
SET(xRegister8(dreg));
if (dreg.GetId() != EEREC_D)
{
std::swap(x86regs[dreg.GetId()], x86regs[EEREC_D]);
_freeX86reg(EEREC_D);
}
}
static void rpsxSLT_consts(int info)
{
rpsxSLTs_const(info, 1, 0);
}
static void rpsxSLT_constt(int info)
{
rpsxSLTs_const(info, 1, 1);
}
static void rpsxSLT_(int info)
{
rpsxSLTs_(info, 1);
}
PSXRECOMPILE_CONSTCODE0(SLT, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT | XMMINFO_NORENAME);
//// SLTU
static void rpsxSLTU_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rs_] < g_psxConstRegs[_Rt_];
}
static void rpsxSLTU_consts(int info)
{
rpsxSLTs_const(info, 0, 0);
}
static void rpsxSLTU_constt(int info)
{
rpsxSLTs_const(info, 0, 1);
}
static void rpsxSLTU_(int info)
{
rpsxSLTs_(info, 0);
}
PSXRECOMPILE_CONSTCODE0(SLTU, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT | XMMINFO_NORENAME);
//// MULT
static void rpsxMULT_const()
{
_deletePSXtoX86reg(PSX_HI, DELETE_REG_FREE_NO_WRITEBACK);
_deletePSXtoX86reg(PSX_LO, DELETE_REG_FREE_NO_WRITEBACK);
u64 res = (s64)((s64) * (int*)&g_psxConstRegs[_Rs_] * (s64) * (int*)&g_psxConstRegs[_Rt_]);
xMOV(ptr32[&psxRegs.GPR.n.hi], (u32)((res >> 32) & 0xffffffff));
xMOV(ptr32[&psxRegs.GPR.n.lo], (u32)(res & 0xffffffff));
}
static void rpsxWritebackHILO(int info)
{
if (EEINST_LIVETEST(PSX_LO))
{
if (info & PROCESS_EE_LO)
xMOV(xRegister32(EEREC_LO), eax);
else
xMOV(ptr32[&psxRegs.GPR.n.lo], eax);
}
if (EEINST_LIVETEST(PSX_HI))
{
if (info & PROCESS_EE_HI)
xMOV(xRegister32(EEREC_HI), edx);
else
xMOV(ptr32[&psxRegs.GPR.n.hi], edx);
}
}
static void rpsxMULTsuperconst(int info, int sreg, int imm, int sign)
{
// Lo/Hi = Rs * Rt (signed)
xMOV(eax, imm);
const int regs = rpsxAllocRegIfUsed(sreg, MODE_READ);
if (sign)
{
if (regs >= 0)
xMUL(xRegister32(regs));
else
xMUL(ptr32[&psxRegs.GPR.r[sreg]]);
}
else
{
if (regs >= 0)
xUMUL(xRegister32(regs));
else
xUMUL(ptr32[&psxRegs.GPR.r[sreg]]);
}
rpsxWritebackHILO(info);
}
static void rpsxMULTsuper(int info, int sign)
{
// Lo/Hi = Rs * Rt (signed)
_psxMoveGPRtoR(eax, _Rs_);
const int regt = rpsxAllocRegIfUsed(_Rt_, MODE_READ);
if (sign)
{
if (regt >= 0)
xMUL(xRegister32(regt));
else
xMUL(ptr32[&psxRegs.GPR.r[_Rt_]]);
}
else
{
if (regt >= 0)
xUMUL(xRegister32(regt));
else
xUMUL(ptr32[&psxRegs.GPR.r[_Rt_]]);
}
rpsxWritebackHILO(info);
}
static void rpsxMULT_consts(int info)
{
rpsxMULTsuperconst(info, _Rt_, g_psxConstRegs[_Rs_], 1);
}
static void rpsxMULT_constt(int info)
{
rpsxMULTsuperconst(info, _Rs_, g_psxConstRegs[_Rt_], 1);
}
static void rpsxMULT_(int info)
{
rpsxMULTsuper(info, 1);
}
PSXRECOMPILE_CONSTCODE3_PENALTY(MULT, 1, psxInstCycles_Mult);
//// MULTU
static void rpsxMULTU_const()
{
_deletePSXtoX86reg(PSX_HI, DELETE_REG_FREE_NO_WRITEBACK);
_deletePSXtoX86reg(PSX_LO, DELETE_REG_FREE_NO_WRITEBACK);
u64 res = (u64)((u64)g_psxConstRegs[_Rs_] * (u64)g_psxConstRegs[_Rt_]);
xMOV(ptr32[&psxRegs.GPR.n.hi], (u32)((res >> 32) & 0xffffffff));
xMOV(ptr32[&psxRegs.GPR.n.lo], (u32)(res & 0xffffffff));
}
static void rpsxMULTU_consts(int info)
{
rpsxMULTsuperconst(info, _Rt_, g_psxConstRegs[_Rs_], 0);
}
static void rpsxMULTU_constt(int info)
{
rpsxMULTsuperconst(info, _Rs_, g_psxConstRegs[_Rt_], 0);
}
static void rpsxMULTU_(int info)
{
rpsxMULTsuper(info, 0);
}
PSXRECOMPILE_CONSTCODE3_PENALTY(MULTU, 1, psxInstCycles_Mult);
//// DIV
static void rpsxDIV_const()
{
_deletePSXtoX86reg(PSX_HI, DELETE_REG_FREE_NO_WRITEBACK);
_deletePSXtoX86reg(PSX_LO, DELETE_REG_FREE_NO_WRITEBACK);
u32 lo, hi;
/*
* Normally, when 0x80000000(-2147483648), the signed minimum value, is divided by 0xFFFFFFFF(-1), the
* operation will result in overflow. However, in this instruction an overflow exception does not occur and the
* result will be as follows:
* Quotient: 0x80000000 (-2147483648), and remainder: 0x00000000 (0)
*/
// Of course x86 cpu does overflow !
if (g_psxConstRegs[_Rs_] == 0x80000000u && g_psxConstRegs[_Rt_] == 0xFFFFFFFFu)
{
xMOV(ptr32[&psxRegs.GPR.n.hi], 0);
xMOV(ptr32[&psxRegs.GPR.n.lo], 0x80000000);
return;
}
if (g_psxConstRegs[_Rt_] != 0)
{
lo = *(int*)&g_psxConstRegs[_Rs_] / *(int*)&g_psxConstRegs[_Rt_];
hi = *(int*)&g_psxConstRegs[_Rs_] % *(int*)&g_psxConstRegs[_Rt_];
xMOV(ptr32[&psxRegs.GPR.n.hi], hi);
xMOV(ptr32[&psxRegs.GPR.n.lo], lo);
}
else
{
xMOV(ptr32[&psxRegs.GPR.n.hi], g_psxConstRegs[_Rs_]);
if (g_psxConstRegs[_Rs_] & 0x80000000u)
{
xMOV(ptr32[&psxRegs.GPR.n.lo], 0x1);
}
else
{
xMOV(ptr32[&psxRegs.GPR.n.lo], 0xFFFFFFFFu);
}
}
}
static void rpsxDIVsuper(int info, int sign, int process = 0)
{
// Lo/Hi = Rs / Rt (signed)
if (process & PROCESS_CONSTT)
xMOV(ecx, g_psxConstRegs[_Rt_]);
else if (info & PROCESS_EE_T)
xMOV(ecx, xRegister32(EEREC_T));
else
xMOV(ecx, ptr32[&psxRegs.GPR.r[_Rt_]]);
if (process & PROCESS_CONSTS)
xMOV(eax, g_psxConstRegs[_Rs_]);
else if (info & PROCESS_EE_S)
xMOV(eax, xRegister32(EEREC_S));
else
xMOV(eax, ptr32[&psxRegs.GPR.r[_Rs_]]);
u8* end1;
if (sign) //test for overflow (x86 will just throw an exception)
{
xCMP(eax, 0x80000000);
u8* cont1 = JNE8(0);
xCMP(ecx, 0xffffffff);
u8* cont2 = JNE8(0);
//overflow case:
xXOR(edx, edx); //EAX remains 0x80000000
end1 = JMP8(0);
x86SetJ8(cont1);
x86SetJ8(cont2);
}
xCMP(ecx, 0);
u8* cont3 = JNE8(0);
//divide by zero
xMOV(edx, eax);
if (sign) //set EAX to (EAX < 0)?1:-1
{
xSAR(eax, 31); //(EAX < 0)?-1:0
xSHL(eax, 1); //(EAX < 0)?-2:0
xNOT(eax); //(EAX < 0)?1:-1
}
else
xMOV(eax, 0xffffffff);
u8* end2 = JMP8(0);
// Normal division
x86SetJ8(cont3);
if (sign)
{
xCDQ();
xDIV(ecx);
}
else
{
xXOR(edx, edx);
xUDIV(ecx);
}
if (sign)
x86SetJ8(end1);
x86SetJ8(end2);
rpsxWritebackHILO(info);
}
static void rpsxDIV_consts(int info)
{
rpsxDIVsuper(info, 1, PROCESS_CONSTS);
}
static void rpsxDIV_constt(int info)
{
rpsxDIVsuper(info, 1, PROCESS_CONSTT);
}
static void rpsxDIV_(int info)
{
rpsxDIVsuper(info, 1);
}
PSXRECOMPILE_CONSTCODE3_PENALTY(DIV, 1, psxInstCycles_Div);
//// DIVU
void rpsxDIVU_const()
{
u32 lo, hi;
_deletePSXtoX86reg(PSX_HI, DELETE_REG_FREE_NO_WRITEBACK);
_deletePSXtoX86reg(PSX_LO, DELETE_REG_FREE_NO_WRITEBACK);
if (g_psxConstRegs[_Rt_] != 0)
{
lo = g_psxConstRegs[_Rs_] / g_psxConstRegs[_Rt_];
hi = g_psxConstRegs[_Rs_] % g_psxConstRegs[_Rt_];
xMOV(ptr32[&psxRegs.GPR.n.hi], hi);
xMOV(ptr32[&psxRegs.GPR.n.lo], lo);
}
else
{
xMOV(ptr32[&psxRegs.GPR.n.hi], g_psxConstRegs[_Rs_]);
xMOV(ptr32[&psxRegs.GPR.n.lo], 0xFFFFFFFFu);
}
}
void rpsxDIVU_consts(int info)
{
rpsxDIVsuper(info, 0, PROCESS_CONSTS);
}
void rpsxDIVU_constt(int info)
{
rpsxDIVsuper(info, 0, PROCESS_CONSTT);
}
void rpsxDIVU_(int info)
{
rpsxDIVsuper(info, 0);
}
PSXRECOMPILE_CONSTCODE3_PENALTY(DIVU, 1, psxInstCycles_Div);
// TLB loadstore functions
static u8* rpsxGetConstantAddressOperand(bool store)
{
#if 0
if (!PSX_IS_CONST1(_Rs_))
return nullptr;
const u32 addr = g_psxConstRegs[_Rs_];
return store ? iopVirtMemW<u8>(addr) : const_cast<u8*>(iopVirtMemR<u8>(addr));
#else
return nullptr;
#endif
}
static void rpsxCalcAddressOperand()
{
// if it's a const register, just flush it, since we'll need to do that
// when we call the load/store function anyway
int rs;
if (PSX_IS_CONST1(_Rs_))
rs = _allocX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
else
rs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
_freeX86reg(arg1regd);
if (rs >= 0)
xMOV(arg1regd, xRegister32(rs));
else
xMOV(arg1regd, ptr32[&psxRegs.GPR.r[_Rs_]]);
if (_Imm_)
xADD(arg1regd, _Imm_);
}
static void rpsxCalcStoreOperand()
{
int rt;
if (PSX_IS_CONST1(_Rt_))
rt = _allocX86reg(X86TYPE_PSX, _Rt_, MODE_READ);
else
rt = _checkX86reg(X86TYPE_PSX, _Rt_, MODE_READ);
_freeX86reg(arg2regd);
if (rt >= 0)
xMOV(arg2regd, xRegister32(rt));
else
xMOV(arg2regd, ptr32[&psxRegs.GPR.r[_Rt_]]);
}
static void rpsxLoad(int size, bool sign)
{
rpsxCalcAddressOperand();
if (_Rt_ != 0)
{
PSX_DEL_CONST(_Rt_);
_deletePSXtoX86reg(_Rt_, DELETE_REG_FREE_NO_WRITEBACK);
}
_psxFlushCall(FLUSH_FULLVTLB);
xTEST(arg1regd, 0x10000000);
xForwardJZ8 is_ram_read;
switch (size)
{
case 8:
xFastCall((void*)iopMemRead8);
break;
case 16:
xFastCall((void*)iopMemRead16);
break;
case 32:
xFastCall((void*)iopMemRead32);
break;
jNO_DEFAULT
}
if (_Rt_ == 0)
{
// dummy read
is_ram_read.SetTarget();
return;
}
xForwardJump8 done;
is_ram_read.SetTarget();
// read from psM directly
xAND(arg1regd, 0x1fffff);
auto addr = xComplexAddress(rax, iopMem->Main, arg1reg);
switch (size)
{
case 8:
xMOVZX(eax, ptr8[addr]);
break;
case 16:
xMOVZX(eax, ptr16[addr]);
break;
case 32:
xMOV(eax, ptr32[addr]);
break;
jNO_DEFAULT
}
done.SetTarget();
const int rt = rpsxAllocRegIfUsed(_Rt_, MODE_WRITE);
const xRegister32 dreg((rt < 0) ? eax.GetId() : rt);
// sign/zero extend as needed
switch (size)
{
case 8:
sign ? xMOVSX(dreg, al) : xMOVZX(dreg, al);
break;
case 16:
sign ? xMOVSX(dreg, ax) : xMOVZX(dreg, ax);
break;
case 32:
xMOV(dreg, eax);
break;
jNO_DEFAULT
}
// if not caching, write back
if (rt < 0)
xMOV(ptr32[&psxRegs.GPR.r[_Rt_]], eax);
}
REC_FUNC(LWL);
REC_FUNC(LWR);
REC_FUNC(SWL);
REC_FUNC(SWR);
static void rpsxLB()
{
rpsxLoad(8, true);
}
static void rpsxLBU()
{
rpsxLoad(8, false);
}
static void rpsxLH()
{
rpsxLoad(16, true);
}
static void rpsxLHU()
{
rpsxLoad(16, false);
}
static void rpsxLW()
{
rpsxLoad(32, false);
}
static void rpsxSB()
{
rpsxCalcAddressOperand();
rpsxCalcStoreOperand();
_psxFlushCall(FLUSH_FULLVTLB);
xFastCall((void*)iopMemWrite8);
}
static void rpsxSH()
{
rpsxCalcAddressOperand();
rpsxCalcStoreOperand();
_psxFlushCall(FLUSH_FULLVTLB);
xFastCall((void*)iopMemWrite16);
}
static void rpsxSW()
{
u8* ptr = rpsxGetConstantAddressOperand(true);
if (ptr)
{
const int rt = _allocX86reg(X86TYPE_PSX, _Rt_, MODE_READ);
xMOV(ptr32[ptr], xRegister32(rt));
return;
}
rpsxCalcAddressOperand();
rpsxCalcStoreOperand();
_psxFlushCall(FLUSH_FULLVTLB);
xFastCall((void*)iopMemWrite32);
}
//// SLL
static void rpsxSLL_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] << _Sa_;
}
static void rpsxSLLs_(int info, int sa)
{
rpsxMoveTtoD(info);
if (sa != 0)
xSHL(xRegister32(EEREC_D), sa);
}
static void rpsxSLL_(int info)
{
rpsxSLLs_(info, _Sa_);
}
PSXRECOMPILE_CONSTCODE2(SLL, XMMINFO_WRITED | XMMINFO_READS);
//// SRL
static void rpsxSRL_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] >> _Sa_;
}
static void rpsxSRLs_(int info, int sa)
{
rpsxMoveTtoD(info);
if (sa != 0)
xSHR(xRegister32(EEREC_D), sa);
}
static void rpsxSRL_(int info)
{
rpsxSRLs_(info, _Sa_);
}
PSXRECOMPILE_CONSTCODE2(SRL, XMMINFO_WRITED | XMMINFO_READS);
//// SRA
static void rpsxSRA_const()
{
g_psxConstRegs[_Rd_] = *(int*)&g_psxConstRegs[_Rt_] >> _Sa_;
}
static void rpsxSRAs_(int info, int sa)
{
rpsxMoveTtoD(info);
if (sa != 0)
xSAR(xRegister32(EEREC_D), sa);
}
static void rpsxSRA_(int info)
{
rpsxSRAs_(info, _Sa_);
}
PSXRECOMPILE_CONSTCODE2(SRA, XMMINFO_WRITED | XMMINFO_READS);
//// SLLV
static void rpsxShiftV_constt(int info, const xImpl_Group2& shift)
{
pxAssert(_Rs_ != 0);
rpsxMoveSToECX(info);
xMOV(xRegister32(EEREC_D), g_psxConstRegs[_Rt_]);
shift(xRegister32(EEREC_D), cl);
}
static void rpsxShiftV(int info, const xImpl_Group2& shift)
{
pxAssert(_Rs_ != 0);
rpsxMoveSToECX(info);
rpsxMoveTtoD(info);
shift(xRegister32(EEREC_D), cl);
}
static void rpsxSLLV_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] << (g_psxConstRegs[_Rs_] & 0x1f);
}
static void rpsxSLLV_consts(int info)
{
rpsxSLLs_(info, g_psxConstRegs[_Rs_] & 0x1f);
}
static void rpsxSLLV_constt(int info)
{
rpsxShiftV_constt(info, xSHL);
}
static void rpsxSLLV_(int info)
{
rpsxShiftV(info, xSHL);
}
PSXRECOMPILE_CONSTCODE0(SLLV, XMMINFO_WRITED | XMMINFO_READS);
//// SRLV
static void rpsxSRLV_const()
{
g_psxConstRegs[_Rd_] = g_psxConstRegs[_Rt_] >> (g_psxConstRegs[_Rs_] & 0x1f);
}
static void rpsxSRLV_consts(int info)
{
rpsxSRLs_(info, g_psxConstRegs[_Rs_] & 0x1f);
}
static void rpsxSRLV_constt(int info)
{
rpsxShiftV_constt(info, xSHR);
}
static void rpsxSRLV_(int info)
{
rpsxShiftV(info, xSHR);
}
PSXRECOMPILE_CONSTCODE0(SRLV, XMMINFO_WRITED | XMMINFO_READS);
//// SRAV
static void rpsxSRAV_const()
{
g_psxConstRegs[_Rd_] = *(int*)&g_psxConstRegs[_Rt_] >> (g_psxConstRegs[_Rs_] & 0x1f);
}
static void rpsxSRAV_consts(int info)
{
rpsxSRAs_(info, g_psxConstRegs[_Rs_] & 0x1f);
}
static void rpsxSRAV_constt(int info)
{
rpsxShiftV_constt(info, xSAR);
}
static void rpsxSRAV_(int info)
{
rpsxShiftV(info, xSAR);
}
PSXRECOMPILE_CONSTCODE0(SRAV, XMMINFO_WRITED | XMMINFO_READS);
extern void rpsxSYSCALL();
extern void rpsxBREAK();
static void rpsxMFHI()
{
if (!_Rd_)
return;
rpsxCopyReg(_Rd_, PSX_HI);
}
static void rpsxMTHI()
{
rpsxCopyReg(PSX_HI, _Rs_);
}
static void rpsxMFLO()
{
if (!_Rd_)
return;
rpsxCopyReg(_Rd_, PSX_LO);
}
static void rpsxMTLO()
{
rpsxCopyReg(PSX_LO, _Rs_);
}
static void rpsxJ()
{
// j target
u32 newpc = _InstrucTarget_ * 4 + (psxpc & 0xf0000000);
psxRecompileNextInstruction(true, false);
psxSetBranchImm(newpc);
}
static void rpsxJAL()
{
u32 newpc = (_InstrucTarget_ << 2) + (psxpc & 0xf0000000);
_psxDeleteReg(31, DELETE_REG_FREE_NO_WRITEBACK);
PSX_SET_CONST(31);
g_psxConstRegs[31] = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(newpc);
}
static void rpsxJR()
{
psxSetBranchReg(_Rs_);
}
static void rpsxJALR()
{
const u32 newpc = psxpc + 4;
const bool swap = (_Rd_ == _Rs_) ? false : psxTrySwapDelaySlot(_Rs_, 0, _Rd_);
// jalr Rs
int wbreg = -1;
if (!swap)
{
wbreg = _allocX86reg(X86TYPE_PCWRITEBACK, 0, MODE_WRITE | MODE_CALLEESAVED);
_psxMoveGPRtoR(xRegister32(wbreg), _Rs_);
}
if (_Rd_)
{
_psxDeleteReg(_Rd_, DELETE_REG_FREE_NO_WRITEBACK);
PSX_SET_CONST(_Rd_);
g_psxConstRegs[_Rd_] = newpc;
}
if (!swap)
{
psxRecompileNextInstruction(true, false);
if (x86regs[wbreg].inuse && x86regs[wbreg].type == X86TYPE_PCWRITEBACK)
{
xMOV(ptr32[&psxRegs.pc], xRegister32(wbreg));
x86regs[wbreg].inuse = 0;
}
else
{
xMOV(eax, ptr32[&psxRegs.pcWriteback]);
xMOV(ptr32[&psxRegs.pc], eax);
}
}
else
{
if (PSX_IS_DIRTY_CONST(_Rs_) || _hasX86reg(X86TYPE_PSX, _Rs_, 0))
{
const int x86reg = _allocX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
xMOV(ptr32[&psxRegs.pc], xRegister32(x86reg));
}
else
{
_psxMoveGPRtoM((uptr)&psxRegs.pc, _Rs_);
}
}
psxSetBranchReg(0xffffffff);
}
//// BEQ
static u32* s_pbranchjmp;
static void rpsxSetBranchEQ(int process)
{
if (process & PROCESS_CONSTS)
{
const int regt = _checkX86reg(X86TYPE_PSX, _Rt_, MODE_READ);
if (regt >= 0)
xCMP(xRegister32(regt), g_psxConstRegs[_Rs_]);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rt_]], g_psxConstRegs[_Rs_]);
}
else if (process & PROCESS_CONSTT)
{
const int regs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
if (regs >= 0)
xCMP(xRegister32(regs), g_psxConstRegs[_Rt_]);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], g_psxConstRegs[_Rt_]);
}
else
{
// force S into register, since we need to load it, may as well cache.
const int regs = _allocX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
const int regt = _checkX86reg(X86TYPE_PSX, _Rt_, MODE_READ);
if (regt >= 0)
xCMP(xRegister32(regs), xRegister32(regt));
else
xCMP(xRegister32(regs), ptr32[&psxRegs.GPR.r[_Rt_]]);
}
s_pbranchjmp = JNE32(0);
}
static void rpsxBEQ_const()
{
u32 branchTo;
if (g_psxConstRegs[_Rs_] == g_psxConstRegs[_Rt_])
branchTo = ((s32)_Imm_ * 4) + psxpc;
else
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
}
static void rpsxBEQ_process(int process)
{
u32 branchTo = ((s32)_Imm_ * 4) + psxpc;
if (_Rs_ == _Rt_)
{
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
}
else
{
const bool swap = psxTrySwapDelaySlot(_Rs_, _Rt_, 0);
_psxFlushAllDirty();
rpsxSetBranchEQ(process);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
x86SetJ32A(s_pbranchjmp);
if (!swap)
{
// recopy the next inst
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
}
}
static void rpsxBEQ()
{
// prefer using the host register over an immediate, it'll be smaller code.
if (PSX_IS_CONST2(_Rs_, _Rt_))
rpsxBEQ_const();
else if (PSX_IS_CONST1(_Rs_) && _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ) < 0)
rpsxBEQ_process(PROCESS_CONSTS);
else if (PSX_IS_CONST1(_Rt_) && _checkX86reg(X86TYPE_PSX, _Rt_, MODE_READ) < 0)
rpsxBEQ_process(PROCESS_CONSTT);
else
rpsxBEQ_process(0);
}
//// BNE
static void rpsxBNE_const()
{
u32 branchTo;
if (g_psxConstRegs[_Rs_] != g_psxConstRegs[_Rt_])
branchTo = ((s32)_Imm_ * 4) + psxpc;
else
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
}
static void rpsxBNE_process(int process)
{
const u32 branchTo = ((s32)_Imm_ * 4) + psxpc;
if (_Rs_ == _Rt_)
{
psxRecompileNextInstruction(true, false);
psxSetBranchImm(psxpc);
return;
}
const bool swap = psxTrySwapDelaySlot(_Rs_, _Rt_, 0);
_psxFlushAllDirty();
rpsxSetBranchEQ(process);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
x86SetJ32A(s_pbranchjmp);
if (!swap)
{
// recopy the next inst
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
}
static void rpsxBNE()
{
if (PSX_IS_CONST2(_Rs_, _Rt_))
rpsxBNE_const();
else if (PSX_IS_CONST1(_Rs_) && _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ) < 0)
rpsxBNE_process(PROCESS_CONSTS);
else if (PSX_IS_CONST1(_Rt_) && _checkX86reg(X86TYPE_PSX, _Rt_, MODE_READ) < 0)
rpsxBNE_process(PROCESS_CONSTT);
else
rpsxBNE_process(0);
}
//// BLTZ
static void rpsxBLTZ()
{
// Branch if Rs < 0
u32 branchTo = (s32)_Imm_ * 4 + psxpc;
if (PSX_IS_CONST1(_Rs_))
{
if ((int)g_psxConstRegs[_Rs_] >= 0)
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
return;
}
const bool swap = psxTrySwapDelaySlot(_Rs_, 0, 0);
_psxFlushAllDirty();
const int regs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
if (regs >= 0)
xCMP(xRegister32(regs), 0);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], 0);
u32* pjmp = JL32(0);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
x86SetJ32A(pjmp);
if (!swap)
{
// recopy the next inst
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
}
//// BGEZ
static void rpsxBGEZ()
{
u32 branchTo = ((s32)_Imm_ * 4) + psxpc;
if (PSX_IS_CONST1(_Rs_))
{
if ((int)g_psxConstRegs[_Rs_] < 0)
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
return;
}
const bool swap = psxTrySwapDelaySlot(_Rs_, 0, 0);
_psxFlushAllDirty();
const int regs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
if (regs >= 0)
xCMP(xRegister32(regs), 0);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], 0);
u32* pjmp = JGE32(0);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
x86SetJ32A(pjmp);
if (!swap)
{
// recopy the next inst
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
}
//// BLTZAL
static void rpsxBLTZAL()
{
// Branch if Rs < 0
u32 branchTo = (s32)_Imm_ * 4 + psxpc;
_psxDeleteReg(31, DELETE_REG_FREE_NO_WRITEBACK);
PSX_SET_CONST(31);
g_psxConstRegs[31] = psxpc + 4;
if (PSX_IS_CONST1(_Rs_))
{
if ((int)g_psxConstRegs[_Rs_] >= 0)
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
return;
}
const bool swap = psxTrySwapDelaySlot(_Rs_, 0, 0);
_psxFlushAllDirty();
const int regs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
if (regs >= 0)
xCMP(xRegister32(regs), 0);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], 0);
u32* pjmp = JL32(0);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
x86SetJ32A(pjmp);
if (!swap)
{
// recopy the next inst
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
}
//// BGEZAL
static void rpsxBGEZAL()
{
u32 branchTo = ((s32)_Imm_ * 4) + psxpc;
_psxDeleteReg(31, DELETE_REG_FREE_NO_WRITEBACK);
PSX_SET_CONST(31);
g_psxConstRegs[31] = psxpc + 4;
if (PSX_IS_CONST1(_Rs_))
{
if ((int)g_psxConstRegs[_Rs_] < 0)
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
return;
}
const bool swap = psxTrySwapDelaySlot(_Rs_, 0, 0);
_psxFlushAllDirty();
const int regs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
if (regs >= 0)
xCMP(xRegister32(regs), 0);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], 0);
u32* pjmp = JGE32(0);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
x86SetJ32A(pjmp);
if (!swap)
{
// recopy the next inst
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
}
//// BLEZ
static void rpsxBLEZ()
{
// Branch if Rs <= 0
u32 branchTo = (s32)_Imm_ * 4 + psxpc;
if (PSX_IS_CONST1(_Rs_))
{
if ((int)g_psxConstRegs[_Rs_] > 0)
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
return;
}
const bool swap = psxTrySwapDelaySlot(_Rs_, 0, 0);
_psxFlushAllDirty();
const int regs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
if (regs >= 0)
xCMP(xRegister32(regs), 0);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], 0);
u32* pjmp = JLE32(0);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
x86SetJ32A(pjmp);
if (!swap)
{
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
}
//// BGTZ
static void rpsxBGTZ()
{
// Branch if Rs > 0
u32 branchTo = (s32)_Imm_ * 4 + psxpc;
_psxFlushAllDirty();
if (PSX_IS_CONST1(_Rs_))
{
if ((int)g_psxConstRegs[_Rs_] <= 0)
branchTo = psxpc + 4;
psxRecompileNextInstruction(true, false);
psxSetBranchImm(branchTo);
return;
}
const bool swap = psxTrySwapDelaySlot(_Rs_, 0, 0);
_psxFlushAllDirty();
const int regs = _checkX86reg(X86TYPE_PSX, _Rs_, MODE_READ);
if (regs >= 0)
xCMP(xRegister32(regs), 0);
else
xCMP(ptr32[&psxRegs.GPR.r[_Rs_]], 0);
u32* pjmp = JG32(0);
if (!swap)
{
psxSaveBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(psxpc);
x86SetJ32A(pjmp);
if (!swap)
{
psxpc -= 4;
psxLoadBranchState();
psxRecompileNextInstruction(true, false);
}
psxSetBranchImm(branchTo);
}
static void rpsxMFC0()
{
// Rt = Cop0->Rd
if (!_Rt_)
return;
const int rt = _allocX86reg(X86TYPE_PSX, _Rt_, MODE_WRITE);
xMOV(xRegister32(rt), ptr32[&psxRegs.CP0.r[_Rd_]]);
}
static void rpsxCFC0()
{
// Rt = Cop0->Rd
if (!_Rt_)
return;
const int rt = _allocX86reg(X86TYPE_PSX, _Rt_, MODE_WRITE);
xMOV(xRegister32(rt), ptr32[&psxRegs.CP0.r[_Rd_]]);
}
static void rpsxMTC0()
{
// Cop0->Rd = Rt
if (PSX_IS_CONST1(_Rt_))
{
xMOV(ptr32[&psxRegs.CP0.r[_Rd_]], g_psxConstRegs[_Rt_]);
}
else
{
const int rt = _allocX86reg(X86TYPE_PSX, _Rt_, MODE_READ);
xMOV(ptr32[&psxRegs.CP0.r[_Rd_]], xRegister32(rt));
}
}
static void rpsxCTC0()
{
// Cop0->Rd = Rt
rpsxMTC0();
}
static void rpsxRFE()
{
xMOV(eax, ptr32[&psxRegs.CP0.n.Status]);
xMOV(ecx, eax);
xAND(eax, 0xfffffff0);
xAND(ecx, 0x3c);
xSHR(ecx, 2);
xOR(eax, ecx);
xMOV(ptr32[&psxRegs.CP0.n.Status], eax);
// Test the IOP's INTC status, so that any pending ints get raised.
_psxFlushCall(0);
xFastCall((void*)(uptr)&iopTestIntc);
}
//// COP2
REC_GTE_FUNC(RTPS);
REC_GTE_FUNC(NCLIP);
REC_GTE_FUNC(OP);
REC_GTE_FUNC(DPCS);
REC_GTE_FUNC(INTPL);
REC_GTE_FUNC(MVMVA);
REC_GTE_FUNC(NCDS);
REC_GTE_FUNC(CDP);
REC_GTE_FUNC(NCDT);
REC_GTE_FUNC(NCCS);
REC_GTE_FUNC(CC);
REC_GTE_FUNC(NCS);
REC_GTE_FUNC(NCT);
REC_GTE_FUNC(SQR);
REC_GTE_FUNC(DCPL);
REC_GTE_FUNC(DPCT);
REC_GTE_FUNC(AVSZ3);
REC_GTE_FUNC(AVSZ4);
REC_GTE_FUNC(RTPT);
REC_GTE_FUNC(GPF);
REC_GTE_FUNC(GPL);
REC_GTE_FUNC(NCCT);
REC_GTE_FUNC(MFC2);
REC_GTE_FUNC(CFC2);
REC_GTE_FUNC(MTC2);
REC_GTE_FUNC(CTC2);
REC_GTE_FUNC(LWC2);
REC_GTE_FUNC(SWC2);
// R3000A tables
extern void (*rpsxBSC[64])();
extern void (*rpsxSPC[64])();
extern void (*rpsxREG[32])();
extern void (*rpsxCP0[32])();
extern void (*rpsxCP2[64])();
extern void (*rpsxCP2BSC[32])();
static void rpsxSPECIAL() { rpsxSPC[_Funct_](); }
static void rpsxREGIMM() { rpsxREG[_Rt_](); }
static void rpsxCOP0() { rpsxCP0[_Rs_](); }
static void rpsxCOP2() { rpsxCP2[_Funct_](); }
static void rpsxBASIC() { rpsxCP2BSC[_Rs_](); }
static void rpsxNULL()
{
Console.WriteLn("psxUNK: %8.8x", psxRegs.code);
}
// clang-format off
void (*rpsxBSC[64])() = {
rpsxSPECIAL, rpsxREGIMM, rpsxJ , rpsxJAL , rpsxBEQ , rpsxBNE , rpsxBLEZ, rpsxBGTZ,
rpsxADDI , rpsxADDIU , rpsxSLTI, rpsxSLTIU, rpsxANDI, rpsxORI , rpsxXORI, rpsxLUI ,
rpsxCOP0 , rpsxNULL , rpsxCOP2, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxLB , rpsxLH , rpsxLWL , rpsxLW , rpsxLBU , rpsxLHU , rpsxLWR , rpsxNULL,
rpsxSB , rpsxSH , rpsxSWL , rpsxSW , rpsxNULL, rpsxNULL, rpsxSWR , rpsxNULL,
rpsxNULL , rpsxNULL , rgteLWC2, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxNULL , rpsxNULL , rgteSWC2, rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
};
void (*rpsxSPC[64])() = {
rpsxSLL , rpsxNULL, rpsxSRL , rpsxSRA , rpsxSLLV , rpsxNULL , rpsxSRLV, rpsxSRAV,
rpsxJR , rpsxJALR, rpsxNULL, rpsxNULL, rpsxSYSCALL, rpsxBREAK, rpsxNULL, rpsxNULL,
rpsxMFHI, rpsxMTHI, rpsxMFLO, rpsxMTLO, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL,
rpsxMULT, rpsxMULTU, rpsxDIV, rpsxDIVU, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL,
rpsxADD , rpsxADDU, rpsxSUB , rpsxSUBU, rpsxAND , rpsxOR , rpsxXOR , rpsxNOR ,
rpsxNULL, rpsxNULL, rpsxSLT , rpsxSLTU, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL,
rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL,
rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL,
};
void (*rpsxREG[32])() = {
rpsxBLTZ , rpsxBGEZ , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxBLTZAL, rpsxBGEZAL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
};
void (*rpsxCP0[32])() = {
rpsxMFC0, rpsxNULL, rpsxCFC0, rpsxNULL, rpsxMTC0, rpsxNULL, rpsxCTC0, rpsxNULL,
rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxRFE , rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
};
void (*rpsxCP2[64])() = {
rpsxBASIC, rgteRTPS , rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL , rgteNCLIP, rpsxNULL, // 00
rpsxNULL , rpsxNULL , rpsxNULL , rpsxNULL, rgteOP , rpsxNULL , rpsxNULL , rpsxNULL, // 08
rgteDPCS , rgteINTPL, rgteMVMVA, rgteNCDS, rgteCDP , rpsxNULL , rgteNCDT , rpsxNULL, // 10
rpsxNULL , rpsxNULL , rpsxNULL , rgteNCCS, rgteCC , rpsxNULL , rgteNCS , rpsxNULL, // 18
rgteNCT , rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL , rpsxNULL , rpsxNULL, // 20
rgteSQR , rgteDCPL , rgteDPCT , rpsxNULL, rpsxNULL, rgteAVSZ3, rgteAVSZ4, rpsxNULL, // 28
rgteRTPT , rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, rpsxNULL , rpsxNULL , rpsxNULL, // 30
rpsxNULL , rpsxNULL , rpsxNULL , rpsxNULL, rpsxNULL, rgteGPF , rgteGPL , rgteNCCT, // 38
};
void (*rpsxCP2BSC[32])() = {
rgteMFC2, rpsxNULL, rgteCFC2, rpsxNULL, rgteMTC2, rpsxNULL, rgteCTC2, rpsxNULL,
rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL, rpsxNULL,
};
// clang-format on
////////////////////////////////////////////////
// Back-Prob Function Tables - Gathering Info //
////////////////////////////////////////////////
#define rpsxpropSetRead(reg) \
{ \
if (!(pinst->regs[reg] & EEINST_USED)) \
pinst->regs[reg] |= EEINST_LASTUSE; \
prev->regs[reg] |= EEINST_LIVE | EEINST_USED; \
pinst->regs[reg] |= EEINST_USED; \
_recFillRegister(*pinst, XMMTYPE_GPRREG, reg, 0); \
}
#define rpsxpropSetWrite(reg) \
{ \
prev->regs[reg] &= ~(EEINST_LIVE | EEINST_USED); \
if (!(pinst->regs[reg] & EEINST_USED)) \
pinst->regs[reg] |= EEINST_LASTUSE; \
pinst->regs[reg] |= EEINST_USED; \
_recFillRegister(*pinst, XMMTYPE_GPRREG, reg, 1); \
}
void rpsxpropBSC(EEINST* prev, EEINST* pinst);
void rpsxpropSPECIAL(EEINST* prev, EEINST* pinst);
void rpsxpropREGIMM(EEINST* prev, EEINST* pinst);
void rpsxpropCP0(EEINST* prev, EEINST* pinst);
void rpsxpropCP2(EEINST* prev, EEINST* pinst);
//SPECIAL, REGIMM, J , JAL , BEQ , BNE , BLEZ, BGTZ,
//ADDI , ADDIU , SLTI, SLTIU, ANDI, ORI , XORI, LUI ,
//COP0 , NULL , COP2, NULL , NULL, NULL, NULL, NULL,
//NULL , NULL , NULL, NULL , NULL, NULL, NULL, NULL,
//LB , LH , LWL , LW , LBU , LHU , LWR , NULL,
//SB , SH , SWL , SW , NULL, NULL, SWR , NULL,
//NULL , NULL , NULL, NULL , NULL, NULL, NULL, NULL,
//NULL , NULL , NULL, NULL , NULL, NULL, NULL, NULL
void rpsxpropBSC(EEINST* prev, EEINST* pinst)
{
switch (psxRegs.code >> 26)
{
case 0:
rpsxpropSPECIAL(prev, pinst);
break;
case 1:
rpsxpropREGIMM(prev, pinst);
break;
case 2: // j
break;
case 3: // jal
rpsxpropSetWrite(31);
break;
case 4: // beq
case 5: // bne
rpsxpropSetRead(_Rs_);
rpsxpropSetRead(_Rt_);
break;
case 6: // blez
case 7: // bgtz
rpsxpropSetRead(_Rs_);
break;
case 15: // lui
rpsxpropSetWrite(_Rt_);
break;
case 16:
rpsxpropCP0(prev, pinst);
break;
case 18:
rpsxpropCP2(prev, pinst);
break;
// stores
case 40:
case 41:
case 42:
case 43:
case 46:
rpsxpropSetRead(_Rt_);
rpsxpropSetRead(_Rs_);
break;
case 50: // LWC2
case 58: // SWC2
// Operation on COP2 registers/memory. GPRs are left untouched
break;
default:
rpsxpropSetWrite(_Rt_);
rpsxpropSetRead(_Rs_);
break;
}
}
//SLL , NULL, SRL , SRA , SLLV , NULL , SRLV, SRAV,
//JR , JALR, NULL, NULL, SYSCALL, BREAK, NULL, NULL,
//MFHI, MTHI, MFLO, MTLO, NULL , NULL , NULL, NULL,
//MULT, MULTU, DIV, DIVU, NULL , NULL , NULL, NULL,
//ADD , ADDU, SUB , SUBU, AND , OR , XOR , NOR ,
//NULL, NULL, SLT , SLTU, NULL , NULL , NULL, NULL,
//NULL, NULL, NULL, NULL, NULL , NULL , NULL, NULL,
//NULL, NULL, NULL, NULL, NULL , NULL , NULL, NULL,
void rpsxpropSPECIAL(EEINST* prev, EEINST* pinst)
{
switch (_Funct_)
{
case 0: // SLL
case 2: // SRL
case 3: // SRA
rpsxpropSetWrite(_Rd_);
rpsxpropSetRead(_Rt_);
break;
case 8: // JR
rpsxpropSetRead(_Rs_);
break;
case 9: // JALR
rpsxpropSetWrite(_Rd_);
rpsxpropSetRead(_Rs_);
break;
case 12: // syscall
case 13: // break
_recClearInst(prev);
prev->info = 0;
break;
case 15: // sync
break;
case 16: // mfhi
rpsxpropSetWrite(_Rd_);
rpsxpropSetRead(PSX_HI);
break;
case 17: // mthi
rpsxpropSetWrite(PSX_HI);
rpsxpropSetRead(_Rs_);
break;
case 18: // mflo
rpsxpropSetWrite(_Rd_);
rpsxpropSetRead(PSX_LO);
break;
case 19: // mtlo
rpsxpropSetWrite(PSX_LO);
rpsxpropSetRead(_Rs_);
break;
case 24: // mult
case 25: // multu
case 26: // div
case 27: // divu
rpsxpropSetWrite(PSX_LO);
rpsxpropSetWrite(PSX_HI);
rpsxpropSetRead(_Rs_);
rpsxpropSetRead(_Rt_);
break;
case 32: // add
case 33: // addu
case 34: // sub
case 35: // subu
rpsxpropSetWrite(_Rd_);
if (_Rs_)
rpsxpropSetRead(_Rs_);
if (_Rt_)
rpsxpropSetRead(_Rt_);
break;
default:
rpsxpropSetWrite(_Rd_);
rpsxpropSetRead(_Rs_);
rpsxpropSetRead(_Rt_);
break;
}
}
//BLTZ , BGEZ , NULL, NULL, NULL, NULL, NULL, NULL,
//NULL , NULL , NULL, NULL, NULL, NULL, NULL, NULL,
//BLTZAL, BGEZAL, NULL, NULL, NULL, NULL, NULL, NULL,
//NULL , NULL , NULL, NULL, NULL, NULL, NULL, NULL
void rpsxpropREGIMM(EEINST* prev, EEINST* pinst)
{
switch (_Rt_)
{
case 0: // bltz
case 1: // bgez
rpsxpropSetRead(_Rs_);
break;
case 16: // bltzal
case 17: // bgezal
// do not write 31
rpsxpropSetRead(_Rs_);
break;
jNO_DEFAULT
}
}
//MFC0, NULL, CFC0, NULL, MTC0, NULL, CTC0, NULL,
//NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
//RFE , NULL, NULL, NULL, NULL, NULL, NULL, NULL,
//NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
void rpsxpropCP0(EEINST* prev, EEINST* pinst)
{
switch (_Rs_)
{
case 0: // mfc0
case 2: // cfc0
rpsxpropSetWrite(_Rt_);
break;
case 4: // mtc0
case 6: // ctc0
rpsxpropSetRead(_Rt_);
break;
case 16: // rfe
break;
jNO_DEFAULT
}
}
// Basic table:
// gteMFC2, psxNULL, gteCFC2, psxNULL, gteMTC2, psxNULL, gteCTC2, psxNULL,
// psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
// psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
// psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL, psxNULL,
void rpsxpropCP2_basic(EEINST* prev, EEINST* pinst)
{
switch (_Rs_)
{
case 0: // mfc2
case 2: // cfc2
rpsxpropSetWrite(_Rt_);
break;
case 4: // mtc2
case 6: // ctc2
rpsxpropSetRead(_Rt_);
break;
default:
pxFail("iop invalid opcode in const propagation (rpsxpropCP2/BASIC)");
break;
}
}
// Main table:
// psxBASIC, gteRTPS , psxNULL , psxNULL, psxNULL, psxNULL , gteNCLIP, psxNULL, // 00
// psxNULL , psxNULL , psxNULL , psxNULL, gteOP , psxNULL , psxNULL , psxNULL, // 08
// gteDPCS , gteINTPL, gteMVMVA, gteNCDS, gteCDP , psxNULL , gteNCDT , psxNULL, // 10
// psxNULL , psxNULL , psxNULL , gteNCCS, gteCC , psxNULL , gteNCS , psxNULL, // 18
// gteNCT , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, // 20
// gteSQR , gteDCPL , gteDPCT , psxNULL, psxNULL, gteAVSZ3, gteAVSZ4, psxNULL, // 28
// gteRTPT , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, // 30
// psxNULL , psxNULL , psxNULL , psxNULL, psxNULL, gteGPF , gteGPL , gteNCCT, // 38
void rpsxpropCP2(EEINST* prev, EEINST* pinst)
{
switch (_Funct_)
{
case 0: // Basic opcode
rpsxpropCP2_basic(prev, pinst);
break;
default:
// COP2 operation are likely done with internal COP2 registers
// No impact on GPR
break;
}
}