#include <stddef.h>
#include "MA_MIPSIV.h"
#include "MIPS.h"
#include "Jitter.h"
#include "MemoryUtils.h"
#include "COP_SCU.h"
#include "offsetof_def.h"
#include "placeholder_def.h"

uint32 g_LWMaskRight[4] =
{
	0x00FFFFFF,
	0x0000FFFF,
	0x000000FF,
	0x00000000,
};

uint32 g_LWMaskLeft[4] =
{
	0xFFFFFF00,
	0xFFFF0000,
	0xFF000000,
	0x00000000,
};

uint64 g_LDMaskRight[8] =
{
	0x00FFFFFFFFFFFFFFULL,
	0x0000FFFFFFFFFFFFULL,
	0x000000FFFFFFFFFFULL,
	0x00000000FFFFFFFFULL,
	0x0000000000FFFFFFULL,
	0x000000000000FFFFULL,
	0x00000000000000FFULL,
	0x0000000000000000ULL,
};

uint64 g_LDMaskLeft[8] =
{
	0xFFFFFFFFFFFFFF00ULL,
	0xFFFFFFFFFFFF0000ULL,
	0xFFFFFFFFFF000000ULL,
	0xFFFFFFFF00000000ULL,
	0xFFFFFF0000000000ULL,
	0xFFFF000000000000ULL,
	0xFF00000000000000ULL,
	0x0000000000000000ULL,
};

extern "C" uint32 LWL_Proxy(uint32 address, uint32 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x03;
	uint32 byteOffset = address & 0x03;
	uint32 accessType = 3 - byteOffset;
	uint32 memory = MemoryUtils_GetWordProxy(context, alignedAddress);
	memory <<= accessType * 8;
	rt &= g_LWMaskRight[byteOffset];
	rt |= memory;
	return rt;
}

extern "C" uint32 LWR_Proxy(uint32 address, uint32 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x03;
	uint32 byteOffset = address & 0x03;
	uint32 accessType = 3 - byteOffset;
	uint32 memory = MemoryUtils_GetWordProxy(context, alignedAddress);
	memory >>= byteOffset * 8;
	rt &= g_LWMaskLeft[accessType];
	rt |= memory;
	return rt;
}

uint64 LDL_Proxy(uint32 address, uint64 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x07;
	uint32 byteOffset = address & 0x07;
	uint32 accessType = 7 - byteOffset;
	uint64 memory = MemoryUtils_GetDoubleProxy(context, alignedAddress);
	memory <<= accessType * 8;
	rt &= g_LDMaskRight[byteOffset];
	rt |= memory;
	return rt;
}

uint64 LDR_Proxy(uint32 address, uint64 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x07;
	uint32 byteOffset = address & 0x07;
	uint32 accessType = 7 - byteOffset;
	uint64 memory = MemoryUtils_GetDoubleProxy(context, alignedAddress);
	memory >>= byteOffset * 8;
	rt &= g_LDMaskLeft[accessType];
	rt |= memory;
	return rt;
}

extern "C" void SWL_Proxy(uint32 address, uint32 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x03;
	uint32 byteOffset = address & 0x03;
	uint32 accessType = 3 - byteOffset;
	rt >>= accessType * 8;
	uint32 memory = MemoryUtils_GetWordProxy(context, alignedAddress);
	memory &= g_LWMaskLeft[byteOffset];
	memory |= rt;
	MemoryUtils_SetWordProxy(context, memory, alignedAddress);
}

extern "C" void SWR_Proxy(uint32 address, uint32 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x03;
	uint32 byteOffset = address & 0x03;
	uint32 accessType = 3 - byteOffset;
	rt <<= byteOffset * 8;
	uint32 memory = MemoryUtils_GetWordProxy(context, alignedAddress);
	memory &= g_LWMaskRight[accessType];
	memory |= rt;
	MemoryUtils_SetWordProxy(context, memory, alignedAddress);
}

