mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-01 20:41:19 +12:00
785 lines
No EOL
21 KiB
C++
785 lines
No EOL
21 KiB
C++
#pragma once
|
|
|
|
using IMLRegID = uint16; // 16 bit ID
|
|
using IMLPhysReg = sint32; // arbitrary value that is up to the architecture backend, usually this will be the register index. A value of -1 is reserved and means not assigned
|
|
|
|
// format of IMLReg:
|
|
// 0-15 (16 bit) IMLRegID
|
|
// 19-23 (5 bit) Offset In elements, for SIMD registers
|
|
// 24-27 (4 bit) IMLRegFormat RegFormat
|
|
// 28-31 (4 bit) IMLRegFormat BaseFormat
|
|
|
|
enum class IMLRegFormat : uint8
|
|
{
|
|
INVALID_FORMAT,
|
|
I64,
|
|
I32,
|
|
I16,
|
|
I8,
|
|
// I1 ?
|
|
F64,
|
|
F32,
|
|
TYPE_COUNT,
|
|
};
|
|
|
|
class IMLReg
|
|
{
|
|
public:
|
|
IMLReg()
|
|
{
|
|
m_raw = 0; // 0 is invalid
|
|
}
|
|
|
|
IMLReg(IMLRegFormat baseRegFormat, IMLRegFormat regFormat, uint8 viewOffset, IMLRegID regId)
|
|
{
|
|
m_raw = 0;
|
|
m_raw |= ((uint8)baseRegFormat << 28);
|
|
m_raw |= ((uint8)regFormat << 24);
|
|
m_raw |= (uint32)regId;
|
|
}
|
|
|
|
IMLReg(IMLReg&& baseReg, IMLRegFormat viewFormat, uint8 viewOffset, IMLRegID regId)
|
|
{
|
|
DEBUG_BREAK;
|
|
//m_raw = 0;
|
|
//m_raw |= ((uint8)baseRegFormat << 28);
|
|
//m_raw |= ((uint8)viewFormat << 24);
|
|
//m_raw |= (uint32)regId;
|
|
}
|
|
|
|
IMLReg(const IMLReg& other) : m_raw(other.m_raw) {}
|
|
|
|
IMLRegFormat GetBaseFormat() const
|
|
{
|
|
return (IMLRegFormat)((m_raw >> 28) & 0xF);
|
|
}
|
|
|
|
IMLRegFormat GetRegFormat() const
|
|
{
|
|
return (IMLRegFormat)((m_raw >> 24) & 0xF);
|
|
}
|
|
|
|
IMLRegID GetRegID() const
|
|
{
|
|
cemu_assert_debug(GetBaseFormat() != IMLRegFormat::INVALID_FORMAT);
|
|
cemu_assert_debug(GetRegFormat() != IMLRegFormat::INVALID_FORMAT);
|
|
return (IMLRegID)(m_raw & 0xFFFF);
|
|
}
|
|
|
|
void SetRegID(IMLRegID regId)
|
|
{
|
|
cemu_assert_debug(regId <= 0xFFFF);
|
|
m_raw &= ~0xFFFF;
|
|
m_raw |= (uint32)regId;
|
|
}
|
|
|
|
bool IsInvalid() const
|
|
{
|
|
return GetBaseFormat() == IMLRegFormat::INVALID_FORMAT;
|
|
}
|
|
|
|
bool IsValid() const
|
|
{
|
|
return GetBaseFormat() != IMLRegFormat::INVALID_FORMAT;
|
|
}
|
|
|
|
bool IsValidAndSameRegID(IMLRegID regId) const
|
|
{
|
|
return IsValid() && GetRegID() == regId;
|
|
}
|
|
|
|
// compare all fields
|
|
bool operator==(const IMLReg& other) const
|
|
{
|
|
return m_raw == other.m_raw;
|
|
}
|
|
|
|
private:
|
|
uint32 m_raw;
|
|
};
|
|
|
|
static const IMLReg IMLREG_INVALID(IMLRegFormat::INVALID_FORMAT, IMLRegFormat::INVALID_FORMAT, 0, 0);
|
|
static const IMLRegID IMLRegID_INVALID(0xFFFF);
|
|
|
|
using IMLName = uint32;
|
|
|
|
enum
|
|
{
|
|
PPCREC_IML_OP_ASSIGN, // '=' operator
|
|
PPCREC_IML_OP_ENDIAN_SWAP, // '=' operator with 32bit endian swap
|
|
PPCREC_IML_OP_MULTIPLY_SIGNED, // '*' operator (signed multiply)
|
|
PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, // unsigned 64bit multiply, store only high 32bit-word of result
|
|
PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, // signed 64bit multiply, store only high 32bit-word of result
|
|
PPCREC_IML_OP_DIVIDE_SIGNED, // '/' operator (signed divide)
|
|
PPCREC_IML_OP_DIVIDE_UNSIGNED, // '/' operator (unsigned divide)
|
|
|
|
// binary operation
|
|
PPCREC_IML_OP_OR, // '|' operator
|
|
PPCREC_IML_OP_AND, // '&' operator
|
|
PPCREC_IML_OP_XOR, // '^' operator
|
|
PPCREC_IML_OP_LEFT_ROTATE, // left rotate operator
|
|
PPCREC_IML_OP_LEFT_SHIFT, // shift left operator
|
|
PPCREC_IML_OP_RIGHT_SHIFT_U, // right shift operator (unsigned)
|
|
PPCREC_IML_OP_RIGHT_SHIFT_S, // right shift operator (signed)
|
|
// ppc
|
|
PPCREC_IML_OP_SLW, // SLW (shift based on register by up to 63 bits)
|
|
PPCREC_IML_OP_SRW, // SRW (shift based on register by up to 63 bits)
|
|
PPCREC_IML_OP_CNTLZW,
|
|
// FPU
|
|
PPCREC_IML_OP_FPR_ADD_BOTTOM,
|
|
PPCREC_IML_OP_FPR_ADD_PAIR,
|
|
PPCREC_IML_OP_FPR_SUB_PAIR,
|
|
PPCREC_IML_OP_FPR_SUB_BOTTOM,
|
|
PPCREC_IML_OP_FPR_MULTIPLY_BOTTOM,
|
|
PPCREC_IML_OP_FPR_MULTIPLY_PAIR,
|
|
PPCREC_IML_OP_FPR_DIVIDE_BOTTOM,
|
|
PPCREC_IML_OP_FPR_DIVIDE_PAIR,
|
|
PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM_AND_TOP,
|
|
PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM_AND_TOP,
|
|
PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_BOTTOM,
|
|
PPCREC_IML_OP_FPR_COPY_BOTTOM_TO_TOP, // leave bottom of destination untouched
|
|
PPCREC_IML_OP_FPR_COPY_TOP_TO_TOP, // leave bottom of destination untouched
|
|
PPCREC_IML_OP_FPR_COPY_TOP_TO_BOTTOM, // leave top of destination untouched
|
|
PPCREC_IML_OP_FPR_COPY_BOTTOM_AND_TOP_SWAPPED,
|
|
PPCREC_IML_OP_FPR_EXPAND_BOTTOM32_TO_BOTTOM64_AND_TOP64, // expand bottom f32 to f64 in bottom and top half
|
|
PPCREC_IML_OP_FPR_FCMPO_BOTTOM, // deprecated
|
|
PPCREC_IML_OP_FPR_FCMPU_BOTTOM, // deprecated
|
|
PPCREC_IML_OP_FPR_FCMPU_TOP, // deprecated
|
|
PPCREC_IML_OP_FPR_NEGATE_BOTTOM,
|
|
PPCREC_IML_OP_FPR_NEGATE_PAIR,
|
|
PPCREC_IML_OP_FPR_ABS_BOTTOM, // abs(fp0)
|
|
PPCREC_IML_OP_FPR_ABS_PAIR,
|
|
PPCREC_IML_OP_FPR_FRES_PAIR, // 1.0/fp approx (Espresso accuracy)
|
|
PPCREC_IML_OP_FPR_FRSQRTE_PAIR, // 1.0/sqrt(fp) approx (Espresso accuracy)
|
|
PPCREC_IML_OP_FPR_NEGATIVE_ABS_BOTTOM, // -abs(fp0)
|
|
PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM, // round 64bit double to 64bit double with 32bit float precision (in bottom half of xmm register)
|
|
PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_PAIR, // round two 64bit doubles to 64bit double with 32bit float precision
|
|
PPCREC_IML_OP_FPR_BOTTOM_RECIPROCAL_SQRT,
|
|
PPCREC_IML_OP_FPR_BOTTOM_FCTIWZ,
|
|
PPCREC_IML_OP_FPR_SELECT_BOTTOM, // selectively copy bottom value from operand B or C based on value in operand A
|
|
PPCREC_IML_OP_FPR_SELECT_PAIR, // selectively copy top/bottom from operand B or C based on value in top/bottom of operand A
|
|
// PS
|
|
PPCREC_IML_OP_FPR_SUM0,
|
|
PPCREC_IML_OP_FPR_SUM1,
|
|
|
|
|
|
// R_R_R only
|
|
|
|
// R_R_S32 only
|
|
|
|
// R_R_R + R_R_S32
|
|
PPCREC_IML_OP_ADD, // also R_R_R_CARRY
|
|
PPCREC_IML_OP_SUB,
|
|
|
|
// R_R only
|
|
PPCREC_IML_OP_NOT,
|
|
PPCREC_IML_OP_NEG,
|
|
PPCREC_IML_OP_ASSIGN_S16_TO_S32,
|
|
PPCREC_IML_OP_ASSIGN_S8_TO_S32,
|
|
|
|
// R_R_R_carry
|
|
PPCREC_IML_OP_ADD_WITH_CARRY, // similar to ADD but also adds carry bit (0 or 1)
|
|
|
|
// X86 extension
|
|
PPCREC_IML_OP_X86_CMP, // R_R and R_S32
|
|
|
|
PPCREC_IML_OP_INVALID
|
|
};
|
|
|
|
#define PPCREC_IML_OP_FPR_COPY_PAIR (PPCREC_IML_OP_ASSIGN)
|
|
|
|
enum
|
|
{
|
|
PPCREC_IML_MACRO_B_TO_REG, // branch to PPC address in register (used for BCCTR, BCLR)
|
|
|
|
PPCREC_IML_MACRO_BL, // call to different function (can be within same function)
|
|
PPCREC_IML_MACRO_B_FAR, // branch to different function
|
|
PPCREC_IML_MACRO_COUNT_CYCLES, // decrease current remaining thread cycles by a certain amount
|
|
PPCREC_IML_MACRO_HLE, // HLE function call
|
|
PPCREC_IML_MACRO_LEAVE, // leaves recompiler and switches to interpeter
|
|
// debugging
|
|
PPCREC_IML_MACRO_DEBUGBREAK, // throws a debugbreak
|
|
};
|
|
|
|
enum class IMLCondition : uint8
|
|
{
|
|
EQ,
|
|
NEQ,
|
|
SIGNED_GT,
|
|
SIGNED_LT,
|
|
UNSIGNED_GT,
|
|
UNSIGNED_LT,
|
|
|
|
// floating point conditions
|
|
UNORDERED_GT, // a > b, false if either is NaN
|
|
UNORDERED_LT, // a < b, false if either is NaN
|
|
UNORDERED_EQ, // a == b, false if either is NaN
|
|
UNORDERED_U, // unordered (true if either operand is NaN)
|
|
|
|
ORDERED_GT,
|
|
ORDERED_LT,
|
|
ORDERED_EQ,
|
|
ORDERED_U
|
|
};
|
|
|
|
enum
|
|
{
|
|
PPCREC_IML_TYPE_NONE,
|
|
PPCREC_IML_TYPE_NO_OP, // no-op instruction
|
|
PPCREC_IML_TYPE_R_R, // r* = (op) *r (can also be r* (op) *r)
|
|
PPCREC_IML_TYPE_R_R_R, // r* = r* (op) r*
|
|
PPCREC_IML_TYPE_R_R_R_CARRY, // r* = r* (op) r* (reads and/or updates carry)
|
|
PPCREC_IML_TYPE_R_R_S32, // r* = r* (op) s32*
|
|
PPCREC_IML_TYPE_R_R_S32_CARRY, // r* = r* (op) s32* (reads and/or updates carry)
|
|
PPCREC_IML_TYPE_LOAD, // r* = [r*+s32*]
|
|
PPCREC_IML_TYPE_LOAD_INDEXED, // r* = [r*+r*]
|
|
PPCREC_IML_TYPE_STORE, // [r*+s32*] = r*
|
|
PPCREC_IML_TYPE_STORE_INDEXED, // [r*+r*] = r*
|
|
PPCREC_IML_TYPE_R_NAME, // r* = name
|
|
PPCREC_IML_TYPE_NAME_R, // name* = r*
|
|
PPCREC_IML_TYPE_R_S32, // r* (op) imm
|
|
PPCREC_IML_TYPE_MACRO,
|
|
PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK, // jumps only if remaining thread cycles < 0
|
|
|
|
// conditions and branches
|
|
PPCREC_IML_TYPE_COMPARE, // r* = r* CMP[cond] r*
|
|
PPCREC_IML_TYPE_COMPARE_S32, // r* = r* CMP[cond] imm
|
|
PPCREC_IML_TYPE_JUMP, // jump always
|
|
PPCREC_IML_TYPE_CONDITIONAL_JUMP, // jump conditionally based on boolean value in register
|
|
|
|
// atomic
|
|
PPCREC_IML_TYPE_ATOMIC_CMP_STORE,
|
|
|
|
// function call
|
|
PPCREC_IML_TYPE_CALL_IMM, // call to fixed immediate address
|
|
|
|
// FPR
|
|
PPCREC_IML_TYPE_FPR_LOAD, // r* = (bitdepth) [r*+s32*] (single or paired single mode)
|
|
PPCREC_IML_TYPE_FPR_LOAD_INDEXED, // r* = (bitdepth) [r*+r*] (single or paired single mode)
|
|
PPCREC_IML_TYPE_FPR_STORE, // (bitdepth) [r*+s32*] = r* (single or paired single mode)
|
|
PPCREC_IML_TYPE_FPR_STORE_INDEXED, // (bitdepth) [r*+r*] = r* (single or paired single mode)
|
|
PPCREC_IML_TYPE_FPR_R_R,
|
|
PPCREC_IML_TYPE_FPR_R_R_R,
|
|
PPCREC_IML_TYPE_FPR_R_R_R_R,
|
|
PPCREC_IML_TYPE_FPR_R,
|
|
|
|
PPCREC_IML_TYPE_FPR_COMPARE, // r* = r* CMP[cond] r*
|
|
|
|
// X86 specific
|
|
PPCREC_IML_TYPE_X86_EFLAGS_JCC,
|
|
};
|
|
|
|
enum // IMLName
|
|
{
|
|
PPCREC_NAME_NONE,
|
|
PPCREC_NAME_TEMPORARY = 1000,
|
|
PPCREC_NAME_R0 = 2000,
|
|
PPCREC_NAME_SPR0 = 3000,
|
|
PPCREC_NAME_FPR0 = 4000,
|
|
PPCREC_NAME_TEMPORARY_FPR0 = 5000, // 0 to 7
|
|
PPCREC_NAME_XER_CA = 6000, // carry bit from XER
|
|
PPCREC_NAME_XER_OV = 6001, // overflow bit from XER
|
|
PPCREC_NAME_XER_SO = 6002, // summary overflow bit from XER
|
|
PPCREC_NAME_CR = 7000, // CR register bits (31 to 0)
|
|
PPCREC_NAME_CR_LAST = PPCREC_NAME_CR+31,
|
|
PPCREC_NAME_CPU_MEMRES_EA = 8000,
|
|
PPCREC_NAME_CPU_MEMRES_VAL = 8001
|
|
};
|
|
|
|
#define PPC_REC_INVALID_REGISTER 0xFF // deprecated. Use IMLREG_INVALID instead
|
|
|
|
enum
|
|
{
|
|
// fpr load
|
|
PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0,
|
|
PPCREC_FPR_LD_MODE_SINGLE_INTO_PS0_PS1,
|
|
PPCREC_FPR_LD_MODE_DOUBLE_INTO_PS0,
|
|
PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0,
|
|
PPCREC_FPR_LD_MODE_PSQ_GENERIC_PS0_PS1,
|
|
PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0,
|
|
PPCREC_FPR_LD_MODE_PSQ_FLOAT_PS0_PS1,
|
|
PPCREC_FPR_LD_MODE_PSQ_S16_PS0,
|
|
PPCREC_FPR_LD_MODE_PSQ_S16_PS0_PS1,
|
|
PPCREC_FPR_LD_MODE_PSQ_U16_PS0,
|
|
PPCREC_FPR_LD_MODE_PSQ_U16_PS0_PS1,
|
|
PPCREC_FPR_LD_MODE_PSQ_S8_PS0,
|
|
PPCREC_FPR_LD_MODE_PSQ_S8_PS0_PS1,
|
|
PPCREC_FPR_LD_MODE_PSQ_U8_PS0,
|
|
PPCREC_FPR_LD_MODE_PSQ_U8_PS0_PS1,
|
|
// fpr store
|
|
PPCREC_FPR_ST_MODE_SINGLE_FROM_PS0, // store 1 single precision float from ps0
|
|
PPCREC_FPR_ST_MODE_DOUBLE_FROM_PS0, // store 1 double precision float from ps0
|
|
|
|
PPCREC_FPR_ST_MODE_UI32_FROM_PS0, // store raw low-32bit of PS0
|
|
|
|
PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0_PS1,
|
|
PPCREC_FPR_ST_MODE_PSQ_GENERIC_PS0,
|
|
PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0_PS1,
|
|
PPCREC_FPR_ST_MODE_PSQ_FLOAT_PS0,
|
|
PPCREC_FPR_ST_MODE_PSQ_S8_PS0,
|
|
PPCREC_FPR_ST_MODE_PSQ_S8_PS0_PS1,
|
|
PPCREC_FPR_ST_MODE_PSQ_U8_PS0,
|
|
PPCREC_FPR_ST_MODE_PSQ_U8_PS0_PS1,
|
|
PPCREC_FPR_ST_MODE_PSQ_U16_PS0,
|
|
PPCREC_FPR_ST_MODE_PSQ_U16_PS0_PS1,
|
|
PPCREC_FPR_ST_MODE_PSQ_S16_PS0,
|
|
PPCREC_FPR_ST_MODE_PSQ_S16_PS0_PS1,
|
|
};
|
|
|
|
struct IMLUsedRegisters
|
|
{
|
|
IMLUsedRegisters() {};
|
|
|
|
bool IsWrittenByRegId(IMLRegID regId) const
|
|
{
|
|
if (writtenGPR1.IsValid() && writtenGPR1.GetRegID() == regId)
|
|
return true;
|
|
if (writtenGPR2.IsValid() && writtenGPR2.GetRegID() == regId)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool IsBaseGPRWritten(IMLReg imlReg) const
|
|
{
|
|
cemu_assert_debug(imlReg.IsValid());
|
|
auto regId = imlReg.GetRegID();
|
|
return IsWrittenByRegId(regId);
|
|
}
|
|
|
|
template<typename Fn>
|
|
void ForEachWrittenGPR(Fn F) const
|
|
{
|
|
if (writtenGPR1.IsValid())
|
|
F(writtenGPR1);
|
|
if (writtenGPR2.IsValid())
|
|
F(writtenGPR2);
|
|
}
|
|
|
|
template<typename Fn>
|
|
void ForEachReadGPR(Fn F) const
|
|
{
|
|
if (readGPR1.IsValid())
|
|
F(readGPR1);
|
|
if (readGPR2.IsValid())
|
|
F(readGPR2);
|
|
if (readGPR3.IsValid())
|
|
F(readGPR3);
|
|
if (readGPR4.IsValid())
|
|
F(readGPR4);
|
|
}
|
|
|
|
template<typename Fn>
|
|
void ForEachAccessedGPR(Fn F) const
|
|
{
|
|
// GPRs
|
|
if (readGPR1.IsValid())
|
|
F(readGPR1, false);
|
|
if (readGPR2.IsValid())
|
|
F(readGPR2, false);
|
|
if (readGPR3.IsValid())
|
|
F(readGPR3, false);
|
|
if (readGPR4.IsValid())
|
|
F(readGPR4, false);
|
|
if (writtenGPR1.IsValid())
|
|
F(writtenGPR1, true);
|
|
if (writtenGPR2.IsValid())
|
|
F(writtenGPR2, true);
|
|
}
|
|
|
|
IMLReg readGPR1;
|
|
IMLReg readGPR2;
|
|
IMLReg readGPR3;
|
|
IMLReg readGPR4;
|
|
IMLReg writtenGPR1;
|
|
IMLReg writtenGPR2;
|
|
};
|
|
|
|
struct IMLInstruction
|
|
{
|
|
IMLInstruction() {}
|
|
IMLInstruction(const IMLInstruction& other)
|
|
{
|
|
memcpy(this, &other, sizeof(IMLInstruction));
|
|
}
|
|
|
|
uint8 type;
|
|
uint8 operation;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
uint8 _padding[7];
|
|
}padding;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
}op_r_r;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
IMLReg regB;
|
|
}op_r_r_r;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
IMLReg regB;
|
|
IMLReg regCarry;
|
|
}op_r_r_r_carry;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
sint32 immS32;
|
|
}op_r_r_s32;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
IMLReg regCarry;
|
|
sint32 immS32;
|
|
}op_r_r_s32_carry;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLName name;
|
|
}op_r_name; // alias op_name_r
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
sint32 immS32;
|
|
}op_r_immS32;
|
|
struct
|
|
{
|
|
uint32 param;
|
|
uint32 param2;
|
|
uint16 paramU16;
|
|
IMLReg paramReg;
|
|
}op_macro;
|
|
struct
|
|
{
|
|
IMLReg registerData;
|
|
IMLReg registerMem;
|
|
IMLReg registerMem2;
|
|
IMLReg registerGQR;
|
|
uint8 copyWidth;
|
|
struct
|
|
{
|
|
bool swapEndian : 1;
|
|
bool signExtend : 1;
|
|
bool notExpanded : 1; // for floats
|
|
}flags2;
|
|
uint8 mode; // transfer mode (copy width, ps0/ps1 behavior)
|
|
sint32 immS32;
|
|
}op_storeLoad;
|
|
struct
|
|
{
|
|
uintptr_t callAddress;
|
|
IMLReg regParam0;
|
|
IMLReg regParam1;
|
|
IMLReg regParam2;
|
|
IMLReg regReturn;
|
|
}op_call_imm;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
}op_fpr_r_r;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
IMLReg regB;
|
|
}op_fpr_r_r_r;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
IMLReg regA;
|
|
IMLReg regB;
|
|
IMLReg regC;
|
|
}op_fpr_r_r_r_r;
|
|
struct
|
|
{
|
|
IMLReg regR;
|
|
}op_fpr_r;
|
|
struct
|
|
{
|
|
IMLReg regR; // stores the boolean result of the comparison
|
|
IMLReg regA;
|
|
IMLReg regB;
|
|
IMLCondition cond;
|
|
}op_fpr_compare;
|
|
struct
|
|
{
|
|
IMLReg regR; // stores the boolean result of the comparison
|
|
IMLReg regA;
|
|
IMLReg regB;
|
|
IMLCondition cond;
|
|
}op_compare;
|
|
struct
|
|
{
|
|
IMLReg regR; // stores the boolean result of the comparison
|
|
IMLReg regA;
|
|
sint32 immS32;
|
|
IMLCondition cond;
|
|
}op_compare_s32;
|
|
struct
|
|
{
|
|
IMLReg registerBool;
|
|
bool mustBeTrue;
|
|
}op_conditional_jump;
|
|
struct
|
|
{
|
|
IMLReg regEA;
|
|
IMLReg regCompareValue;
|
|
IMLReg regWriteValue;
|
|
IMLReg regBoolOut;
|
|
}op_atomic_compare_store;
|
|
// conditional operations (emitted if supported by target platform)
|
|
struct
|
|
{
|
|
// r_s32
|
|
IMLReg regR;
|
|
sint32 immS32;
|
|
// condition
|
|
uint8 crRegisterIndex;
|
|
uint8 crBitIndex;
|
|
bool bitMustBeSet;
|
|
}op_conditional_r_s32;
|
|
// X86 specific
|
|
struct
|
|
{
|
|
IMLCondition cond;
|
|
bool invertedCondition;
|
|
}op_x86_eflags_jcc;
|
|
};
|
|
|
|
bool IsSuffixInstruction() const
|
|
{
|
|
if (type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_BL ||
|
|
type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_B_FAR ||
|
|
type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_B_TO_REG ||
|
|
type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_LEAVE ||
|
|
type == PPCREC_IML_TYPE_MACRO && operation == PPCREC_IML_MACRO_HLE ||
|
|
type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK ||
|
|
type == PPCREC_IML_TYPE_JUMP ||
|
|
type == PPCREC_IML_TYPE_CONDITIONAL_JUMP ||
|
|
type == PPCREC_IML_TYPE_X86_EFLAGS_JCC)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// instruction setters
|
|
void make_no_op()
|
|
{
|
|
type = PPCREC_IML_TYPE_NO_OP;
|
|
operation = 0;
|
|
}
|
|
|
|
void make_r_name(IMLReg regR, IMLName name)
|
|
{
|
|
cemu_assert_debug(regR.GetBaseFormat() == regR.GetRegFormat()); // for name load/store instructions the register must match the base format
|
|
type = PPCREC_IML_TYPE_R_NAME;
|
|
operation = PPCREC_IML_OP_ASSIGN;
|
|
op_r_name.regR = regR;
|
|
op_r_name.name = name;
|
|
}
|
|
|
|
void make_name_r(IMLName name, IMLReg regR)
|
|
{
|
|
cemu_assert_debug(regR.GetBaseFormat() == regR.GetRegFormat()); // for name load/store instructions the register must match the base format
|
|
type = PPCREC_IML_TYPE_NAME_R;
|
|
operation = PPCREC_IML_OP_ASSIGN;
|
|
op_r_name.regR = regR;
|
|
op_r_name.name = name;
|
|
}
|
|
|
|
void make_debugbreak(uint32 currentPPCAddress = 0)
|
|
{
|
|
make_macro(PPCREC_IML_MACRO_DEBUGBREAK, 0, currentPPCAddress, 0, IMLREG_INVALID);
|
|
}
|
|
|
|
void make_macro(uint32 macroId, uint32 param, uint32 param2, uint16 paramU16, IMLReg regParam)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_MACRO;
|
|
this->operation = macroId;
|
|
this->op_macro.param = param;
|
|
this->op_macro.param2 = param2;
|
|
this->op_macro.paramU16 = paramU16;
|
|
this->op_macro.paramReg = regParam;
|
|
}
|
|
|
|
void make_cjump_cycle_check()
|
|
{
|
|
this->type = PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK;
|
|
this->operation = 0;
|
|
}
|
|
|
|
void make_r_r(uint32 operation, IMLReg regR, IMLReg regA)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_R_R;
|
|
this->operation = operation;
|
|
this->op_r_r.regR = regR;
|
|
this->op_r_r.regA = regA;
|
|
}
|
|
|
|
void make_r_s32(uint32 operation, IMLReg regR, sint32 immS32)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_R_S32;
|
|
this->operation = operation;
|
|
this->op_r_immS32.regR = regR;
|
|
this->op_r_immS32.immS32 = immS32;
|
|
}
|
|
|
|
void make_r_r_r(uint32 operation, IMLReg regR, IMLReg regA, IMLReg regB)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_R_R_R;
|
|
this->operation = operation;
|
|
this->op_r_r_r.regR = regR;
|
|
this->op_r_r_r.regA = regA;
|
|
this->op_r_r_r.regB = regB;
|
|
}
|
|
|
|
void make_r_r_r_carry(uint32 operation, IMLReg regR, IMLReg regA, IMLReg regB, IMLReg regCarry)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_R_R_R_CARRY;
|
|
this->operation = operation;
|
|
this->op_r_r_r_carry.regR = regR;
|
|
this->op_r_r_r_carry.regA = regA;
|
|
this->op_r_r_r_carry.regB = regB;
|
|
this->op_r_r_r_carry.regCarry = regCarry;
|
|
}
|
|
|
|
void make_r_r_s32(uint32 operation, IMLReg regR, IMLReg regA, sint32 immS32)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_R_R_S32;
|
|
this->operation = operation;
|
|
this->op_r_r_s32.regR = regR;
|
|
this->op_r_r_s32.regA = regA;
|
|
this->op_r_r_s32.immS32 = immS32;
|
|
}
|
|
|
|
void make_r_r_s32_carry(uint32 operation, IMLReg regR, IMLReg regA, sint32 immS32, IMLReg regCarry)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_R_R_S32_CARRY;
|
|
this->operation = operation;
|
|
this->op_r_r_s32_carry.regR = regR;
|
|
this->op_r_r_s32_carry.regA = regA;
|
|
this->op_r_r_s32_carry.immS32 = immS32;
|
|
this->op_r_r_s32_carry.regCarry = regCarry;
|
|
}
|
|
|
|
void make_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_COMPARE;
|
|
this->operation = PPCREC_IML_OP_INVALID;
|
|
this->op_compare.regR = regR;
|
|
this->op_compare.regA = regA;
|
|
this->op_compare.regB = regB;
|
|
this->op_compare.cond = cond;
|
|
}
|
|
|
|
void make_compare_s32(IMLReg regA, sint32 immS32, IMLReg regR, IMLCondition cond)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_COMPARE_S32;
|
|
this->operation = PPCREC_IML_OP_INVALID;
|
|
this->op_compare_s32.regR = regR;
|
|
this->op_compare_s32.regA = regA;
|
|
this->op_compare_s32.immS32 = immS32;
|
|
this->op_compare_s32.cond = cond;
|
|
}
|
|
|
|
void make_conditional_jump(IMLReg regBool, bool mustBeTrue)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_CONDITIONAL_JUMP;
|
|
this->operation = PPCREC_IML_OP_INVALID;
|
|
this->op_conditional_jump.registerBool = regBool;
|
|
this->op_conditional_jump.mustBeTrue = mustBeTrue;
|
|
}
|
|
|
|
void make_jump()
|
|
{
|
|
this->type = PPCREC_IML_TYPE_JUMP;
|
|
this->operation = PPCREC_IML_OP_INVALID;
|
|
}
|
|
|
|
// load from memory
|
|
void make_r_memory(IMLReg regD, IMLReg regMem, sint32 immS32, uint32 copyWidth, bool signExtend, bool switchEndian)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_LOAD;
|
|
this->operation = 0;
|
|
this->op_storeLoad.registerData = regD;
|
|
this->op_storeLoad.registerMem = regMem;
|
|
this->op_storeLoad.immS32 = immS32;
|
|
this->op_storeLoad.copyWidth = copyWidth;
|
|
this->op_storeLoad.flags2.swapEndian = switchEndian;
|
|
this->op_storeLoad.flags2.signExtend = signExtend;
|
|
}
|
|
|
|
// store to memory
|
|
void make_memory_r(IMLReg regS, IMLReg regMem, sint32 immS32, uint32 copyWidth, bool switchEndian)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_STORE;
|
|
this->operation = 0;
|
|
this->op_storeLoad.registerData = regS;
|
|
this->op_storeLoad.registerMem = regMem;
|
|
this->op_storeLoad.immS32 = immS32;
|
|
this->op_storeLoad.copyWidth = copyWidth;
|
|
this->op_storeLoad.flags2.swapEndian = switchEndian;
|
|
this->op_storeLoad.flags2.signExtend = false;
|
|
}
|
|
|
|
void make_atomic_cmp_store(IMLReg regEA, IMLReg regCompareValue, IMLReg regWriteValue, IMLReg regSuccessOutput)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_ATOMIC_CMP_STORE;
|
|
this->operation = 0;
|
|
this->op_atomic_compare_store.regEA = regEA;
|
|
this->op_atomic_compare_store.regCompareValue = regCompareValue;
|
|
this->op_atomic_compare_store.regWriteValue = regWriteValue;
|
|
this->op_atomic_compare_store.regBoolOut = regSuccessOutput;
|
|
}
|
|
|
|
void make_call_imm(uintptr_t callAddress, IMLReg param0, IMLReg param1, IMLReg param2, IMLReg regReturn)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_CALL_IMM;
|
|
this->operation = 0;
|
|
this->op_call_imm.callAddress = callAddress;
|
|
this->op_call_imm.regParam0 = param0;
|
|
this->op_call_imm.regParam1 = param1;
|
|
this->op_call_imm.regParam2 = param2;
|
|
this->op_call_imm.regReturn = regReturn;
|
|
}
|
|
|
|
void make_fpr_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_FPR_COMPARE;
|
|
this->operation = -999;
|
|
this->op_fpr_compare.regR = regR;
|
|
this->op_fpr_compare.regA = regA;
|
|
this->op_fpr_compare.regB = regB;
|
|
this->op_fpr_compare.cond = cond;
|
|
}
|
|
|
|
/* X86 specific */
|
|
void make_x86_eflags_jcc(IMLCondition cond, bool invertedCondition)
|
|
{
|
|
this->type = PPCREC_IML_TYPE_X86_EFLAGS_JCC;
|
|
this->operation = -999;
|
|
this->op_x86_eflags_jcc.cond = cond;
|
|
this->op_x86_eflags_jcc.invertedCondition = invertedCondition;
|
|
}
|
|
|
|
void CheckRegisterUsage(IMLUsedRegisters* registersUsed) const;
|
|
bool HasSideEffects() const; // returns true if the instruction has side effects beyond just reading and writing registers. Dead code elimination uses this to know if an instruction can be dropped when the regular register outputs are not used
|
|
|
|
void RewriteGPR(const std::unordered_map<IMLRegID, IMLRegID>& translationTable);
|
|
};
|
|
|
|
// architecture specific constants
|
|
namespace IMLArchX86
|
|
{
|
|
static constexpr int PHYSREG_GPR_BASE = 0;
|
|
static constexpr int PHYSREG_FPR_BASE = 16;
|
|
}; |