318 lines
10 KiB
C
318 lines
10 KiB
C
#ifndef _I_PGXP_H_
|
|
#define _I_PGXP_H_
|
|
|
|
// Microsoft Windows uses a different x86_64 calling convention than everyone
|
|
// else. I have not yet bothered implementing it here because;
|
|
//
|
|
// 1. Nobody cares about a Windows 64-bit build of PCSXR since that would mean
|
|
// dropping popular closed source 32-bit plugins like Pete's OpenGL2
|
|
// 2. The Windows convention is annoying (only 4 register params, caller must
|
|
// reserve stack space for register spilling) and would require more
|
|
// extensive code changes (e.g. the PGXP_DBG_OP_E() macro would have to
|
|
// handle cases where the arg needs to go on the stack instead of in a
|
|
// register, and cleanup afterwards).
|
|
//
|
|
// See https://msdn.microsoft.com/en-us/library/ms235286.aspx
|
|
// and https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
|
|
//
|
|
// MrLavender
|
|
#ifdef _MSC_VER
|
|
#error PGXP dynarec support is not implemented for Windows 64-bit
|
|
#endif
|
|
|
|
/////////////////////////////////////////////
|
|
// PGXP wrapper functions
|
|
/////////////////////////////////////////////
|
|
|
|
void pgxpRecNULL() {}
|
|
|
|
// Debug wrappers for x86_64 (because eOp will be last)
|
|
#ifdef PGXP_CPU_DEBUG
|
|
static void PGXP64_psxTraceOp(u32 code, u32 eOp) {
|
|
PGXP_psxTraceOp(eOp, code);
|
|
}
|
|
static void PGXP64_psxTraceOp1(u32 code, u32 op1, u32 eOp) {
|
|
PGXP_psxTraceOp1(eOp, code, op1);
|
|
}
|
|
static void PGXP64_psxTraceOp2(u32 code, u32 op1, u32 op2, u32 eOp) {
|
|
PGXP_psxTraceOp2(eOp, code, op1, op2);
|
|
}
|
|
static void PGXP64_psxTraceOp3(u32 code, u32 op1, u32 op2, u32 op3, u32 eOp) {
|
|
PGXP_psxTraceOp3(eOp, code, op1, op2, op3);
|
|
}
|
|
static void PGXP64_psxTraceOp4(u32 code, u32 op1, u32 op2, u32 op3, u32 op4, u32 eOp) {
|
|
PGXP_psxTraceOp4(eOp, code, op1, op2, op3, op4);
|
|
}
|
|
#endif
|
|
|
|
// Choose between debug and direct function
|
|
#ifdef PGXP_CPU_DEBUG
|
|
#define PGXP_REC_FUNC_OP(pu, op, nReg) PGXP64_psxTraceOp##nReg
|
|
#define PGXP_DBG_OP_E(op, arg) MOV32ItoR(arg, DBG_E_##op);
|
|
#else
|
|
#define PGXP_REC_FUNC_OP(pu, op, nReg) PGXP_##pu##_##op
|
|
#define PGXP_DBG_OP_E(op, arg)
|
|
#endif
|
|
|
|
#define PGXP_REC_FUNC_PASS(pu, op) \
|
|
static void pgxpRec##op() { \
|
|
rec##op();\
|
|
}
|
|
|
|
#define PGXP_REC_FUNC(pu, op) \
|
|
static void pgxpRec##op() { \
|
|
MOV32ItoR(X86ARG1, psxRegs.code); \
|
|
PGXP_DBG_OP_E(op, X86ARG2) \
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, )); \
|
|
rec##op();\
|
|
}
|
|
|
|
#define PGXP_REC_FUNC_1(pu, op, reg1) \
|
|
static void pgxpRec##op() { \
|
|
reg1;\
|
|
MOV32ItoR(X86ARG1, psxRegs.code); \
|
|
POP64R(X86ARG2); \
|
|
PGXP_DBG_OP_E(op, X86ARG3) \
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, 1)); \
|
|
rec##op();\
|
|
}
|
|
|
|
//#define PGXP_REC_FUNC_2_2(pu, op, test, nReg, reg1, reg2, reg3, reg4) \
|
|
//static void pgxpRec##op() { \
|
|
// if(test) { rec##op(); return; }\
|
|
// reg1;\
|
|
// reg2;\
|
|
// rec##op();\
|
|
// reg3;\
|
|
// reg4;\
|
|
// PUSH32I(psxRegs.code); \
|
|
// PGXP_DBG_OP_E(op) \
|
|
// CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, nReg)); \
|
|
// resp += (4 * nReg) + 4; \
|
|
//}
|
|
|
|
#define PGXP_REC_FUNC_2(pu, op, reg1, reg2) \
|
|
static void pgxpRec##op() { \
|
|
reg1;\
|
|
reg2;\
|
|
MOV32ItoR(X86ARG1, psxRegs.code); \
|
|
POP64R(X86ARG2); \
|
|
POP64R(X86ARG3); \
|
|
PGXP_DBG_OP_E(op, X86ARG4) \
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, 2)); \
|
|
rec##op();\
|
|
}
|
|
|
|
static u32 gTempAddr = 0;
|
|
#define PGXP_REC_FUNC_ADDR_1(pu, op, reg1) \
|
|
static void pgxpRec##op() \
|
|
{ \
|
|
if (IsConst(_Rs_)) \
|
|
{ \
|
|
MOV32ItoR(EAX, iRegs[_Rs_].k + _Imm_); \
|
|
} \
|
|
else\
|
|
{\
|
|
MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[_Rs_]);\
|
|
if (_Imm_)\
|
|
{\
|
|
ADD32ItoR(EAX, _Imm_);\
|
|
}\
|
|
}\
|
|
MOV32RtoM((uptr)&gTempAddr, EAX);\
|
|
rec##op();\
|
|
reg1;\
|
|
MOV32ItoR(X86ARG1, psxRegs.code); \
|
|
POP64R(X86ARG2); \
|
|
MOV32MtoR(X86ARG3, (uptr)&gTempAddr); \
|
|
PGXP_DBG_OP_E(op, X86ARG4) \
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, 2)); \
|
|
}
|
|
|
|
|
|
#define CPU_REG_NC(idx) MOV32MtoR(EAX,(uptr)&psxRegs.GPR.r[idx])
|
|
|
|
#define CPU_REG(idx) \
|
|
if (IsConst(idx)) \
|
|
MOV32ItoR(EAX, iRegs[idx].k); \
|
|
else\
|
|
MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[idx]);
|
|
|
|
#define CP0_REG(idx) MOV32MtoR(EAX,(uptr)&psxRegs.CP0.r[idx])
|
|
#define GTE_DATA_REG(idx) MOV32MtoR(EAX,(uptr)&psxRegs.CP2D.r[idx])
|
|
#define GTE_CTRL_REG(idx) MOV32MtoR(EAX,(uptr)&psxRegs.CP2C.r[idx])
|
|
|
|
static u32 gTempInstr = 0;
|
|
static u32 gTempReg1 = 0;
|
|
static u32 gTempReg2 = 0;
|
|
#define PGXP_REC_FUNC_R1_1(pu, op, test, reg1, reg2) \
|
|
static void pgxpRec##op() \
|
|
{ \
|
|
if(test) { rec##op(); return; }\
|
|
reg1;\
|
|
MOV32RtoM((uptr)&gTempReg1, EAX);\
|
|
rec##op();\
|
|
reg2;\
|
|
MOV32ItoR(X86ARG1, psxRegs.code); \
|
|
POP64R(X86ARG2); \
|
|
MOV32MtoR(X86ARG3, (uptr)&gTempReg1); \
|
|
PGXP_DBG_OP_E(op, X86ARG4) \
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, 2)); \
|
|
}
|
|
|
|
#define PGXP_REC_FUNC_R2_1(pu, op, test, reg1, reg2, reg3) \
|
|
static void pgxpRec##op() \
|
|
{ \
|
|
if(test) { rec##op(); return; }\
|
|
reg1;\
|
|
MOV32RtoM((uptr)&gTempReg1, EAX);\
|
|
reg2;\
|
|
MOV32RtoM((uptr)&gTempReg2, EAX);\
|
|
rec##op();\
|
|
reg3;\
|
|
MOV32ItoR(X86ARG1, psxRegs.code); \
|
|
POP64R(X86ARG2); \
|
|
MOV32MtoR(X86ARG3, (uptr)&gTempReg2); \
|
|
MOV32MtoR(X86ARG4, (uptr)&gTempReg1); \
|
|
PGXP_DBG_OP_E(op, X86ARG5) \
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, 3)); \
|
|
}
|
|
|
|
#define PGXP_REC_FUNC_R2_2(pu, op, test, reg1, reg2, reg3, reg4) \
|
|
static void pgxpRec##op() \
|
|
{ \
|
|
if(test) { rec##op(); return; }\
|
|
reg1;\
|
|
MOV32RtoM((uptr)&gTempReg1, EAX);\
|
|
reg2;\
|
|
MOV32RtoM((uptr)&gTempReg2, EAX);\
|
|
rec##op();\
|
|
reg3;\
|
|
reg4;\
|
|
MOV32ItoR(X86ARG1, psxRegs.code); \
|
|
POP64R(X86ARG2); \
|
|
POP64R(X86ARG3); \
|
|
MOV32MtoR(X86ARG4, (uptr)&gTempReg2); \
|
|
MOV32MtoR(X86ARG5, (uptr)&gTempReg1); \
|
|
PGXP_DBG_OP_E(op, X86ARG6) \
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, 4)); \
|
|
}
|
|
|
|
//#define PGXP_REC_FUNC_R1i_1(pu, op, test, reg1, reg2) \
|
|
//static void pgxpRec##op() \
|
|
//{ \
|
|
// if(test) { rec##op(); return; }\
|
|
// if (IsConst(reg1)) \
|
|
// MOV32ItoR(EAX, iRegs[reg1].k); \
|
|
// else\
|
|
// MOV32MtoR(EAX, (uptr)&psxRegs.GPR.r[reg1]);\
|
|
// MOV32RtoM((uptr)&gTempReg, EAX);\
|
|
// rec##op();\
|
|
// PUSH64M((uptr)&gTempReg);\
|
|
// reg2;\
|
|
// PUSH32I(psxRegs.code); \
|
|
// CALLFunc((uptr)PGXP_REC_FUNC_OP(pu, op, 2)); \
|
|
// resp += 12; \
|
|
//}
|
|
|
|
// Push m32
|
|
#define PUSH32M(from) MOV32MtoR(EAX, from); PUSH64R(RAX);
|
|
|
|
static void iPushReg(int reg)
|
|
{
|
|
if (IsConst(reg)) {
|
|
PUSH32I(iRegs[reg].k);
|
|
}
|
|
else {
|
|
PUSH32M((uptr)&psxRegs.GPR.r[reg]);
|
|
}
|
|
}
|
|
|
|
// Rt = Rs op imm
|
|
PGXP_REC_FUNC_R1_1(CPU, ADDI, !_Rt_, CPU_REG(_Rs_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CPU, ADDIU, !_Rt_, CPU_REG(_Rs_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CPU, ANDI, !_Rt_, CPU_REG(_Rs_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CPU, ORI, !_Rt_, CPU_REG(_Rs_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CPU, XORI, !_Rt_, CPU_REG(_Rs_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CPU, SLTI, !_Rt_, CPU_REG(_Rs_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CPU, SLTIU, !_Rt_, CPU_REG(_Rs_), iPushReg(_Rt_))
|
|
|
|
// Rt = imm
|
|
//PGXP_REC_FUNC_2_2(CPU, LUI, !_Rt_, 1, , , iPushReg(_Rt_), )
|
|
//This macro is harder to implement for x86_64, and only used once, so... :) MrL
|
|
static void pgxpRecLUI()
|
|
{
|
|
if (!_Rt_) { recLUI(); return; }
|
|
recLUI();
|
|
iPushReg(_Rt_);
|
|
MOV32ItoR(X86ARG1, psxRegs.code);
|
|
POP64R(X86ARG2);
|
|
PGXP_DBG_OP_E(LUI, X86ARG3)
|
|
CALLFunc((uptr)PGXP_REC_FUNC_OP(CPU, LUI, 1));
|
|
}
|
|
|
|
// Rd = Rs op Rt
|
|
PGXP_REC_FUNC_R2_1(CPU, ADD, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, ADDU, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, SUB, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, SUBU, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, AND, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, OR, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, XOR, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, NOR, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, SLT, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, SLTU, !_Rd_, CPU_REG(_Rt_), CPU_REG(_Rs_), iPushReg(_Rd_))
|
|
|
|
// Hi/Lo = Rs op Rt
|
|
PGXP_REC_FUNC_R2_2(CPU, MULT, 0, CPU_REG(_Rt_), CPU_REG(_Rs_), PUSH32M((uptr)&psxRegs.GPR.n.lo), PUSH32M((uptr)&psxRegs.GPR.n.hi))
|
|
PGXP_REC_FUNC_R2_2(CPU, MULTU, 0, CPU_REG(_Rt_), CPU_REG(_Rs_), PUSH32M((uptr)&psxRegs.GPR.n.lo), PUSH32M((uptr)&psxRegs.GPR.n.hi))
|
|
PGXP_REC_FUNC_R2_2(CPU, DIV, 0, CPU_REG(_Rt_), CPU_REG(_Rs_), PUSH32M((uptr)&psxRegs.GPR.n.lo), PUSH32M((uptr)&psxRegs.GPR.n.hi))
|
|
PGXP_REC_FUNC_R2_2(CPU, DIVU, 0, CPU_REG(_Rt_), CPU_REG(_Rs_), PUSH32M((uptr)&psxRegs.GPR.n.lo), PUSH32M((uptr)&psxRegs.GPR.n.hi))
|
|
|
|
PGXP_REC_FUNC_ADDR_1(CPU, SB, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, SH, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, SW, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, SWL, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, SWR, iPushReg(_Rt_))
|
|
|
|
PGXP_REC_FUNC_ADDR_1(CPU, LWL, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, LW, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, LWR, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, LH, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, LHU, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, LB, iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_ADDR_1(CPU, LBU, iPushReg(_Rt_))
|
|
|
|
//Rd = Rt op Sa
|
|
PGXP_REC_FUNC_R1_1(CPU, SLL, !_Rd_, CPU_REG(_Rt_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R1_1(CPU, SRL, !_Rd_, CPU_REG(_Rt_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R1_1(CPU, SRA, !_Rd_, CPU_REG(_Rt_), iPushReg(_Rd_))
|
|
|
|
// Rd = Rt op Rs
|
|
PGXP_REC_FUNC_R2_1(CPU, SLLV, !_Rd_, CPU_REG(_Rs_), CPU_REG(_Rt_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, SRLV, !_Rd_, CPU_REG(_Rs_), CPU_REG(_Rt_), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R2_1(CPU, SRAV, !_Rd_, CPU_REG(_Rs_), CPU_REG(_Rt_), iPushReg(_Rd_))
|
|
|
|
PGXP_REC_FUNC_R1_1(CPU, MFHI, !_Rd_, CPU_REG_NC(33), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R1_1(CPU, MTHI, 0, CPU_REG(_Rd_), PUSH32M((uptr)&psxRegs.GPR.n.hi))
|
|
PGXP_REC_FUNC_R1_1(CPU, MFLO, !_Rd_, CPU_REG_NC(32), iPushReg(_Rd_))
|
|
PGXP_REC_FUNC_R1_1(CPU, MTLO, 0, CPU_REG(_Rd_), PUSH32M((uptr)&psxRegs.GPR.n.lo))
|
|
|
|
// COP2 (GTE)
|
|
PGXP_REC_FUNC_R1_1(GTE, MFC2, !_Rt_, GTE_DATA_REG(_Rd_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(GTE, CFC2, !_Rt_, GTE_CTRL_REG(_Rd_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(GTE, MTC2, 0, CPU_REG(_Rt_), PUSH32M((uptr)&psxRegs.CP2D.r[_Rd_]))
|
|
PGXP_REC_FUNC_R1_1(GTE, CTC2, 0, CPU_REG(_Rt_), PUSH32M((uptr)&psxRegs.CP2C.r[_Rd_]))
|
|
|
|
PGXP_REC_FUNC_ADDR_1(GTE, LWC2, PUSH32M((uptr)&psxRegs.CP2D.r[_Rt_]))
|
|
PGXP_REC_FUNC_ADDR_1(GTE, SWC2, PUSH32M((uptr)&psxRegs.CP2D.r[_Rt_]))
|
|
|
|
// COP0
|
|
PGXP_REC_FUNC_R1_1(CP0, MFC0, !_Rd_, CP0_REG(_Rd_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CP0, CFC0, !_Rd_, CP0_REG(_Rd_), iPushReg(_Rt_))
|
|
PGXP_REC_FUNC_R1_1(CP0, MTC0, !_Rt_, CPU_REG(_Rt_), PUSH32M((uptr)&psxRegs.CP0.r[_Rd_]))
|
|
PGXP_REC_FUNC_R1_1(CP0, CTC0, !_Rt_, CPU_REG(_Rt_), PUSH32M((uptr)&psxRegs.CP0.r[_Rd_]))
|
|
PGXP_REC_FUNC(CP0, RFE)
|
|
|
|
#endif//_I_PGXP_H_
|