// SPDX-FileCopyrightText: 2002-2024 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;
	};

	// --------------------------------------------------------------------------------------
	//  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
