First Commit

This commit is contained in:
2025-11-18 14:18:26 -07:00
parent 33eb6e3707
commit 27277ec342
6106 changed files with 3571167 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
struct xImplAVX_Move
{
u8 Prefix;
u8 LoadOpcode;
u8 StoreOpcode;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from) const;
void operator()(const xIndirectVoid& to, const xRegisterSSE& from) const;
};
struct xImplAVX_ThreeArg
{
u8 Prefix;
u8 Opcode;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from1, const xRegisterSSE& from2) const;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from1, const xIndirectVoid& from2) const;
};
struct xImplAVX_ThreeArgYMM : xImplAVX_ThreeArg
{
void operator()(const xRegisterSSE& to, const xRegisterSSE& from1, const xRegisterSSE& from2) const;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from1, const xIndirectVoid& from2) const;
};
struct xImplAVX_ArithFloat
{
xImplAVX_ThreeArgYMM PS;
xImplAVX_ThreeArgYMM PD;
xImplAVX_ThreeArg SS;
xImplAVX_ThreeArg SD;
};
struct xImplAVX_CmpFloatHelper
{
SSE2_ComparisonType CType;
void PS(const xRegisterSSE& to, const xRegisterSSE& from1, const xRegisterSSE& from2) const;
void PS(const xRegisterSSE& to, const xRegisterSSE& from1, const xIndirectVoid& from2) const;
void PD(const xRegisterSSE& to, const xRegisterSSE& from1, const xRegisterSSE& from2) const;
void PD(const xRegisterSSE& to, const xRegisterSSE& from1, const xIndirectVoid& from2) const;
void SS(const xRegisterSSE& to, const xRegisterSSE& from1, const xRegisterSSE& from2) const;
void SS(const xRegisterSSE& to, const xRegisterSSE& from1, const xIndirectVoid& from2) const;
void SD(const xRegisterSSE& to, const xRegisterSSE& from1, const xRegisterSSE& from2) const;
void SD(const xRegisterSSE& to, const xRegisterSSE& from1, const xIndirectVoid& from2) const;
};
struct xImplAVX_CmpFloat
{
xImplAVX_CmpFloatHelper EQ;
xImplAVX_CmpFloatHelper LT;
xImplAVX_CmpFloatHelper LE;
xImplAVX_CmpFloatHelper UO;
xImplAVX_CmpFloatHelper NE;
xImplAVX_CmpFloatHelper GE;
xImplAVX_CmpFloatHelper GT;
xImplAVX_CmpFloatHelper OR;
};
struct xImplAVX_CmpInt
{
// Compare packed bytes for equality.
// If a data element in dest is equal to the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplAVX_ThreeArgYMM EQB;
// Compare packed words for equality.
// If a data element in dest is equal to the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplAVX_ThreeArgYMM EQW;
// Compare packed doublewords [32-bits] for equality.
// If a data element in dest is equal to the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplAVX_ThreeArgYMM EQD;
// Compare packed signed bytes for greater than.
// If a data element in dest is greater than the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplAVX_ThreeArgYMM GTB;
// Compare packed signed words for greater than.
// If a data element in dest is greater than the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplAVX_ThreeArgYMM GTW;
// Compare packed signed doublewords [32-bits] for greater than.
// If a data element in dest is greater than the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplAVX_ThreeArgYMM GTD;
};
} // namespace x86Emitter

View File

@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// Implement BMI1/BMI2 instruction set
namespace x86Emitter
{
struct xImplBMI_RVM
{
u8 Prefix;
u8 MbPrefix;
u8 Opcode;
// RVM
// MULX Unsigned multiply without affecting flags, and arbitrary destination registers
// PDEP Parallel bits deposit
// PEXT Parallel bits extract
// ANDN Logical and not ~x & y
void operator()(const xRegisterInt& to, const xRegisterInt& from1, const xRegisterInt& from2) const;
void operator()(const xRegisterInt& to, const xRegisterInt& from1, const xIndirectVoid& from2) const;
#if 0
// RMV
// BEXTR Bit field extract (with register) (src >> start) & ((1 << len)-1)[9]
// BZHI Zero high bits starting with specified bit position
// SARX Shift arithmetic right without affecting flags
// SHRX Shift logical right without affecting flags
// SHLX Shift logical left without affecting flags
// FIXME: WARNING same as above but V and M are inverted
//void operator()( const xRegisterInt& to, const xRegisterInt& from1, const xRegisterInt& from2) const;
//void operator()( const xRegisterInt& to, const xIndirectVoid& from1, const xRegisterInt& from2) const;
// VM
// BLSI Extract lowest set isolated bit x & -x
// BLSMSK Get mask up to lowest set bit x ^ (x - 1)
// BLSR Reset lowest set bit x & (x - 1)
void operator()( const xRegisterInt& to, const xRegisterInt& from) const;
void operator()( const xRegisterInt& to, const xIndirectVoid& from) const;
// RMI
//RORX Rotate right logical without affecting flags
void operator()( const xRegisterInt& to, const xRegisterInt& from, u8 imm) const;
void operator()( const xRegisterInt& to, const xIndirectVoid& from, u8 imm) const;
#endif
};
} // namespace x86Emitter

View File

