/*
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.
*/

#ifndef INSTRUCTION_20080314_TCC_
#define INSTRUCTION_20080314_TCC_

#include <sstream>
#include <string>
#include <locale>

#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 "ModRM.h"
#include "SIB.h"
#include "REX.h"
#include <cassert>

//------------------------------------------------------------------------------
// Name: decode_Gx(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
template <typename Operand<N>::Register (*REG_DECODE)(uint8_t)>
void Instruction<N>::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();
	if(BITS == 64 && m_RexByte.isREX()) {
		regindex |= (m_RexByte.r() << 3);
	}

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

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

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

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

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

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

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

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

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

	operand_t &operand = nextOperand();

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

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

	operand_t &operand = nextOperand();

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

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

	operand_t &operand = nextOperand();

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

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

	operand_t &operand = nextOperand();

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

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

	operand_t &operand = nextOperand();

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

//------------------------------------------------------------------------------
// Name: getImmediate(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
template <class T>
T Instruction<N>::getImmediate(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_ImmediateSize]);
	m_ImmediateSize += sizeof(T);
	return ret;
}

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

	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_t &operand)
//------------------------------------------------------------------------------
template <int N>
template <typename Operand<N>::Type TYPE, typename Operand<N>::Register (*REG_DECODE)(uint8_t)>
void Instruction<N>::decode_ModRM_0_32(const uint8_t *buf, const ModRM &rm, operand_t &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]);


		int sibindex = sib.index();

		if(BITS == 64 && m_RexByte.isREX()) {
			sibindex |= (m_RexByte.x() << 3);
		}

		if(sibindex != 0x04) {

			if(BITS == 64) {
				operand.expression.index	= indexToReg64(sibindex);
			} else {
				operand.expression.index	= indexToReg32(sibindex);
			}
			
			
			operand.expression.scale	= 1 << sib.scale();
		} else {
			operand.expression.index	= operand_t::REG_NULL;
			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_t::REG_NULL;
			operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
			operand.expression.displacement_type	= operand_t::DISP_S32;

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

			if(BITS == 64 && m_RexByte.isREX()) {
				sibbase |= (m_RexByte.b() << 3);
			}
			
			if(BITS == 64) {
				operand.expression.base				= indexToReg64(sibbase);
			} else {
				operand.expression.base				= indexToReg32(sibbase);
			}
			operand.expression.displacement_type	= operand_t::DISP_NONE;
		}
	} else if(rm.rm() == 0x05) {
		if(BITS == 64) {
			operand.expression.index				= operand_t::REG_NULL;
			operand.expression.scale				= 1;
			operand.expression.base					= operand_t::REG_RIP;
			operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
			operand.expression.displacement_type	= operand_t::DISP_S32;
		} else {
			operand.expression.index				= operand_t::REG_NULL;
			operand.expression.scale				= 1;
			operand.expression.base					= operand_t::REG_NULL;
			operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
			operand.expression.displacement_type	= operand_t::DISP_S32;
		}
	} else {
		operand.expression.index				= operand_t::REG_NULL;
		operand.expression.scale				= 1;
		
		int rmbase = rm.rm();

		if(BITS == 64 && m_RexByte.isREX()) {
			rmbase |= (m_RexByte.b() << 3);
		}
		
		if(BITS == 64) {
			operand.expression.base					= indexToReg64(rmbase);
		} else {
			operand.expression.base					= indexToReg32(rmbase);
		}
		operand.expression.s_disp8				= 0;
		operand.expression.displacement_type	= operand_t::DISP_NONE;
	}
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_1_32(const uint8_t *buf, const ModRM &rm, operand_t &operand)
//------------------------------------------------------------------------------
template <int N>
template <typename Operand<N>::Type TYPE, typename Operand<N>::Register (*REG_DECODE)(uint8_t)>
void Instruction<N>::decode_ModRM_1_32(const uint8_t *buf, const ModRM &rm, operand_t &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]);

		int sibbase = sib.base();

		if(BITS == 64 && m_RexByte.isREX()) {
			sibbase |= (m_RexByte.b() << 3);
		}

		if(BITS == 64) {
			operand.expression.base	= indexToReg64(sibbase);
		} else {
			operand.expression.base	= indexToReg32(sibbase);
		}
		operand.expression.scale	= 1 << sib.scale();

		int sibindex = sib.index();

		if(BITS == 64 && m_RexByte.isREX()) {
			sibindex |= (m_RexByte.x() << 3);
		}

		if(sibindex != 0x04) {			
			if(BITS == 64) {			
				operand.expression.index	= indexToReg64(sibindex);
			} else {
				operand.expression.index	= indexToReg32(sibindex);
			}
		} else {
			operand.expression.index	= operand_t::REG_NULL;		
		}
	} else {
		operand.expression.index	= operand_t::REG_NULL;
		operand.expression.scale	= 1;

		int rmbase = rm.rm();

		if(BITS == 64 && m_RexByte.isREX()) {
			rmbase |= (m_RexByte.b() << 3);
		}

		if(BITS == 64) {
			operand.expression.base		= indexToReg64(rmbase);
		} else {
			operand.expression.base		= indexToReg32(rmbase);
		}
		
	}
	
	operand.m_Type							= TYPE;
	operand.expression.s_disp8				= getDisplacement<int8_t>(buf);
	operand.expression.displacement_type	= operand_t::DISP_S8;
}

