// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "Common/CommonTypes.h"
#include "Common/CPUDetect.h"
#include "Common/MsgHandler.h"
#include "Common/x64Emitter.h"
#include "Core/ConfigManager.h"
#include "Core/PowerPC/Jit64/Jit.h"
#include "Core/PowerPC/Jit64/JitRegCache.h"

using namespace Gen;

void Jit64::ps_mr(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITPairedOff);
	FALLBACK_IF(inst.Rc);

	int d = inst.FD;
	int b = inst.FB;
	if (d == b)
		return;

	fpr.BindToRegister(d, false);
	MOVAPD(fpr.RX(d), fpr.R(b));
}

void Jit64::ps_sum(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITPairedOff);
	FALLBACK_IF(inst.Rc);

	int d = inst.FD;
	int a = inst.FA;
	int b = inst.FB;
	int c = inst.FC;
	fpr.Lock(a, b, c, d);
	OpArg op_a = fpr.R(a);
	fpr.BindToRegister(d, d == b || d == c);
	X64Reg tmp = XMM1;
	MOVDDUP(tmp, op_a);   // {a.ps0, a.ps0}
	ADDPD(tmp, fpr.R(b)); // {a.ps0 + b.ps0, a.ps0 + b.ps1}
	switch (inst.SUBOP5)
	{
	case 10: // ps_sum0: {a.ps0 + b.ps1, c.ps1}
		UNPCKHPD(tmp, fpr.R(c));
		break;
	case 11: // ps_sum1: {c.ps0, a.ps0 + b.ps1}
		if (fpr.R(c).IsSimpleReg())
		{
			if (cpu_info.bSSE4_1)
			{
				BLENDPD(tmp, fpr.R(c), 1);
			}
			else
			{
				MOVAPD(XMM0, fpr.R(c));
				SHUFPD(XMM0, R(tmp), 2);
				tmp = XMM0;
			}
		}
		else
		{
			MOVLPD(tmp, fpr.R(c));
		}
		break;
	default:
		PanicAlert("ps_sum WTF!!!");
	}
	HandleNaNs(inst, fpr.RX(d), tmp, tmp == XMM1 ? XMM0 : XMM1);
	ForceSinglePrecision(fpr.RX(d), fpr.R(d));
	SetFPRFIfNeeded(fpr.RX(d));
	fpr.UnlockAll();
}


void Jit64::ps_muls(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITPairedOff);
	FALLBACK_IF(inst.Rc);

	int d = inst.FD;
	int a = inst.FA;
	int c = inst.FC;
	bool round_input = !jit->js.op->fprIsSingle[c];
	fpr.Lock(a, c, d);
	switch (inst.SUBOP5)
	{
	case 12: // ps_muls0
		MOVDDUP(XMM1, fpr.R(c));
		break;
	case 13: // ps_muls1
		avx_op(&XEmitter::VSHUFPD, &XEmitter::SHUFPD, XMM1, fpr.R(c), fpr.R(c), 3);
		break;
	default:
		PanicAlert("ps_muls WTF!!!");
	}
	if (round_input)
		Force25BitPrecision(XMM1, R(XMM1), XMM0);
	MULPD(XMM1, fpr.R(a));
	fpr.BindToRegister(d, false);
	HandleNaNs(inst, fpr.RX(d), XMM1);
	ForceSinglePrecision(fpr.RX(d), fpr.R(d));
	SetFPRFIfNeeded(fpr.RX(d));
	fpr.UnlockAll();
}


void Jit64::ps_mergeXX(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITPairedOff);
	FALLBACK_IF(inst.Rc);

	int d = inst.FD;
	int a = inst.FA;
	int b = inst.FB;
	fpr.Lock(a, b, d);
	fpr.BindToRegister(d, d == a || d == b);

	switch (inst.SUBOP10)
	{
	case 528:
		avx_op(&XEmitter::VUNPCKLPD, &XEmitter::UNPCKLPD, fpr.RX(d), fpr.R(a), fpr.R(b));
		break; //00
	case 560:
		avx_op(&XEmitter::VSHUFPD, &XEmitter::SHUFPD, fpr.RX(d), fpr.R(a), fpr.R(b), 2);
		break; //01
	case 592:
		avx_op(&XEmitter::VSHUFPD, &XEmitter::SHUFPD, fpr.RX(d), fpr.R(a), fpr.R(b), 1);
		break; //10
	case 624:
		avx_op(&XEmitter::VUNPCKHPD, &XEmitter::UNPCKHPD, fpr.RX(d), fpr.R(a), fpr.R(b));
		break; //11
	default:
		_assert_msg_(DYNA_REC, 0, "ps_merge - invalid op");
	}
	fpr.UnlockAll();
}

void Jit64::ps_rsqrte(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITFloatingPointOff);
	FALLBACK_IF(inst.Rc);
	int b = inst.FB;
	int d = inst.FD;

	gpr.FlushLockX(RSCRATCH_EXTRA);
	fpr.Lock(b, d);
	fpr.BindToRegister(b, true, false);
	fpr.BindToRegister(d, false);

	MOVSD(XMM0, fpr.R(b));
	CALL(asm_routines.frsqrte);
	MOVSD(fpr.R(d), XMM0);

	MOVHLPS(XMM0, fpr.RX(b));
	CALL(asm_routines.frsqrte);
	MOVLHPS(fpr.RX(d), XMM0);

	ForceSinglePrecision(fpr.RX(d), fpr.R(d));
	SetFPRFIfNeeded(fpr.RX(d));
	fpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::ps_res(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITFloatingPointOff);
	FALLBACK_IF(inst.Rc);
	int b = inst.FB;
	int d = inst.FD;

	gpr.FlushLockX(RSCRATCH_EXTRA);
	fpr.Lock(b, d);
	fpr.BindToRegister(b, true, false);
	fpr.BindToRegister(d, false);

	MOVSD(XMM0, fpr.R(b));
	CALL(asm_routines.fres);
	MOVSD(fpr.R(d), XMM0);

	MOVHLPS(XMM0, fpr.RX(b));
	CALL(asm_routines.fres);
	MOVLHPS(fpr.RX(d), XMM0);

	ForceSinglePrecision(fpr.RX(d), fpr.R(d));
	SetFPRFIfNeeded(fpr.RX(d));
	fpr.UnlockAll();
	gpr.UnlockAllX();
}

void Jit64::ps_cmpXX(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITFloatingPointOff);

	FloatCompare(inst, !!(inst.SUBOP10 & 64));
}