void SDL_Proxy(uint32 address, uint64 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x07;
	uint32 byteOffset = address & 0x07;
	uint32 accessType = 7 - byteOffset;
	rt >>= accessType * 8;
	uint64 memory = MemoryUtils_GetDoubleProxy(context, alignedAddress);
	memory &= g_LDMaskLeft[byteOffset];
	memory |= rt;
	MemoryUtils_SetDoubleProxy(context, memory, alignedAddress);
}

void SDR_Proxy(uint32 address, uint64 rt, CMIPS* context)
{
	uint32 alignedAddress = address & ~0x07;
	uint32 byteOffset = address & 0x07;
	uint32 accessType = 7 - byteOffset;
	rt <<= byteOffset * 8;
	uint64 memory = MemoryUtils_GetDoubleProxy(context, alignedAddress);
	memory &= g_LDMaskRight[accessType];
	memory |= rt;
	MemoryUtils_SetDoubleProxy(context, memory, alignedAddress);
}

CMA_MIPSIV::CMA_MIPSIV(MIPS_REGSIZE nRegSize) :
CMIPSArchitecture(nRegSize)
{
	SetupInstructionTables();
	SetupReflectionTables();
}

CMA_MIPSIV::~CMA_MIPSIV()
{

}

void CMA_MIPSIV::SetupInstructionTables()
{
	for(unsigned int i = 0; i < MAX_GENERAL_OPS; i++)
	{
		m_pOpGeneral[i] = std::bind(m_cOpGeneral[i], this);
	}

	for(unsigned int i = 0; i < MAX_SPECIAL_OPS; i++)
	{
		m_pOpSpecial[i] = std::bind(m_cOpSpecial[i], this);
	}

	for(unsigned int i = 0; i < MAX_SPECIAL2_OPS; i++)
	{
		m_pOpSpecial2[i] = std::bind(&CMA_MIPSIV::Illegal, this);
	}

	for(unsigned int i = 0; i < MAX_REGIMM_OPS; i++)
	{
		m_pOpRegImm[i] = std::bind(m_cOpRegImm[i], this);
	}
}

void CMA_MIPSIV::CompileInstruction(uint32 nAddress, CMipsJitter* codeGen, CMIPS* pCtx)
{
	SetupQuickVariables(nAddress, codeGen, pCtx);

	m_nRS			= (uint8)((m_nOpcode >> 21) & 0x1F);
	m_nRT			= (uint8)((m_nOpcode >> 16) & 0x1F);
	m_nRD			= (uint8)((m_nOpcode >> 11) & 0x1F);
	m_nSA			= (uint8)((m_nOpcode >> 6) & 0x1F);
	m_nImmediate	= (uint16)(m_nOpcode & 0xFFFF);

	if(m_nOpcode)
	{
		m_pOpGeneral[(m_nOpcode >> 26)]();
	}
}

void CMA_MIPSIV::SPECIAL()
{
	m_pOpSpecial[m_nImmediate & 0x3F]();
}

void CMA_MIPSIV::SPECIAL2()
{
	m_pOpSpecial2[m_nImmediate & 0x3F]();
}

void CMA_MIPSIV::REGIMM()
{
	m_pOpRegImm[m_nRT]();
}

//////////////////////////////////////////////////
//General Opcodes
//////////////////////////////////////////////////

//02
void CMA_MIPSIV::J()
{
	m_codeGen->PushCst((m_nAddress & 0xF0000000) | ((m_nOpcode & 0x03FFFFFF) << 2));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));
}

//03
void CMA_MIPSIV::JAL()
{
	//64-bit addresses?

	//Save the address in RA
	m_codeGen->PushCst(m_nAddress + 8);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[31].nV[0]));

	//Update jump address
	m_codeGen->PushCst((m_nAddress & 0xF0000000) | ((m_nOpcode & 0x03FFFFFF) << 2));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));
}

//04
void CMA_MIPSIV::BEQ()
{
	Template_BranchEq(true, false);
}

//05
void CMA_MIPSIV::BNE()
{
	Template_BranchEq(false, false);
}

//06
void CMA_MIPSIV::BLEZ()
{
	//Less/Equal & Not Likely
	Template_BranchLez(true, false);
}