//------------------------------------------------------------------------------
// Name: decode_ModRM_2_32(const uint8_t *buf, const ModRM &rm, operand_t &operand)
//------------------------------------------------------------------------------
template <int N>
template <typename Operand<N>::Type TYPE, typename Operand<N>::Register (*REG_DECODE)(uint8_t)>
void Instruction<N>::decode_ModRM_2_32(const uint8_t *buf, const ModRM &rm, operand_t &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]);
	
		int sibbase = sib.base();

		if(BITS == 64 && m_RexByte.isREX()) {
			sibbase |= (m_RexByte.b() << 3);
		}

		if(BITS == 64) {
			operand.expression.base		= indexToReg64(sibbase);
		} else {
			operand.expression.base		= indexToReg32(sibbase);
		}
		
		operand.expression.scale	= 1 << sib.scale();

		int sibindex = sib.index();

		if(BITS == 64 && m_RexByte.isREX()) {
			sibindex |= (m_RexByte.x() << 3);
		}

		if(sibindex != 0x04) {
			if(BITS == 64) {
				operand.expression.index	= indexToReg64(sibindex);
			} else {
				operand.expression.index	= indexToReg32(sibindex);
			}
			
		} else {
			operand.expression.index	= operand_t::REG_NULL;
		}
	} else {
		operand.expression.index	= operand_t::REG_NULL;
		operand.expression.scale	= 1;
	
		int rmbase = rm.rm();

		if(BITS == 64 && m_RexByte.isREX()) {
			rmbase |= (m_RexByte.b() << 3);
		}
		
		if(BITS == 64) {
			operand.expression.base		= indexToReg64(rmbase);
		} else {
			operand.expression.base		= indexToReg32(rmbase);
		}
		
	}
	
	operand.m_Type							= TYPE;
	operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
	operand.expression.displacement_type	= operand_t::DISP_S32;
}

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

	int rmbase	= rm.rm();
	
	if(BITS == 64 && m_RexByte.isREX()) {
		rmbase |= (m_RexByte.b() << 3);
	}
	
	operand.reg			= (*REG_DECODE)(rmbase);
	operand.m_Type		= operand_t::TYPE_REGISTER;
}

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