@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
// Implementations here cover SHLD and SHRD.
// --------------------------------------------------------------------------------------
// xImpl_DowrdShift
// --------------------------------------------------------------------------------------
// I use explicit method declarations here instead of templates, in order to provide
// *only* 32 and 16 bit register operand forms (8 bit registers are not valid in SHLD/SHRD).
//
// Optimization Note: Imm shifts by 0 are ignore (no code generated). This is a safe optimization
// because shifts by 0 do *not* affect flags status (intel docs cited).
//
struct xImpl_DwordShift
{
u16 OpcodeBase;
void operator()(const xRegister16or32or64& to, const xRegister16or32or64& from, const xRegisterCL& clreg) const;
void operator()(const xRegister16or32or64& to, const xRegister16or32or64& from, u8 shiftcnt) const;
void operator()(const xIndirectVoid& dest, const xRegister16or32or64& from, const xRegisterCL& clreg) const;
void operator()(const xIndirectVoid& dest, const xRegister16or32or64& from, u8 shiftcnt) const;
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,131 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
enum G1Type
{
G1Type_ADD = 0,
G1Type_OR,
G1Type_ADC,
G1Type_SBB,
G1Type_AND,
G1Type_SUB,
G1Type_XOR,
G1Type_CMP
};
extern void _g1_EmitOp(G1Type InstType, const xRegisterInt& to, const xRegisterInt& from);
// --------------------------------------------------------------------------------------
// xImpl_Group1
// --------------------------------------------------------------------------------------
struct xImpl_Group1
{
G1Type InstType;
void operator()(const xRegisterInt& to, const xRegisterInt& from) const;
void operator()(const xIndirectVoid& to, const xRegisterInt& from) const;
void operator()(const xRegisterInt& to, const xIndirectVoid& from) const;
void operator()(const xRegisterInt& to, int imm) const;
void operator()(const xIndirect64orLess& to, int imm) const;
#if 0
// ------------------------------------------------------------------------
template< typename T > __noinline void operator()( const ModSibBase& to, const xImmReg<T>& immOrReg ) const
{
_DoI_helpermess( *this, to, immOrReg );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, const xImmReg<T>& immOrReg ) const
{
_DoI_helpermess( *this, to, immOrReg );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, int imm ) const
{
_DoI_helpermess( *this, to, imm );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, const xDirectOrIndirect<T>& from ) const
{
_DoI_helpermess( *this, to, from );
}
// FIXME : Make this struct to 8, 16, and 32 bit registers
template< typename T > __noinline void operator()( const xRegisterBase& to, const xDirectOrIndirect<T>& from ) const
{
_DoI_helpermess( *this, xDirectOrIndirect<T>( to ), from );
}
// FIXME : Make this struct to 8, 16, and 32 bit registers
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, const xRegisterBase& from ) const
{
_DoI_helpermess( *this, to, xDirectOrIndirect<T>( from ) );
}
#endif
};
// ------------------------------------------------------------------------
// This class combines x86 with SSE/SSE2 logic operations (ADD, OR, and NOT).
// Note: ANDN [AndNot] is handled below separately.
//
struct xImpl_G1Logic
{
G1Type InstType;
void operator()(const xRegisterInt& to, const xRegisterInt& from) const;
void operator()(const xIndirectVoid& to, const xRegisterInt& from) const;
void operator()(const xRegisterInt& to, const xIndirectVoid& from) const;
void operator()(const xRegisterInt& to, int imm) const;
void operator()(const xIndirect64orLess& to, int imm) const;
xImplSimd_DestRegSSE PS; // packed single precision
xImplSimd_DestRegSSE PD; // packed double precision
};
// ------------------------------------------------------------------------
// This class combines x86 with SSE/SSE2 arithmetic operations (ADD/SUB).
//
struct xImpl_G1Arith
{
G1Type InstType;
void operator()(const xRegisterInt& to, const xRegisterInt& from) const;
void operator()(const xIndirectVoid& to, const xRegisterInt& from) const;
void operator()(const xRegisterInt& to, const xIndirectVoid& from) const;
void operator()(const xRegisterInt& to, int imm) const;
void operator()(const xIndirect64orLess& to, int imm) const;
xImplSimd_DestRegSSE PS; // packed single precision
xImplSimd_DestRegSSE PD; // packed double precision
xImplSimd_DestRegSSE SS; // scalar single precision
xImplSimd_DestRegSSE SD; // scalar double precision
};
// ------------------------------------------------------------------------
struct xImpl_G1Compare
{
void operator()(const xRegisterInt& to, const xRegisterInt& from) const;
void operator()(const xIndirectVoid& to, const xRegisterInt& from) const;
void operator()(const xRegisterInt& to, const xIndirectVoid& from) const;
void operator()(const xRegisterInt& to, int imm) const;
void operator()(const xIndirect64orLess& to, int imm) const;
xImplSimd_DestSSE_CmpImm PS;
xImplSimd_DestSSE_CmpImm PD;
xImplSimd_DestSSE_CmpImm SS;
xImplSimd_DestSSE_CmpImm SD;
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
enum G2Type
{
G2Type_ROL = 0,
G2Type_ROR,
G2Type_RCL,
G2Type_RCR,
G2Type_SHL,
G2Type_SHR,
G2Type_Unused,
G2Type_SAR
};
// --------------------------------------------------------------------------------------
// xImpl_Group2
// --------------------------------------------------------------------------------------
// Group 2 (shift) instructions have no Sib/ModRM forms.
// Optimization Note: For Imm forms, we ignore the instruction if the shift count is zero.
// This is a safe optimization since any zero-value shift does not affect any flags.
//
struct xImpl_Group2
{
G2Type InstType;
void operator()(const xRegisterInt& to, const xRegisterCL& from) const;
void operator()(const xIndirect64orLess& to, const xRegisterCL& from) const;
void operator()(const xRegisterInt& to, u8 imm) const;
void operator()(const xIndirect64orLess& to, u8 imm) const;
#if 0
// ------------------------------------------------------------------------
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, u8 imm ) const
{
_DoI_helpermess( *this, to, imm );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, const xRegisterCL& from ) const
{
_DoI_helpermess( *this, to, from );
}
#endif
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,97 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
enum G3Type
{
G3Type_NOT = 2,
G3Type_NEG = 3,
G3Type_MUL = 4,
G3Type_iMUL = 5, // partial implementation, iMul has additional forms in ix86.cpp
G3Type_DIV = 6,
G3Type_iDIV = 7
};
// --------------------------------------------------------------------------------------
// xImpl_Group3
// --------------------------------------------------------------------------------------
struct xImpl_Group3
{
G3Type InstType;
void operator()(const xRegisterInt& from) const;
void operator()(const xIndirect64orLess& from) const;
#if 0
template< typename T >
void operator()( const xDirectOrIndirect<T>& from ) const
{
_DoI_helpermess( *this, from );
}
#endif
};
// --------------------------------------------------------------------------------------
// xImpl_MulDivBase
// --------------------------------------------------------------------------------------
// This class combines x86 and SSE/SSE2 instructions for iMUL and iDIV.
//
struct xImpl_MulDivBase
{
G3Type InstType;
u16 OpcodeSSE;
void operator()(const xRegisterInt& from) const;
void operator()(const xIndirect64orLess& from) const;
const xImplSimd_DestRegSSE PS;
const xImplSimd_DestRegSSE PD;
const xImplSimd_DestRegSSE SS;
const xImplSimd_DestRegSSE SD;
};
// --------------------------------------------------------------------------------------
// xImpl_iDiv
// --------------------------------------------------------------------------------------
struct xImpl_iDiv
{
void operator()(const xRegisterInt& from) const;
void operator()(const xIndirect64orLess& from) const;
const xImplSimd_DestRegSSE PS;
const xImplSimd_DestRegSSE PD;
const xImplSimd_DestRegSSE SS;
const xImplSimd_DestRegSSE SD;
};
// --------------------------------------------------------------------------------------
// xImpl_iMul
// --------------------------------------------------------------------------------------
//
struct xImpl_iMul
{
void operator()(const xRegisterInt& from) const;
void operator()(const xIndirect64orLess& from) const;
// The following iMul-specific forms are valid for 16 and 32 bit register operands only!
void operator()(const xRegister32& to, const xRegister32& from) const;
void operator()(const xRegister32& to, const xIndirectVoid& src) const;
void operator()(const xRegister16& to, const xRegister16& from) const;
void operator()(const xRegister16& to, const xIndirectVoid& src) const;
void operator()(const xRegister32& to, const xRegister32& from, s32 imm) const;
void operator()(const xRegister32& to, const xIndirectVoid& from, s32 imm) const;
void operator()(const xRegister16& to, const xRegister16& from, s16 imm) const;
void operator()(const xRegister16& to, const xIndirectVoid& from, s16 imm) const;
const xImplSimd_DestRegSSE PS;
const xImplSimd_DestRegSSE PD;
const xImplSimd_DestRegSSE SS;
const xImplSimd_DestRegSSE SD;
};
} // namespace x86Emitter

View File