//07
void CMA_MIPSIV::BGTZ()
{
	//Not Less/Equal & Not Likely
	Template_BranchLez(false, false);
}

//08
void CMA_MIPSIV::ADDI()
{
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PushCst(static_cast<int16>(m_nImmediate));
	m_codeGen->Add();
	if(m_regSize == MIPS_REGSIZE_64)
	{
		m_codeGen->PushTop();
		m_codeGen->SignExt();
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
}

//09
void CMA_MIPSIV::ADDIU()
{
	if(m_nRT == 0 && m_nRS == 0) 
	{
		//Hack: PS2 IOP uses ADDIU R0, R0, $x for dynamic linking
		m_codeGen->PushCst(m_nAddress);
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nCOP0[CCOP_SCU::EPC]));

		m_codeGen->PushCst(MIPS_EXCEPTION_SYSCALL);
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nHasException));
	}
	else
	{
		if(m_nRT == 0) return;

		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
		m_codeGen->PushCst(static_cast<int16>(m_nImmediate));
		m_codeGen->Add();
		if(m_regSize == MIPS_REGSIZE_64)
		{
			m_codeGen->PushTop();
			m_codeGen->SignExt();
			m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
		}
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	}
}

//0A
void CMA_MIPSIV::SLTI()
{
	Template_SetLessThanImm(true);
}

//0B
void CMA_MIPSIV::SLTIU()
{
	Template_SetLessThanImm(false);
}

//0C
void CMA_MIPSIV::ANDI()
{
	if(m_nRT == 0) return;

	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PushCst(m_nImmediate);
	
	m_codeGen->And();
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));

	if(m_regSize == MIPS_REGSIZE_64)
	{
		m_codeGen->PushCst(0);
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
}

//0D
void CMA_MIPSIV::ORI()
{
	if(m_nRT == 0) return;

	//Lower 32-bits
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PushCst(m_nImmediate);
	m_codeGen->Or();
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));

	//Higher 32-bits (only if registers are different)
	if((m_regSize == MIPS_REGSIZE_64) && (m_nRS != m_nRT))
	{
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[1]));
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
}

//0E
void CMA_MIPSIV::XORI()
{
	if(m_nRT == 0) return;

	//Lower 32-bits
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PushCst(m_nImmediate);
	m_codeGen->Xor();
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));

	//Higher 32-bits
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[1]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
}

//0F
void CMA_MIPSIV::LUI()
{
	if(m_nRT == 0) return;

	m_codeGen->PushCst(m_nImmediate << 16);
	if(m_regSize == MIPS_REGSIZE_64)
	{
		m_codeGen->PushCst((m_nImmediate & 0x8000) ? 0xFFFFFFFF : 0x00000000);
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
}

//10
void CMA_MIPSIV::COP0()
{
	if(m_pCtx->m_pCOP[0] != NULL)
	{
		m_pCtx->m_pCOP[0]->CompileInstruction(m_nAddress, m_codeGen, m_pCtx);
	}
	else
	{
		Illegal();
	}
}

//11
void CMA_MIPSIV::COP1()
{
	if(m_pCtx->m_pCOP[1] != NULL)
	{
		m_pCtx->m_pCOP[1]->CompileInstruction(m_nAddress, m_codeGen, m_pCtx);
	}
	else
	{
		Illegal();
	}
}

//12
void CMA_MIPSIV::COP2()
{
	if(m_pCtx->m_pCOP[2] != NULL)
	{
		m_pCtx->m_pCOP[2]->CompileInstruction(m_nAddress, m_codeGen, m_pCtx);
	}
	else
	{
		Illegal();
	}
}

//14
void CMA_MIPSIV::BEQL()
{
	Template_BranchEq(true, true);
}

//15
void CMA_MIPSIV::BNEL()
{
	Template_BranchEq(false, true);
}

//16
void CMA_MIPSIV::BLEZL()
{
	//Less/Equal & Likely
	Template_BranchLez(true, true);
}

//17
void CMA_MIPSIV::BGTZL()
{
	//Not Less/Equal & Likely
	Template_BranchLez(false, true);
}

//18
void CMA_MIPSIV::DADDI()
{
	//TODO: Check for overflow
	DADDIU();
}

//19
void CMA_MIPSIV::DADDIU()
{
	if(m_nRT == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PushCst64(static_cast<int16>(m_nImmediate));
	m_codeGen->Add64();
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
}

//1A
void CMA_MIPSIV::LDL()
{
	if(m_nRT == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	ComputeMemAccessAddr();
	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&LDL_Proxy), 3, Jitter::CJitter::RETURN_VALUE_64);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));
}

