/*
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 "Instruction.h"
#include <sstream>

#include "OPTable_FPU.tcc"
#include "OPTable_1byte.tcc"
#include "OPTable_2byte.tcc"
#include "OPTable_3byte.tcc"
#include "OPTable_Group1.tcc"
#include "OPTable_Group2.tcc"
#include "OPTable_Group3.tcc"
#include "OPTable_Group4.tcc"
#include "OPTable_Group5.tcc"
#include "OPTable_Group6.tcc"
#include "OPTable_Group7.tcc"
#include "OPTable_Group8.tcc"
#include "OPTable_Group9.tcc"
#include "OPTable_Group10.tcc"
#include "OPTable_Group11.tcc"
#include "OPTable_Group12.tcc"
#include "OPTable_Group13.tcc"
#include "OPTable_Group14.tcc"
#include "OPTable_Group15.tcc"
#include "OPTable_Group16.tcc"
#include "OPTable_Group17.tcc"
#include "Instruction.tcc"

//------------------------------------------------------------------------------
// Name: Instruction(const uint8_t *buf, std::size_t size, uint64_t rva, const std::nothrow_t&, Mode mode) throw()
//------------------------------------------------------------------------------
Instruction::Instruction(const uint8_t *buf, std::size_t size, uint64_t rva, const std::nothrow_t&, Mode mode) throw()
		: m_RIP(rva), m_Buffer(buf), m_BufferSize(size), m_Opcode(0), m_Type(OP_INVALID), m_Mode(mode),
		m_Prefix(0), m_MandatoryPrefix(0), m_OperandCount(0), m_ModRMSize(0), 
		m_SIBSize(0), m_DispSize(0), m_PrefixSize(0), m_ImmidiateSize(0),
		m_OpcodeSize(0), m_RexByte(0), m_RexSize(0) {
		
	try {
		initialize(buf, size);
	} catch(instruction_too_big) {
		//throw;
	} catch(invalid_operand) {
		//throw;
	} catch(invalid_instruction) {
		//throw;
	}
}

//------------------------------------------------------------------------------
// Name: Instruction(const uint8_t *buf, std::size_t size, uint64_t rva, Mode mode)
//------------------------------------------------------------------------------
Instruction::Instruction(const uint8_t *buf, std::size_t size, uint64_t rva, Mode mode) 
		: m_RIP(rva), m_Buffer(buf), m_BufferSize(size), m_Opcode(0), m_Type(OP_INVALID), m_Mode(mode), 
		m_Prefix(0), m_MandatoryPrefix(0), m_OperandCount(0), m_ModRMSize(0), 
		m_SIBSize(0), m_DispSize(0), m_PrefixSize(0), m_ImmidiateSize(0),
		m_OpcodeSize(0), m_RexByte(0), m_RexSize(0) {
		
	initialize(buf, size);
}

//------------------------------------------------------------------------------
// Name: initialize(const uint8_t *buf, std::size_t size)
//------------------------------------------------------------------------------
void Instruction::initialize(const uint8_t *buf, std::size_t size) {
	
	for(int i = 0; i < MAX_OPERANDS; ++i) {
		m_Operands[i].m_Type = Operand::TYPE_INVALID;
	}
	
	processPrefixes(buf, size);
	
	// is there any space left for the opcode/operands?
	BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
	
	// find the entry in the table
	m_Opcode = &m_Opcodes[*buf];	
	m_OpcodeSize = 1;

	// decode it
	(this->*(m_Opcode->decoder))(buf);

	// these will not be set until we are sure which opcode we are looking 
	// at, this way any exceptions will default the instruction to invalid
	m_Type		= m_Opcode->type;
	m_Mnemonic	= m_Opcode->mnemonic;
}

//------------------------------------------------------------------------------
// Name: processPrefixes(const uint8_t *&buf, std::size_t size)
//------------------------------------------------------------------------------
void Instruction::processPrefixes(const uint8_t *&buf, std::size_t size) {

	bool done = false;
	
	// we only allow one prefix from each group to be set,
	// currently, the last one from a given group in the stream
	// will take precidence
	do {
		if(size == 0) {
			break;
		}
		
		switch(*buf) {
		// group1
		case 0xf0:
			m_Prefix = (m_Prefix & 0xffffff00) | PREFIX_LOCK;
			break;
		case 0xf2:
			m_Prefix = (m_Prefix & 0xffffff00) | PREFIX_REPNE;
			break;
		case 0xf3:
			m_Prefix = (m_Prefix & 0xffffff00) | PREFIX_REP;
			break;
		// group2
		case 0x2e:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_CS;
			break;
		case 0x36:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_SS;
			break;
		case 0x3e:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_DS;
			break;
		case 0x26:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_ES;
			break;
		case 0x64:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_FS;
			break;
		case 0x65:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_GS;
			break;
		#if 0
		case 0x2e:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_BRANCH_NOT_TAKEN;
			break;
		case 0x3e:
			m_Prefix = (m_Prefix & 0xffff00ff) | PREFIX_BRANCH_TAKEN;
			break;	
		#endif
		// group3
		case 0x66:
			m_Prefix = (m_Prefix & 0xff00ffff) | PREFIX_OPERAND;
			break;
		// group4
		case 0x67:
			m_Prefix = (m_Prefix & 0x00ffffff) | PREFIX_ADDRESS;
			break;
		default:
			done = true;
			break;
		}

		// this if will get merged with the while's condition if the optimiser
		// is smart
		if(!done) {
			++buf;
			++m_PrefixSize;
			--size;
		}
	} while(!done);
	
	// TODO: here is where the REX byte would be accepted
	
}

//------------------------------------------------------------------------------
// Name: nextOperand()
//------------------------------------------------------------------------------
Operand &Instruction::nextOperand() {

	if(m_OperandCount >= MAX_OPERANDS) {
		throw too_many_operands(size());
	}
	
	Operand &ret = m_Operands[m_OperandCount++];
	ret.m_Owner = this;
	return ret;
}

//------------------------------------------------------------------------------
// Name: decode_invalid(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_invalid(const uint8_t *buf) {
	UNUSED(buf);
	throw invalid_instruction(Instruction::size());
}

#define EMIT_ALT_NAME_HANDLER(name1, name2, pfx, decoder_func, OP1, OP2) \
void Instruction::decode_ ## name1 ## _ ## name2 (const uint8_t *buf) {	\
	static const OpcodeEntry opcodes[] = {				\
		{ #name1, (decoder_func), OP1 },			\
		{ #name2, (decoder_func), OP2 }				\
	};												\
	if(m_Prefix & (pfx)) {							\
		m_MandatoryPrefix |= (pfx);					\
		m_Opcode = &opcodes[1];	/* with 0x66 */		\
	} else {										\
		m_Opcode = &opcodes[0];	/* without 0x66 */	\
	}												\
	(this->*(m_Opcode->decoder))(buf);		\
}


