/****************************************************************
 *                                                              *
 *      Copyright 2007, 2012 Fidelity Information Services, Inc *
 *                                                              *
 *      This source code contains the intellectual property     *
 *      of its copyright holder(s), and is made available       *
 *      under a license.  If you do not know the terms of       *
 *      the license, please stop and do not read further.       *
 *                                                              *
 ****************************************************************/

#include "mdef.h"

#include <sys/types.h>
#include "gtm_string.h"
#include <errno.h>
#include "gtm_fcntl.h"
#include "gtm_stat.h"
#include "gtm_stdio.h"
#include "opcode.h"
#include "mdq.h"
#include <rtnhdr.h>
#include "vxi.h"
#include "vxt.h"
#include "cgp.h"
#include "compiler.h"
#include "list_file.h"
#include <emit_code.h>
GBLDEF struct emit_base_info emit_base_info;


#define SET_OBPT_STR(str, len)	\
	memcpy(obpt, str, len);	\
	obpt += len;
#define SET_OBPT_INT4(value)	\
	obpt = i2asc(obpt, value);
#define SET_OBPT_INT8(value)	\
	obpt = i2asclx(obpt, value);

/* Possible values for instruction Byte's Meaning */
#define one_byte_opcode		0
#define two_byte_opcode		1
#define modrm_sib_bytes		2
#define one_byte_immediate	3
#define double_word_immediate	4
#define quad_word_immediate	5
#define one_byte_offset		6
#define double_word_offset 	7
#define quad_word_offset	8

/* Now Define The Instruction Structure .... */
#define grp_prefix	4
/* flags to be used by "operand_class" */
#define undefined_class 0
#define register_class	1
#define memory_class 	2
#define immediate_class	3

struct instruction_mnemonics
{
	char *opcode_mnemonic;
	char opcode_suffix;

	short reg_rip;
	char  reg_prefix;
	short num_operands;  /* Some instructions have one, some two and some None operands..
				one operand will be taken in source.. num_operands = 4 would
				mean that modrm reg_opcode will denote opcode extension
			      */
	short source_operand_class;
	char  *source_operand_reg;
	short destination_operand_class;
	char  *destination_operand_reg;
	long  offset;
	short has_immediate;
	long  immediate;
} instruction;


#undef I386_OP
#define I386_OP(opcode, operand, num)	#opcode ,

LITDEF char *mnemonic_list[] = {
	#include "i386_ops.h"
	};

LITDEF char *mnemonic_list_2b[] = {
	#include "i386_ops_2b.h"
	};

LITDEF char *mnemonic_list_g1[] = {
	#include "i386_ops_g1.h"
	};

LITDEF char *mnemonic_list_g2[] = {
	#include "i386_ops_g2.h"
	};

LITDEF char *mnemonic_list_g3[] = {
	#include "i386_ops_g3.h"
	};

LITDEF char *mnemonic_list_g4[] = {
	#include "i386_ops_g4.h"
	};

LITDEF char *mnemonic_list_g5[] = {
	#include "i386_ops_g5.h"
	};


/* Structures and unions for different Bytes .. */
struct Rex
{
	short Base;
	short Index;
	short Reg;
	short Word64;
} rex_prefix;

static modrm_byte_type modrm_byte;
static sib_byte_type sib_byte;

GBLREF int 	call_4lcldo_variant;
GBLREF int	jmp_offset;	/* Offset to jump target */
GBLREF char	cg_phase;	/* code generation phase */
GBLREF char 	code_buf[];
GBLREF int	code_idx;
GBLREF unsigned char	*obpt;	 /* output buffer index */
GBLREF unsigned char	outbuf[];
GBLREF int	curr_addr;
GBLDEF int	instidx, prev_idx;

#define REG_RIP 16
LITDEF char	*register_list[] = {
	"AX",
	"CX",
	"DX",
	"BX",
	"SP",
	"BP",
	"SI",
	"DI",
	"8",
	"9",
	"10",
	"11",
	"12",
	"13",
	"14",
	"15",
	"RIP"
};

GBLREF boolean_t force_32;	/* We want to generate 4 byte offets even for an offset lesser than 8bits long,
				   to keep things consistent between CGP_APPROX_ADDR phase and CGP_MACHINE phase */

int	x86_64_arg_reg(int indx)
{
	switch(indx)
	{
		case 0: return I386_REG_RDI ;
		case 1: return I386_REG_RSI ;
		case 2: return I386_REG_RDX ;
		case 3: return I386_REG_RCX ;
		case 4: return I386_REG_R8 ;
		case 5: return I386_REG_R9 ;
		default: GTMASSERT ; break ;
	}
	/* Control will never reach here */
	return - 1 ;
}