//1B
void CMA_MIPSIV::LDR()
{
	if(m_nRT == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	ComputeMemAccessAddr();
	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&LDR_Proxy), 3, Jitter::CJitter::RETURN_VALUE_64);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));
}

//20
void CMA_MIPSIV::LB()
{
	if(m_nRT == 0) return;

	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushIdx(1);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_GetByteProxy), 2, true);

	m_codeGen->SignExt8();
	if(m_regSize == MIPS_REGSIZE_64)
	{
		m_codeGen->PushTop();
		m_codeGen->SignExt();
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));

	m_codeGen->PullTop();
}

//21
void CMA_MIPSIV::LH()
{
	if(m_nRT == 0) return;

	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushIdx(1);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_GetHalfProxy), 2, true);

	m_codeGen->SignExt16();
	if(m_regSize == MIPS_REGSIZE_64)
	{
		m_codeGen->PushTop();
		m_codeGen->SignExt();
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));

	m_codeGen->PullTop();
}

//22
void CMA_MIPSIV::LWL()
{
	if(m_nRT == 0) return;

	ComputeMemAccessAddr();
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&LWL_Proxy), 3, true);

	if(m_regSize == MIPS_REGSIZE_64)
	{
		m_codeGen->PushTop();
		m_codeGen->SignExt();
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
}

//23
void CMA_MIPSIV::LW()
{
	Template_LoadUnsigned32(reinterpret_cast<void*>(&MemoryUtils_GetWordProxy));
}

//24
void CMA_MIPSIV::LBU()
{
	Template_LoadUnsigned32(reinterpret_cast<void*>(&MemoryUtils_GetByteProxy));
}

//25
void CMA_MIPSIV::LHU()
{
	Template_LoadUnsigned32(reinterpret_cast<void*>(&MemoryUtils_GetHalfProxy));
}

//26
void CMA_MIPSIV::LWR()
{
	if(m_nRT == 0) return;

	ComputeMemAccessAddr();
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&LWR_Proxy), 3, true);

	if(m_regSize == MIPS_REGSIZE_64)
	{
		m_codeGen->PushTop();
		m_codeGen->SignExt();
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));
	}
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
}

//27
void CMA_MIPSIV::LWU()
{
	if(m_nRT == 0) return;

	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushIdx(1);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_GetWordProxy), 2, true);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));

	m_codeGen->PushCst(0);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[1]));

	m_codeGen->PullTop();
}

//28
void CMA_MIPSIV::SB()
{
	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushIdx(2);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_SetByteProxy), 3, false);

	m_codeGen->PullTop();
}

//29
void CMA_MIPSIV::SH()
{
	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushIdx(2);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_SetHalfProxy), 3, false);

	m_codeGen->PullTop();
}

//2A
void CMA_MIPSIV::SWL()
{
	ComputeMemAccessAddr();
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&SWL_Proxy), 3, false);
}

//2B
void CMA_MIPSIV::SW()
{
	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushIdx(2);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_SetWordProxy), 3, false);

	m_codeGen->PullTop();
}

//2C
void CMA_MIPSIV::SDL()
{
	assert(m_regSize == MIPS_REGSIZE_64);

	ComputeMemAccessAddr();
	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&SDL_Proxy), 3, false);
}

//2D
void CMA_MIPSIV::SDR()
{
	assert(m_regSize == MIPS_REGSIZE_64);

	ComputeMemAccessAddr();
	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&SDR_Proxy), 3, false);
}

//2E
void CMA_MIPSIV::SWR()
{
	ComputeMemAccessAddr();
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushCtx();
	m_codeGen->Call(reinterpret_cast<void*>(&SWR_Proxy), 3, false);
}