@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
// helpermess is currently broken >_<
#if 0
template< typename xImpl, typename T >
void _DoI_helpermess( const xImpl& helpme, const xDirectOrIndirect& to, const xImmReg<T>& immOrReg )
{
if( to.IsDirect() )
{
if( immOrReg.IsReg() )
helpme( to.GetReg(), immOrReg.GetReg() );
else
helpme( to.GetReg(), immOrReg.GetImm() );
}
else
{
if( immOrReg.IsReg() )
helpme( to.GetMem(), immOrReg.GetReg() );
else
helpme( to.GetMem(), immOrReg.GetImm() );
}
}
template< typename xImpl, typename T >
void _DoI_helpermess( const xImpl& helpme, const ModSibBase& to, const xImmReg<T>& immOrReg )
{
if( immOrReg.IsReg() )
helpme( to, immOrReg.GetReg() );
else
helpme( (ModSibStrict)to, immOrReg.GetImm() );
}
template< typename xImpl, typename T >
void _DoI_helpermess( const xImpl& helpme, const xDirectOrIndirect<T>& to, int imm )
{
if( to.IsDirect() )
helpme( to.GetReg(), imm );
else
helpme( to.GetMem(), imm );
}
template< typename xImpl, typename T >
void _DoI_helpermess( const xImpl& helpme, const xDirectOrIndirect<T>& parm )
{
if( parm.IsDirect() )
helpme( parm.GetReg() );
else
helpme( parm.GetMem() );
}
template< typename xImpl, typename T >
void _DoI_helpermess( const xImpl& helpme, const xDirectOrIndirect<T>& to, const xDirectOrIndirect<T>& from )
{
if( to.IsDirect() && from.IsDirect() )
helpme( to.GetReg(), from.GetReg() );
else if( to.IsDirect() )
helpme( to.GetReg(), from.GetMem() );
else if( from.IsDirect() )
helpme( to.GetMem(), from.GetReg() );
else
// One of the fields needs to be direct, or else we cannot complete the operation.
// (intel doesn't support indirects in both fields)
pxFailDev( "Invalid asm instruction: Both operands are indirect memory addresses." );
}
#endif
} // End namespace x86Emitter

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// Implementations found here: Increment and Decrement Instructions!
// (They're soooo lonely... but I dunno where else to stick this class!)
namespace x86Emitter
{
// --------------------------------------------------------------------------------------
// xImpl_IncDec
// --------------------------------------------------------------------------------------
struct xImpl_IncDec
{
bool isDec;
void operator()(const xRegisterInt& to) const;
void operator()(const xIndirect64orLess& to) const;
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// Implementations found here: CALL and JMP! (unconditional only)
namespace x86Emitter
{
extern void xJccKnownTarget(JccComparisonType comparison, const void* target, bool slideForward);
// ------------------------------------------------------------------------
struct xImpl_JmpCall
{
bool isJmp;
void operator()(const xAddressReg& absreg) const;
void operator()(const xIndirectNative& src) const;
// Special form for calling functions. This form automatically resolves the
// correct displacement based on the size of the instruction being generated.
void operator()(const void* func) const
{
if (isJmp)
xJccKnownTarget(Jcc_Unconditional, (const void*)(uptr)func, false); // double cast to/from (uptr) needed to appease GCC
else
{
// calls are relative to the instruction after this one, and length is
// always 5 bytes (16 bit calls are bad mojo, so no bother to do special logic).
sptr dest = (sptr)func - ((sptr)xGetPtr() + 5);
pxAssertMsg(dest == (s32)dest, "Indirect jump is too far, must use a register!");
xWrite8(0xe8);
xWrite32(dest);
}
}
};
// yes it is awful. Due to template code is in a header with a nice circular dep.
extern const xImpl_Mov xMOV;
extern const xImpl_JmpCall xCALL;
struct xImpl_FastCall
{
// FIXME: current 64 bits is mostly a copy/past potentially it would require to push/pop
// some registers. But I think it is enough to handle the first call.
void operator()(const void* f, const xRegister32& a1 = xEmptyReg, const xRegister32& a2 = xEmptyReg) const;
void operator()(const void* f, u32 a1, const xRegister32& a2) const;
void operator()(const void* f, const xIndirect32& a1) const;
void operator()(const void* f, u32 a1, u32 a2) const;
void operator()(const void* f, void* a1) const;
void operator()(const void* f, const xRegisterLong& a1, const xRegisterLong& a2 = xEmptyReg) const;
void operator()(const void* f, u32 a1, const xRegisterLong& a2) const;
template <typename T>
__fi void operator()(T* func, u32 a1, const xRegisterLong& a2 = xEmptyReg) const
{
(*this)((const void*)func, a1, a2);
}
template <typename T>
__fi void operator()(T* func, const xIndirect32& a1) const
{
(*this)((const void*)func, a1);
}
template <typename T>
__fi void operator()(T* func, u32 a1, u32 a2) const
{
(*this)((const void*)func, a1, a2);
}
void operator()(const xIndirectNative& f, const xRegisterLong& a1 = xEmptyReg, const xRegisterLong& a2 = xEmptyReg) const;
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,128 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// Header: ix86_impl_movs.h -- covers mov, cmov, movsx/movzx, and SETcc (which shares
// with cmov many similarities).
namespace x86Emitter
{
// --------------------------------------------------------------------------------------
// MovImplAll
// --------------------------------------------------------------------------------------
// MOV instruction Implementation, plus many SIMD sub-mov variants.
//
struct xImpl_Mov
{
xImpl_Mov() {} // Satisfy GCC's whims.
void operator()(const xRegisterInt& to, const xRegisterInt& from) const;
void operator()(const xIndirectVoid& dest, const xRegisterInt& from) const;
void operator()(const xRegisterInt& to, const xIndirectVoid& src) const;
void operator()(const xIndirect64orLess& dest, sptr imm) const;
void operator()(const xRegisterInt& to, sptr imm, bool preserve_flags = false) const;
#if 0
template< typename T > __noinline void operator()( const ModSibBase& to, const xImmReg<T>& immOrReg ) const
{
_DoI_helpermess( *this, to, immOrReg );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, const xImmReg<T>& immOrReg ) const
{
_DoI_helpermess( *this, to, immOrReg );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, int imm ) const
{
_DoI_helpermess( *this, to, imm );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, const xDirectOrIndirect<T>& from ) const
{
if( to == from ) return;
_DoI_helpermess( *this, to, from );
}
/*template< typename T > __noinline void operator()( const xRegister<T>& to, const xDirectOrIndirect<T>& from ) const
{
_DoI_helpermess( *this, xDirectOrIndirect<T>( to ), from );
}
template< typename T > __noinline void operator()( const xDirectOrIndirect<T>& to, const xRegister<T>& from ) const
{
_DoI_helpermess( *this, to, xDirectOrIndirect<T>( from ) );
}*/
#endif
};
// --------------------------------------------------------------------------------------
// xImpl_MovImm64
// --------------------------------------------------------------------------------------
// Mov with 64-bit immediates (only available on 64-bit platforms)
//
struct xImpl_MovImm64
{
xImpl_MovImm64() {} // Satisfy GCC's whims.
void operator()(const xRegister64& to, s64 imm, bool preserve_flags = false) const;
};
// --------------------------------------------------------------------------------------
// xImpl_CMov
// --------------------------------------------------------------------------------------
// CMOVcc !! [in all of it's disappointing lack-of glory] .. and ..
// SETcc !! [more glory, less lack!]
//
// CMOV Disclaimer: Caution! This instruction can look exciting and cool, until you
// realize that it cannot load immediate values into registers. -_-
//
// I use explicit method declarations here instead of templates, in order to provide
// *only* 32 and 16 bit register operand forms (8 bit registers are not valid in CMOV).
//
struct xImpl_CMov
{
JccComparisonType ccType;
void operator()(const xRegister16or32or64& to, const xRegister16or32or64& from) const;
void operator()(const xRegister16or32or64& to, const xIndirectVoid& sibsrc) const;
//void operator()( const xDirectOrIndirect32& to, const xDirectOrIndirect32& from );
//void operator()( const xDirectOrIndirect16& to, const xDirectOrIndirect16& from ) const;
};
struct xImpl_Set
{
JccComparisonType ccType;
void operator()(const xRegister8& to) const;
void operator()(const xIndirect8& dest) const;
//void operator()( const xDirectOrIndirect8& dest ) const;
};
// --------------------------------------------------------------------------------------
// xImpl_MovExtend
// --------------------------------------------------------------------------------------
// Mov with sign/zero extension implementations (movsx / movzx)
//
struct xImpl_MovExtend
{
bool SignExtend;
void operator()(const xRegister16or32or64& to, const xRegister8& from) const;
void operator()(const xRegister16or32or64& to, const xIndirect8& sibsrc) const;
void operator()(const xRegister32or64& to, const xRegister16& from) const;
void operator()(const xRegister32or64& to, const xIndirect16& sibsrc) const;
void operator()(const xRegister64& to, const xRegister32& from) const;
void operator()(const xRegister64& to, const xIndirect32& sibsrc) const;
//void operator()( const xRegister32& to, const xDirectOrIndirect16& src ) const;
//void operator()( const xRegister16or32& to, const xDirectOrIndirect8& src ) const;
//void operator()( const xRegister16& to, const xDirectOrIndirect8& src ) const;
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,304 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
// --------------------------------------------------------------------------------------
// _SimdShiftHelper
// --------------------------------------------------------------------------------------
struct _SimdShiftHelper
{
u8 Prefix;
u16 Opcode;
u16 OpcodeImm;
u8 Modcode;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from) const;
void operator()(const xRegisterSSE& to, u8 imm8) const;
};
// --------------------------------------------------------------------------------------
// xImplSimd_Shift / xImplSimd_ShiftWithoutQ
// --------------------------------------------------------------------------------------
// Used for PSRA, which lacks the Q form.
//
struct xImplSimd_ShiftWithoutQ
{
const _SimdShiftHelper W;
const _SimdShiftHelper D;
};
// Implements PSRL and PSLL
//
struct xImplSimd_Shift
{
const _SimdShiftHelper W;
const _SimdShiftHelper D;
const _SimdShiftHelper Q;
void DQ(const xRegisterSSE& to, u8 imm8) const;
};
//////////////////////////////////////////////////////////////////////////////////////////
//
struct xImplSimd_AddSub
{
const xImplSimd_DestRegEither B;
const xImplSimd_DestRegEither W;
const xImplSimd_DestRegEither D;
const xImplSimd_DestRegEither Q;
// Add/Sub packed signed byte [8bit] integers from src into dest, and saturate the results.
const xImplSimd_DestRegEither SB;
// Add/Sub packed signed word [16bit] integers from src into dest, and saturate the results.
const xImplSimd_DestRegEither SW;
// Add/Sub packed unsigned byte [8bit] integers from src into dest, and saturate the results.
const xImplSimd_DestRegEither USB;
// Add/Sub packed unsigned word [16bit] integers from src into dest, and saturate the results.
const xImplSimd_DestRegEither USW;
};
//////////////////////////////////////////////////////////////////////////////////////////
//
struct xImplSimd_PMul
{
const xImplSimd_DestRegEither LW;
const xImplSimd_DestRegEither HW;
const xImplSimd_DestRegEither HUW;
const xImplSimd_DestRegEither UDQ;
// [SSE-3] PMULHRSW multiplies vertically each signed 16-bit integer from dest with the
// corresponding signed 16-bit integer of source, producing intermediate signed 32-bit
// integers. Each intermediate 32-bit integer is truncated to the 18 most significant
// bits. Rounding is always performed by adding 1 to the least significant bit of the
// 18-bit intermediate result. The final result is obtained by selecting the 16 bits
// immediately to the right of the most significant bit of each 18-bit intermediate
// result and packed to the destination operand.
//
// Both operands can be MMX or XMM registers. Source can be register or memory.
//
const xImplSimd_DestRegEither HRSW;
// [SSE-4.1] Multiply the packed dword signed integers in dest with src, and store
// the low 32 bits of each product in xmm1.
const xImplSimd_DestRegSSE LD;
// [SSE-4.1] Multiply the packed signed dword integers in dest with src.
const xImplSimd_DestRegSSE DQ;
};
//////////////////////////////////////////////////////////////////////////////////////////
// For instructions that have PS/SS form only (most commonly reciprocal Sqrt functions)
//
struct xImplSimd_rSqrt
{
const xImplSimd_DestRegSSE PS;
const xImplSimd_DestRegSSE SS;
};
//////////////////////////////////////////////////////////////////////////////////////////
// SQRT has PS/SS/SD forms, but not the PD form.
//
struct xImplSimd_Sqrt
{
const xImplSimd_DestRegSSE PS;
const xImplSimd_DestRegSSE SS;
const xImplSimd_DestRegSSE SD;
};
//////////////////////////////////////////////////////////////////////////////////////////
//
struct xImplSimd_AndNot
{
const xImplSimd_DestRegSSE PS;
const xImplSimd_DestRegSSE PD;
};
//////////////////////////////////////////////////////////////////////////////////////////
// Packed absolute value. [sSSE3 only]
//
struct xImplSimd_PAbsolute
{
// [sSSE-3] Computes the absolute value of bytes in the src, and stores the result
// in dest, as UNSIGNED.
const xImplSimd_DestRegEither B;
// [sSSE-3] Computes the absolute value of word in the src, and stores the result
// in dest, as UNSIGNED.
const xImplSimd_DestRegEither W;
// [sSSE-3] Computes the absolute value of doublewords in the src, and stores the
// result in dest, as UNSIGNED.
const xImplSimd_DestRegEither D;
};
//////////////////////////////////////////////////////////////////////////////////////////
// Packed Sign [sSSE3 only] - Negate/zero/preserve packed integers in dest depending on the
// corresponding sign in src.
//
struct xImplSimd_PSign
{
// [sSSE-3] negates each byte element of dest if the signed integer value of the
// corresponding data element in src is less than zero. If the signed integer value
// of a data element in src is positive, the corresponding data element in dest is
// unchanged. If a data element in src is zero, the corresponding data element in
// dest is set to zero.
const xImplSimd_DestRegEither B;
// [sSSE-3] negates each word element of dest if the signed integer value of the
// corresponding data element in src is less than zero. If the signed integer value
// of a data element in src is positive, the corresponding data element in dest is
// unchanged. If a data element in src is zero, the corresponding data element in
// dest is set to zero.
const xImplSimd_DestRegEither W;
// [sSSE-3] negates each doubleword element of dest if the signed integer value
// of the corresponding data element in src is less than zero. If the signed integer
// value of a data element in src is positive, the corresponding data element in dest
// is unchanged. If a data element in src is zero, the corresponding data element in
// dest is set to zero.
const xImplSimd_DestRegEither D;
};
//////////////////////////////////////////////////////////////////////////////////////////
// Packed Multiply and Add!!
//
struct xImplSimd_PMultAdd
{
// Multiplies the individual signed words of dest by the corresponding signed words
// of src, producing temporary signed, doubleword results. The adjacent doubleword
// results are then summed and stored in the destination operand.
//
// DEST[31:0] = ( DEST[15:0] * SRC[15:0]) + (DEST[31:16] * SRC[31:16] );
// DEST[63:32] = ( DEST[47:32] * SRC[47:32]) + (DEST[63:48] * SRC[63:48] );
// [.. repeat in the case of XMM src/dest operands ..]
//
const xImplSimd_DestRegEither WD;
// [sSSE-3] multiplies vertically each unsigned byte of dest with the corresponding
// signed byte of src, producing intermediate signed 16-bit integers. Each adjacent
// pair of signed words is added and the saturated result is packed to dest.
// For example, the lowest-order bytes (bits 7-0) in src and dest are multiplied
// and the intermediate signed word result is added with the corresponding
// intermediate result from the 2nd lowest-order bytes (bits 15-8) of the operands;
// the sign-saturated result is stored in the lowest word of dest (bits 15-0).
// The same operation is performed on the other pairs of adjacent bytes.
//
// In Coder Speak:
// DEST[15-0] = SaturateToSignedWord( SRC[15-8] * DEST[15-8] + SRC[7-0] * DEST[7-0] );
// DEST[31-16] = SaturateToSignedWord( SRC[31-24] * DEST[31-24] + SRC[23-16] * DEST[23-16] );
// [.. repeat for each 16 bits up to 64 (mmx) or 128 (xmm) ..]
//
const xImplSimd_DestRegEither UBSW;
};
//////////////////////////////////////////////////////////////////////////////////////////
// Packed Horizontal Add [SSE3 only]
//
struct xImplSimd_HorizAdd
{
// [SSE-3] Horizontal Add of Packed Data. A three step process:
// * Adds the single-precision floating-point values in the first and second dwords of
// dest and stores the result in the first dword of dest.
// * Adds single-precision floating-point values in the third and fourth dword of dest
// stores the result in the second dword of dest.
// * Adds single-precision floating-point values in the first and second dword of *src*
// and stores the result in the third dword of dest.
const xImplSimd_DestRegSSE PS;
// [SSE-3] Horizontal Add of Packed Data. A two step process:
// * Adds the double-precision floating-point values in the high and low quadwords of
// dest and stores the result in the low quadword of dest.
// * Adds the double-precision floating-point values in the high and low quadwords of
// *src* stores the result in the high quadword of dest.
const xImplSimd_DestRegSSE PD;
};
//////////////////////////////////////////////////////////////////////////////////////////
// DotProduct calculation (SSE4.1 only!)
//
struct xImplSimd_DotProduct
{
// [SSE-4.1] Conditionally multiplies the packed single precision floating-point
// values in dest with the packed single-precision floats in src depending on a
// mask extracted from the high 4 bits of the immediate byte. If a condition mask
// bit in Imm8[7:4] is zero, the corresponding multiplication is replaced by a value
// of 0.0. The four resulting single-precision values are summed into an inter-
// mediate result.
//
// The intermediate result is conditionally broadcasted to the destination using a
// broadcast mask specified by bits [3:0] of the immediate byte. If a broadcast
// mask bit is 1, the intermediate result is copied to the corresponding dword
// element in dest. If a broadcast mask bit is zero, the corresponding element in
// the destination is set to zero.
//
xImplSimd_DestRegImmSSE PS;
// [SSE-4.1]
xImplSimd_DestRegImmSSE PD;
};
//////////////////////////////////////////////////////////////////////////////////////////
// Rounds floating point values (packed or single scalar) by an arbitrary rounding mode.
// (SSE4.1 only!)
struct xImplSimd_Round
{
// [SSE-4.1] Rounds the 4 packed single-precision src values and stores them in dest.
//
// Imm8 specifies control fields for the rounding operation:
// Bit 3 - processor behavior for a precision exception (0: normal, 1: inexact)
// Bit 2 - If enabled, use MXCSR.RC, else use RC specified in bits 1:0 of this Imm8.
// Bits 1:0 - Specifies a rounding mode for this instruction only.
//
// Rounding Mode Reference:
// 0 - Nearest, 1 - Negative Infinity, 2 - Positive infinity, 3 - Truncate.
//
const xImplSimd_DestRegImmSSE PS;
// [SSE-4.1] Rounds the 2 packed double-precision src values and stores them in dest.
//
// Imm8 specifies control fields for the rounding operation:
// Bit 3 - processor behavior for a precision exception (0: normal, 1: inexact)
// Bit 2 - If enabled, use MXCSR.RC, else use RC specified in bits 1:0 of this Imm8.
// Bits 1:0 - Specifies a rounding mode for this instruction only.
//
// Rounding Mode Reference:
// 0 - Nearest, 1 - Negative Infinity, 2 - Positive infinity, 3 - Truncate.
//
const xImplSimd_DestRegImmSSE PD;
// [SSE-4.1] Rounds the single-precision src value and stores in dest.
//
// Imm8 specifies control fields for the rounding operation:
// Bit 3 - processor behavior for a precision exception (0: normal, 1: inexact)
// Bit 2 - If enabled, use MXCSR.RC, else use RC specified in bits 1:0 of this Imm8.
// Bits 1:0 - Specifies a rounding mode for this instruction only.
//
// Rounding Mode Reference:
// 0 - Nearest, 1 - Negative Infinity, 2 - Positive infinity, 3 - Truncate.
//
const xImplSimd_DestRegImmSSE SS;
// [SSE-4.1] Rounds the double-precision src value and stores in dest.
//
// Imm8 specifies control fields for the rounding operation:
// Bit 3 - processor behavior for a precision exception (0: normal, 1: inexact)
// Bit 2 - If enabled, use MXCSR.RC, else use RC specified in bits 1:0 of this Imm8.
// Bits 1:0 - Specifies a rounding mode for this instruction only.
//
// Rounding Mode Reference:
// 0 - Nearest, 1 - Negative Infinity, 2 - Positive infinity, 3 - Truncate.
//
const xImplSimd_DestRegImmSSE SD;
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
struct xImplSimd_MinMax
{
const xImplSimd_DestRegSSE PS; // packed single precision
const xImplSimd_DestRegSSE PD; // packed double precision
const xImplSimd_DestRegSSE SS; // scalar single precision
const xImplSimd_DestRegSSE SD; // scalar double precision
};
//////////////////////////////////////////////////////////////////////////////////////////
//
struct xImplSimd_Compare
{
SSE2_ComparisonType CType;
void PS(const xRegisterSSE& to, const xRegisterSSE& from) const;
void PS(const xRegisterSSE& to, const xIndirectVoid& from) const;
void PD(const xRegisterSSE& to, const xRegisterSSE& from) const;
void PD(const xRegisterSSE& to, const xIndirectVoid& from) const;
void SS(const xRegisterSSE& to, const xRegisterSSE& from) const;
void SS(const xRegisterSSE& to, const xIndirectVoid& from) const;
void SD(const xRegisterSSE& to, const xRegisterSSE& from) const;
void SD(const xRegisterSSE& to, const xIndirectVoid& from) const;
};
//////////////////////////////////////////////////////////////////////////////////////////
// Compare scalar floating point values and set EFLAGS (Ordered or Unordered)
//
struct xImplSimd_COMI
{
const xImplSimd_DestRegSSE SS;
const xImplSimd_DestRegSSE SD;
};
//////////////////////////////////////////////////////////////////////////////////////////
//
struct xImplSimd_PCompare
{
public:
// Compare packed bytes for equality.
// If a data element in dest is equal to the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplSimd_DestRegEither EQB;
// Compare packed words for equality.
// If a data element in dest is equal to the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplSimd_DestRegEither EQW;
// Compare packed doublewords [32-bits] for equality.
// If a data element in dest is equal to the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplSimd_DestRegEither EQD;
// Compare packed signed bytes for greater than.
// If a data element in dest is greater than the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplSimd_DestRegEither GTB;
// Compare packed signed words for greater than.
// If a data element in dest is greater than the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplSimd_DestRegEither GTW;
// Compare packed signed doublewords [32-bits] for greater than.
// If a data element in dest is greater than the corresponding date element src, the
// corresponding data element in dest is set to all 1s; otherwise, it is set to all 0s.
const xImplSimd_DestRegEither GTD;
};
//////////////////////////////////////////////////////////////////////////////////////////
//
struct xImplSimd_PMinMax
{
// Compare packed unsigned byte integers in dest to src and store packed min/max
// values in dest.
const xImplSimd_DestRegEither UB;
// Compare packed signed word integers in dest to src and store packed min/max
// values in dest.
const xImplSimd_DestRegEither SW;
// [SSE-4.1] Compare packed signed byte integers in dest to src and store
// packed min/max values in dest. (SSE operands only)
const xImplSimd_DestRegSSE SB;
// [SSE-4.1] Compare packed signed doubleword integers in dest to src and store
// packed min/max values in dest. (SSE operands only)
const xImplSimd_DestRegSSE SD;
// [SSE-4.1] Compare packed unsigned word integers in dest to src and store
// packed min/max values in dest. (SSE operands only)
const xImplSimd_DestRegSSE UW;
// [SSE-4.1] Compare packed unsigned doubleword integers in dest to src and store
// packed min/max values in dest. (SSE operands only)
const xImplSimd_DestRegSSE UD;
};
} // end namespace x86Emitter

View File

@@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
// =====================================================================================================
// xImpl_SIMD Types (template free!)
// =====================================================================================================
// ------------------------------------------------------------------------
// For implementing SSE-only logic operations that have xmmreg,xmmreg/rm forms only,
// like ANDPS/ANDPD
//
struct xImplSimd_DestRegSSE
{
u8 Prefix;
u16 Opcode;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from) const;
};
// ------------------------------------------------------------------------
// For implementing SSE-only logic operations that have xmmreg,reg/rm,imm forms only
// (PSHUFD / PSHUFHW / etc).
//
struct xImplSimd_DestRegImmSSE
{
u8 Prefix;
u16 Opcode;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from, u8 imm) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from, u8 imm) const;
};
struct xImplSimd_DestSSE_CmpImm
{
u8 Prefix;
u16 Opcode;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from, SSE2_ComparisonType imm) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from, SSE2_ComparisonType imm) const;
};
// ------------------------------------------------------------------------
// For implementing SSE operations that have reg,reg/rm forms only,
// but accept either MM or XMM destinations (most PADD/PSUB and other P arithmetic ops).
//
struct xImplSimd_DestRegEither
{
u8 Prefix;
u16 Opcode;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from) const;
};
} // end namespace x86Emitter