void	emit_jmp(uint4 branch_op, short **instp, int reg) /* Note that the 'reg' parameter is ignored */
{
	assert (jmp_offset != 0);
	force_32 = TRUE;
	jmp_offset -= code_idx * SIZEOF(code_buf[0]);	/* size of this particular instruction */

	switch (cg_phase)
	{
#ifdef DEBUG
		case CGP_ASSEMBLY:
			*obpt++ = 'x';
			*obpt++ = '^';
			*obpt++ = '0';
			*obpt++ = 'x';
			obpt += i2hex_nofill(jmp_offset, (uchar_ptr_t)obpt, 8);
			*obpt++ = ',';
			*obpt++ = ' ';
		/***** WARNING - FALL THRU *****/
#endif
		case CGP_ADDR_OPT:
		case CGP_APPROX_ADDR:
		case CGP_MACHINE:
			assert (**instp == VXT_JMP);
			*instp += 1;
			assert (**instp == 1);
			*instp += 1;
			if (jmp_offset == 0)
			{
				/*code_buf[code_idx++] = I386_INS_NOP__; */
			} else if (((jmp_offset - 2) >= -128 && (jmp_offset - 2) <= 127 &&
				    JMP_LONG_INST_SIZE != call_4lcldo_variant) && (force_32 == FALSE))
			{
				jmp_offset -= 2;
				switch (branch_op)
				{
					case GENERIC_OPCODE_BEQ:
						code_buf[code_idx++] = I386_INS_JZ_Jb;
						break;
					case GENERIC_OPCODE_BGE:
						code_buf[code_idx++] = I386_INS_JNL_Jb;
						break;
					case GENERIC_OPCODE_BGT:
						code_buf[code_idx++] = I386_INS_JNLE_Jb;
						break;
					case GENERIC_OPCODE_BLE:
						code_buf[code_idx++] = I386_INS_JLE_Jb;
						break;
					case GENERIC_OPCODE_BLT:
						code_buf[code_idx++] = I386_INS_JL_Jb;
						break;
					case GENERIC_OPCODE_BNE:
						code_buf[code_idx++] = I386_INS_JNZ_Jb;
						break;
					case GENERIC_OPCODE_BR:
						assert(0 == call_4lcldo_variant || BRB_INST_SIZE == call_4lcldo_variant);
						code_buf[code_idx++] = I386_INS_JMP_Jb;
						break;
					default:
						GTMASSERT;
						break;
				}
				code_buf[code_idx++] = jmp_offset & 0xff;
			} else
			{
				if (branch_op == GENERIC_OPCODE_BR)
				{
					assert(0 == call_4lcldo_variant || JMP_LONG_INST_SIZE == call_4lcldo_variant || force_32);
					jmp_offset -= SIZEOF(int4) + 1;
					code_buf[code_idx++] = I386_INS_JMP_Jv;
				} else
				{
					jmp_offset -= SIZEOF(int4) + 2;
					code_buf[code_idx++] = I386_INS_Two_Byte_Escape_Prefix;
					switch (branch_op)
					{
						case GENERIC_OPCODE_BEQ:
							code_buf[code_idx++] = I386_INS_JZ_Jv;
							break;
						case GENERIC_OPCODE_BGE:
							code_buf[code_idx++] = I386_INS_JNL_Jv;
							break;
						case GENERIC_OPCODE_BGT:
							code_buf[code_idx++] = I386_INS_JNLE_Jv;
							break;
						case GENERIC_OPCODE_BLE:
							code_buf[code_idx++] = I386_INS_JLE_Jv;
							break;
						case GENERIC_OPCODE_BLT:
							code_buf[code_idx++] = I386_INS_JL_Jv;
							break;
						case GENERIC_OPCODE_BNE:
							code_buf[code_idx++] = I386_INS_JNZ_Jv;
							break;
						default:
							GTMASSERT;
							break;
					}
				}
				*((int4 *)&code_buf[code_idx]) = jmp_offset;
				code_idx += SIZEOF(int4);
			}
	}
	force_32 = FALSE;
}