//2F
void CMA_MIPSIV::CACHE()
{
	//No cache in our system. Nothing to do.
}

//31
void CMA_MIPSIV::LWC1()
{
	if(m_pCtx->m_pCOP[1] != NULL)
	{
		m_pCtx->m_pCOP[1]->CompileInstruction(m_nAddress, m_codeGen, m_pCtx);
	}
	else
	{
		Illegal();
	}
}

//33
void CMA_MIPSIV::PREF()
{
	//Nothing to do
}

//36
void CMA_MIPSIV::LDC2()
{
	if(m_pCtx->m_pCOP[2] != NULL)
	{
		m_pCtx->m_pCOP[2]->CompileInstruction(m_nAddress, m_codeGen, m_pCtx);
	}
	else
	{
		Illegal();
	}
}

//37
void CMA_MIPSIV::LD()
{
	if(m_nRT == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushIdx(1);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_GetDoubleProxy), 2, Jitter::CJitter::RETURN_VALUE_64);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));

	m_codeGen->PullTop();
}

//39
void CMA_MIPSIV::SWC1()
{
	if(m_pCtx->m_pCOP[1] != NULL)
	{
		m_pCtx->m_pCOP[1]->CompileInstruction(m_nAddress, m_codeGen, m_pCtx);
	}
	else
	{
		Illegal();
	}
}

//3E
void CMA_MIPSIV::SDC2()
{
	if(m_pCtx->m_pCOP[2] != NULL)
	{
		m_pCtx->m_pCOP[2]->CompileInstruction(m_nAddress, m_codeGen, m_pCtx);
	}
	else
	{
		Illegal();
	}
}

//3F
void CMA_MIPSIV::SD()
{
	assert(m_regSize == MIPS_REGSIZE_64);

	ComputeMemAccessAddr();

	m_codeGen->PushCtx();
	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT]));
	m_codeGen->PushIdx(2);
	m_codeGen->Call(reinterpret_cast<void*>(&MemoryUtils_SetDoubleProxy), 3, Jitter::CJitter::RETURN_VALUE_NONE);

	m_codeGen->PullTop();
}

//////////////////////////////////////////////////
//Special Opcodes
//////////////////////////////////////////////////

//00
void CMA_MIPSIV::SLL()
{
	void (Jitter::CJitter::*shiftFunction)(uint8) = &Jitter::CJitter::Shl;
	Template_ShiftCst32(std::bind(shiftFunction, m_codeGen, std::placeholders::_1));
}

//02
void CMA_MIPSIV::SRL()
{
	void (Jitter::CJitter::*shiftFunction)(uint8) = &Jitter::CJitter::Srl;	
	Template_ShiftCst32(std::bind(shiftFunction, m_codeGen, std::placeholders::_1));
}

//03
void CMA_MIPSIV::SRA()
{
	void (Jitter::CJitter::*shiftFunction)(uint8) = &Jitter::CJitter::Sra;	
	Template_ShiftCst32(std::bind(shiftFunction, m_codeGen, std::placeholders::_1));
}

//04
void CMA_MIPSIV::SLLV()
{
	void (Jitter::CJitter::*shiftFunction)() = &Jitter::CJitter::Shl;
	Template_ShiftVar32(std::bind(shiftFunction, m_codeGen));
}

//06
void CMA_MIPSIV::SRLV()
{
	void (Jitter::CJitter::*shiftFunction)() = &Jitter::CJitter::Srl;	
	Template_ShiftVar32(std::bind(shiftFunction, m_codeGen));
}

//07
void CMA_MIPSIV::SRAV()
{
	void (Jitter::CJitter::*shiftFunction)() = &Jitter::CJitter::Sra;
	Template_ShiftVar32(std::bind(shiftFunction, m_codeGen));
}

//08
void CMA_MIPSIV::JR()
{
	//TODO: 64-bits addresses
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));
}

//09
void CMA_MIPSIV::JALR()
{
	//TODO: 64-bits addresses

	//Set the jump address
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nDelayedJumpAddr));

	//Save address in register
	m_codeGen->PushCst(m_nAddress + 8);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//0A
