pcsxr/libpcsxcore/ix86_64/iPGXP-64.h

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_