void	emit_base_offset(int base_reg, int offset)
{
	memset((void *)&emit_base_info, 0, SIZEOF(emit_base_info));
	emit_base_info.rex = REX_OP; /* All instructions that we generate need to set the REX prefix */

	emit_base_info.modrm_byte_set = 1;
/*
 *	if (offset == 0)
 *		emit_base_info.modrm_byte.modrm.mod = I386_MOD32_BASE;
 *		 else
 */
	if ((offset >= -128  &&  offset <= 127)  &&  force_32 == FALSE)
		emit_base_info.modrm_byte.modrm.mod = I386_MOD32_BASE_DISP_8;
	else
		emit_base_info.modrm_byte.modrm.mod = I386_MOD32_BASE_DISP_32;

	if (((base_reg & 0x7) == I386_REG_ESP ) ||  ((base_reg & 0x7) == I386_REG_EBP  &&  offset == 0))
	{
		emit_base_info.modrm_byte.modrm.r_m = I386_REG_SIB_FOLLOWS;

		/* Refer to the comment in emit_code_sp.h before SET_REX_PREFIX */
		emit_base_info.sib_byte.sib.base = base_reg & 0x7; /* Need only the bottom 3 bits */
		SET_REX_PREFIX(0, REX_B, base_reg)
		emit_base_info.sib_byte.sib.ss = I386_SS_TIMES_1;
		emit_base_info.sib_byte.sib.index = I386_REG_NO_INDEX;
		emit_base_info.sib_byte_set = 1;
	} else
	{
		emit_base_info.modrm_byte.modrm.r_m = base_reg & 0x7; /* Need only the bottom 3 bits */
		SET_REX_PREFIX(0, REX_B, base_reg)
	}

	if ((offset >= -128  &&  offset <= 127)  &&  force_32 == FALSE)
	{
		emit_base_info.offset8 = offset & 0xff;
		emit_base_info.offset8_set = 1;
	} else
	{
		emit_base_info.offset32 = offset;
		emit_base_info.offset32_set = 1;
	}
}

#ifdef DEBUG

void reset_instruction()
{
	rex_prefix.Base	= 0;
	rex_prefix.Index = 0;
	rex_prefix.Reg = 0;
	rex_prefix.Word64 = 0;

	instruction.opcode_mnemonic = NULL;
	instruction.opcode_suffix = 'l';
	instruction.reg_rip = FALSE;
	instruction.reg_prefix = 'e';
	instruction.num_operands = 0;

	instruction.source_operand_class = undefined_class;
	instruction.source_operand_reg = NULL;
	instruction.destination_operand_class = undefined_class;
	instruction.destination_operand_reg = NULL;
	instruction.offset = 0;
	instruction.has_immediate = 0;
	instruction.immediate = 0;
}

/* Now the functions which will print the actual instruction(mnemonics).. */
void print_source_operand()
{
	switch(instruction.source_operand_class)
	{
		case undefined_class :
			GTMASSERT;
			break;
		case register_class :
			assert(instruction.source_operand_reg != NULL);
			*obpt++ = '%';
			*obpt++ = instruction.reg_prefix;
			SET_OBPT_STR(instruction.source_operand_reg, STRLEN(instruction.source_operand_reg))
			break;
		case memory_class :
			assert(instruction.destination_operand_class != memory_class);
			if (instruction.source_operand_reg != NULL)
			{
				SET_OBPT_INT4(instruction.offset);
				*obpt++ = '(';
				*obpt++ = '%';
				*obpt++ = instruction.reg_prefix;
				SET_OBPT_STR(instruction.source_operand_reg, STRLEN(instruction.source_operand_reg))
				*obpt++ = ')';
			} else
			{
				SET_OBPT_INT4(instruction.offset);
			}
			break;
		case immediate_class :
			*obpt++ = '0';
			*obpt++ = 'x';
			SET_OBPT_INT8(instruction.immediate);
			break;
		default :
			GTMASSERT;
	}
}

void print_destination_operand()
{
	switch(instruction.destination_operand_class)
	{
		case undefined_class :
			GTMASSERT;
			break;
		case register_class :
			assert(instruction.destination_operand_reg != NULL);
			*obpt++ = '%';
			*obpt++ = instruction.reg_prefix;
			SET_OBPT_STR(instruction.destination_operand_reg, STRLEN(instruction.destination_operand_reg))
			break;
		case memory_class :
			assert(instruction.source_operand_class != memory_class);
			if (instruction.destination_operand_reg != NULL)
			{
				SET_OBPT_INT4(instruction.offset);
				*obpt++ = '(';
				*obpt++ = '%';
				*obpt++ = instruction.reg_prefix;
				SET_OBPT_STR(instruction.destination_operand_reg, STRLEN(instruction.destination_operand_reg))
				*obpt++ = ')';
			} else
			{
				SET_OBPT_INT4(instruction.offset);
			}
			break;
		case immediate_class :
			*obpt++ = '0';
			*obpt++ = 'x';
			SET_OBPT_INT8(instruction.immediate);
			break;
		default :
			GTMASSERT;
	}
}