void CMA_MIPSIV::MOVZ()
{
	Template_MovEqual(true);
}

//0B
void CMA_MIPSIV::MOVN()
{
	Template_MovEqual(false);
}

//0C
void CMA_MIPSIV::SYSCALL()
{
	//Save current EPC
	m_codeGen->PushCst(m_nAddress);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nCOP0[CCOP_SCU::EPC]));
	m_codeGen->PushCst(MIPS_EXCEPTION_SYSCALL);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nHasException));
}

//0D
void CMA_MIPSIV::BREAK()
{
	//NOP
}

//0F
void CMA_MIPSIV::SYNC()
{
	//NOP
}

//10
void CMA_MIPSIV::MFHI()
{
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nHI[0]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));

	m_codeGen->PushRel(offsetof(CMIPS, m_State.nHI[1]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[1]));
}

//11
void CMA_MIPSIV::MTHI()
{
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nHI[0]));

	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[1]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nHI[1]));
}

//12
void CMA_MIPSIV::MFLO()
{
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nLO[0]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));

	m_codeGen->PushRel(offsetof(CMIPS, m_State.nLO[1]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[1]));
}

//13
void CMA_MIPSIV::MTLO()
{
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nLO[0]));

	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[1]));
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nLO[1]));
}

//14
void CMA_MIPSIV::DSLLV()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->Shl64();
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//16
void CMA_MIPSIV::DSRLV()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->Srl64();
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//17
void CMA_MIPSIV::DSRAV()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
	m_codeGen->Sra64();
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//18
void CMA_MIPSIV::MULT()
{
	Template_Mult32(true, 0);
}

//19
void CMA_MIPSIV::MULTU()
{
	Template_Mult32(false, 0);
}

//1A
void CMA_MIPSIV::DIV()
{
	Template_Div32(true, 0);
}

//1B
void CMA_MIPSIV::DIVU()
{
	Template_Div32(false, 0);
}

//20
void CMA_MIPSIV::ADD()
{
	Template_Add32(true);
}

//21
void CMA_MIPSIV::ADDU()
{
	Template_Add32(false);
}

//22
void CMA_MIPSIV::SUB()
{
	Template_Sub32(true);
}

//23
void CMA_MIPSIV::SUBU()
{
	Template_Sub32(false);
}

//24
void CMA_MIPSIV::AND()
{
	if(m_nRD == 0) return;

	if(m_regSize == MIPS_REGSIZE_32)
	{
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
		m_codeGen->And();
		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
	}
	else
	{
		m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[0]));
		m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
		m_codeGen->And64();
		m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
	}
}

//25
void CMA_MIPSIV::OR()
{
	if(m_nRD == 0) return;

	//TODO: Use a 64-bits op
	unsigned int regCount = (m_regSize == MIPS_REGSIZE_64) ? 2 : 1;
	for(unsigned int i = 0; i < regCount; i++)
	{
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[i]));
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[i]));

		m_codeGen->Or();

		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[i]));
	}
}

//26
void CMA_MIPSIV::XOR()
{
	if(m_nRD == 0) return;

	unsigned int regCount = (m_regSize == MIPS_REGSIZE_64) ? 2 : 1;
	for(unsigned int i = 0; i < regCount; i++)
	{
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[i]));
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[i]));

		m_codeGen->Xor();

		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[i]));
	}
}

//27
void CMA_MIPSIV::NOR()
{
	if(m_nRD == 0) return;

	unsigned int regCount = (m_regSize == MIPS_REGSIZE_64) ? 2 : 1;
	for(unsigned int i = 0; i < regCount; i++)
	{
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRS].nV[i]));
		m_codeGen->PushRel(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[i]));

		m_codeGen->Or();
		m_codeGen->Not();

		m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[i]));
	}
}

//2A
void CMA_MIPSIV::SLT()
{
	Template_SetLessThanReg(true);
}

//2B
void CMA_MIPSIV::SLTU()
{
	Template_SetLessThanReg(false);
}