View File

@@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
// --------------------------------------------------------------------------------------
// xImplSimd_MovHL
// --------------------------------------------------------------------------------------
// Moves to/from high/low portions of an xmm register.
// These instructions cannot be used in reg/reg form.
//
struct xImplSimd_MovHL
{
u16 Opcode;
void PS(const xRegisterSSE& to, const xIndirectVoid& from) const;
void PS(const xIndirectVoid& to, const xRegisterSSE& from) const;
void PD(const xRegisterSSE& to, const xIndirectVoid& from) const;
void PD(const xIndirectVoid& to, const xRegisterSSE& from) const;
};
// --------------------------------------------------------------------------------------
// xImplSimd_MovHL_RtoR
// --------------------------------------------------------------------------------------
// RegtoReg forms of MOVHL/MOVLH -- these are the same opcodes as MOVH/MOVL but
// do something kinda different! Fun!
//
struct xImplSimd_MovHL_RtoR
{
u16 Opcode;
void PS(const xRegisterSSE& to, const xRegisterSSE& from) const;
void PD(const xRegisterSSE& to, const xRegisterSSE& from) const;
};
// --------------------------------------------------------------------------------------
// xImplSimd_MoveSSE
// --------------------------------------------------------------------------------------
// Legends in their own right: MOVAPS / MOVAPD / MOVUPS / MOVUPD
//
// All implementations of Unaligned Movs will, when possible, use aligned movs instead.
// This happens when using Mem,Reg or Reg,Mem forms where the address is simple displacement
// which can be checked for alignment at runtime.
//
struct xImplSimd_MoveSSE
{
u8 Prefix;
bool isAligned;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from) const;
void operator()(const xIndirectVoid& to, const xRegisterSSE& from) const;
};
// --------------------------------------------------------------------------------------
// xImplSimd_MoveDQ
// --------------------------------------------------------------------------------------
// Implementations for MOVDQA / MOVDQU
//
// All implementations of Unaligned Movs will, when possible, use aligned movs instead.
// This happens when using Mem,Reg or Reg,Mem forms where the address is simple displacement
// which can be checked for alignment at runtime.
struct xImplSimd_MoveDQ
{
u8 Prefix;
bool isAligned;
void operator()(const xRegisterSSE& to, const xRegisterSSE& from) const;
void operator()(const xRegisterSSE& to, const xIndirectVoid& from) const;
void operator()(const xIndirectVoid& to, const xRegisterSSE& from) const;
};
// --------------------------------------------------------------------------------------
// xImplSimd_Blend
// --------------------------------------------------------------------------------------
// Blend - Conditional copying of values in src into dest.
//
struct xImplSimd_Blend
{
// [SSE-4.1] Conditionally copies dword values from src to dest, depending on the
// mask bits in the immediate operand (bits [3:0]). Each mask bit corresponds to a
// dword element in a 128-bit operand.
//
// If a mask bit is 1, then the corresponding dword in the source operand is copied
// to dest, else the dword element in dest is left unchanged.
//
xImplSimd_DestRegImmSSE PS;
// [SSE-4.1] Conditionally copies quadword values from src to dest, depending on the
// mask bits in the immediate operand (bits [1:0]). Each mask bit corresponds to a
// quadword element in a 128-bit operand.
//
// If a mask bit is 1, then the corresponding dword in the source operand is copied
// to dest, else the dword element in dest is left unchanged.
//
xImplSimd_DestRegImmSSE PD;
// [SSE-4.1] Conditionally copies dword values from src to dest, depending on the
// mask (bits [3:0]) in XMM0 (yes, the fixed register). Each mask bit corresponds
// to a dword element in the 128-bit operand.
//
// If a mask bit is 1, then the corresponding dword in the source operand is copied
// to dest, else the dword element in dest is left unchanged.
//
xImplSimd_DestRegSSE VPS;
// [SSE-4.1] Conditionally copies quadword values from src to dest, depending on the
// mask (bits [1:0]) in XMM0 (yes, the fixed register). Each mask bit corresponds
// to a quadword element in the 128-bit operand.
//
// If a mask bit is 1, then the corresponding dword in the source operand is copied
// to dest, else the dword element in dest is left unchanged.
//
xImplSimd_DestRegSSE VPD;
};
struct xImplSimd_PBlend
{
xImplSimd_DestRegImmSSE W;
xImplSimd_DestRegSSE VB;
};
// --------------------------------------------------------------------------------------
// xImplSimd_PMove
// --------------------------------------------------------------------------------------
// Packed Move with Sign or Zero extension.
//
struct xImplSimd_PMove
{
u16 OpcodeBase;
// [SSE-4.1] Zero/Sign-extend the low byte values in src into word integers
// and store them in dest.
void BW(const xRegisterSSE& to, const xRegisterSSE& from) const;
void BW(const xRegisterSSE& to, const xIndirect64& from) const;
// [SSE-4.1] Zero/Sign-extend the low byte values in src into dword integers
// and store them in dest.
void BD(const xRegisterSSE& to, const xRegisterSSE& from) const;
void BD(const xRegisterSSE& to, const xIndirect32& from) const;
// [SSE-4.1] Zero/Sign-extend the low byte values in src into qword integers
// and store them in dest.
void BQ(const xRegisterSSE& to, const xRegisterSSE& from) const;
void BQ(const xRegisterSSE& to, const xIndirect16& from) const;
// [SSE-4.1] Zero/Sign-extend the low word values in src into dword integers
// and store them in dest.
void WD(const xRegisterSSE& to, const xRegisterSSE& from) const;
void WD(const xRegisterSSE& to, const xIndirect64& from) const;
// [SSE-4.1] Zero/Sign-extend the low word values in src into qword integers
// and store them in dest.
void WQ(const xRegisterSSE& to, const xRegisterSSE& from) const;
void WQ(const xRegisterSSE& to, const xIndirect32& from) const;
// [SSE-4.1] Zero/Sign-extend the low dword values in src into qword integers
// and store them in dest.
void DQ(const xRegisterSSE& to, const xRegisterSSE& from) const;
void DQ(const xRegisterSSE& to, const xIndirect64& from) const;
};
} // namespace x86Emitter