void print_instruction()
{

	list_chkpage();
	obpt = &outbuf[0];
	memset(obpt, SP, ASM_OUT_BUFF);
	obpt += 10;
	i2hex((curr_addr - SIZEOF(rhdtyp)), obpt, 8);
	curr_addr += (instidx - prev_idx);
	obpt += 10;
	for( ;  prev_idx < instidx; prev_idx++)
	{
		i2hex(code_buf[prev_idx], obpt, 2);
		obpt += 2;
	}
	obpt += 10;
	*obpt++ = '\n';
	*obpt++ = '\t';
	*obpt++ = '\t';
	*obpt++ = '\t';
	*obpt++ = '\t';
	*obpt++ = '\t';
	*obpt++ = '\t';
	assert( instruction.opcode_mnemonic != NULL );
	SET_OBPT_STR(instruction.opcode_mnemonic, STRLEN(instruction.opcode_mnemonic))
	*obpt++ = instruction.opcode_suffix;
	*obpt++ = '\t';
	instruction.num_operands = (instruction.num_operands > grp_prefix) ? \
					(instruction.num_operands - grp_prefix) : instruction.num_operands;

	switch (instruction.num_operands)
	{
		case 0 :
			break;
		case 1 :
		/* single operand assumed to be in the source operand only.. */
			assert(instruction.destination_operand_class == undefined_class);
			print_source_operand();
			break;
		case 2 :
			print_source_operand();
			*obpt++ = ',';
			print_destination_operand();
			break;
		default :
		GTMASSERT;
	}
	/*  Now reset the instruction structure  */
	emit_eoi();
	reset_instruction();
}

void set_memory_reg()
{
	instruction.reg_prefix = 'r';

	if (instruction.source_operand_class == memory_class)
		instruction.source_operand_reg =(char *) register_list[modrm_byte.modrm.r_m + 8 * rex_prefix.Base];
	else if (instruction.destination_operand_class == memory_class)
		instruction.destination_operand_reg =(char *) register_list[modrm_byte.modrm.r_m + 8 * rex_prefix.Base];

	/* Printing of RIP has to be handled differently	*/
 	if (instruction.reg_rip)
		if (instruction.source_operand_class == memory_class)
			instruction.source_operand_reg =(char *) register_list[REG_RIP];
		else if (instruction.destination_operand_class == memory_class)
			instruction.destination_operand_reg =(char *) register_list[REG_RIP];
		else
			GTMASSERT;
}

void set_register_reg()
{
	if (instruction.source_operand_class == register_class)
		instruction.source_operand_reg =(char *) register_list[modrm_byte.modrm.reg_opcode + 8 * rex_prefix.Reg];
	else if (instruction.destination_operand_class == register_class)
		instruction.destination_operand_reg = (char *)register_list[modrm_byte.modrm.reg_opcode + 8 * rex_prefix.Reg];
}

void clear_memory_reg()
{
	if (instruction.source_operand_class == memory_class)
		instruction.source_operand_reg = NULL;
	else if (instruction.destination_operand_class == memory_class)
		instruction.destination_operand_reg = NULL;
}

