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

#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Core/ConfigManager.h"
#include "Core/HW/CPU.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/JitILCommon/JitILBase.h"

void JitILBase::lhax(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
	if (inst.RA)
		addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));

	IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
	val = ibuild.EmitSExt16(val);
	ibuild.EmitStoreGReg(val, inst.RD);
}

void JitILBase::lhaux(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
	addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));

	IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
	val = ibuild.EmitSExt16(val);
	ibuild.EmitStoreGReg(val, inst.RD);
	ibuild.EmitStoreGReg(addr, inst.RA);
}

void JitILBase::lXz(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
	if (inst.RA)
		addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
	if (inst.OPCD & 1)
		ibuild.EmitStoreGReg(addr, inst.RA);

	IREmitter::InstLoc val;

	// Idle Skipping.
	// TODO: This really should be done somewhere else. Either lower in the IR
	// or higher in PPCAnalyst
	// TODO: We shouldn't use debug reads here.
	if (SConfig::GetInstance().bSkipIdle &&
		CPU::GetState() != CPU::CPU_STEPPING &&
		inst.OPCD == 32 && // Lwx
		(inst.hex & 0xFFFF0000) == 0x800D0000 &&
		(PowerPC::HostRead_U32(js.compilerPC + 4) == 0x28000000 ||
		(SConfig::GetInstance().bWii && PowerPC::HostRead_U32(js.compilerPC + 4) == 0x2C000000)) &&
		PowerPC::HostRead_U32(js.compilerPC + 8) == 0x4182fff8)
	{
		val = ibuild.EmitLoad32(addr);
		ibuild.EmitIdleBranch(val, ibuild.EmitIntConst(js.compilerPC));
		ibuild.EmitStoreGReg(val, inst.RD);
		return;
	}

	switch (inst.OPCD & ~0x1)
	{
	case 32: // lwz
		val = ibuild.EmitLoad32(addr);
		break;
	case 40: // lhz
		val = ibuild.EmitLoad16(addr);
		break;
	case 34: // lbz
		val = ibuild.EmitLoad8(addr);
		break;
	default:
		PanicAlert("lXz: invalid access size");
		val = nullptr;
		break;
	}

	ibuild.EmitStoreGReg(val, inst.RD);
}

void JitILBase::lbzu(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	const IREmitter::InstLoc uAddress = ibuild.EmitAdd(ibuild.EmitLoadGReg(inst.RA), ibuild.EmitIntConst((int)inst.SIMM_16));
	const IREmitter::InstLoc temp = ibuild.EmitLoad8(uAddress);
	ibuild.EmitStoreGReg(temp, inst.RD);
	ibuild.EmitStoreGReg(uAddress, inst.RA);
}

void JitILBase::lha(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitIntConst((s32)(s16)inst.SIMM_16);

	if (inst.RA)
		addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));

	IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
	val = ibuild.EmitSExt16(val);
	ibuild.EmitStoreGReg(val, inst.RD);
}

void JitILBase::lhau(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitIntConst((s32)inst.SIMM_16);

	addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));

	IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
	val = ibuild.EmitSExt16(val);
	ibuild.EmitStoreGReg(val, inst.RD);
	ibuild.EmitStoreGReg(addr, inst.RA);
}

void JitILBase::lXzx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);

	if (inst.RA)
	{
		addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
		if (inst.SUBOP10 & 32)
			ibuild.EmitStoreGReg(addr, inst.RA);
	}

	IREmitter::InstLoc val;
	switch (inst.SUBOP10 & ~32)
	{
	default:
		PanicAlert("lXzx: invalid access size");
	case 23:  // lwzx
		val = ibuild.EmitLoad32(addr);
		break;
	case 279: // lhzx
		val = ibuild.EmitLoad16(addr);
		break;
	case 87:  // lbzx
		val = ibuild.EmitLoad8(addr);
		break;
	}
	ibuild.EmitStoreGReg(val, inst.RD);
}

void JitILBase::dcbst(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);

	// If the dcbst instruction is preceded by dcbt, it is flushing a prefetched
	// memory location.  Do not invalidate the JIT cache in this case as the memory
	// will be the same.
	// dcbt = 0x7c00022c
	// TODO: We shouldn't use a debug read here; it should be possible to get the
	// previous instruction from the JIT state.
	FALLBACK_IF((PowerPC::HostRead_U32(js.compilerPC - 4) & 0x7c00022c) != 0x7c00022c);
}

// Zero cache line.
void JitILBase::dcbz(UGeckoInstruction inst)
{
	FALLBACK_IF(true);

	// TODO!
#if 0
	if (SConfig::GetInstance().bJITOff || SConfig::GetInstance().bJITLoadStoreOff)
	{
		Default(inst);
		return;
	}
	INSTRUCTION_START;
		MOV(32, R(RSCRATCH), gpr.R(inst.RB));
	if (inst.RA)
		ADD(32, R(RSCRATCH), gpr.R(inst.RA));
	AND(32, R(RSCRATCH), Imm32(~31));
	PXOR(XMM0, R(XMM0));
	MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 0), XMM0);
	MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 16), XMM0);
#endif
}

void JitILBase::stX(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
	IREmitter::InstLoc value = ibuild.EmitLoadGReg(inst.RS);

	if (inst.RA)
		addr = ibuild.EmitAdd(ibuild.EmitLoadGReg(inst.RA), addr);
	if (inst.OPCD & 1)
		ibuild.EmitStoreGReg(addr, inst.RA);

	switch (inst.OPCD & ~1)
	{
	case 36: // stw
		ibuild.EmitStore32(value, addr);
		break;
	case 44: // sth
		ibuild.EmitStore16(value, addr);
		break;
	case 38: // stb
		ibuild.EmitStore8(value, addr);
		break;
	default:
		_assert_msg_(DYNA_REC, 0, "stX: Invalid access size.");
		return;
	}
}

void JitILBase::stXx(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
	IREmitter::InstLoc value = ibuild.EmitLoadGReg(inst.RS);

	addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));

	if (inst.SUBOP10 & 32)
		ibuild.EmitStoreGReg(addr, inst.RA);

	switch (inst.SUBOP10 & ~32)
	{
	case 151: // stw
		ibuild.EmitStore32(value, addr);
		break;
	case 407: // sth
		ibuild.EmitStore16(value, addr);
		break;
	case 215: // stb
		ibuild.EmitStore8(value, addr);
		break;
	default:
		_assert_msg_(DYNA_REC, 0, "stXx: Invalid store size.");
		return;
	}
}

// A few games use these heavily in video codecs. (GFZP01 @ 0x80020E18)
void JitILBase::lmw(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);

	if (inst.RA)
		addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));

	for (int i = inst.RD; i < 32; i++)
	{
		IREmitter::InstLoc val = ibuild.EmitLoad32(addr);
		ibuild.EmitStoreGReg(val, i);
		addr = ibuild.EmitAdd(addr, ibuild.EmitIntConst(4));
	}
}

void JitILBase::stmw(UGeckoInstruction inst)
{
	INSTRUCTION_START
	JITDISABLE(bJITLoadStoreOff);
	FALLBACK_IF(jo.memcheck);

	IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);

	if (inst.RA)
		addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));

	for (int i = inst.RD; i < 32; i++)
	{
		IREmitter::InstLoc val = ibuild.EmitLoadGReg(i);
		ibuild.EmitStore32(val, addr);
		addr = ibuild.EmitAdd(addr, ibuild.EmitIntConst(4));
	}
}

void JitILBase::icbi(UGeckoInstruction inst)
{
	FallBackToInterpreter(inst);
	ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4));
}