View File

@@ -0,0 +1,226 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
namespace x86Emitter
{
// --------------------------------------------------------------------------------------
// xImplSimd_Shuffle
// --------------------------------------------------------------------------------------
struct xImplSimd_Shuffle
{
inline void _selector_assertion_check(u8 selector) const;
void PS(const xRegisterSSE& to, const xRegisterSSE& from, u8 selector) const;
void PS(const xRegisterSSE& to, const xIndirectVoid& from, u8 selector) const;
void PD(const xRegisterSSE& to, const xRegisterSSE& from, u8 selector) const;
void PD(const xRegisterSSE& to, const xIndirectVoid& from, u8 selector) const;
};
// --------------------------------------------------------------------------------------
// xImplSimd_PShuffle
// --------------------------------------------------------------------------------------
struct xImplSimd_PShuffle
{
// Copies doublewords from src and inserts them into dest at dword locations selected
// with the order operand (8 bit immediate).
const xImplSimd_DestRegImmSSE D;
// Copies words from the low quadword of src and inserts them into the low quadword
// of dest at word locations selected with the order operand (8 bit immediate).
// The high quadword of src is copied to the high quadword of dest.
const xImplSimd_DestRegImmSSE LW;
// Copies words from the high quadword of src and inserts them into the high quadword
// of dest at word locations selected with the order operand (8 bit immediate).
// The low quadword of src is copied to the low quadword of dest.
const xImplSimd_DestRegImmSSE HW;
// [sSSE-3] Performs in-place shuffles of bytes in dest according to the shuffle
// control mask in src. If the most significant bit (bit[7]) of each byte of the
// shuffle control mask is set, then constant zero is written in the result byte.
// Each byte in the shuffle control mask forms an index to permute the corresponding
// byte in dest. The value of each index is the least significant 4 bits (128-bit
// operation) or 3 bits (64-bit operation) of the shuffle control byte.
//
const xImplSimd_DestRegEither B;
// below is my test bed for a new system, free of subclasses. Was supposed to improve intellisense
// but it doesn't (makes it worse). Will try again in MSVC 2010. --air
#if 0
// Copies words from src and inserts them into dest at word locations selected with
// the order operand (8 bit immediate).
// Copies doublewords from src and inserts them into dest at dword locations selected
// with the order operand (8 bit immediate).
void D( const xRegisterSSE& to, const xRegisterSSE& from, u8 imm ) const { xOpWrite0F( 0x66, 0x70, to, from, imm ); }
void D( const xRegisterSSE& to, const xIndirectVoid& from, u8 imm ) const { xOpWrite0F( 0x66, 0x70, to, from, imm ); }
// Copies words from the low quadword of src and inserts them into the low quadword
// of dest at word locations selected with the order operand (8 bit immediate).
// The high quadword of src is copied to the high quadword of dest.
void LW( const xRegisterSSE& to, const xRegisterSSE& from, u8 imm ) const { xOpWrite0F( 0xf2, 0x70, to, from, imm ); }
void LW( const xRegisterSSE& to, const xIndirectVoid& from, u8 imm ) const { xOpWrite0F( 0xf2, 0x70, to, from, imm ); }
// Copies words from the high quadword of src and inserts them into the high quadword
// of dest at word locations selected with the order operand (8 bit immediate).
// The low quadword of src is copied to the low quadword of dest.
void HW( const xRegisterSSE& to, const xRegisterSSE& from, u8 imm ) const { xOpWrite0F( 0xf3, 0x70, to, from, imm ); }
void HW( const xRegisterSSE& to, const xIndirectVoid& from, u8 imm ) const { xOpWrite0F( 0xf3, 0x70, to, from, imm ); }
// [sSSE-3] Performs in-place shuffles of bytes in dest according to the shuffle
// control mask in src. If the most significant bit (bit[7]) of each byte of the
// shuffle control mask is set, then constant zero is written in the result byte.
// Each byte in the shuffle control mask forms an index to permute the corresponding
// byte in dest. The value of each index is the least significant 4 bits (128-bit
// operation) or 3 bits (64-bit operation) of the shuffle control byte.
//
void B( const xRegisterSSE& to, const xRegisterSSE& from ) const { OpWriteSSE( 0x66, 0x0038 ); }
void B( const xRegisterSSE& to, const xIndirectVoid& from ) const { OpWriteSSE( 0x66, 0x0038 ); }
#endif
};
// --------------------------------------------------------------------------------------
// SimdImpl_PUnpack
// --------------------------------------------------------------------------------------
struct SimdImpl_PUnpack
{
// Unpack and interleave low-order bytes from src and dest into dest.
const xImplSimd_DestRegEither LBW;
// Unpack and interleave low-order words from src and dest into dest.
const xImplSimd_DestRegEither LWD;
// Unpack and interleave low-order doublewords from src and dest into dest.
const xImplSimd_DestRegEither LDQ;
// Unpack and interleave low-order quadwords from src and dest into dest.
const xImplSimd_DestRegSSE LQDQ;
// Unpack and interleave high-order bytes from src and dest into dest.
const xImplSimd_DestRegEither HBW;
// Unpack and interleave high-order words from src and dest into dest.
const xImplSimd_DestRegEither HWD;
// Unpack and interleave high-order doublewords from src and dest into dest.
const xImplSimd_DestRegEither HDQ;
// Unpack and interleave high-order quadwords from src and dest into dest.
const xImplSimd_DestRegSSE HQDQ;
};
// --------------------------------------------------------------------------------------
// SimdImpl_Pack
// --------------------------------------------------------------------------------------
// Pack with Signed or Unsigned Saturation
//
struct SimdImpl_Pack
{
// Converts packed signed word integers from src and dest into packed signed
// byte integers in dest, using signed saturation.
const xImplSimd_DestRegEither SSWB;
// Converts packed signed dword integers from src and dest into packed signed
// word integers in dest, using signed saturation.
const xImplSimd_DestRegEither SSDW;
// Converts packed unsigned word integers from src and dest into packed unsigned
// byte integers in dest, using unsigned saturation.
const xImplSimd_DestRegEither USWB;
// [SSE-4.1] Converts packed unsigned dword integers from src and dest into packed
// unsigned word integers in dest, using signed saturation.
const xImplSimd_DestRegSSE USDW;
};
// --------------------------------------------------------------------------------------
// SimdImpl_Unpack
// --------------------------------------------------------------------------------------
struct xImplSimd_Unpack
{
// Unpacks the high doubleword [single-precision] values from src and dest into
// dest, such that the result of dest looks like this:
// dest[0] <- dest[2]
// dest[1] <- src[2]
// dest[2] <- dest[3]
// dest[3] <- src[3]
//
const xImplSimd_DestRegSSE HPS;
// Unpacks the high quadword [double-precision] values from src and dest into
// dest, such that the result of dest looks like this:
// dest.lo <- dest.hi
// dest.hi <- src.hi
//
const xImplSimd_DestRegSSE HPD;
// Unpacks the low doubleword [single-precision] values from src and dest into
// dest, such that the result of dest looks like this:
// dest[3] <- src[1]
// dest[2] <- dest[1]
// dest[1] <- src[0]
// dest[0] <- dest[0]
//
const xImplSimd_DestRegSSE LPS;
// Unpacks the low quadword [double-precision] values from src and dest into
// dest, effectively moving the low portion of src into the upper portion of dest.
// The result of dest is loaded as such:
// dest.hi <- src.lo
// dest.lo <- dest.lo [remains unchanged!]
//
const xImplSimd_DestRegSSE LPD;
};
// --------------------------------------------------------------------------------------
// SimdImpl_PInsert
// --------------------------------------------------------------------------------------
// PINSRW/B/D [all but Word form are SSE4.1 only!]
//
struct xImplSimd_PInsert
{
void B(const xRegisterSSE& to, const xRegister32& from, u8 imm8) const;
void B(const xRegisterSSE& to, const xIndirect32& from, u8 imm8) const;
void W(const xRegisterSSE& to, const xRegister32& from, u8 imm8) const;
void W(const xRegisterSSE& to, const xIndirect32& from, u8 imm8) const;
void D(const xRegisterSSE& to, const xRegister32& from, u8 imm8) const;
void D(const xRegisterSSE& to, const xIndirect32& from, u8 imm8) const;
void Q(const xRegisterSSE& to, const xRegister64& from, u8 imm8) const;
void Q(const xRegisterSSE& to, const xIndirect64& from, u8 imm8) const;
};
//////////////////////////////////////////////////////////////////////////////////////////
// PEXTRW/B/D [all but Word form are SSE4.1 only!]
//
// Note: Word form's indirect memory form is only available in SSE4.1.
//
struct SimdImpl_PExtract
{
// [SSE-4.1] Copies the byte element specified by imm8 from src to dest. The upper bits
// of dest are zero-extended (cleared). This can be used to extract any single packed
// byte value from src into an x86 32 bit register.
void B(const xRegister32& to, const xRegisterSSE& from, u8 imm8) const;
void B(const xIndirect32& dest, const xRegisterSSE& from, u8 imm8) const;
// Copies the word element specified by imm8 from src to dest. The upper bits
// of dest are zero-extended (cleared). This can be used to extract any single packed
// word value from src into an x86 32 bit register.
//
// [SSE-4.1] Note: Indirect memory forms of this instruction are an SSE-4.1 extension!
//
void W(const xRegister32& to, const xRegisterSSE& from, u8 imm8) const;
void W(const xIndirect32& dest, const xRegisterSSE& from, u8 imm8) const;
// [SSE-4.1] Copies the dword element specified by imm8 from src to dest. This can be
// used to extract any single packed dword value from src into an x86 32 bit register.
void D(const xRegister32& to, const xRegisterSSE& from, u8 imm8) const;
void D(const xIndirect32& dest, const xRegisterSSE& from, u8 imm8) const;
// Insert a qword integer value from r/m64 into the xmm1 at the destination element specified by imm8.
void Q(const xRegister64& to, const xRegisterSSE& from, u8 imm8) const;
void Q(const xIndirect64& dest, const xRegisterSSE& from, u8 imm8) const;
};
} // namespace x86Emitter