void format_machine_inst()
{
	short	next_inst_byte_meaning = one_byte_opcode;
	int	i, tot_inst_len = 0;
	unsigned char	inst_curr_byte;
	short	lock_prefix_seen;
	short	rep_e_prefix_seen;
	short	repne_prefix_seen;
	short	operand_size_prefix_seen;
	short	address_size_prefix_seen;


	/*	Start Parsing the Instruction Buffer	*/
	instidx = 0;
	prev_idx = 0;
	while(instidx < code_idx)
	{
		switch(next_inst_byte_meaning)
		{			/* Can be a Prefix, or opcode !! */
			case one_byte_opcode :
				inst_curr_byte = code_buf[instidx++];
				instruction.opcode_mnemonic =(char *) mnemonic_list[inst_curr_byte];
				switch(inst_curr_byte)
				{
					/*	If Prefixes, set corresponding Flag and continue... 	*/
					case I386_INS_Two_Byte_Escape_Prefix :
						next_inst_byte_meaning = two_byte_opcode;
						break;
					case I386_INS_REX_PREFIX_None :
					case I386_INS_REX_PREFIX__B :
					case I386_INS_REX_PREFIX__X :
					case I386_INS_REX_PREFIX__X_B :
					case I386_INS_REX_PREFIX__R :
					case I386_INS_REX_PREFIX__R_B :
					case I386_INS_REX_PREFIX__R_X :
					case I386_INS_REX_PREFIX__R_X_B :
					case I386_INS_REX_PREFIX__W :
					case I386_INS_REX_PREFIX__W_B :
					case I386_INS_REX_PREFIX__W_X :
					case I386_INS_REX_PREFIX__W_X_B :
					case I386_INS_REX_PREFIX__W_R :
					case I386_INS_REX_PREFIX__W_R_B :
					case I386_INS_REX_PREFIX__W_R_X :
					case I386_INS_REX_PREFIX__W_R_X_B :
						rex_prefix.Base = (inst_curr_byte & 0x01);
						rex_prefix.Index = (inst_curr_byte & 0x02) ? 1 : 0;
						rex_prefix.Reg = (inst_curr_byte & 0x04) ? 1 : 0;
						rex_prefix.Word64 = (inst_curr_byte & 0x08) ? 1 : 0;
						if (rex_prefix.Word64)
						{
							instruction.opcode_suffix = 'q';
							instruction.reg_prefix = 'r';
						} else
						{
							instruction.opcode_suffix = 'l';
							instruction.reg_prefix = 'e';
						}
						break;
					case I386_INS_LOCK_Prefix :
						lock_prefix_seen = TRUE;
						break;
					case I386_INS_REPNE_Prefix :
						repne_prefix_seen = TRUE;
						break;
					case I386_INS_REP_E_Prefix :
						rep_e_prefix_seen = TRUE;
						break;
					case I386_INS_Operand_Size_Prefix :
						operand_size_prefix_seen = TRUE;
						break;
					case I386_INS_Address_Size_Prefix :
						address_size_prefix_seen = TRUE;
						break;

			/* now the instructions having Opcode Extension in the modrm.reg field.. */
					case I386_INS_Grp1_Ev_Iv_Prefix :
					case I386_INS_Grp2_Ev_Iv_Prefix :
						instruction.destination_operand_class = memory_class;
						instruction.source_operand_class = immediate_class;
						instruction.has_immediate = double_word_immediate;
						instruction.num_operands = grp_prefix + 2;
						next_inst_byte_meaning = modrm_sib_bytes;
						break;
					case I386_INS_Grp1_Eb_Ib_Prefix :
					case I386_INS_Grp1_Ev_Ib_Prefix :
					case I386_INS_Grp2_Eb_Ib_Prefix :
						instruction.opcode_suffix = 'b';
						instruction.destination_operand_class = memory_class;
						instruction.source_operand_class = immediate_class;
						instruction.has_immediate = one_byte_immediate;
						instruction.num_operands = grp_prefix + 2;
						next_inst_byte_meaning = modrm_sib_bytes;
						break;
					case I386_INS_Grp2_Eb_1_Prefix :
					case I386_INS_Grp2_Ev_1_Prefix :
					case I386_INS_Grp2_Eb_CL_Prefix :
					case I386_INS_Grp2_Ev_CL_Prefix :
						next_inst_byte_meaning = modrm_sib_bytes;
						GTMASSERT;  /* Not taking care of this case for now - not used!! */
						break;
					case I386_INS_Grp3_Eb_Prefix :
						modrm_byte.byte = code_buf[instidx + 1];
						if (modrm_byte.modrm.reg_opcode < 2)
						{
							instruction.destination_operand_class = memory_class;
							instruction.source_operand_class = immediate_class;
							instruction.has_immediate = one_byte_immediate;
							instruction.num_operands = grp_prefix + 2;
						} else
						{
							instruction.source_operand_class = memory_class;
							instruction.opcode_suffix = 'b';
							instruction.num_operands = grp_prefix + 1;
						}
						next_inst_byte_meaning = modrm_sib_bytes;
						break;
					case I386_INS_Grp3_Ev_Prefix :
						modrm_byte.byte = code_buf[instidx + 1];
						if (modrm_byte.modrm.reg_opcode < 2)
						{
							instruction.destination_operand_class = memory_class;
							instruction.source_operand_class = immediate_class;
							instruction.has_immediate = double_word_immediate;
							instruction.num_operands = grp_prefix + 2;
						} else
						{
							instruction.source_operand_class = memory_class;
							instruction.num_operands = grp_prefix + 1;
						}
						next_inst_byte_meaning = modrm_sib_bytes;
						break;
					case I386_INS_Grp4_Prefix :
					case I386_INS_Grp5_Prefix :
						instruction.source_operand_class = memory_class;
						instruction.num_operands = grp_prefix + 1;
						next_inst_byte_meaning = modrm_sib_bytes;
						break;

				/*	Now the instructions :  Mainly those who have been used in the code generation in .c
				 *	files.
				 */
				/*	Ins :: OPCODE		*/

					case (I386_INS_PUSH_eAX + I386_REG_RAX) :
					case (I386_INS_PUSH_eAX + I386_REG_RCX) :
					case (I386_INS_PUSH_eAX + I386_REG_RDX) :
					case (I386_INS_PUSH_eAX + I386_REG_RBX) :
					case (I386_INS_PUSH_eAX + I386_REG_RSP) :
					case (I386_INS_PUSH_eAX + I386_REG_RBP) :
					case (I386_INS_PUSH_eAX + I386_REG_RSI) :
					case (I386_INS_PUSH_eAX + I386_REG_RDI) :
						instruction.opcode_suffix = ' ';
						instruction.reg_prefix = 'r';
						instruction.num_operands = 1;
						instruction.source_operand_class = register_class;
						instruction.source_operand_reg = (char *)\
							register_list[8 * rex_prefix.Base + inst_curr_byte - I386_INS_PUSH_eAX];
						print_instruction();
						break;
					case (I386_INS_POP_eAX + I386_REG_RAX) :
					case (I386_INS_POP_eAX + I386_REG_RCX) :
					case (I386_INS_POP_eAX + I386_REG_RDX) :
					case (I386_INS_POP_eAX + I386_REG_RBX) :
					case (I386_INS_POP_eAX + I386_REG_RSP) :
					case (I386_INS_POP_eAX + I386_REG_RBP) :
					case (I386_INS_POP_eAX + I386_REG_RSI) :
					case (I386_INS_POP_eAX + I386_REG_RDI) :
						instruction.opcode_suffix = ' ';
						instruction.reg_prefix = 'r';
						instruction.num_operands = 1;
						instruction.source_operand_class = register_class;
						instruction.source_operand_reg =(char *) \
							register_list[8 * rex_prefix.Base + inst_curr_byte - I386_INS_POP_eAX];
						print_instruction();
						break;
					case I386_INS_NOP__ :
					case I386_INS_MOVSB_Xb_Yb :
						print_instruction();
						break;

				/*	Ins :: OPCODE disp8(%rip) 	*/
					case I386_INS_JZ_Jb :
					case I386_INS_JNL_Jb :
					case I386_INS_JNLE_Jb :
					case I386_INS_JLE_Jb :
					case I386_INS_JL_Jb :
					case I386_INS_JMP_Jb :
					case I386_INS_JNZ_Jb :
						instruction.opcode_suffix = ' ';
 						instruction.reg_rip = TRUE;
						instruction.source_operand_class = memory_class;
						set_memory_reg();
						instruction.num_operands = 1;
						next_inst_byte_meaning = one_byte_offset;
						break;

				/*	Ins :: OPCODE disp32(%rip) 	*/
					case I386_INS_CALL_Jv :
					case I386_INS_JMP_Jv :
						instruction.opcode_suffix = ' ';
 						instruction.reg_rip = TRUE;
						instruction.source_operand_class = memory_class;
						set_memory_reg();
						instruction.num_operands = 1;
						next_inst_byte_meaning = double_word_offset;
						break;

				/*	Ins :: OPCODE IMM8 	*/
					case I386_INS_PUSH_Ib :
						instruction.opcode_suffix = 'b';
						instruction.num_operands = 1;
						instruction.source_operand_class = immediate_class;
						next_inst_byte_meaning = one_byte_immediate;
						break;

				/* 	Ins :: OPCODE IMM32/64	*/
					case I386_INS_PUSH_Iv :
						instruction.opcode_suffix = 'l';
						instruction.num_operands = 1;
						instruction.source_operand_class = immediate_class;
						if (rex_prefix.Word64 == 0)
							next_inst_byte_meaning = double_word_immediate;
						else
							next_inst_byte_meaning = quad_word_immediate;
						break;
					case I386_INS_CMP_eAX_Iv :
						instruction.num_operands = 2;
						instruction.destination_operand_class = register_class;
						instruction.destination_operand_reg =(char *) register_list[I386_REG_RAX];
						instruction.source_operand_class = immediate_class;
						if (rex_prefix.Word64 == 0)
							next_inst_byte_meaning = double_word_immediate;
						else
							next_inst_byte_meaning = quad_word_immediate;
						break;
					case (I386_INS_MOV_eAX + I386_REG_RAX) :
					case (I386_INS_MOV_eAX + I386_REG_RCX) :
					case (I386_INS_MOV_eAX + I386_REG_RDX) :
					case (I386_INS_MOV_eAX + I386_REG_RBX) :
					case (I386_INS_MOV_eAX + I386_REG_RSP) :
					case (I386_INS_MOV_eAX + I386_REG_RBP) :
					case (I386_INS_MOV_eAX + I386_REG_RSI) :
					case (I386_INS_MOV_eAX + I386_REG_RDI) :
						instruction.num_operands = 2;
						instruction.destination_operand_class = register_class;
						instruction.destination_operand_reg =(char *) \
							register_list[8 * rex_prefix.Base + inst_curr_byte - I386_INS_MOV_eAX];
						instruction.source_operand_class = immediate_class;
						if (rex_prefix.Word64 == 0)
							next_inst_byte_meaning = double_word_immediate;
						else
							next_inst_byte_meaning = quad_word_immediate;
						break;

				/*	Ins :: OPCODE ModRM (Reg, Mem)/(No_IMM)	*/
					case I386_INS_LEA_Gv_M :
					case I386_INS_MOV_Gv_Ev :
					case I386_INS_CMP_Gv_Ev :
					case I386_INS_XOR_Gv_Ev :
					case I386_INS_MOVSXD_Gv_Ev :
						instruction.num_operands = 2;
						instruction.source_operand_class = memory_class;
						instruction.destination_operand_class = register_class;
						next_inst_byte_meaning = modrm_sib_bytes;
						break;


				/*	Ins :: OPCODE ModRM (Mem, Reg)/(No_IMM)	*/
					case I386_INS_MOV_Ev_Gv :
						instruction.num_operands = 2;
						instruction.source_operand_class = register_class;
						instruction.destination_operand_class = memory_class;
						next_inst_byte_meaning = modrm_sib_bytes;
						break;

				/*	Ins :: OPCODE ModRM (Mem, IMM) */
					case I386_INS_MOV_Ev_Iv :
						instruction.num_operands = 2;
						instruction.destination_operand_class = memory_class;
						instruction.has_immediate = double_word_immediate;
						instruction.source_operand_class = immediate_class;
						next_inst_byte_meaning = modrm_sib_bytes;
						break;

					default :
						GTMASSERT;
				}
				break;
			case two_byte_opcode :
				inst_curr_byte = code_buf[instidx++];
				switch(inst_curr_byte)
				{
					case I386_INS_JO_Jv :
					case I386_INS_JNO_Jv :
					case I386_INS_JB_Jv :
					case I386_INS_JNB_Jv :
					case I386_INS_JZ_Jv :
					case I386_INS_JNZ_Jv :
					case I386_INS_JBE_Jv :
					case I386_INS_JNBE_Jv :
					case I386_INS_JS_Jv :
					case I386_INS_JNS_Jv :
					case I386_INS_JP_Jv :
					case I386_INS_JNP_Jv :
					case I386_INS_JL_Jv :
					case I386_INS_JNL_Jv :
					case I386_INS_JLE_Jv :
					case I386_INS_JNLE_Jv :
 						instruction.reg_rip = TRUE;
						instruction.opcode_mnemonic =(char *) mnemonic_list_2b[inst_curr_byte];
						instruction.source_operand_class = memory_class;
						set_memory_reg();
						instruction.num_operands = 1;
						next_inst_byte_meaning = double_word_offset;
						break;
					default :
						GTMASSERT;
				}
				break;
			case modrm_sib_bytes :
				inst_curr_byte = code_buf[instidx++];
				modrm_byte.byte = inst_curr_byte;

				if (instruction.num_operands >= grp_prefix) /* Means reg_opcode = op ext */
				{
					switch((unsigned char) code_buf[instidx - 2])
					{
						case I386_INS_Grp1_Eb_Ib_Prefix :
						case I386_INS_Grp1_Ev_Iv_Prefix :
						case I386_INS_Grp1_Ev_Ib_Prefix :
							instruction.opcode_mnemonic =(char *) \
								mnemonic_list_g1[modrm_byte.modrm.reg_opcode];
							break;
						case I386_INS_Grp2_Eb_Ib_Prefix :
						case I386_INS_Grp2_Ev_Iv_Prefix :
						case I386_INS_Grp2_Eb_1_Prefix :
						case I386_INS_Grp2_Ev_1_Prefix :
						case I386_INS_Grp2_Eb_CL_Prefix :
						case I386_INS_Grp2_Ev_CL_Prefix :
							instruction.opcode_mnemonic = (char *)\
								mnemonic_list_g2[modrm_byte.modrm.reg_opcode];
							break;
						case I386_INS_Grp3_Eb_Prefix :
						case I386_INS_Grp3_Ev_Prefix :
							instruction.opcode_mnemonic =(char *) \
								 mnemonic_list_g3[modrm_byte.modrm.reg_opcode];
							break;
						case I386_INS_Grp4_Prefix :
							instruction.opcode_mnemonic =(char *) \
								mnemonic_list_g4[modrm_byte.modrm.reg_opcode];
							break;
						case I386_INS_Grp5_Prefix :
							instruction.opcode_suffix = ' ';
							instruction.opcode_mnemonic =(char *) \
								mnemonic_list_g5[modrm_byte.modrm.reg_opcode];
							break;
					}
				} else
					set_register_reg();

				set_memory_reg();

				/* Handle the SIB byte ! */
				if ((modrm_byte.modrm.mod != I386_MOD32_REGISTER) &&
				    (modrm_byte.modrm.r_m == I386_REG_SIB_FOLLOWS))
				{
					inst_curr_byte = code_buf[instidx++];
					sib_byte.byte = inst_curr_byte;
			/*	Assert that the SIB is not used for any complex addressing but is actually a dummy */
					assert((sib_byte.sib.base == I386_REG_ESP) || (sib_byte.sib.base == I386_REG_EBP));
					assert(sib_byte.sib.ss == I386_SS_TIMES_1);
					assert(sib_byte.sib.index == I386_REG_NO_INDEX);

					if (instruction.source_operand_class == memory_class)
						instruction.source_operand_reg =
							(char *) register_list[sib_byte.sib.base + 8 * rex_prefix.Base];
					else if (instruction.destination_operand_class == memory_class)
						instruction.destination_operand_reg =
							(char *) register_list[sib_byte.sib.base + 8 * rex_prefix.Base];

					switch(modrm_byte.modrm.mod)
					{
						case I386_MOD32_BASE :
							if (sib_byte.sib.base == I386_REG_disp32_NO_BASE)
							{
								clear_memory_reg();
								next_inst_byte_meaning = double_word_offset;
							} else if (instruction.has_immediate)
								next_inst_byte_meaning = instruction.has_immediate;
							else
							{
								print_instruction();
								next_inst_byte_meaning = one_byte_opcode;
							}
							break;
						case I386_MOD32_BASE_DISP_8 :
							next_inst_byte_meaning = one_byte_offset;
							break;
						case I386_MOD32_BASE_DISP_32 :
							next_inst_byte_meaning = double_word_offset;
							break;
						default :
							GTMASSERT;
					}
				} else 		/* No SIB */
				{
					switch(modrm_byte.modrm.mod)
					{
						case I386_MOD32_BASE :
							if (modrm_byte.modrm.r_m == I386_REG_disp32_FROM_RIP)
							{
								instruction.reg_rip = TRUE;
								set_memory_reg();
								next_inst_byte_meaning = double_word_offset;
							} else
							{
								instruction.offset = 0;
								if (instruction.has_immediate)
									next_inst_byte_meaning = instruction.has_immediate;
								else
								{
									print_instruction();
									next_inst_byte_meaning = one_byte_opcode;
								}
							}
							break;
						case I386_MOD32_BASE_DISP_8 :
							next_inst_byte_meaning = one_byte_offset;
							break;
						case I386_MOD32_BASE_DISP_32 :
							next_inst_byte_meaning = double_word_offset;
							break;
						case I386_MOD32_REGISTER :
							if (instruction.source_operand_class == memory_class)
								instruction.source_operand_class = register_class;
							else if (instruction.destination_operand_class == memory_class)
								instruction.destination_operand_class = register_class;

							if (instruction.has_immediate)
								next_inst_byte_meaning = instruction.has_immediate;
							else
							{
								print_instruction();
								next_inst_byte_meaning = one_byte_opcode;
							}
							break;
						default :
							GTMASSERT;
					}
				}
				break;
			case one_byte_immediate :
				instruction.immediate = code_buf[instidx++];
				print_instruction();
				next_inst_byte_meaning = one_byte_opcode;
				break;
			case double_word_immediate :
				instruction.immediate = *((int *)&code_buf[instidx]);
				instidx += 4;
				print_instruction();
				next_inst_byte_meaning = one_byte_opcode;
				break;
			case quad_word_immediate :
				instruction.immediate = *((long *)&code_buf[instidx]);
				instidx += 8;
				print_instruction();
				next_inst_byte_meaning = one_byte_opcode;
				break;
			case one_byte_offset :
				instruction.offset = code_buf[instidx++];
				if (instruction.has_immediate)
					next_inst_byte_meaning = instruction.has_immediate;
				else
				{
					print_instruction();
					next_inst_byte_meaning = one_byte_opcode;
				}
				break;
			case double_word_offset :
				instruction.offset = *((int *)&code_buf[instidx]);
				instidx += 4;
				if (instruction.has_immediate)
					next_inst_byte_meaning = instruction.has_immediate;
				else
				{
					print_instruction();
					next_inst_byte_meaning = one_byte_opcode;
				}
				break;
			case quad_word_offset :
				instruction.offset = *((long *)&code_buf[instidx]);
				instidx += 8;
				if (instruction.has_immediate)
					next_inst_byte_meaning = instruction.has_immediate;
				else
				{
					print_instruction();
					next_inst_byte_meaning = one_byte_opcode;
				}
				break;
			default :
				GTMASSERT;

		 }
	}
}
#endif /* DEBUG */