EMIT_ALT_NAME_HANDLER(insd, insw, PREFIX_OPERAND, &Instruction::decode, OP_INS, OP_INS)
EMIT_ALT_NAME_HANDLER(outsd, outsw, PREFIX_OPERAND, &Instruction::decode, OP_OUTS, OP_OUTS)
EMIT_ALT_NAME_HANDLER(cwde, cbw, PREFIX_OPERAND, &Instruction::decode, OP_CWDE, OP_CBW)
EMIT_ALT_NAME_HANDLER(cdq, cwd, PREFIX_OPERAND, &Instruction::decode, OP_CDQ, OP_CWD)
EMIT_ALT_NAME_HANDLER(pushfd, pushfw, PREFIX_OPERAND, &Instruction::decode, OP_PUSHF, OP_PUSHF)
EMIT_ALT_NAME_HANDLER(popfd, popfw, PREFIX_OPERAND, &Instruction::decode, OP_POPF, OP_POPF)
EMIT_ALT_NAME_HANDLER(movsd, movsw, PREFIX_OPERAND, &Instruction::decode, OP_MOVS, OP_MOVS)
EMIT_ALT_NAME_HANDLER(cmpsd, cmpsw, PREFIX_OPERAND, &Instruction::decode, OP_CMPS, OP_CMPS)
EMIT_ALT_NAME_HANDLER(stosd, stosw, PREFIX_OPERAND, &Instruction::decode, OP_STOS, OP_STOS)
EMIT_ALT_NAME_HANDLER(lodsd, lodsw, PREFIX_OPERAND, &Instruction::decode, OP_LODS, OP_LODS)
EMIT_ALT_NAME_HANDLER(scasd, scasw, PREFIX_OPERAND, &Instruction::decode, OP_SCAS, OP_SCAS)
EMIT_ALT_NAME_HANDLER(iret, iretw, PREFIX_OPERAND, &Instruction::decode, OP_IRET, OP_IRET)
EMIT_ALT_NAME_HANDLER(nop, pause, PREFIX_REP, &Instruction::decode, OP_NOP, OP_PAUSE)
EMIT_ALT_NAME_HANDLER(pushad, pushaw, PREFIX_OPERAND, &Instruction::decode, OP_PUSHA, OP_PUSHA)
EMIT_ALT_NAME_HANDLER(popad, popaw, PREFIX_OPERAND, &Instruction::decode, OP_POPA, OP_POPA)