//2C
void CMA_MIPSIV::DADD()
{
	Template_Add64(true);
}

//2D
void CMA_MIPSIV::DADDU()
{
	Template_Add64(false);
}

//2E
void CMA_MIPSIV::DSUB()
{
	Template_Sub64(true);
}

//2F
void CMA_MIPSIV::DSUBU()
{
	Template_Sub64(false);
}

//34
void CMA_MIPSIV::TEQ()
{
	//Not implemented
	assert(false);
}

//38
void CMA_MIPSIV::DSLL()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->Shl64(m_nSA);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//3A
void CMA_MIPSIV::DSRL()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->Srl64(m_nSA);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//3B
void CMA_MIPSIV::DSRA()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->Sra64(m_nSA);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//3C
void CMA_MIPSIV::DSLL32()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->Shl64(m_nSA + 32);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//3E
void CMA_MIPSIV::DSRL32()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->Srl64(m_nSA + 32);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//3F
void CMA_MIPSIV::DSRA32()
{
	if(m_nRD == 0) return;

	assert(m_regSize == MIPS_REGSIZE_64);

	m_codeGen->PushRel64(offsetof(CMIPS, m_State.nGPR[m_nRT].nV[0]));
	m_codeGen->Sra64(m_nSA + 32);
	m_codeGen->PullRel64(offsetof(CMIPS, m_State.nGPR[m_nRD].nV[0]));
}

//////////////////////////////////////////////////
//RegImm Opcodes
//////////////////////////////////////////////////

//00
void CMA_MIPSIV::BLTZ()
{
	//Not greater/equal & not likely
	Template_BranchGez(false, false);
}

//01
void CMA_MIPSIV::BGEZ()
{
	//Greater/equal & not likely
	Template_BranchGez(true, false);
}

//02
void CMA_MIPSIV::BLTZL()
{
	//Not greater/equal & likely
	Template_BranchGez(false, true);
}

//03
void CMA_MIPSIV::BGEZL()
{
	//Greater/equal & likely
	Template_BranchGez(true, true);
}

//10
void CMA_MIPSIV::BLTZAL()
{
	m_codeGen->PushCst(m_nAddress + 8);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[CMIPS::RA].nV[0]));

	BLTZ();
}

//11
void CMA_MIPSIV::BGEZAL()
{
	m_codeGen->PushCst(m_nAddress + 8);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[CMIPS::RA].nV[0]));

	BGEZ();
}

//12
void CMA_MIPSIV::BLTZALL()
{
	m_codeGen->PushCst(m_nAddress + 8);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[CMIPS::RA].nV[0]));

	BLTZL();
}

//13
void CMA_MIPSIV::BGEZALL()
{
	m_codeGen->PushCst(m_nAddress + 8);
	m_codeGen->PullRel(offsetof(CMIPS, m_State.nGPR[CMIPS::RA].nV[0]));

	BGEZL();
}

//////////////////////////////////////////////////
//Opcode Tables
//////////////////////////////////////////////////