//------------------------------------------------------------------------------
// Name: decode_Ex(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
template <typename Operand<N>::Type TYPE, typename Operand<N>::Register (*REG_DECODE)(uint8_t)>
void Instruction<N>::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_t &operand = nextOperand();

	if(m_Prefix & PREFIX_ADDRESS) {
	
		if(BITS == 64) {
			throw invalid_instruction(Instruction::size());
		} else {
	
			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: decode3(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
template <void (Instruction<N>::*F1)(const uint8_t *), void (Instruction<N>::*F2)(const uint8_t *), void (Instruction<N>::*F3)(const uint8_t *)>
void Instruction<N>::decode3(const uint8_t *buf) {
	(this->*F1)(buf);
	(this->*F2)(buf);
	(this->*F3)(buf);
}

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

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

//------------------------------------------------------------------------------
// Name: Instruction(const uint8_t *buf, std::size_t size, address_t rva, const std::nothrow_t&) throw()
//------------------------------------------------------------------------------
template <int N>
Instruction<N>::Instruction(const uint8_t *buf, std::size_t size, address_t rva, const std::nothrow_t&) throw()
		: m_RVA(rva), m_Buffer(buf), m_BufferSize(size), m_Opcode(0), m_Type(OP_INVALID),
		m_Prefix(0), m_MandatoryPrefix(0), m_OperandCount(0), m_ModRMSize(0), 
		m_SIBSize(0), m_DispSize(0), m_PrefixSize(0), m_ImmediateSize(0),
		m_OpcodeSize(0), m_RexByte(0), m_RexSize(0) {
		
	try {
		initialize(buf, size);
	} catch(const instruction_too_big &) {
		//throw;
	} catch(const invalid_operand &) {
		//throw;
	} catch(const invalid_instruction &) {
		//throw;
	}
}

//------------------------------------------------------------------------------
// Name: Instruction(const uint8_t *buf, std::size_t size, address_t rva)
//------------------------------------------------------------------------------
template <int N>
Instruction<N>::Instruction(const uint8_t *buf, std::size_t size, address_t rva) 
		: m_RVA(rva), m_Buffer(buf), m_BufferSize(size), m_Opcode(0), m_Type(OP_INVALID), 
		m_Prefix(0), m_MandatoryPrefix(0), m_OperandCount(0), m_ModRMSize(0), 
		m_SIBSize(0), m_DispSize(0), m_PrefixSize(0), m_ImmediateSize(0),
		m_OpcodeSize(0), m_RexByte(0), m_RexSize(0) {
		
	initialize(buf, size);
}

//------------------------------------------------------------------------------
// Name: initialize(const uint8_t *buf, std::size_t size)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::initialize(const uint8_t *buf, std::size_t size) {
	
	for(int i = 0; i < MAX_OPERANDS; ++i) {
		m_Operands[i].m_Type = operand_t::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;
}

#include <iostream>

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::assemble(std::string instruction) {

	std::istringstream ss(instruction);
	std::string token;
	std::string prefix;
	std::string opcode;
	std::string operands[3];
	int operand_index = 0;
	
	while(ss >> token) {
		token = edisassm::util::tolower( edisassm::util::trim(token));
		if(token == "lock" || token == "repe" || token == "repne" || token == "rep") {
			prefix += token;
			prefix += " ";
		} else {
			break;
		}
	}
	
	opcode = token;
	
	while(std::getline(ss, token, ',')) {
		operands[operand_index++] = edisassm::util::tolower( edisassm::util::trim(token));
	}

	
	std::cout << "PREFIX  : " << prefix << std::endl;
	std::cout << "OPCODE  : " << opcode << std::endl;
	for(int i = 0; i < operand_index; ++i) {
		std::cout << "OPERAND : " << operands[i] << std::endl;
	}
}

//------------------------------------------------------------------------------
// Name: processPrefixes(const uint8_t *&buf, std::size_t size)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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);
	
	if(BITS == 64) {
		if(size != 0) {
			m_RexByte = *buf;
			if(m_RexByte.isREX()) {
				++buf;
				++m_RexSize;
				--size;
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: nextOperand()
//------------------------------------------------------------------------------
template <int N>
typename Instruction<N>::operand_t &Instruction<N>::nextOperand() {

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

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

#define EMIT_ALT_NAME_HANDLER(name1, name2, decoder_func, OP1, OP2)			\
template <int N>																\
void Instruction<N>::decode_ ## name1 ## _ ## name2 (const uint8_t *buf) {	\
	static const OpcodeEntry opcodes[] = {							\
		{ #name1, (decoder_func), OP1 },							\
		{ #name2, (decoder_func), OP2 },							\
		{ "invalid", &Instruction::decode_invalid, OP_INVALID },	\
	};																\
	switch(operandSize()) {											\
	case 16: m_Opcode = &opcodes[0]; break;							\
	case 32: m_Opcode = &opcodes[1]; break;							\
	case 64: m_Opcode = &opcodes[2]; break;							\
	}																\
	(this->*(m_Opcode->decoder))(buf);								\
}

#define EMIT_ALT_NAME_HANDLER2(name1, name2, name3, decoder_func, OP1, OP2, OP3)	\
template <int N>																		\
void Instruction<N>::decode_ ## name1 ## _ ## name2 ## _ ## name3(const uint8_t *buf) {			\
	static const OpcodeEntry opcodes[] = {		\
		{ #name1, (decoder_func), OP1 },		\
		{ #name2, (decoder_func), OP2 },		\
		{ #name3, (decoder_func), OP3 }			\
	};											\
	/* this only works because the entry 		\
	 * in the primary table, which has things 	\
	 * like PUSH/POP							\
	 */											\
	switch(operandSize()) {						\
	case 16: m_Opcode = &opcodes[0]; break;		\
	case 32: m_Opcode = &opcodes[1]; break;		\
	case 64: m_Opcode = &opcodes[2]; break;		\
	}											\
	(this->*(m_Opcode->decoder))(buf);			\
}

EMIT_ALT_NAME_HANDLER2(cbw, cwde, cdqe, &Instruction::decode0, OP_CBW, OP_CWDE, OP_CDQE)
EMIT_ALT_NAME_HANDLER2(cwd, cdq, cqo, &Instruction::decode0, OP_CWD, OP_CDQ, OP_CQO)
EMIT_ALT_NAME_HANDLER2(stosw, stosd, stosq, &Instruction::decode0, OP_STOS, OP_STOS, OP_STOS)
EMIT_ALT_NAME_HANDLER2(lodsw, lodsd, lodsq, &Instruction::decode0, OP_LODS, OP_LODS, OP_LODS)
EMIT_ALT_NAME_HANDLER2(scasw, scasd, scasq, &Instruction::decode0, OP_SCAS, OP_SCAS, OP_SCAS)
EMIT_ALT_NAME_HANDLER2(iretw, iret, iretq, &Instruction::decode0, OP_IRET, OP_IRET, OP_IRET)
EMIT_ALT_NAME_HANDLER2(movsw, movsd, movsq, &Instruction::decode0, OP_MOVS, OP_MOVS, OP_MOVS)
EMIT_ALT_NAME_HANDLER2(popfw, popfd, popfq, &Instruction::decode0, OP_POPF, OP_POPF, OP_POPF)
EMIT_ALT_NAME_HANDLER2(pushfw, pushfd, pushfq, &Instruction::decode0, OP_PUSHF, OP_PUSHF, OP_PUSHF)

EMIT_ALT_NAME_HANDLER(insw, insd, &Instruction::decode0, OP_INS, OP_INS)
EMIT_ALT_NAME_HANDLER(outsw, outsd, &Instruction::decode0, OP_OUTS, OP_OUTS)
EMIT_ALT_NAME_HANDLER(cmpsw, cmpsd, &Instruction::decode0, OP_CMPS, OP_CMPS)
EMIT_ALT_NAME_HANDLER(pushaw, pushad, &Instruction::decode0, OP_PUSHA, OP_PUSHA)
EMIT_ALT_NAME_HANDLER(popaw, popad, &Instruction::decode0, OP_POPA, OP_POPA)


#undef EMIT_ALT_NAME_HANDLER
#undef EMIT_ALT_NAME_HANDLER2

//------------------------------------------------------------------------------
// Name: decode_x87(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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 if(m_Prefix & PREFIX_REPNE) {
		// 0xf2
		m_MandatoryPrefix |= PREFIX_REPNE;
		m_Opcode = &m_Opcodes3Byte_38_F2[index];
	} else {
		// N/A
		m_Opcode = &m_Opcodes3Byte_38_NA[index];
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: decode_3byte_3A(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Ap(const uint8_t *buf) {
	
	operand_t &operand		= nextOperand();
	
	operand.m_Type			= operand_t::TYPE_ABSOLUTE;
	
	if(m_Prefix & PREFIX_OPERAND) {
		operand.absolute.offset	= getImmediate<uint16_t>(buf);
	} else {
		operand.absolute.offset	= getImmediate<uint32_t>(buf);
	}
	
	operand.absolute.seg	= getImmediate<uint16_t>(buf);	
}

//------------------------------------------------------------------------------
// Name: decode_Ib(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Ib(const uint8_t *buf) {
	operand_t &operand = nextOperand();

	operand.sbyte	= getImmediate<int8_t>(buf);
	operand.m_Type	= operand_t::TYPE_IMMEDIATE8;
}

//------------------------------------------------------------------------------
// Name: decode_Iw(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Iw(const uint8_t *buf) {
	operand_t &operand = nextOperand();

	operand.sword	= getImmediate<int16_t>(buf);
	operand.m_Type	= operand_t::TYPE_IMMEDIATE16;
}

//------------------------------------------------------------------------------
// Name: decode_Id(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Id(const uint8_t *buf) {
	operand_t &operand = nextOperand();

	operand.sdword	= getImmediate<int32_t>(buf);
	operand.m_Type	= operand_t::TYPE_IMMEDIATE32;
}

//------------------------------------------------------------------------------
// Name: decode_Iq(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Iq(const uint8_t *buf) {
	operand_t &operand = nextOperand();
	
	operand.sqword	= getImmediate<int64_t>(buf);	
	operand.m_Type	= operand_t::TYPE_IMMEDIATE64;
}

//------------------------------------------------------------------------------
// Name: decode_Jb(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Jb(const uint8_t *buf) {
	
	operand_t &operand = nextOperand();
	
	operand.sbyte	= getImmediate<int8_t>(buf);
	operand.m_Type	= operand_t::TYPE_REL8;
}

//------------------------------------------------------------------------------
// Name: decode_Jw(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Jw(const uint8_t *buf) {
	
	operand_t &operand = nextOperand();
	
	operand.sword	= getImmediate<int16_t>(buf);
	operand.m_Type	= operand_t::TYPE_REL16;
}

//------------------------------------------------------------------------------
// Name: decode_Jd(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Jd(const uint8_t *buf) {
	
	operand_t &operand = nextOperand();
	
	operand.sdword	= getImmediate<int32_t>(buf);
	operand.m_Type	= operand_t::TYPE_REL32;
}

//------------------------------------------------------------------------------
// Name: decode_Jq(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Jq(const uint8_t *buf) {
	
	operand_t &operand = nextOperand();
	
	operand.sqword	= getImmediate<int64_t>(buf);
	operand.m_Type	= operand_t::TYPE_REL64;
}

//------------------------------------------------------------------------------
// Name: decode_Ev(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Ev(const uint8_t *buf) {

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

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

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

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::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: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Ob(const uint8_t *buf) {
	operand_t &operand = nextOperand();

	operand.m_Type							= operand_t::TYPE_EXPRESSION8;
	operand.expression.displacement_type	= operand_t::DISP_S32;
	operand.expression.index				= operand_t::REG_NULL;
	operand.expression.base					= operand_t::REG_NULL;
	operand.expression.scale				= 1;
	operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Ow(const uint8_t *buf) {
	operand_t &operand = nextOperand();

	operand.m_Type							= operand_t::TYPE_EXPRESSION16;
	operand.expression.displacement_type	= operand_t::DISP_S32;
	operand.expression.index				= operand_t::REG_NULL;
	operand.expression.base					= operand_t::REG_NULL;
	operand.expression.scale				= 1;
	operand.expression.s_disp32				= getDisplacement<int32_t>(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Od(const uint8_t *buf) {
	operand_t &operand = nextOperand();

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

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Ov(const uint8_t *buf) {
	switch(operandSize()) {
	case 16: decode_Ow(buf); break;
	case 32: decode_Od(buf); break;
	case 64: throw invalid_instruction(Instruction::size()); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_Mv(const uint8_t *buf) {
	switch(operandSize()) {
	case 16: decode_Mw(buf); break;
	case 32: decode_Md(buf); break;
	case 64: decode_Mq(buf); break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_AL(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R8B>(buf); 
	} else {
		decode_Reg<operand_t::REG_AL>(buf);
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_CL(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R9B>(buf); 
	} else {
		decode_Reg<operand_t::REG_CL>(buf);
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_DL(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R10B>(buf); 
	} else {
		decode_Reg<operand_t::REG_DL>(buf);
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_BL(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R11B>(buf); 
	} else {
		decode_Reg<operand_t::REG_BL>(buf);
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_AH(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R12B>(buf); 
	} else {
		decode_Reg<operand_t::REG_AH>(buf);
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_CH(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R13B>(buf); 
	} else {
		decode_Reg<operand_t::REG_CH>(buf);
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_DH(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R14B>(buf); 
	} else {
		decode_Reg<operand_t::REG_DH>(buf);
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_BH(const uint8_t *buf) {
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		decode_Reg<operand_t::REG_R15B>(buf); 
	} else {
		decode_Reg<operand_t::REG_BH>(buf);
	}
}

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

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_eAX(const uint8_t *buf) {
	// TODO: is this correct, it seems to be the same
	// because eAX is only used for ops where REX is illegal
	decode_rAX_NOREX(buf);
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rAX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R8W>(buf); 
		} else {
			decode_Reg<operand_t::REG_AX>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R8D>(buf); 
		} else {
			decode_Reg<operand_t::REG_EAX>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R8>(buf); 
		} else {
			decode_Reg<operand_t::REG_RAX>(buf); 
		}
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rCX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R9W>(buf); 
		} else {
			decode_Reg<operand_t::REG_CX>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R9D>(buf); 
		} else {
			decode_Reg<operand_t::REG_ECX>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R9>(buf); 
		} else {
			decode_Reg<operand_t::REG_RCX>(buf); 
		}
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rDX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R10W>(buf); 
		} else {
			decode_Reg<operand_t::REG_DX>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R10D>(buf); 
		} else {
			decode_Reg<operand_t::REG_EDX>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R10>(buf); 
		} else {
			decode_Reg<operand_t::REG_RDX>(buf); 
		}
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rBX(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R11W>(buf); 
		} else {
			decode_Reg<operand_t::REG_BX>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R11D>(buf); 
		} else {
			decode_Reg<operand_t::REG_EBX>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R11>(buf); 
		} else {
			decode_Reg<operand_t::REG_RBX>(buf); 
		}
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rSP(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R12W>(buf); 
		} else {
			decode_Reg<operand_t::REG_SP>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R12D>(buf); 
		} else {
			decode_Reg<operand_t::REG_ESP>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R12>(buf); 
		} else {
			decode_Reg<operand_t::REG_RSP>(buf); 
		}
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rBP(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R13W>(buf); 
		} else {
			decode_Reg<operand_t::REG_BP>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R13D>(buf); 
		} else {
			decode_Reg<operand_t::REG_EBP>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R13>(buf); 
		} else {
			decode_Reg<operand_t::REG_RBP>(buf); 
		}
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rSI(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R14W>(buf); 
		} else {
			decode_Reg<operand_t::REG_SI>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R14D>(buf); 
		} else {
			decode_Reg<operand_t::REG_ESI>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R14>(buf); 
		} else {
			decode_Reg<operand_t::REG_RSI>(buf); 
		}
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rDI(const uint8_t *buf) { 
	switch(operandSize()) {
	case 16: 
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R15W>(buf); 
		} else {
			decode_Reg<operand_t::REG_DI>(buf); 
		}
		break;
	case 32:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R15D>(buf); 
		} else {
			decode_Reg<operand_t::REG_EDI>(buf); 
		}
		break;
	case 64:
		if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
			decode_Reg<operand_t::REG_R15>(buf); 
		} else {
			decode_Reg<operand_t::REG_RDI>(buf); 
		}
		break;
	}
}


//EMIT_ALT_NAME_HANDLER(nop, pause, PREFIX_REP, &Instruction::decode, OP_NOP, OP_PAUSE)
//------------------------------------------------------------------------------
// Name: decode_rAX_rAX_NOREX(const uint8_t *buf
// NOTE: special case because this represents 3 possible ops!
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode_rAX_rAX_NOREX(const uint8_t *buf) {
	static const OpcodeEntry opcodes[] = {
		{ "nop",	&Instruction::decode0, OP_NOP },
		{ "pause",	&Instruction::decode0, OP_PAUSE },
		{ "xchg", 	&Instruction::template decode2<&instruction_t::decode_rAX, &instruction_t::decode_rAX_NOREX>, OP_XCHG }
	};
	
	// TODO: does F3 or xchg r8, rAX take precidence
	if(BITS == 64 && m_RexByte.isREX() && m_RexByte.b()) {
		m_Opcode = &opcodes[2];
	} else if(m_Prefix & PREFIX_REP) {
		m_MandatoryPrefix |= PREFIX_REP;
		m_Opcode = &opcodes[1];	/* with 0x66 */
	} else {
		m_Opcode = &opcodes[0];	/* without 0x66 */
	}
	
	(this->*(m_Opcode->decoder))(buf);
}

//------------------------------------------------------------------------------
// Name: operandSize() const
//------------------------------------------------------------------------------
template <int N>
int Instruction<N>::operandSize() const {
	int ret = 32;
		
	// we check if 16-bit mode is enabled
	if(m_Prefix & PREFIX_OPERAND) {
		ret = 16;
	}
	
	// we check if 64-bit mode is enabled
	if(BITS == 64) {
		if(m_RexByte.isREX() && m_RexByte.w()) {
			ret = 64;
		} else {
			const Type type = m_Opcode->type;
			// push/pop/jmp/call defaults to 64-bit even without prefix
			if(type == OP_PUSH || type == OP_POP || type == OP_PUSHF || type == OP_POPF || type == OP_CALL || type == OP_JMP || type == OP_JCC) {
				ret = 64;
			}
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: decode0(const uint8_t *buf)
//------------------------------------------------------------------------------
template <int N>
void Instruction<N>::decode0(const uint8_t *buf) {
	UNUSED(buf);
	// does nothing
}

//------------------------------------------------------------------------------
// Name: formatPrefix() const
//------------------------------------------------------------------------------
template <int N>
std::string Instruction<N>::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
		ret = "lock ";
		
	} else if((m_Prefix & PREFIX_REP) && !(m_MandatoryPrefix & PREFIX_REP)) {
		if(type() == OP_CMPS || type() == OP_SCAS) {
			ret = "repe ";
		} else {
			// TODO: this is only legal for:
			// INS, OUTS, MOVS, LODS and STOS
			ret = "rep ";
		}
	} else if((m_Prefix & PREFIX_REPNE) && !(m_MandatoryPrefix & PREFIX_REPNE)) {
		// TODO: this is only legal for:
		// CMPS and SCAS
		ret = "repne ";
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: format() const
//------------------------------------------------------------------------------
template <int N>
std::string Instruction<N>::format() const {
	return format(false);
}

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

	return ss.str();
}

template <int N>
template <void (Instruction<N>::*F1)(const uint8_t *), void (Instruction<N>::*F2)(const uint8_t *)>
void Instruction<N>::decode_Reg_Mem(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;

	if(rm.mod() == 0x03) {
		(this->*F1)(buf);
	} else {
		(this->*F2)(buf);
	}
}

#endif