#undef EMIT_ALT_NAME_HANDLER

//------------------------------------------------------------------------------
// Name: decode_x87(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_x87(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	const uint8_t escNum = (buf[0] - 0xd8);
	const uint8_t byte2 = buf[m_OpcodeSize];
	
	if((byte2 & 0xc0) != 0xc0) {
		m_Opcode = &m_OpcodesX87Lo[modrm.reg() + escNum * 8];
	} else {
		m_Opcode = &m_OpcodesX87Hi[(byte2 & 0x3f) + escNum * 64];
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_2byte(const uint8_t *buf) {
	
	// we got enough room for next byte?
	BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
	
	m_OpcodeSize = 2;
	
	const uint8_t index = buf[m_OpcodeSize - 1];

	if(m_Prefix & PREFIX_OPERAND) {
		// 0x66
		m_MandatoryPrefix |= PREFIX_OPERAND;
		m_Opcode = &m_Opcodes2Byte_66[index];
	} else if(m_Prefix & PREFIX_REPNE) {
		// 0xf2
		m_MandatoryPrefix |= PREFIX_REPNE;
		m_Opcode = &m_Opcodes2Byte_F2[index];
	} else if(m_Prefix & PREFIX_REP) {
		// 0xf3
		m_MandatoryPrefix |= PREFIX_REP;
		m_Opcode = &m_Opcodes2Byte_F3[index];
	} else {
		// N/A
		m_Opcode = &m_Opcodes2Byte_NA[index];	
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_3byte_38(const uint8_t *buf) {
	
	// we got enough room for next byte?
	BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
	
	m_OpcodeSize = 3;
	
	const uint8_t index = buf[m_OpcodeSize - 1];
	
	if(m_Prefix & PREFIX_OPERAND) {
		// 0x66
		m_MandatoryPrefix |= PREFIX_OPERAND;
		m_Opcode = &m_Opcodes3Byte_38_66[index];
	} else {
		// N/A
		m_Opcode = &m_Opcodes3Byte_38_NA[index];
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_3byte_3A(const uint8_t *buf) {
	
	// we got enough room for next byte?
	BOUNDS_CHECK(Instruction::size() + 1, m_BufferSize);
	
	m_OpcodeSize = 3;
	
	const uint8_t index = buf[m_OpcodeSize - 1];
		
	if(m_Prefix & PREFIX_OPERAND) {
		// 0x66
		m_MandatoryPrefix |= PREFIX_OPERAND;
		m_Opcode = &m_Opcodes3Byte_3A_66[index];
	} else {
		// N/A
		m_Opcode = &m_Opcodes3Byte_3A_NA[index];
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group1(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group1(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup1[modrm.reg() + 8 * (buf[0] & 3)];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group2(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group2(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;

	m_Opcode = &m_OpcodesGroup2[modrm.reg() + 8 * (buf[0] & 1)];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group2D(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group2D(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
		
	m_Opcode = &m_OpcodesGroup2D[modrm.reg() + 8 * (buf[0] & 3)];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group3(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group3(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
		
	m_Opcode = &m_OpcodesGroup3[modrm.reg() + 8 * ((buf[0] - 6) & 1)];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group4(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group4(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup4[modrm.reg()];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group5(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group5(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup5[modrm.reg()];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group6(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group6(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup6[modrm.reg()];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group7(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group7(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	const uint8_t index = modrm.reg();
	
	if(modrm.mod() == 0x03) {
		m_Opcode = &m_OpcodesGroup7A[(index << 3) | modrm.rm()];
	} else {
		m_Opcode = &m_OpcodesGroup7[index];
	}
	(this->*(m_Opcode->decoder))(buf);	
}

//------------------------------------------------------------------------------
// Name: decode_group8(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group8(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup8[modrm.reg()];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group9(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group9(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	const uint8_t index = modrm.reg();

	if(m_Prefix & PREFIX_OPERAND) {
		// 0x66
		m_MandatoryPrefix |= PREFIX_OPERAND;
		m_Opcode = &m_OpcodesGroup9_66[index];
	} else if(m_Prefix & PREFIX_REP) {
		// 0xf3
		m_MandatoryPrefix |= PREFIX_REP;
		m_Opcode = &m_OpcodesGroup9_F3[index];
	} else {
		// N/A
		m_Opcode = &m_OpcodesGroup9[index];	
	}
	
	(this->*(m_Opcode->decoder))(buf);
}


//------------------------------------------------------------------------------
// Name: decode_group10(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group10(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup10[modrm.reg()];
	(this->*(m_Opcode->decoder))(buf);
}


//------------------------------------------------------------------------------
// Name: decode_group11(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group11(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup11[modrm.reg()];
	(this->*(m_Opcode->decoder))(buf);
}


//------------------------------------------------------------------------------
// Name: decode_group12(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group12(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup12[modrm.reg() + 8 * ((buf[0] - 6) & 1)];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group13(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group13(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	const uint8_t index = modrm.reg();
	
	if(m_Prefix & PREFIX_OPERAND) {
		// 0x66
		m_Opcode = &m_OpcodesGroup13_66[index];
	} else {
		// N/A
		m_Opcode = &m_OpcodesGroup13[index];	
	}
	
	(this->*(m_Opcode->decoder))(buf);
}


//------------------------------------------------------------------------------
// Name: decode_group14(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group14(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	const uint8_t index = modrm.reg();
	
	if(m_Prefix & PREFIX_OPERAND) {
		// 0x66
		m_Opcode = &m_OpcodesGroup14_66[index];
	} else {
		// N/A
		m_Opcode = &m_OpcodesGroup14[index];	
	}
	
	(this->*(m_Opcode->decoder))(buf);
}


//------------------------------------------------------------------------------
// Name: decode_group15(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group15(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	const uint8_t index = modrm.reg();
	
	if(m_Prefix & PREFIX_OPERAND) {
		// 0x66
		m_Opcode = &m_OpcodesGroup15_66[index];
	} else {
		// N/A
		m_Opcode = &m_OpcodesGroup15[index];	
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group16(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group16(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	const uint8_t index = modrm.reg();
	
	if(modrm.mod() == 0x03) {
		m_Opcode = &m_OpcodesGroup16_Reg[index];
	} else {
		m_Opcode = &m_OpcodesGroup16_Mem[index];
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_group17(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_group17(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 modrm(buf[m_OpcodeSize]);
	m_ModRMSize = 1;
	
	m_Opcode = &m_OpcodesGroup17[modrm.reg() + 8 * (buf[1] - 0x18)];
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_Ap(const uint8_t *buf)
// Desc: absolute pointer (32 or 48 bit)
//------------------------------------------------------------------------------
void Instruction::decode_Ap(const uint8_t *buf) {
	
	Operand &operand		= nextOperand();
	
	operand.m_Type			= Operand::TYPE_ABSOLUTE;
	
	if(m_Prefix & PREFIX_OPERAND) {
		operand.absolute.offset	= getImmidiate<uint16_t>(buf);
	} else {
		operand.absolute.offset	= getImmidiate<uint32_t>(buf);
	}
	
	operand.absolute.seg	= getImmidiate<uint16_t>(buf);	
}

//------------------------------------------------------------------------------
// Name: decode_Ib(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Ib(const uint8_t *buf) {
	
	Operand &operand = nextOperand();

	operand.sbyte	= getImmidiate<int8_t>(buf);
	operand.m_Type	= Operand::TYPE_IMMEDIATE8;
}

//------------------------------------------------------------------------------
// Name: decode_Iw(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Iw(const uint8_t *buf) {
	Operand &operand = nextOperand();

	operand.sword	= getImmidiate<int16_t>(buf);
	operand.m_Type	= Operand::TYPE_IMMEDIATE16;
}

//------------------------------------------------------------------------------
// Name: decode_Id(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Id(const uint8_t *buf) {
	
	Operand &operand = nextOperand();

	operand.sdword	= getImmidiate<int32_t>(buf);
	operand.m_Type	= Operand::TYPE_IMMEDIATE32;
}

//------------------------------------------------------------------------------
// Name: decode_Iq(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Iq(const uint8_t *buf) {
	
	Operand &operand = nextOperand();

	operand.sqword	= getImmidiate<int64_t>(buf);
	operand.m_Type	= Operand::TYPE_IMMEDIATE64;
}

//------------------------------------------------------------------------------
// Name: decode_Jb(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Jb(const uint8_t *buf) {
	
	Operand &operand = nextOperand();
	
	operand.sbyte	= getImmidiate<int8_t>(buf);
	operand.m_Type	= Operand::TYPE_REL8;
}

//------------------------------------------------------------------------------
// Name: decode_Jw(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Jw(const uint8_t *buf) {
	
	Operand &operand = nextOperand();
	
	operand.sword	= getImmidiate<int16_t>(buf);
	operand.m_Type	= Operand::TYPE_REL16;
}

//------------------------------------------------------------------------------
// Name: decode_Jd(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Jd(const uint8_t *buf) {
	
	Operand &operand = nextOperand();
	
	operand.sdword	= getImmidiate<int32_t>(buf);
	operand.m_Type	= Operand::TYPE_REL32;
}

//------------------------------------------------------------------------------
// Name: decode_Jq(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Jq(const uint8_t *buf) {
	
	Operand &operand = nextOperand();
	
	operand.sqword	= getImmidiate<int64_t>(buf);
	operand.m_Type	= Operand::TYPE_REL64;
}

//------------------------------------------------------------------------------
// Name: decode_Ev(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Ev(const uint8_t *buf) {

	switch(operandSize()) {
	case 16: decode_Ex<Operand::TYPE_EXPRESSION16, indexToReg16>(buf); break;
	case 32: decode_Ex<Operand::TYPE_EXPRESSION32, indexToReg32>(buf); break;
	case 64: decode_Ex<Operand::TYPE_EXPRESSION64, indexToReg64>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: decode_Rv(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Rv(const uint8_t *buf) {
	switch(operandSize()) {
	case 16: decode_Ex<Operand::TYPE_INVALID, indexToReg16>(buf); break;
	case 32: decode_Ex<Operand::TYPE_INVALID, indexToReg32>(buf); break;
	case 64: decode_Ex<Operand::TYPE_INVALID, indexToReg64>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: decode_Gv(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode_Gv(const uint8_t *buf) {
	switch(operandSize()) {
	case 16: decode_Gx<indexToReg16>(buf); break;
	case 32: decode_Gx<indexToReg32>(buf); break;
	case 64: decode_Gx<indexToReg64>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_Iv(const uint8_t *buf) {
	switch(operandSize()) {
	case 16: decode_Iw(buf); break;
	case 32: decode_Id(buf); break;
	case 64: decode_Iq(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_Ob(const uint8_t *buf) {
	Operand &operand = nextOperand();

	operand.m_Type							= Operand::TYPE_EXPRESSION8;
	operand.expression.displacement_type	= Operand::DISP_S8;
	operand.expression.index				= Operand::REG_NONE;
	operand.expression.base					= Operand::REG_NONE;
	operand.expression.scale				= 1;
	operand.expression.s_disp8				= getDisplacement<int8_t>(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_Ow(const uint8_t *buf) {
	Operand &operand = nextOperand();

	operand.m_Type							= Operand::TYPE_EXPRESSION16;
	operand.expression.displacement_type	= Operand::DISP_S16;
	operand.expression.index				= Operand::REG_NONE;
	operand.expression.base					= Operand::REG_NONE;
	operand.expression.scale				= 1;
	operand.expression.s_disp16				= getDisplacement<int16_t>(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_Od(const uint8_t *buf) {
	Operand &operand = nextOperand();

	operand.m_Type							= Operand::TYPE_EXPRESSION32;
	operand.expression.displacement_type	= Operand::DISP_S32;
	operand.expression.index				= Operand::REG_NONE;
	operand.expression.base					= Operand::REG_NONE;
	operand.expression.scale				= 1;
	operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_Ov(const uint8_t *buf) {
	switch(operandSize()) {
	case 16: decode_Ow(buf); break;
	case 32: decode_Od(buf); break;
	case 64: assert(0); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rAX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_AX>(buf); break;
	case 32: decode_Reg<Operand::REG_EAX>(buf); break;
	case 64: decode_Reg<Operand::REG_RAX>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rCX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_CX>(buf); break;
	case 32: decode_Reg<Operand::REG_ECX>(buf); break;
	case 64: decode_Reg<Operand::REG_RCX>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rDX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_DX>(buf); break;
	case 32: decode_Reg<Operand::REG_EDX>(buf); break;
	case 64: decode_Reg<Operand::REG_RDX>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rBX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_BX>(buf); break;
	case 32: decode_Reg<Operand::REG_EBX>(buf); break;
	case 64: decode_Reg<Operand::REG_RBX>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rSP(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_SP>(buf); break;
	case 32: decode_Reg<Operand::REG_ESP>(buf); break;
	case 64: decode_Reg<Operand::REG_RSP>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rBP(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_BP>(buf); break;
	case 32: decode_Reg<Operand::REG_EBP>(buf); break;
	case 64: decode_Reg<Operand::REG_RBP>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rSI(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_SI>(buf); break;
	case 32: decode_Reg<Operand::REG_ESI>(buf); break;
	case 64: decode_Reg<Operand::REG_RSI>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
void Instruction::decode_rDI(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: decode_Reg<Operand::REG_DI>(buf); break;
	case 32: decode_Reg<Operand::REG_EDI>(buf); break;
	case 64: decode_Reg<Operand::REG_RDI>(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: operandSize() const
//------------------------------------------------------------------------------
int Instruction::operandSize() const {
	int ret = 32;
	
	// we check if 16-bit mode is enabled
	if(m_Prefix & PREFIX_OPERAND) {
		ret = 16;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: decode(const uint8_t *buf)
//------------------------------------------------------------------------------
void Instruction::decode(const uint8_t *buf) {
	UNUSED(buf);
	// does nothing
}

//------------------------------------------------------------------------------
// Name: formatPrefix() const
//------------------------------------------------------------------------------
std::string Instruction::formatPrefix() const {
	std::string ret;
	
	if((m_Prefix & PREFIX_LOCK) && !(m_MandatoryPrefix & PREFIX_LOCK)) {
		// TODO: this is only legal for the memory dest versions of:
		// ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, 
		// DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, XCHG
		return "lock ";
		
	} else if((m_Prefix & PREFIX_REP) && !(m_MandatoryPrefix & PREFIX_REP)) {
		if(type() == OP_CMPS || type() == OP_SCAS) {
			return "repe ";
		} else {
			// TODO: this is only legal for:
			// INS, OUTS, MOVS, LODS and STOS
			return "rep ";
		}
	} else if((m_Prefix & PREFIX_REPNE) && !(m_MandatoryPrefix & PREFIX_REPNE)) {
		// TODO: this is only legal for:
		// CMPS and SCAS
		return "repne ";
	}
	
	return "";
}

//------------------------------------------------------------------------------
// Name: format() const
//------------------------------------------------------------------------------
std::string Instruction::format() const {
	return format(false);
}

//------------------------------------------------------------------------------
// Name: toupper(std::string &s)
//------------------------------------------------------------------------------
static std::string &toupper(std::string &s) {
	std::transform(s.begin(), s.end(), s.begin(), std::ptr_fun<int, int>(std::toupper)); 
	return s; 
}

//------------------------------------------------------------------------------
// Name: format(bool upper) const
//------------------------------------------------------------------------------
std::string Instruction::format(bool upper) const {
	std::stringstream ss;
	
	std::string prefix = formatPrefix();
	std::string op = mnemonic();
	
	if(upper) {
		ss << toupper(prefix);
		ss << toupper(op);
	} else {
		ss << prefix;
		ss << op;
	}
	
	const std::size_t count = operandCount();
	
	for(uint_t i = 0; i < count; ++i) {
		if(i == 0) {
			ss << ' ';
		}
		ss << m_Operands[i].format(upper);
		if(i != count - 1) {
			ss << ", ";
		}
	}

	return ss.str();
}