CMA_MIPSIV::InstructionFuncConstant CMA_MIPSIV::m_cOpGeneral[MAX_GENERAL_OPS] =
{
	//0x00
	&CMA_MIPSIV::SPECIAL,		&CMA_MIPSIV::REGIMM,		&CMA_MIPSIV::J,				&CMA_MIPSIV::JAL,			&CMA_MIPSIV::BEQ,			&CMA_MIPSIV::BNE,			&CMA_MIPSIV::BLEZ,			&CMA_MIPSIV::BGTZ,
	//0x08
	&CMA_MIPSIV::ADDI,			&CMA_MIPSIV::ADDIU,			&CMA_MIPSIV::SLTI,			&CMA_MIPSIV::SLTIU,			&CMA_MIPSIV::ANDI,			&CMA_MIPSIV::ORI,			&CMA_MIPSIV::XORI,			&CMA_MIPSIV::LUI,
	//0x10
	&CMA_MIPSIV::COP0,			&CMA_MIPSIV::COP1,			&CMA_MIPSIV::COP2,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::BEQL,			&CMA_MIPSIV::BNEL,			&CMA_MIPSIV::BLEZL,			&CMA_MIPSIV::BGTZL,
	//0x18
	&CMA_MIPSIV::DADDI,			&CMA_MIPSIV::DADDIU,		&CMA_MIPSIV::LDL,			&CMA_MIPSIV::LDR,			&CMA_MIPSIV::SPECIAL2,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,
	//0x20
	&CMA_MIPSIV::LB,			&CMA_MIPSIV::LH,			&CMA_MIPSIV::LWL,			&CMA_MIPSIV::LW,			&CMA_MIPSIV::LBU,			&CMA_MIPSIV::LHU,			&CMA_MIPSIV::LWR,			&CMA_MIPSIV::LWU,
	//0x28
	&CMA_MIPSIV::SB,			&CMA_MIPSIV::SH,			&CMA_MIPSIV::SWL,			&CMA_MIPSIV::SW,			&CMA_MIPSIV::SDL,			&CMA_MIPSIV::SDR,			&CMA_MIPSIV::SWR,			&CMA_MIPSIV::CACHE,
	//0x30
	&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::LWC1,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::PREF,	    	&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::LDC2,			&CMA_MIPSIV::LD,
	//0x38
	&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::SWC1,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::SDC2,			&CMA_MIPSIV::SD,
};

CMA_MIPSIV::InstructionFuncConstant CMA_MIPSIV::m_cOpSpecial[MAX_SPECIAL_OPS] = 
{
	//0x00
	&CMA_MIPSIV::SLL,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::SRL,			&CMA_MIPSIV::SRA,			&CMA_MIPSIV::SLLV,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::SRLV,			&CMA_MIPSIV::SRAV,
	//0x08
	&CMA_MIPSIV::JR,			&CMA_MIPSIV::JALR,			&CMA_MIPSIV::MOVZ,			&CMA_MIPSIV::MOVN,			&CMA_MIPSIV::SYSCALL,		&CMA_MIPSIV::BREAK,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::SYNC,
	//0x10
	&CMA_MIPSIV::MFHI,			&CMA_MIPSIV::MTHI,			&CMA_MIPSIV::MFLO,			&CMA_MIPSIV::MTLO,			&CMA_MIPSIV::DSLLV,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::DSRLV,			&CMA_MIPSIV::DSRAV,
	//0x18
	&CMA_MIPSIV::MULT,			&CMA_MIPSIV::MULTU,			&CMA_MIPSIV::DIV,			&CMA_MIPSIV::DIVU,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,
	//0x20
	&CMA_MIPSIV::ADD,			&CMA_MIPSIV::ADDU,			&CMA_MIPSIV::SUB,			&CMA_MIPSIV::SUBU,			&CMA_MIPSIV::AND,			&CMA_MIPSIV::OR,			&CMA_MIPSIV::XOR,			&CMA_MIPSIV::NOR,
	//0x28
	&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::SLT,			&CMA_MIPSIV::SLTU,			&CMA_MIPSIV::DADD,  		&CMA_MIPSIV::DADDU,			&CMA_MIPSIV::DSUB,			&CMA_MIPSIV::DSUBU,
	//0x30
	&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::TEQ,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,
	//0x38
	&CMA_MIPSIV::DSLL,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::DSRL,			&CMA_MIPSIV::DSRA,			&CMA_MIPSIV::DSLL32,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::DSRL32,		&CMA_MIPSIV::DSRA32,
};

CMA_MIPSIV::InstructionFuncConstant CMA_MIPSIV::m_cOpRegImm[MAX_REGIMM_OPS] = 
{
	//0x00
	&CMA_MIPSIV::BLTZ,			&CMA_MIPSIV::BGEZ,			&CMA_MIPSIV::BLTZL,			&CMA_MIPSIV::BGEZL,			&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,
	//0x08
	&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,
	//0x10
	&CMA_MIPSIV::BLTZAL,		&CMA_MIPSIV::BGEZAL,		&CMA_MIPSIV::BLTZALL,		&CMA_MIPSIV::BGEZALL,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,
	//0x18
	&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,		&CMA_MIPSIV::Illegal,
};