View File

@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
//////////////////////////////////////////////////////////////////////////////////////////
// MMX / SSE Helper Functions!
// ------------------------------------------------------------------------
// For implementing SSE-only logic operations that have xmmreg,xmmreg/rm forms only,
// like ANDPS/ANDPD
//
template <u8 Prefix, u16 Opcode>
class SimdImpl_DestRegSSE
{
public:
__forceinline void operator()(const xRegisterSSE& to, const xRegisterSSE& from) const { xOpWrite0F(Prefix, Opcode, to, from); }
__forceinline void operator()(const xRegisterSSE& to, const ModSibBase& from) const
{
bool isReallyAligned = ((from.Displacement & 0x0f) == 0) && from.Index.IsEmpty() && from.Base.IsEmpty();
pxAssertMsg(isReallyAligned, "Alignment check failed on SSE indirect load.");
xOpWrite0F(Prefix, Opcode, to, from);
}
SimdImpl_DestRegSSE() {} //GCWho?
};

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// Implementations found here: TEST + BTS/BT/BTC/BTR + BSF/BSR! (for lack of better location)
namespace x86Emitter
{
// --------------------------------------------------------------------------------------
// xImpl_Test
// --------------------------------------------------------------------------------------
//
struct xImpl_Test
{
void operator()(const xRegisterInt& to, const xRegisterInt& from) const;
void operator()(const xIndirect64orLess& dest, int imm) const;
void operator()(const xRegisterInt& to, int imm) const;
};
enum G8Type
{
G8Type_BT = 4,
G8Type_BTS,
G8Type_BTR,
G8Type_BTC,
};
// --------------------------------------------------------------------------------------
// BSF / BSR
// --------------------------------------------------------------------------------------
// 16/32 operands are available. No 8 bit ones, not that any of you cared, I bet.
//
struct xImpl_BitScan
{
// 0xbc [fwd] / 0xbd [rev]
u16 Opcode;
void operator()(const xRegister16or32or64& to, const xRegister16or32or64& from) const;
void operator()(const xRegister16or32or64& to, const xIndirectVoid& sibsrc) const;
};
// --------------------------------------------------------------------------------------
// xImpl_Group8
// --------------------------------------------------------------------------------------
// Bit Test Instructions - Valid on 16/32 bit instructions only.
//
struct xImpl_Group8
{
G8Type InstType;
void operator()(const xRegister16or32or64& bitbase, const xRegister16or32or64& bitoffset) const;
void operator()(const xRegister16or32or64& bitbase, u8 bitoffset) const;
void operator()(const xIndirectVoid& bitbase, const xRegister16or32or64& bitoffset) const;
void operator()(const xIndirect64& bitbase, u8 bitoffset) const;
void operator()(const xIndirect32& bitbase, u8 bitoffset) const;
void operator()(const xIndirect16& bitbase, u8 bitoffset) const;
};
} // End namespace x86Emitter

View File

@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// This header file is intended to be the future home of xchg, cmpxchg, xadd, and
// other threading-related exchange instructions.
namespace x86Emitter
{
} // End namespace x86Emitter