/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "ModRM.h"
#include "SIB.h"
#include "REX.h"
#include <cassert>

//------------------------------------------------------------------------------
// Name: decode_Gx(const uint8_t *buf)
//------------------------------------------------------------------------------
template<Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_Gx(const uint8_t *buf) {

	// we got enough room for the ModRM byte?
	if(m_ModRMSize == 0) {
		BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
	}

	const ModRM rm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;

	int regindex = rm.reg();

	Operand &operand	= nextOperand();
	operand.reg			= (*REG_DECODE)(regindex);
	operand.m_Type		= Operand::TYPE_REGISTER;
}

//------------------------------------------------------------------------------
// Name: decode_STi(const uint8_t *buf)
//------------------------------------------------------------------------------
template<int index>
void Instruction::decode_STi(const uint8_t *buf) {
	UNUSED(buf);

	Operand &operand	= nextOperand();
	operand.reg			= indexToRegFPU(index);
	operand.m_Type		= Operand::TYPE_REGISTER;
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_0_16(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_0_16(const uint8_t *buf, const ModRM &rm, Operand &operand) {

	operand.m_Type							= TYPE;
	operand.expression.scale				= 1;
	operand.expression.displacement_type	= Operand::DISP_NONE;
	
	switch(rm.rm()) {
	case 0x00:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_BX;
		break;
	case 0x01:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_BX;
		break;
	case 0x02:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x03:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x04:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_NONE;
		break;
	case 0x05:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_NONE;
		break;
	case 0x06:
		operand.expression.index			= Operand::REG_NONE;
		operand.expression.base				= Operand::REG_NONE;
		operand.expression.s_disp16			= getDisplacement<int16_t>(buf);
		operand.expression.displacement_type= Operand::DISP_S16;
		break;
	case 0x07:
		operand.expression.index			= Operand::REG_NONE;
		operand.expression.base				= Operand::REG_BX;
		break;
	}
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_1_16(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_1_16(const uint8_t *buf, const ModRM &rm, Operand &operand) {

	operand.m_Type							= TYPE;
	operand.expression.scale				= 1;
	operand.expression.s_disp8				= getDisplacement<int8_t>(buf);
	operand.expression.displacement_type	= Operand::DISP_S8;
	
	switch(rm.rm()) {
	case 0x00:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_BX;
		break;
	case 0x01:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_BX;
		break;
	case 0x02:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x03:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x04:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_NONE;
		break;
	case 0x05:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_NONE;
		break;
	case 0x06:
		operand.expression.index			= Operand::REG_NONE;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x07:
		operand.expression.index			= Operand::REG_NONE;
		operand.expression.base				= Operand::REG_BX;
		break;
	}
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_2_16(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_2_16(const uint8_t *buf, const ModRM &rm, Operand &operand) {
	
	operand.m_Type							= TYPE;
	operand.expression.scale				= 1;
	operand.expression.s_disp16				= getDisplacement<int16_t>(buf);
	operand.expression.displacement_type	= Operand::DISP_S16;
	
	switch(rm.rm()) {
	case 0x00:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_BX;
		break;
	case 0x01:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_BX;
		break;
	case 0x02:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x03:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x04:
		operand.expression.index			= Operand::REG_SI;
		operand.expression.base				= Operand::REG_NONE;
		break;
	case 0x05:
		operand.expression.index			= Operand::REG_DI;
		operand.expression.base				= Operand::REG_NONE;
		break;
	case 0x06:
		operand.expression.index			= Operand::REG_NONE;
		operand.expression.base				= Operand::REG_BP;
		break;
	case 0x07:
		operand.expression.index			= Operand::REG_NONE;
		operand.expression.base				= Operand::REG_BX;
		break;
	}
}

//------------------------------------------------------------------------------
// Name: decode_Reg(const uint8_t *buf)
//------------------------------------------------------------------------------
template<Operand::Register REG>
void Instruction::decode_Reg(const uint8_t *buf) {
	UNUSED(buf);

	Operand &operand = nextOperand();

	operand.reg 	= REG;
	operand.m_Type	= Operand::TYPE_REGISTER;
}

//------------------------------------------------------------------------------
// Name: decode_const_Iq(const uint8_t *buf)
//------------------------------------------------------------------------------
template<int64_t IMM>
void Instruction::decode_const_Iq(const uint8_t *buf) {
	UNUSED(buf);

	Operand &operand = nextOperand();

	operand.sqword	= IMM;
	operand.m_Type	= Operand::TYPE_IMMEDIATE64;
	// NOTE: so far this is only used for implicit constants, other
	// ones may need a size!
	//m_ImmidiateSize	+= sizeof(int64_t);
}

//------------------------------------------------------------------------------
// Name: decode_const_Id(const uint8_t *buf)
//------------------------------------------------------------------------------
template<int32_t IMM>
void Instruction::decode_const_Id(const uint8_t *buf) {
	UNUSED(buf);

	Operand &operand = nextOperand();

	operand.sdword	= IMM;
	operand.m_Type	= Operand::TYPE_IMMEDIATE32;
	// NOTE: so far this is only used for implicit constants, other
	// ones may need a size!
	//m_ImmidiateSize	+= sizeof(int32_t);
}

//------------------------------------------------------------------------------
// Name: decode_const_Iw(const uint8_t *buf)
//------------------------------------------------------------------------------
template<int16_t IMM>
void Instruction::decode_const_Iw(const uint8_t *buf) {
	UNUSED(buf);
	UNUSED(size);

	Operand &operand = nextOperand();

	operand.sword	= IMM;
	operand.m_Type	= Operand::TYPE_IMMEDIATE16;
	// NOTE: so far this is only used for implicit constants, other
	// ones may need a size!
	//m_ImmidiateSize	+= sizeof(int16_t);
}

//------------------------------------------------------------------------------
// Name: decode_const_Ib(const uint8_t *buf)
//------------------------------------------------------------------------------
template<int8_t IMM>
void Instruction::decode_const_Ib(const uint8_t *buf) {
	UNUSED(buf);

	Operand &operand = nextOperand();

	operand.sbyte	= IMM;
	operand.m_Type	= Operand::TYPE_IMMEDIATE8;
	// NOTE: so far this is only used for implicit constants, other
	// ones may need a size!
	//m_ImmidiateSize	+= sizeof(int8_t);
}

//------------------------------------------------------------------------------
// Name: getImmidiate(const uint8_t *buf)
//------------------------------------------------------------------------------
template<typename T>
T Instruction::getImmidiate(const uint8_t *buf) {

	BOUNDS_CHECK(Instruction::size() + sizeof(T), m_BufferSize);

	const T ret = *reinterpret_cast<const T *>(&buf[m_OpcodeSize + m_ModRMSize + m_SIBSize + m_DispSize + m_ImmidiateSize]);
	m_ImmidiateSize += sizeof(T);
	return ret;
}

//------------------------------------------------------------------------------
// Name: getDisplacement(const uint8_t *buf)
//------------------------------------------------------------------------------
template<typename T>
T Instruction::getDisplacement(const uint8_t *buf) {		
	
	BOUNDS_CHECK(Instruction::size() + sizeof(T), m_BufferSize);
	
	// there should only every be one displacement value!
	assert(m_DispSize == 0);

	const T ret = *reinterpret_cast<const T *>(&buf[m_OpcodeSize + m_ModRMSize + m_SIBSize + m_DispSize]);
	m_DispSize = sizeof(T);
	return ret;
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_0_32(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_0_32(const uint8_t *buf, const ModRM &rm, Operand &operand) {

	operand.m_Type = TYPE;

	if(rm.rm() == 0x04) {
		// we got enough room for the SIB byte?
		if(m_SIBSize == 0) {
			BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
		}

		m_SIBSize = 1;
		const SIB sib(buf[m_OpcodeSize + m_ModRMSize]);

		if(sib.index() != 0x04) {
		
			int sibindex = sib.index();

			// indexToReg64 in 64-bit mode
			operand.expression.index	= indexToReg32(sibindex);
			operand.expression.scale	= 1 << sib.scale();
		} else {
			operand.expression.index	= Operand::REG_NONE;
			operand.expression.scale	= 1;
		}
			
		if(sib.base() == 0x05) {
		
			// we could only get here if modrm.mod == 0x00
			// so it is always sdword
			operand.expression.base					= Operand::REG_NONE;
			operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
			operand.expression.displacement_type	= Operand::DISP_S32;

		} else {
			int sibbase = sib.base();

			// indexToReg64 in 64-bit mode
			operand.expression.base					= indexToReg32(sibbase);
			operand.expression.displacement_type	= Operand::DISP_NONE;
		}
	} else if(rm.rm() == 0x05) {
		operand.expression.index				= Operand::REG_NONE;
		operand.expression.scale				= 1;
		operand.expression.base					= Operand::REG_NONE;
		operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
		operand.expression.displacement_type	= Operand::DISP_S32;
	} else {
		operand.expression.index				= Operand::REG_NONE;
		operand.expression.scale				= 1;
		// indexToReg64 in 64-bit mode
		operand.expression.base					= indexToReg32(rm.rm());
		operand.expression.s_disp8				= 0;
		operand.expression.displacement_type	= Operand::DISP_NONE;
	}
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_1_32(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_1_32(const uint8_t *buf, const ModRM &rm, Operand &operand) {
	
	
	
	if(rm.rm() == 0x04) {
		
		// we got enough room for the SIB byte?
		if(m_SIBSize == 0) {
			BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
		}

		m_SIBSize = 1;
		const SIB sib(buf[m_OpcodeSize + m_ModRMSize]);

		// indexToReg64 in 64-bit mode
		operand.expression.base	= indexToReg32(sib.base());
		operand.expression.scale	= 1 << sib.scale();

		if(sib.index() != 0x04) {
			// indexToReg64 in 64-bit mode
			operand.expression.index	= indexToReg32(sib.index());
		} else {
			operand.expression.index	= Operand::REG_NONE;		
		}
	} else {
	
		operand.expression.index	= Operand::REG_NONE;
		operand.expression.scale	= 1;
		// indexToReg64 in 64-bit mode
		operand.expression.base		= indexToReg32(rm.rm());
	}
	
	operand.m_Type							= TYPE;
	operand.expression.s_disp8				= getDisplacement<int8_t>(buf);
	operand.expression.displacement_type	= Operand::DISP_S8;
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_2_32(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_2_32(const uint8_t *buf, const ModRM &rm, Operand &operand) {
	
	if(rm.rm() == 0x04) {
		// we got enough room for the SIB byte?
		if(m_SIBSize == 0) {
			BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
		}

		m_SIBSize = 1;
		const SIB sib(buf[m_OpcodeSize + m_ModRMSize]);

		// indexToReg64 in 64-bit mode
		operand.expression.base		= indexToReg32(sib.base());
		operand.expression.scale	= 1 << sib.scale();

		if(sib.index() != 0x04) {
			// indexToReg64 in 64-bit mode
			operand.expression.index	= indexToReg32(sib.index());
		} else {
			operand.expression.index	= Operand::REG_NONE;
		}
	} else {
		operand.expression.index	= Operand::REG_NONE;
		operand.expression.scale	= 1;
	
		int rmbase = rm.rm();
		// indexToReg64 in 64-bit mode
		operand.expression.base		= indexToReg32(rmbase);
	}
	
	operand.m_Type							= TYPE;
	operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
	operand.expression.displacement_type	= Operand::DISP_S32;
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_3_32(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_3_32(const uint8_t *buf, const ModRM &rm, Operand &operand) {
	UNUSED(buf);

	int rmindex = rm.rm();
	operand.reg		= (*REG_DECODE)(rmindex);
	operand.m_Type	= Operand::TYPE_REGISTER;
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_Invalid(const uint8_t *buf, const ModRM &rm, Operand &operand)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_ModRM_Invalid(const uint8_t *buf, const ModRM &rm, Operand &operand) {
	UNUSED(rm);
	UNUSED(operand);
	UNUSED(buf);
	throw invalid_operand(Instruction::size());
}

//------------------------------------------------------------------------------
// Name: decode_Ex(const uint8_t *buf)
//------------------------------------------------------------------------------
template<Operand::Type TYPE, Operand::Register (*REG_DECODE)(uint8_t)>
void Instruction::decode_Ex(const uint8_t *buf) {

	// we got enough room for the ModRM byte?
	if(m_ModRMSize == 0) {
		BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
	}
	
	const ModRM rm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;

	Operand &operand = nextOperand();

	if(m_Prefix & PREFIX_ADDRESS) {			
		switch(rm.mod()) {
		case 0x00:
			decode_ModRM_0_16<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		case 0x01:
			decode_ModRM_1_16<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		case 0x02:
			decode_ModRM_2_16<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		case 0x03:
			// same as in 32-bit mode!
			decode_ModRM_3_32<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		}
	} else {
	
	
	
	
		switch(rm.mod()) {
		case 0x00:
			decode_ModRM_0_32<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		case 0x01:
			decode_ModRM_1_32<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		case 0x02:
			decode_ModRM_2_32<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		case 0x03:
			decode_ModRM_3_32<TYPE, REG_DECODE>(buf, rm, operand);
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: decode(const uint8_t *buf)
//------------------------------------------------------------------------------
template<Instruction::DecoderFunction F1, Instruction::DecoderFunction F2, Instruction::DecoderFunction F3>
void Instruction::decode(const uint8_t *buf) {
	(this->*F1)(buf);
	(this->*F2)(buf);
	(this->*F3)(buf);
}

//------------------------------------------------------------------------------
// Name: decode(const uint8_t *buf)
//------------------------------------------------------------------------------
template<Instruction::DecoderFunction F1, Instruction::DecoderFunction F2>
void Instruction::decode(const uint8_t *buf) {
	(this->*F1)(buf);
	(this->*F2)(buf);
}

//------------------------------------------------------------------------------
// Name: decode(const uint8_t *buf)
//------------------------------------------------------------------------------
template<Instruction::DecoderFunction F1>
void Instruction::decode(const uint8_t *buf) {
	(this->*F1)(buf);
}
