mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-14 18:58:29 +12:00
833 lines
No EOL
22 KiB
C++
833 lines
No EOL
22 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_RLWIMI, // RLWIMI instruction (rotate, merge based on mask)
|
|
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,
|
|
PPCREC_IML_OP_DCBZ, // clear 32 bytes aligned to 0x20
|
|
// 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_BOTTOM_FRES_TO_BOTTOM_AND_TOP, // calculate reciprocal with Espresso accuracy of source bottom half and write result to destination 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_MFTB, // get TB register value (low or high)
|
|
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,
|
|
|
|
// conditional (legacy)
|
|
PPCREC_IML_TYPE_CONDITIONAL_R_S32,
|
|
|
|
// 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() {};
|
|
|
|
// GPR
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
IMLReg readGPR1;
|
|
IMLReg readGPR2;
|
|
IMLReg readGPR3;
|
|
IMLReg writtenGPR1;
|
|
IMLReg writtenGPR2;
|
|
};
|
|
};
|
|
// FPR
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
// note: If destination operand is not fully written (PS0 and PS1) it will be added to the read registers
|
|
IMLReg readFPR1;
|
|
IMLReg readFPR2;
|
|
IMLReg readFPR3;
|
|
IMLReg readFPR4;
|
|
IMLReg writtenFPR1;
|
|
};
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
bool IsRegIdRead(IMLRegID regId) const
|
|
{
|
|
if (readGPR1.IsValid() && readGPR1.GetRegID() == regId)
|
|
return true;
|
|
if (readGPR2.IsValid() && readGPR2.GetRegID() == regId)
|
|
return true;
|
|
if (readGPR3.IsValid() && readGPR3.GetRegID() == regId)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 (writtenGPR1.IsValid())
|
|
F(writtenGPR1, true);
|
|
if (writtenGPR2.IsValid())
|
|
F(writtenGPR2, true);
|
|
// FPRs
|
|
if (readFPR1.IsValid())
|
|
F(readFPR1, false);
|
|
if (readFPR2.IsValid())
|
|
F(readFPR2, false);
|
|
if (readFPR3.IsValid())
|
|
F(readFPR3, false);
|
|
if (readFPR4.IsValid())
|
|
F(readFPR4, false);
|
|
if (writtenFPR1.IsValid())
|
|
F(writtenFPR1, true);
|
|
}
|
|
|
|
};
|
|
|
|
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_MACRO && operation == PPCREC_IML_MACRO_MFTB ||
|
|
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;
|
|
|
|
void RewriteGPR(const std::unordered_map<IMLRegID, IMLRegID>& translationTable);
|
|
void ReplaceFPRs(IMLReg fprRegisterSearched[4], IMLReg fprRegisterReplaced[4]);
|
|
void ReplaceFPR(IMLRegID fprRegisterSearched, IMLRegID fprRegisterReplaced);
|
|
|
|
};
|
|
|
|
// architecture specific constants
|
|
namespace IMLArchX86
|
|
{
|
|
static constexpr int PHYSREG_GPR_BASE = 0;
|
|
static constexpr int PHYSREG_FPR_BASE = 16;
|
|
}; |