Click here to Skip to main content
15,895,557 members
Articles / Programming Languages / C++

Intercepting Calls to COM Interfaces

,
Rate me:
Please Sign up or sign in to vote.
4.91/5 (34 votes)
2 Feb 2011CPOL8 min read 87.2K   2.6K   102  
In this article, I’m going to describe how to implement COM interface hooks.
// Copyright (C) 2004, Matt Conover (mconover@gmail.com)
#undef NDEBUG
#include <assert.h>
#include "disasm.h"
#include "cpu.h"

// Since addresses are internally represented as 64-bit, we need to specially handle
// cases where IP + Displacement wraps around for 16-bit/32-bit operand size
// Otherwise, ignorethe possibility of wraparounds
#define SUPPORT_WRAPAROUND

#ifdef NO_SANITY_CHECKS
#undef NDEBUG
#undef DEBUG_DISASM
#undef assert
#define assert(x)
#endif

#ifdef DEBUG_DISASM
#define DISASM_OUTPUT(x) printf x
#else
#define DISASM_OUTPUT(x)
#endif

#include "disasm_x86_tables.h"

#ifdef _WIN64
#pragma warning(disable:4311 4312)
#endif

////////////////////////////////////////////////////////////////////////
// Internal macros
////////////////////////////////////////////////////////////////////////

#define VIRTUAL_ADDRESS ((U64)Instruction->Address + Instruction->VirtualAddressDelta)

#define AMD64_DIFF (AMD64_8BIT_OFFSET-X86_8BIT_OFFSET)
#define IS_AMD64() (INS_ARCH_TYPE(Instruction) == ARCH_X64)
#define IS_X86_32() (INS_ARCH_TYPE(Instruction) == ARCH_X86)
#define IS_X86_16() (INS_ARCH_TYPE(Instruction) == ARCH_X86_16)

#define X86_BOUND 0x62
#define X86_PUSH_REG 0x50
#define X86_PUSH_CS 0x0e
#define X86_PUSH_DS 0x1e
#define X86_PUSH_SS 0x16
#define X86_PUSH_ES 0x06
#define X86_PUSH_FS 0xa0
#define X86_PUSH_GS 0xa8
#define X86_PUSH_U8 0x6a
#define X86_PUSH_U32 0x68
#define X86_POP_DS 0x1f
#define X86_POP_ES 0x07
#define X86_POP_SS 0x17
#define X86_POP_FS 0xa1
#define X86_POP_GS 0xa9
#define X86_POP_REG 0x58

#define OPCSTR Instruction->String+Instruction->StringIndex
#define APPEND Instruction->StringIndex += (U8)_snprintf
#define APPENDPAD(x) \
{ \
	if (Instruction->StringAligned) \
	{  \
			if (Instruction->StringIndex > x) assert(0); \
			while (x != Instruction->StringIndex) APPENDB(' ');  \
	}  \
	else if (Instruction->StringIndex) \
	{  \
		APPENDB(' '); \
	} \
}

#define APPENDB(a) Instruction->String[Instruction->StringIndex++] = a
#define APPENDS(a) APPEND(OPCSTR, SIZE_LEFT, a);

#define SIZE_LEFT (MAX_OPCODE_DESCRIPTION-1 > Instruction->StringIndex ? MAX_OPCODE_DESCRIPTION-Instruction->StringIndex : 0)

// If an address size prefix is used for an instruction that doesn't make sense, restore it
// to the default

#define SANITY_CHECK_OPERAND_SIZE() \
{ \
	if (!Instruction->AnomalyOccurred && X86Instruction->HasOperandSizePrefix) \
	{ \
		if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Unexpected operand size prefix\n", VIRTUAL_ADDRESS); \
		Instruction->AnomalyOccurred = TRUE; \
		X86Instruction->HasOperandSizePrefix = FALSE; \
		switch (X86Instruction->OperandSize) \
		{ \
			case 4: X86Instruction->OperandSize = 2; break; \
			case 2: X86Instruction->OperandSize = 4; break; \
			default: assert(0); \
		} \
	} \
}

#define SANITY_CHECK_ADDRESS_SIZE() \
{ \
	if (!Instruction->AnomalyOccurred && X86Instruction->HasAddressSizePrefix) \
	{ \
		if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Unexpected address size prefix\n", VIRTUAL_ADDRESS); \
		Instruction->AnomalyOccurred = TRUE; \
	} \
	X86Instruction->HasAddressSizePrefix = FALSE; \
	switch (INS_ARCH_TYPE(Instruction)) \
	{ \
		case ARCH_X64: X86Instruction->AddressSize = 8; break; \
		case ARCH_X86: X86Instruction->AddressSize = 4; break; \
		case ARCH_X86_16: X86Instruction->AddressSize = 2; break; \
	} \
}

#define SANITY_CHECK_SEGMENT_OVERRIDE() \
	if (!Instruction->AnomalyOccurred && X86Instruction->HasSegmentOverridePrefix) \
	{ \
		if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Unexpected segment override\n", VIRTUAL_ADDRESS); \
		Instruction->AnomalyOccurred = TRUE; \
	}

#define INSTR_INC(size) \
{ \
	Instruction->Length += size; \
	Address += size; \
}

#define X86_SET_TARGET() \
{ \
	if (X86Instruction->HasSelector) \
	{ \
		if (!Instruction->AnomalyOccurred) \
		{ \
			if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: unexpected segment 0x%02X\n", VIRTUAL_ADDRESS, X86Instruction->Selector); \
			Instruction->AnomalyOccurred = TRUE; \
		} \
	} \
	else \
	{ \
		switch (X86Instruction->Segment) \
		{ \
			case SEG_CS: \
			case SEG_DS: \
			case SEG_SS: \
			case SEG_ES: \
				assert(!X86Instruction->HasSelector); \
				Operand->TargetAddress = (U64)X86Instruction->Displacement; \
				/* assert(!GetAbsoluteAddressFromSegment((BYTE)X86Instruction->Segment, (DWORD)X86Instruction->Displacement) || GetAbsoluteAddressFromSegment(X86Instruction->Segment, (DWORD)X86Instruction->Displacement) == Operand->TargetAddress); */ \
				break; \
			case SEG_FS: \
			case SEG_GS: \
				assert(!X86Instruction->HasSelector); \
				Operand->TargetAddress = (U64)GetAbsoluteAddressFromSegment((BYTE)X86Instruction->Segment, (DWORD)X86Instruction->Displacement); \
				break; \
			default: \
				assert(0); /* shouldn't be possible */ \
				break; \
		} \
	} \
}

#define X86_SET_SEG(reg) \
{ \
	if (!X86Instruction->HasSegmentOverridePrefix && (reg == REG_EBP || reg == REG_ESP)) \
	{ \
		assert(!X86Instruction->HasSelector); \
		X86Instruction->Segment = SEG_SS; \
	} \
}

#define X86_SET_ADDR() \
{ \
	if (Operand->Flags & OP_DST) \
	{ \
		assert(!X86Instruction->HasDstAddressing); \
		X86Instruction->HasDstAddressing = TRUE; \
		X86Instruction->DstOpIndex[X86Instruction->DstOpCount] = (U8)OperandIndex; \
		X86Instruction->DstOpCount++; \
		X86Instruction->DstAddressIndex = (U8)OperandIndex; \
	} \
	if (Operand->Flags & OP_SRC) \
	{ \
		if (Instruction->Type != ITYPE_STRCMP) assert(!X86Instruction->HasSrcAddressing); \
		X86Instruction->HasSrcAddressing = TRUE; \
		X86Instruction->SrcOpIndex[X86Instruction->SrcOpCount] = (U8)OperandIndex; \
		X86Instruction->SrcOpCount++; \
		X86Instruction->SrcAddressIndex = (U8)OperandIndex; \
	} \
}

#define X86_SET_REG(reg) \
{ \
	if (Operand->Flags & OP_DST) \
	{ \
		X86Instruction->DstOpIndex[X86Instruction->DstOpCount] = (U8)OperandIndex; \
		X86Instruction->DstOpCount++; \
		assert(OperandIndex < 2); \
		if (Operand->Length > 1 && reg == REG_ESP) Instruction->Groups |= ITYPE_STACK; \
	} \
	if (Operand->Flags & OP_SRC) \
	{ \
		X86Instruction->SrcOpIndex[X86Instruction->SrcOpCount] = (U8)OperandIndex; \
		X86Instruction->SrcOpCount++; \
	} \
} 

#define CHECK_AMD64_REG() { if (IS_AMD64()) Operand->Register += AMD64_DIFF; }

////////////////////////////////////////////////////////////////////////
// Internal structures/variables
////////////////////////////////////////////////////////////////////////

ARCHITECTURE_FORMAT_FUNCTIONS X86 = 
{
	X86_InitInstruction, 
	NULL,
	X86_GetInstruction,
	X86_FindFunctionByPrologue 
};

char *X86_Registers[0xE0] = 
{
	// Segments
	"es", // 0x00
	"cs", // 0x01
	"ss", // 0x02
	"ds", // 0x03
	"fs", // 0x04
	"gs", // 0x05
	"flags", // 0x06
	"eflags", // 0x07
	"rflags", // 0x08
	"ip+ilen", // 0x09
	"eip+ilen", // 0x0A
	"rip+ilen", // 0x0B
	NULL, // 0x0C
	NULL, // 0x0D
	NULL, // 0x0E
	NULL, // 0x0F

	// Test
	"tr0", // 0x10
	"tr1", // 0x11
	"tr2", // 0x12
	"tr3", // 0x13
	"tr4", // 0x14
	"tr5", // 0x15
	"tr6", // 0x16
	"tr7", // 0x17
	"tr8", // 0x18
	"tr9", // 0x19
	"tr10", // 0x1A
	"tr11", // 0x1B
	"tr12", // 0x1C
	"tr13", // 0x1D
	"tr14", // 0x1E
	"tr15", // 0x1F

	// Control
	"cr0", // 0x20
	"cr1", // 0x21
	"cr2", // 0x22
	"cr3", // 0x23
	"cr4", // 0x24
	"cr5", // 0x25
	"cr6", // 0x26
	"cr7", // 0x27
	"cr8", // 0x18
	"cr9", // 0x19
	"cr10", // 0x1A
	"cr11", // 0x1B
	"cr12", // 0x1C
	"cr13", // 0x1D
	"cr14", // 0x1E
	"cr15", // 0x1F

	// Debug
	"dr0", // 0x30
	"dr1", // 0x31
	"dr2", // 0x32
	"dr3", // 0x33
	"dr4", // 0x34
	"dr5", // 0x35
	"dr6", // 0x36
	"dr7", // 0x37
	"dr8", // 0x38
	"dr9", // 0x39
	"dr10", // 0x3A
	"dr11", // 0x3B
	"dr12", // 0x3C
	"dr13", // 0x3D
	"dr14", // 0x3E
	"dr15", // 0x3F

	// FPU
	"st(0)", // 0x40
	"st(1)", // 0x41
	"st(2)", // 0x42
	"st(3)", // 0x43
	"st(4)", // 0x44
	"st(5)", // 0x45
	"st(6)", // 0x46
	"st(7)", // 0x47
	NULL, // 0x48
	NULL, // 0x49
	NULL, // 0x4A
	NULL, // 0x4B
	NULL, // 0x4C
	NULL, // 0x4D
	NULL, // 0x4E
	NULL, // 0x4F

	// MMX
	"mm0", // 0x50
	"mm1", // 
	"mm2",
	"mm3",
	"mm4",
	"mm5",
	"mm6",
	"mm7",
	NULL, // 0x58
	NULL, // 0x59
	NULL, // 0x5A
	NULL, // 0x5B
	NULL, // 0x5C
	NULL, // 0x5D
	NULL, // 0x5E
	NULL, // 0x5F

	// XMM
	"xmm0", // 0x60
	"xmm1", // 0x61
	"xmm2", // 0x62
	"xmm3", // 0x63
	"xmm4", // 0x64
	"xmm5", // 0x65
	"xmm6", // 0x66
	"xmm7", // 0x67
	"xmm8", // 0x68
	"xmm9", // 0x69
	"xmm10", // 0x6a
	"xmm11", // 0x6b
	"xmm12", // 0x6c
	"xmm13", // 0x6d
	"xmm14", // 0x6e
	"xmm15", // 0x6f

	// 8-bit
	"al", // 0x70
	"cl", // 0x71
	"dl", // 0x72
	"bl", // 0x73
	"ah", // 0x74
	"ch", // 0x75
	"dh", // 0x76
	"bh", // 0x77
 	NULL, // 0x78
	NULL, // 0x79
	NULL, // 0x7A
	NULL, // 0x7B
	NULL, // 0x7C
	NULL, // 0x7D
	NULL, // 0x7E
	NULL, // 0x7F

	// 16-bit
	"ax", // 0x80
	"cx", // 0x81
	"dx", // 0x82
	"bx", // 0x83
	"sp", // 0x84
	"bp", // 0x85
	"si", // 0x86
	"di", // 0x87
	NULL, // 0x88
	NULL, // 0x89
	NULL, // 0x8A
	NULL, // 0x8B
	NULL, // 0x8C
	NULL, // 0x8D
	NULL, // 0x8E
	NULL, // 0x8F

	// 32-bit
	"eax", // 0x90
	"ecx", // 0x91
	"edx", // 0x92
	"ebx", // 0x93
	"esp", // 0x94
	"ebp", // 0x95
	"esi", // 0x96
	"edi", // 0x97
	NULL, // 0x98
	NULL, // 0x99
	NULL, // 0x9A
	NULL, // 0x9B
	NULL, // 0x9C
	NULL, // 0x9D
	NULL, // 0x9E
	NULL, // 0x9F

	// X86-64 8-bit register
	"al", // 0xA0
	"cl", // 0xA1
	"dl", // 0xA2
	"bl", // 0xA3
	"spl", // 0xA4
	"bpl", // 0xA5
	"sil", // 0xA6
	"dil", // 0xA7
	"r8b", // 0xA8
	"r9b", // 0xA9
	"r10b", // 0xAA
	"r11b", // 0xAB
	"r12b", // 0xAC
	"r13b", // 0xAD
	"r14b", // 0xAE
	"r15b", // 0xAF

	// X86-64 16-bit register	
	"ax", // 0xB0
	"cx", // 0xB1
	"dx", // 0xB2
	"bx", // 0xB3
	"sp", // 0xB4
	"bp", // 0xB5
	"si", // 0xB6
	"di", // 0xB7
	"r8w", // 0xB8
	"r9w", // 0xB9
	"r10w", // 0xBA
	"r11w", // 0xBB
	"r12w", // 0xBC
	"r13w", // 0xBD
	"r14w", // 0xBE
	"r15w", // 0xBF

	// X86-64 32-bit register
	"eax", // 0xC0
	"ecx", // 0xC1
	"edx", // 0xC2
	"ebx", // 0xC3
	"esp", // 0xC4
	"ebp", // 0xC5
	"esi", // 0xC6
	"edi", // 0xC7
	"r8d", // 0xC8
	"r9d", // 0xC9
	"r10d", // 0xCA
	"r11d", // 0xCB
	"r12d", // 0xCC
	"r13d", // 0xCD
	"r14d", // 0xCE
	"r15d", // 0xCF

	// X86-64 64-bit register	
	"rax", // 0xD0
	"rcx", // 0xD1
	"rdx", // 0xD2
	"rbx", // 0xD3
	"rsp", // 0xD4
	"rbp", // 0xD5
	"rsi", // 0xD6
	"rdi", // 0xD7
	"r8", // 0xD8
	"r9", // 0xD9
	"r10", // 0xDA
	"r11", // 0xDB
	"r12", // 0xDC
	"r13", // 0xDD
	"r14", // 0xDE
	"r15" // 0xDF
};

void OutputBounds(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputGeneral(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputDescriptor(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputSegOffset(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputPackedReal(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputPackedBCD(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputScalarReal(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputScalarGeneral(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputFPUEnvironment(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputFPUState(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
void OutputCPUState(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);

typedef void (*OUTPUT_OPTYPE)(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex);
#define OPTYPE_SHIFT 24
#define MAX_OPTYPE_INDEX 26
OUTPUT_OPTYPE OptypeHandlers[] =
{
	NULL,
	OutputBounds,         // 01 OPTYPE_a
	OutputGeneral,        // 02 OPTYPE_b
	OutputGeneral,        // 03 OPTYPE_d
	OutputSegOffset,      // 04 OPTYPE_p
	OutputGeneral,        // 05 OPTYPE_q
	OutputDescriptor,     // 06 OPTYPE_dt
	OutputGeneral,        // 07 OPTYPE_v
	OutputGeneral,        // 08 OPTYPE_w
	OutputPackedReal,     // 09 OPTYPE_ps
	OutputPackedReal,     // 0A OPTYPE_pd
	OutputPackedBCD,      // 0B OPTYPE_pb
	OutputScalarReal,     // 0C OPTYPE_ss
	OutputScalarReal,     // 0D OPTYPE_sd
	OutputScalarReal,     // 0E OPTYPE_se
	OutputFPUEnvironment, // 0F OPTYPE_fev
	OutputFPUState,       // 10 OPTYPE_fst1
	OutputFPUState,       // 11 OPTYPE_fst2
	OutputGeneral,        // 12 OPTYPE_z
	OutputGeneral,        // 13 OPTYPE_o
	OutputGeneral,        // 14 OPTYPE_dq
	OutputGeneral,        // 15 OPTYPE_mw
	OutputScalarGeneral,  // 16 OPTYPE_sso
	OutputScalarGeneral,  // 17 OPTYPE_sdo
	OutputCPUState,       // 18 OPTYPE_cpu
	OutputGeneral,        // 19 OPTYPE_lea
};

#define OPTYPE_a    0x01000000
#define OPTYPE_b    0x02000000
#define OPTYPE_d    0x03000000
#define OPTYPE_p    0x04000000
#define OPTYPE_q    0x05000000
#define OPTYPE_dt   0x06000000
#define OPTYPE_v    0x07000000
#define OPTYPE_w    0x08000000
#define OPTYPE_ps   0x09000000 // packed 128-bit single real
#define OPTYPE_pd   0x0A000000 // packed 128-bit double real
#define OPTYPE_pb	  0x0B000000 // packed BCD (10 bytes, 18-bit precision)
#define OPTYPE_ss   0x0C000000 // scalar single real
#define OPTYPE_sd   0x0D000000 // scalar double real
#define OPTYPE_se   0x0E000000 // scalar extended real
#define OPTYPE_fev  0x0F000000 // FPU environment (28 bytes if 32-bit modes, 14 bytes in 16-bit mode)
#define OPTYPE_fst1 0x10000000 // FPU state (108 bytes in 32-bit modes, 94 bytes in 16-bit real mode)
#define OPTYPE_fst2 0x11000000 // FPU/MMX/XMM/MXCSR state (512 bytes)
#define OPTYPE_z	  0x12000000
#define OPTYPE_o	  0x13000000
#define OPTYPE_dq   0x14000000 // OPTYPE_d or OPTYPE_o
#define OPTYPE_mw   0x15000000 // word if memory, register size otherwise
#define OPTYPE_sso  0x16000000 // OPTYPE_ss or OPTYPE_o
#define OPTYPE_sdo  0x17000000 // OPTYPE_ss or OPTYPE_o
#define OPTYPE_cpu  0x18000000 // pointer to CPU state structure
#define OPTYPE_lea  0x19000000 // size set by other operand

////////////////////////////////////////////////////////////////////////
// Internal functions
////////////////////////////////////////////////////////////////////////

#ifdef TEST_DISASM // TODO: remove
U32 X86_GetLength(INSTRUCTION *Instruction, U8 *Address);
#endif

INTERNAL BOOL IsValidLockPrefix(X86_INSTRUCTION *Instruction, U8 Opcode, U32 OpcodeLength, U8 Group, U8 OpcodeExtension);
INTERNAL U8 *SetOperands(INSTRUCTION *Instruction, U8 *Address, U32 Flags);
INTERNAL U8 *SetModRM32(INSTRUCTION *Instruction, U8 *Address, INSTRUCTION_OPERAND *Operand, U32 OperandIndex, BOOL SuppressErrors);
INTERNAL U8 *SetModRM16(INSTRUCTION *Instruction, U8 *Address, INSTRUCTION_OPERAND *Operand, U32 OperandIndex, BOOL SuppressErrors);
INTERNAL U8 *SetSIB(INSTRUCTION *Instruction, U8 *Address, INSTRUCTION_OPERAND *Operand, U32 OperandIndex, BOOL SuppressErrors);
INTERNAL U64 ApplyDisplacement(U64 Address, INSTRUCTION *Instruction);

//////////////////////////////////////////////////////////
// Instruction setup
//////////////////////////////////////////////////////////

#define APPLY_OFFSET(addr) \
{ \
	switch (X86Instruction->OperandSize) \
	{ \
		case 8: addr = ((U64)(addr + Instruction->VirtualAddressDelta)); break; \
		case 4: addr = (U64)((U32)(addr + Instruction->VirtualAddressDelta)); break; \
		case 2: addr = (U64)((U8)(addr + Instruction->VirtualAddressDelta)); break; \
		default: assert(0); break; \
	} \
}

BOOL X86_InitInstruction(INSTRUCTION *Instruction)
{
	X86_INSTRUCTION *X86Instruction;
#ifdef NO_SANITY_CHECKS
	assert(0); // be sure assertions are disabled
#endif
	X86Instruction = &Instruction->X86;
	memset(X86Instruction, 0, sizeof(X86_INSTRUCTION));
	
	switch (INS_ARCH_TYPE(Instruction))
	{
		case ARCH_X64:
			X86Instruction->AddressSize = 8;
			X86Instruction->OperandSize = 4;
			break;
		case ARCH_X86:
			X86Instruction->AddressSize = 4;
			X86Instruction->OperandSize = 4;
			break;
		case ARCH_X86_16:
			X86Instruction->AddressSize = 2;
			X86Instruction->OperandSize = 2;
			break;
		default:
			assert(0);
			return FALSE;
	}
	X86Instruction->Instruction = Instruction;
	X86Instruction->Segment = SEG_DS;
	return TRUE;
}

////////////////////////////////////////////////////////////
// Formatting
// You can change these to whatever you prefer
////////////////////////////////////////////////////////////

#define X86_WRITE_OPFLAGS() \
	if (Flags & DISASM_SHOWFLAGS) \
	{ \
		APPENDB('{'); \
		assert(Operand->Flags & (OP_EXEC|OP_SRC|OP_DST)); \
		if (Operand->Flags & OP_IPREL) APPENDB('r'); \
		if (Operand->Flags & OP_FAR) APPENDB('f'); \
		if (Operand->Flags & OP_CONDR) APPENDB('c'); \
		if (Operand->Flags & OP_EXEC) APPENDB('X'); \
		else if (Operand->Flags & OP_SRC) APPENDB('R'); \
		if (Operand->Flags & OP_CONDW) APPENDB('c'); \
		if (Operand->Flags & OP_DST) APPENDB('W'); \
		if (Operand->Flags & OP_SYS) APPENDB('S'); \
		if (Operand->Flags & OP_ADDRESS) APPENDB('A'); \
		if (Operand->Flags & OP_PARAM) APPENDB('P'); \
		if (Operand->Flags & OP_LOCAL) APPENDB('L'); \
		if (Operand->Flags & OP_GLOBAL) APPENDB('G'); \
		APPENDB('}'); \
	}

#define X86_WRITE_IMMEDIATE() \
{ \
	switch (Operand->Length) \
	{ \
		case 8: \
			APPEND(OPCSTR, SIZE_LEFT, "0x%02I64X=", Operand->Value_U64); \
			if (Operand->Value_S64 >= 0 || !(Operand->Flags & OP_SIGNED)) APPEND(OPCSTR, SIZE_LEFT, "%I64u", Operand->Value_U64); \
			/*else APPEND(OPCSTR, SIZE_LEFT, "-0x%02I64X=%I64d", -Operand->Value_S64, Operand->Value_S64);*/ \
			else APPEND(OPCSTR, SIZE_LEFT, "%I64d", Operand->Value_S64); \
			break; \
		case 4: \
			APPEND(OPCSTR, SIZE_LEFT, "0x%02lX=", (U32)Operand->Value_U64); \
			if (Operand->Value_S64 >= 0 || !(Operand->Flags & OP_SIGNED)) APPEND(OPCSTR, SIZE_LEFT, "%lu", (U32)Operand->Value_U64); \
			/*else APPEND(OPCSTR, SIZE_LEFT, "-0x%02lX=%ld", (U32)-Operand->Value_S64, (S32)Operand->Value_S64);*/ \
			else APPEND(OPCSTR, SIZE_LEFT, "%ld", (S32)Operand->Value_S64); \
			break; \
		case 2: \
			APPEND(OPCSTR, SIZE_LEFT, "0x%02X=", (U16)Operand->Value_U64); \
			if (Operand->Value_S64 >= 0 || !(Operand->Flags & OP_SIGNED)) APPEND(OPCSTR, SIZE_LEFT, "%u", (U16)Operand->Value_U64); \
			/*else APPEND(OPCSTR, SIZE_LEFT, "-0x%02X=%d", (U16)-Operand->Value_S64, (S16)Operand->Value_S64);*/ \
			else APPEND(OPCSTR, SIZE_LEFT, "%d", (S16)Operand->Value_S64); \
			break; \
		case 1: \
			APPEND(OPCSTR, SIZE_LEFT, "0x%02X=", (U8)Operand->Value_U64); \
			if (Operand->Value_S64 >= 0 || !(Operand->Flags & OP_SIGNED)) APPEND(OPCSTR, SIZE_LEFT, "%u", (U8)Operand->Value_U64); \
			/*else APPEND(OPCSTR, SIZE_LEFT, "-0x%02X=%d", (U8)-Operand->Value_S64, (S8)Operand->Value_S64);*/ \
			else APPEND(OPCSTR, SIZE_LEFT, "%d", (S8)Operand->Value_S64); \
			break; \
		default: assert(0); break; \
	} \
}

#define X86_WRITE_ABSOLUTE_DISPLACEMENT() \
{  \
	switch (X86Instruction->AddressSize) \
	{ \
		case 8: \
			APPEND(OPCSTR, SIZE_LEFT, "0x%04I64X", X86Instruction->Displacement); \
			break; \
		case 4: \
			APPEND(OPCSTR, SIZE_LEFT, "0x%04lX", (U32)X86Instruction->Displacement); \
			break; \
		case 2: \
			APPEND(OPCSTR, SIZE_LEFT, "0x%04X", (U16)X86Instruction->Displacement); \
			break; \
		default: assert(0); break; \
	} \
}

#define X86_WRITE_RELATIVE_DISPLACEMENT64() \
	if (X86Instruction->Displacement >= 0) APPEND(OPCSTR, SIZE_LEFT, "+0x%02I64X", X86Instruction->Displacement); \
	else APPEND(OPCSTR, SIZE_LEFT, "-0x%02I64X", -X86Instruction->Displacement);

#define X86_WRITE_RELATIVE_DISPLACEMENT32() \
	if (X86Instruction->Displacement >= 0) APPEND(OPCSTR, SIZE_LEFT, "+0x%02lX", (U32)X86Instruction->Displacement); \
	else APPEND(OPCSTR, SIZE_LEFT, "-0x%02lX", (U32)-X86Instruction->Displacement);

#define X86_WRITE_RELATIVE_DISPLACEMENT16() \
	if (X86Instruction->Displacement >= 0) APPEND(OPCSTR, SIZE_LEFT, "+0x%02X", (U16)X86Instruction->Displacement); \
	else APPEND(OPCSTR, SIZE_LEFT, "-0x%02X", (U16)-X86Instruction->Displacement);

#define X86_WRITE_RELATIVE_DISPLACEMENT() \
{  \
	switch (X86Instruction->AddressSize) \
	{ \
		case 8: \
			X86_WRITE_RELATIVE_DISPLACEMENT64() \
			break; \
		case 4: \
			X86_WRITE_RELATIVE_DISPLACEMENT32() \
			break; \
		case 2: \
			X86_WRITE_RELATIVE_DISPLACEMENT16() \
			break; \
		default: assert(0); break; \
	} \
}

#define X86_WRITE_IP_OFFSET(op) \
{ \
	switch (X86Instruction->OperandSize) \
	{ \
		case 8: \
			APPENDS("[rip+ilen"); \
			assert((op)->TargetAddress); \
			X86_WRITE_RELATIVE_DISPLACEMENT64() \
			APPEND(OPCSTR, SIZE_LEFT, "]=0x%04I64X", (op)->TargetAddress+Instruction->VirtualAddressDelta); \
			break; \
		case 4: \
			APPENDS("[eip+ilen"); \
			assert((op)->TargetAddress); \
			X86_WRITE_RELATIVE_DISPLACEMENT32() \
			APPEND(OPCSTR, SIZE_LEFT, "]=0x%04lX", (U32)((op)->TargetAddress+Instruction->VirtualAddressDelta)); \
			break; \
		case 2: \
			APPENDS("[ip+ilen"); \
			X86_WRITE_RELATIVE_DISPLACEMENT16() \
			APPEND(OPCSTR, SIZE_LEFT, "]=0x%04X", (U16)((op)->TargetAddress+Instruction->VirtualAddressDelta)); \
			break; \
		default: assert(0); break; \
	} \
}

#define X86_WRITE_OFFSET(op) \
{ \
	assert((op)->Length <= 8); \
	if (X86Instruction->HasSelector) \
	{ \
		assert((op)->Flags & OP_FAR); \
		APPEND(OPCSTR, SIZE_LEFT, "%s 0x%02X:[", DataSizes[((op)->Length >> 1)], X86Instruction->Selector); \
	} \
	else \
	{ \
		assert(!((op)->Flags & OP_FAR)); \
		assert(X86Instruction->Segment < SEG_MAX) ; \
		APPEND(OPCSTR, SIZE_LEFT, "%s %s:[", DataSizes[((op)->Length >> 1)], Segments[X86Instruction->Segment]); \
	} \
	X86_WRITE_ABSOLUTE_DISPLACEMENT() \
	APPENDB(']'); \
}

void OutputAddress(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	BOOL ShowDisplacement = FALSE;
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;

	assert(!X86Instruction->HasSelector);
	assert(X86Instruction->SrcAddressIndex == OperandIndex || X86Instruction->DstAddressIndex == OperandIndex);
	if (Operand->Length > 16 || (Operand->Length > 1 && (Operand->Length & 1))) APPEND(OPCSTR, SIZE_LEFT, "%d_byte ptr ", Operand->Length);
	else APPEND(OPCSTR, SIZE_LEFT, "%s ", DataSizes[Operand->Length >> 1]);

	//
	// This attempts to display the address intelligently
	// If it has a positive 32-bit displacement, it is shown as seg:Displacement[base+index*scale]
	// If it is a negative displacement or 8-bit, it is shown as seg:[base+index*scale+displacement]
	//
	APPEND(OPCSTR, SIZE_LEFT, "%s:", Segments[X86Instruction->Segment]);
	if (X86Instruction->HasBaseRegister)
	{
		if (X86Instruction->Displacement)
		{
			if (X86Instruction->HasFullDisplacement) X86_WRITE_ABSOLUTE_DISPLACEMENT()
			else ShowDisplacement = TRUE;
		}
		APPEND(OPCSTR, SIZE_LEFT, "[%s", X86_Registers[X86Instruction->BaseRegister]);
		if (X86Instruction->HasIndexRegister)
		{
			APPEND(OPCSTR, SIZE_LEFT, "+%s", X86_Registers[X86Instruction->IndexRegister]);
			if (X86Instruction->Scale > 1) APPEND(OPCSTR, SIZE_LEFT, "*%d", X86Instruction->Scale);
		}
		if (ShowDisplacement) X86_WRITE_RELATIVE_DISPLACEMENT()
		APPENDB(']');
		if (X86Instruction->Relative)
		{
			U64 Address = Operand->TargetAddress;
			assert(Address);
			APPLY_OFFSET(Address)
			APPEND(OPCSTR, SIZE_LEFT, "=[0x%04I64X]", Address);
		}
	}
	else if (X86Instruction->HasIndexRegister)
	{
		if (X86Instruction->Displacement)
		{
			if (X86Instruction->HasFullDisplacement) X86_WRITE_ABSOLUTE_DISPLACEMENT()
			else ShowDisplacement = TRUE;
		}
		APPEND(OPCSTR, SIZE_LEFT, "[%s", X86_Registers[X86Instruction->IndexRegister]);
		if (X86Instruction->Scale > 1) APPEND(OPCSTR, SIZE_LEFT, "*%d", X86Instruction->Scale);
		if (ShowDisplacement) X86_WRITE_RELATIVE_DISPLACEMENT()
		APPENDB(']');
	}
	else // just a displacement
	{
		APPENDB('[');
		X86_WRITE_ABSOLUTE_DISPLACEMENT()
		APPENDB(']');
	}
}

void OutputBounds(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	assert(X86Instruction->HasSrcAddressing);
	assert(!(Operand->Length & 1));
	Operand->Length >>= 1;
	APPENDB('(');
	OutputAddress(Instruction, Operand, OperandIndex);
	APPENDS(", ");
	X86Instruction->Displacement += Operand->Length;
	OutputAddress(Instruction, Operand, OperandIndex);
	X86Instruction->Displacement -= Operand->Length;
	APPENDB(')');
	Operand->Length <<= 1;
}

void OutputGeneral(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	if ((X86Instruction->HasDstAddressing && X86Instruction->DstAddressIndex == OperandIndex) ||
		(X86Instruction->HasSrcAddressing && X86Instruction->SrcAddressIndex == OperandIndex))
	{
		OutputAddress(Instruction, Operand, OperandIndex);
	}
	else
	{
		APPENDS(X86_Registers[Operand->Register]);
	}
}

void OutputDescriptor(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	assert(X86Instruction->HasSrcAddressing || X86Instruction->HasDstAddressing);
	OutputAddress(Instruction, Operand, OperandIndex);
}

void OutputPackedReal(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	if ((X86Instruction->HasDstAddressing && X86Instruction->DstAddressIndex == OperandIndex) ||
		(X86Instruction->HasSrcAddressing && X86Instruction->SrcAddressIndex == OperandIndex))
	{
		OutputAddress(Instruction, Operand, OperandIndex);
	}
	else
	{
		APPENDS(X86_Registers[Operand->Register]);
	}
}

void OutputPackedBCD(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	if ((X86Instruction->HasDstAddressing && X86Instruction->DstAddressIndex == OperandIndex) ||
		(X86Instruction->HasSrcAddressing && X86Instruction->SrcAddressIndex == OperandIndex))
	{
		OutputAddress(Instruction, Operand, OperandIndex);
	}
	else
	{
		APPENDS(X86_Registers[Operand->Register]);
	}
}

void OutputScalarReal(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	if ((X86Instruction->HasDstAddressing && X86Instruction->DstAddressIndex == OperandIndex) ||
		(X86Instruction->HasSrcAddressing && X86Instruction->SrcAddressIndex == OperandIndex))
	{
		OutputAddress(Instruction, Operand, OperandIndex);
	}
	else
	{
		APPENDS(X86_Registers[Operand->Register]);
	}
}

void OutputScalarGeneral(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	if (Operand->Type == OPTYPE_FLOAT)
	{
		OutputScalarReal(Instruction, Operand, OperandIndex);
	}
	else
	{
		OutputGeneral(Instruction, Operand, OperandIndex);
	}
}

void OutputFPUEnvironment(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	assert(X86Instruction->HasSrcAddressing || X86Instruction->HasDstAddressing);
	OutputAddress(Instruction, Operand, OperandIndex);
}

void OutputFPUState(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	assert(X86Instruction->HasSrcAddressing || X86Instruction->HasDstAddressing);
	OutputAddress(Instruction, Operand, OperandIndex);
}

void OutputCPUState(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	assert(X86Instruction->HasSrcAddressing);
	OutputAddress(Instruction, Operand, OperandIndex);
}

void OutputSegOffset(INSTRUCTION *Instruction, INSTRUCTION_OPERAND *Operand, U32 OperandIndex)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	assert(X86Instruction->HasSrcAddressing);
	OutputAddress(Instruction, Operand, OperandIndex);	
}

////////////////////////////////////////////////////////////
// Prologue support
////////////////////////////////////////////////////////////

typedef struct _PROLOGUE
{
	char *Data;
	U32 Length;
} PROLOGUE;

PROLOGUE StandardPrologues[] =
{
	{ "\x55\x8b\xec", 3 },
	{ "\x55\x89\xe5", 3 },
	{ "\x83\xec", 2 },
	{ "\x81\xec", 2 },
	// TODO: add AMD64 prologues
	// TODO: add VS2003/VS2003 prologues
	// TODO: add any unique prologues from other compilers
	{ NULL, 0 }
};

// Find the first function between StartAddress and EndAddress
// 
// This will match a standard prologue and then analyze the following instructions to verify
// it is a valid function
U8 *X86_FindFunctionByPrologue(INSTRUCTION *Instruction, U8 *StartAddress, U8 *EndAddress, U32 Flags)
{
	assert(0); // TODO
	return NULL;
}

//////////////////////////////////////////////////////////
// Instruction decoder
//////////////////////////////////////////////////////////

BOOL X86_GetInstruction(INSTRUCTION *Instruction, U8 *Address, U32 Flags)
{
	BOOL SpecialExtension = FALSE;
	U8 Opcode = 0, OpcodeExtension = 0, Group = 0, SSE_Prefix = 0, Suffix;
	U32 i = 0, Result = 0, tmpScale;
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	X86_OPCODE *X86Opcode;
#ifdef TEST_DISASM
	U32 InstructionLength = 0;
#endif
	INSTRUCTION_OPERAND *Operand, *Operand1 = NULL;
	DISASSEMBLER *Disassembler = Instruction->Disassembler;
	BOOL Decode = Flags & DISASM_DECODE;
	BOOL Disassemble = Flags & DISASM_DISASSEMBLE;
	BOOL SuppressErrors = Flags & DISASM_SUPPRESSERRORS;

	if (Disassemble && !Decode)
	{
		assert(0);
		Decode = TRUE;
	}

	if (!Address || !X86_InitInstruction(Instruction))
	{
		assert(0);
		goto abort;
	}

	assert(Instruction->Address == Address);
	assert(!Instruction->StringIndex && !Instruction->Length);

	Disassembler->Stage1Count++;
	if (Flags & DISASM_ALIGNOUTPUT) Instruction->StringAligned = TRUE;

	//
	// Get prefixes or three byte opcode
	//
	while (TRUE)
	{
		Opcode = *Address;
		INSTR_INC(1); // increment Instruction->Length and address
		X86Opcode = &X86_Opcodes_1[Opcode];

		// Handle a misplaced REX prefix -- AMD64 manual says it is just ignored
		if (IS_AMD64() && (Opcode >= REX_PREFIX_START && Opcode <= REX_PREFIX_END) && X86_PREFIX((&X86_Opcodes_1[*Address])))
		{
			if (!Instruction->AnomalyOccurred)
			{
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: REX prefix before legacy prefix 0x%02X\n", VIRTUAL_ADDRESS, Opcode);
				Instruction->AnomalyOccurred = TRUE;
			}
			continue;
		}
		
		if (X86_PREFIX(X86Opcode))
		{
			if (!Instruction->AnomalyOccurred)
			{
				for (i = 0; i < Instruction->PrefixCount; i++)
				{
					if (Instruction->Prefixes[i] == Opcode)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Duplicate prefix 0x%02X\n", VIRTUAL_ADDRESS, Opcode);
						Instruction->AnomalyOccurred = TRUE;
						break;
					}
				}
			}

			switch (Opcode)
			{
				case PREFIX_REPNE: // may be three byte opcode
					SSE_Prefix = Opcode;
					if (!Instruction->AnomalyOccurred && X86Instruction->HasRepeatWhileEqualPrefix)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Conflicting prefix\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					Instruction->Repeat = TRUE;
					X86Instruction->HasRepeatWhileEqualPrefix = FALSE;
					X86Instruction->HasRepeatWhileNotEqualPrefix = TRUE;
					break;
				case PREFIX_REP: // may be three byte opcode
					SSE_Prefix = Opcode;
					if (!Instruction->AnomalyOccurred && X86Instruction->HasRepeatWhileNotEqualPrefix)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Conflicting prefix\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}

					Instruction->Repeat = TRUE;
					X86Instruction->HasRepeatWhileNotEqualPrefix = FALSE;
					X86Instruction->HasRepeatWhileEqualPrefix = TRUE;
					break;

				case PREFIX_OPERAND_SIZE: // may be three byte opcode
					SSE_Prefix = Opcode;
					if (!Instruction->AnomalyOccurred && X86Instruction->HasOperandSizePrefix)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Conflicting prefix\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					
					if (X86Instruction->HasOperandSizePrefix) break;
					else X86Instruction->HasOperandSizePrefix = TRUE;
					switch (X86Instruction->OperandSize)
					{
						case 4: X86Instruction->OperandSize = 2; break;
						case 2: X86Instruction->OperandSize = 4; break;
						default: assert(0); goto abort;
					}
					break;

				case PREFIX_ADDRESS_SIZE:
					if (!Instruction->AnomalyOccurred && X86Instruction->HasAddressSizePrefix)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Conflicting prefix\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}

					if (X86Instruction->HasAddressSizePrefix) break;
					else X86Instruction->HasAddressSizePrefix = TRUE;
					switch (X86Instruction->AddressSize)
					{
						case 8:
							X86Instruction->AddressSize = 4;
							break;
						case 4:
							assert(!IS_AMD64()); // this should not be possible
							X86Instruction->AddressSize = 2;
							break;
						case 2:
							X86Instruction->AddressSize = 4;
							break;
						default: 
							assert(0); goto abort;
					}
					break;

				case PREFIX_SEGMENT_OVERRIDE_ES:
					SANITY_CHECK_SEGMENT_OVERRIDE();
					if (!IS_AMD64())
					{
						X86Instruction->HasSegmentOverridePrefix = TRUE;
						X86Instruction->Segment = SEG_ES;
					}
					else if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Meaningless segment override\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					break;
				case PREFIX_SEGMENT_OVERRIDE_CS:
					SANITY_CHECK_SEGMENT_OVERRIDE();
					if (!IS_AMD64())
					{
						X86Instruction->HasSegmentOverridePrefix = TRUE;
						X86Instruction->Segment = SEG_CS;  
					}
					else if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Meaningless segment override\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					break;
				case PREFIX_SEGMENT_OVERRIDE_SS:
					SANITY_CHECK_SEGMENT_OVERRIDE();
					if (!IS_AMD64())
					{
						X86Instruction->HasSegmentOverridePrefix = TRUE;
						X86Instruction->Segment = SEG_SS;
					}
					else if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Meaningless segment override\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					break;
				case PREFIX_SEGMENT_OVERRIDE_DS:
					SANITY_CHECK_SEGMENT_OVERRIDE();
					if (!IS_AMD64())
					{
						X86Instruction->HasSegmentOverridePrefix = TRUE;
						X86Instruction->Segment = SEG_DS;
					}
					else if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Meaningless segment override\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					break;
				case PREFIX_SEGMENT_OVERRIDE_FS:
					SANITY_CHECK_SEGMENT_OVERRIDE();
					X86Instruction->HasSegmentOverridePrefix = TRUE;
					X86Instruction->Segment = SEG_FS;
					break;
				case PREFIX_SEGMENT_OVERRIDE_GS:
					SANITY_CHECK_SEGMENT_OVERRIDE();
					X86Instruction->HasSegmentOverridePrefix = TRUE;
					X86Instruction->Segment = SEG_GS;
					break;

				case PREFIX_LOCK:
					if (!Instruction->AnomalyOccurred && X86Instruction->HasLockPrefix)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Conflicting prefix\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					X86Instruction->HasLockPrefix = TRUE;
					break;

				default:
					assert(0);
					goto abort;
			}

			if (Instruction->PrefixCount >= X86_MAX_INSTRUCTION_LEN)
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Reached maximum prefix count %d\n", VIRTUAL_ADDRESS, X86_MAX_PREFIX_LENGTH);
				goto abort;
			}
			else if (Instruction->PrefixCount == X86_MAX_PREFIX_LENGTH)
			{
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Reached maximum prefix count %d\n", VIRTUAL_ADDRESS, X86_MAX_PREFIX_LENGTH);
				Instruction->AnomalyOccurred = TRUE;
			}

			assert(Instruction->AnomalyOccurred || Instruction->PrefixCount < X86_MAX_PREFIX_LENGTH);
			Instruction->Prefixes[Instruction->PrefixCount] = Opcode;
			Instruction->PrefixCount++;
			//DISASM_OUTPUT(("[0x%08I64X] Prefix 0x%02X (prefix count %d)\n", VIRTUAL_ADDRESS, Opcode, Instruction->PrefixCount));
		}
		else
		{
			break;
		}
	}

	// Check for REX opcode
	// This is checked here instead of the prefix loop above because it must be the
	// last prefix
	if (IS_AMD64() && (Opcode >= REX_PREFIX_START && Opcode <= REX_PREFIX_END))
	{
		if (Instruction->PrefixCount >= X86_MAX_INSTRUCTION_LEN)
		{
			if (!SuppressErrors) printf("[0x%08I64X] ERROR: Reached maximum prefix count %d\n", VIRTUAL_ADDRESS, X86_MAX_PREFIX_LENGTH);
			goto abort;
		}
		else if (!Instruction->AnomalyOccurred && Instruction->PrefixCount == AMD64_MAX_PREFIX_LENGTH)
		{
			if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Reached maximum prefix count %d\n", VIRTUAL_ADDRESS, X86_MAX_PREFIX_LENGTH);
			Instruction->AnomalyOccurred = TRUE;
		}

		assert(Instruction->AnomalyOccurred || Instruction->PrefixCount < AMD64_MAX_PREFIX_LENGTH);

		Instruction->Prefixes[Instruction->PrefixCount] = Opcode;
		Instruction->PrefixCount++;
		X86Instruction->rex_b = Opcode;
		SET_REX(X86Instruction->rex, X86Instruction->rex_b);
		DISASM_OUTPUT(("[0x%08I64X] REX prefix 0x%02X (prefix count %d, w=%d, r=%d, x=%d, b=%d)\n", VIRTUAL_ADDRESS, Opcode, Instruction->PrefixCount, X86Instruction->rex.w, X86Instruction->rex.r, X86Instruction->rex.x, X86Instruction->rex.b));

		assert(X86Instruction->AddressSize >= 4);
		if (X86Instruction->rex.w)
		{
			X86Instruction->OperandSize = 8;
			X86Instruction->HasOperandSizePrefix = FALSE;
		}
		else if (X86Instruction->HasOperandSizePrefix)
		{
			assert(X86Instruction->OperandSize == 2);
		}
		else if (X86Instruction->rex_b == REX_PREFIX_START)
		{
			if (!Instruction->AnomalyOccurred)
			{
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: meaningless REX prefix used\n", VIRTUAL_ADDRESS);
				Instruction->AnomalyOccurred = TRUE;
			}
			X86Instruction->rex_b = 0;
		}

		Opcode = *Address;
		INSTR_INC(1); // increment Instruction->Length and address

		X86Opcode = &X86_Opcodes_1[Opcode];
		assert(!X86_PREFIX(X86Opcode));
	}
	//DISASM_OUTPUT(("[0x%08I64X] OperandSize = %d, AddressSize = %d\n", VIRTUAL_ADDRESS, X86Instruction->OperandSize, X86Instruction->AddressSize));
	Instruction->LastOpcode = Opcode;
	Instruction->OpcodeAddress = Address-1;

	if (X86_INVALID(X86Opcode))
	{
		if (!SuppressErrors) printf("[0x%08I64X] ERROR: Invalid opcode 0x%02X\n", VIRTUAL_ADDRESS, Opcode);
		goto abort;
	}

	if (Opcode == X86_TWO_BYTE_OPCODE)
	{
		//
		// Handle case that it is a group (with opcode extension), floating point, or two byte opcode
		//
		assert(!Instruction->OpcodeLength);
		Instruction->LastOpcode = Opcode = *Address;
		INSTR_INC(1); // increment Instruction->Length and address
		assert(X86Opcode->Table == X86_Opcodes_2);
		X86Opcode = &X86_Opcodes_2[Opcode];

		//
		// Check for errors
		//
		if (X86_INVALID(X86Opcode))
		{
			if (!SuppressErrors) printf("[0x%08I64X] ERROR: Invalid two byte opcode 0x%02X 0x%02X\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode);
			goto abort;
		}
		
		if (X86Instruction->AddressSize == 8)
		{
			if (X86_Invalid_Addr64_2[Opcode])
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Opcode 0x%02X 0x%02X (\"%s\") illegal in 64-bit mode\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode, X86Opcode->Mnemonic);
				goto abort;
			}
#if 0
			if (X86Instruction->rex_b &&
					(GET_REX_B(X86Instruction->rex_b) && !GET_REX_B(X86_REX_2[Opcode]) ||
					 GET_REX_X(X86Instruction->rex_b) && !GET_REX_X(X86_REX_2[Opcode]) ||
					 GET_REX_R(X86Instruction->rex_b) && !GET_REX_R(X86_REX_2[Opcode]) ||
					 GET_REX_W(X86Instruction->rex_b) && !GET_REX_W(X86_REX_2[Opcode])))
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Illegal REX prefix 0x%02X for opcode 0x%02X 0x%02X\n", VIRTUAL_ADDRESS, X86Instruction->rex_b, X86_TWO_BYTE_OPCODE, Opcode);
				assert(0);
				goto abort;
			}
#endif
		}

		if (X86Instruction->OperandSize == 2 && X86_Invalid_Op16_2[Opcode])
		{
			if (!SuppressErrors) printf("[0x%08I64X] ERROR: Opcode 0x%02X 0x%02X (\"%s\") illegal with 16-bit operand size\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode, X86Opcode->Mnemonic);
			goto abort;
		}

		X86Instruction->HasModRM = X86_ModRM_2[Opcode];
		if (X86Instruction->HasModRM) X86Instruction->modrm_b = *Address;
		Instruction->OpcodeBytes[0] = X86_TWO_BYTE_OPCODE;
		Instruction->OpcodeBytes[1] = Opcode;
		Instruction->OpcodeLength = 2;

		if (X86_SPECIAL_EXTENSION(X86Opcode))
		{
			DISASM_OUTPUT(("[0x%08I64X] Special opcode extension 0x%02X 0x%02X\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode));
			SpecialExtension = TRUE;
			goto HasSpecialExtension;
		}
		else if (SSE_Prefix && !X86_INVALID(&X86_SSE[Opcode])) // SSEx instruction
		{
			Instruction->OpcodeLength = 3;
			Instruction->OpcodeBytes[2] = SSE_Prefix;
			assert(Instruction->OpcodeBytes[1] == Opcode);

			// Since the prefix was really an opcode extension, remove it from
			// the prefix list
			for (i = 0; i < Instruction->PrefixCount; i++)
			{
				if (Instruction->Prefixes[i]) break;
			}
			assert(i != Instruction->PrefixCount);
			Instruction->PrefixCount--;
			Instruction->Prefixes[i] = 0;

			// Slide any prefixes following the removed prefix down by 1
			memmove(&Instruction->Prefixes[i], &Instruction->Prefixes[i+1], Instruction->PrefixCount-i);
			Instruction->Prefixes[Instruction->PrefixCount] = 0;
			Instruction->Repeat = FALSE;
			X86Instruction->HasRepeatWhileEqualPrefix = FALSE;
			X86Instruction->HasRepeatWhileNotEqualPrefix = FALSE;
			X86Instruction->HasOperandSizePrefix = FALSE;
			if (SSE_Prefix == PREFIX_OPERAND_SIZE)
			{
				if (IS_AMD64() && X86Instruction->rex.w) X86Instruction->OperandSize = 8;
				else X86Instruction->OperandSize = 4;
			}

			if (IS_X86_16())
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: SSE invalid in 16-bit mode\n", VIRTUAL_ADDRESS);
				goto abort;
			}
		
			assert(X86Instruction->HasModRM);
			switch (SSE_Prefix)
			{
				case PREFIX_OPERAND_SIZE: X86Opcode = &X86_SSE[0x000+Opcode]; break;
				case PREFIX_REPNE: X86Opcode = &X86_SSE[0x100+Opcode]; break;
				case PREFIX_REP: X86Opcode = &X86_SSE[0x200+Opcode]; break;
			}

			if (X86_INVALID(X86Opcode))
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Illegal SSE instruction opcode 0x%02X 0x%02X + prefix 0x%02X\n", VIRTUAL_ADDRESS, Instruction->OpcodeBytes[0], Instruction->OpcodeBytes[1], Instruction->OpcodeBytes[2]);
				goto abort;
			}
			else if (X86_EXTENDED_OPCODE(X86Opcode))
			{
				// SSE in group (13, 14, or 15)
				OpcodeExtension = GET_MODRM_EXT(X86Instruction->modrm_b);
				Group = X86_Groups_2[Opcode];
				X86Instruction->Group = (U8)Group;
				assert(Group >= 13 && Group <= 15 && X86Opcode->Table);
				switch (SSE_Prefix)
				{
					case PREFIX_OPERAND_SIZE: X86Opcode = &X86Opcode->Table[0x00+OpcodeExtension]; break;
					case PREFIX_REPNE: X86Opcode = &X86Opcode->Table[0x08+OpcodeExtension]; break;
					case PREFIX_REP: X86Opcode = &X86Opcode->Table[0x10+OpcodeExtension]; break;
				}

				if (X86_INVALID(X86Opcode))
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: Illegal SSE instruction opcode 0x%02X 0x%02X + prefix 0x%02X + extension %d\n", VIRTUAL_ADDRESS, Instruction->OpcodeBytes[0], Instruction->OpcodeBytes[1], Instruction->OpcodeBytes[2], OpcodeExtension);
					goto abort;
				}
			}

			Instruction->Repeat = FALSE;
			X86Instruction->HasRepeatWhileEqualPrefix = FALSE;
			X86Instruction->HasRepeatWhileNotEqualPrefix = FALSE;
			X86Instruction->HasOperandSizePrefix = FALSE;
			switch (X86_GET_CATEGORY(X86Opcode))
			{
				case ITYPE_SSE: case ITYPE_SSE2: case ITYPE_SSE3: break;
				default: assert(0); goto abort;
			}
		}
		else if (X86_EXTENDED_OPCODE(X86Opcode)) // 2 byte group
		{
			assert(!X86Opcode->MnemonicFlags);
			OpcodeExtension = GET_MODRM_EXT(X86Instruction->modrm_b);

			assert(X86Opcode->Table);
			X86Opcode = &X86Opcode->Table[OpcodeExtension];
			if (X86_INVALID(X86Opcode))
			{
				Instruction->Length++;
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Invalid group opcode 0x%02X 0x%02X extension 0x%02X\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode, OpcodeExtension);
				goto abort;
			}

			assert(!X86_SPECIAL_EXTENSION(X86Opcode));
			Group = X86_Groups_2[Opcode];
			X86Instruction->Group = (U8)Group;
			assert(Group > 0 && Group <= 19);
			assert(X86Opcode->Mnemonic);
			DISASM_OUTPUT(("[0x%08I64X] Group %d (bytes 0x%02X 0x%02X) extension 0x%02X (\"%s\")\n", VIRTUAL_ADDRESS, Group, X86_TWO_BYTE_OPCODE, Opcode, OpcodeExtension, X86Opcode->Mnemonic));
		}
		else
		{
			assert(X86Opcode->Mnemonic);
			DISASM_OUTPUT(("[0x%08I64X] Two byte opcode 0x%02X 0x%02X (\"%s\")\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode, X86Opcode->Mnemonic));
			X86Instruction->HasModRM = X86_ModRM_2[Opcode];
			if (X86Instruction->HasModRM) X86Instruction->modrm_b = *Address;
		}
	}
	else // 1-byte opcode
	{
		if (X86Instruction->AddressSize == 8)
		{
			if (X86_Invalid_Addr64_1[Opcode])
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Opcode 0x%02X (\"%s\") illegal in 64-bit mode\n", VIRTUAL_ADDRESS, Opcode, X86Opcode->Mnemonic);
				goto abort;
			}

#if 0
			if (X86Instruction->rex_b &&
				(GET_REX_B(X86Instruction->rex_b) && !GET_REX_B(X86_REX_1[Opcode]) ||
				 GET_REX_X(X86Instruction->rex_b) && !GET_REX_X(X86_REX_1[Opcode]) ||
				 GET_REX_R(X86Instruction->rex_b) && !GET_REX_R(X86_REX_1[Opcode]) ||
				 GET_REX_W(X86Instruction->rex_b) && !GET_REX_W(X86_REX_1[Opcode])))
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Illegal REX prefix 0x%02X for opcode 0x%02X\n", VIRTUAL_ADDRESS, X86Instruction->rex_b, Opcode);
				assert(0);
				goto abort;
			}
#endif
		}

		if (X86Instruction->OperandSize == 2 && X86_Invalid_Op16_1[Opcode])
		{
			if (!SuppressErrors) printf("[0x%08I64X] ERROR: Opcode 0x%02X (\"%s\") illegal with 16-bit operand size\n", VIRTUAL_ADDRESS, Opcode, X86Opcode->Mnemonic);
			goto abort;
		}

		Instruction->OpcodeBytes[0] = Opcode;
		Instruction->OpcodeLength = 1;
		X86Instruction->HasModRM = X86_ModRM_1[Opcode];
		if (X86Instruction->HasModRM) X86Instruction->modrm_b = *Address;

		if (X86_EXTENDED_OPCODE(X86Opcode)) // a group
		{
			assert(X86Instruction->HasModRM);
			OpcodeExtension = GET_MODRM_EXT(*Address); // leave Address pointing at ModRM byte

			if (X86_SPECIAL_EXTENSION(X86Opcode))
			{
				DISASM_OUTPUT(("[0x%08I64X] Special opcode extension 0x%02X 0x%02X\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode));
				SpecialExtension = TRUE;
				goto HasSpecialExtension;
			}

			assert(X86Opcode->Table);
			X86Opcode = &X86Opcode->Table[OpcodeExtension];
			if (X86_INVALID(X86Opcode))
			{
				Instruction->Length++;
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Invalid group opcode 0x%02X extension 0x%02X\n", VIRTUAL_ADDRESS, Opcode, OpcodeExtension);
				goto abort;
			}

			Group = X86_Groups_1[Opcode];
			X86Instruction->Group = (U8)Group;
			DISASM_OUTPUT(("[0x%08I64X] Group %d (byte 0x%02X) extension 0x%02X (\"%s\")\n", VIRTUAL_ADDRESS, Group, Opcode, OpcodeExtension, X86Opcode->Mnemonic));
			assert(Group > 0 && Group <= 17);
			assert(X86Opcode->Mnemonic);
		}
		else
		{
			if (X86_SPECIAL_EXTENSION(X86Opcode))
			{
				DISASM_OUTPUT(("[0x%08I64X] Special opcode extension 0x%02X\n", VIRTUAL_ADDRESS, Opcode));
				SpecialExtension = TRUE;
				goto HasSpecialExtension;
			}

			DISASM_OUTPUT(("[0x%08I64X] One byte opcode 0x%02X (\"%s\")\n", VIRTUAL_ADDRESS, Opcode, X86Opcode->Mnemonic));
		}
	}

HasSpecialExtension:
	if (SpecialExtension)
	{
		if (X86Opcode->MnemonicFlags & ITYPE_EXT_MODRM)
		{
			assert(X86Opcode->Table);
			assert(Instruction->OpcodeLength == 2);
			assert(X86Instruction->HasModRM);
			X86Opcode = &X86Opcode->Table[*Address];
			if (X86_INVALID(X86Opcode))
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Illegal opcode 0x%02X 0x%02X + modrm 0x%02X\n", VIRTUAL_ADDRESS, Instruction->OpcodeBytes[0], Instruction->OpcodeBytes[1], *Address);
				goto abort;
			}
			else if (X86_EXTENDED_OPCODE(X86Opcode))
			{
				assert(!X86Opcode->MnemonicFlags);
				OpcodeExtension = GET_MODRM_EXT(X86Instruction->modrm_b);

				assert(X86Opcode->Table);
				X86Opcode = &X86Opcode->Table[OpcodeExtension];
				if (X86_INVALID(X86Opcode))
				{
					Instruction->Length++;
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: Invalid group opcode 0x%02X 0x%02X extension 0x%02X\n", VIRTUAL_ADDRESS, X86_TWO_BYTE_OPCODE, Opcode, OpcodeExtension);
					goto abort;
				}

				assert(!X86_SPECIAL_EXTENSION(X86Opcode));
				Group = X86_Groups_2[Opcode];
				X86Instruction->Group = (U8)Group;
				assert(Group > 0 && Group <= 19);
				assert(X86Opcode->Mnemonic);
				DISASM_OUTPUT(("[0x%08I64X] Group %d (bytes 0x%02X 0x%02X) extension 0x%02X (\"%s\")\n", VIRTUAL_ADDRESS, Group, X86_TWO_BYTE_OPCODE, Opcode, OpcodeExtension, X86Opcode->Mnemonic));
			}
			else if (!X86_OPERAND_COUNT(X86Opcode))
			{
				INSTR_INC(1); // increment Instruction->Length and address
			}
		}
		else if (X86Opcode->MnemonicFlags & ITYPE_EXT_FPU)
		{
			assert(X86Opcode->Table);		
			if (X86Instruction->modrm_b < 0xC0)
			{
				// It is an opcode extension, use the X86Opcode->Table
				OpcodeExtension = GET_MODRM_EXT(X86Instruction->modrm_b);
				X86Opcode = &X86Opcode->Table[OpcodeExtension];
			}
			else
			{
				// The whole ModRM byte is used, these start at index 0x08 in X86Opcode->Table
				OpcodeExtension = (X86Instruction->modrm_b & 0x3F);
				X86Opcode = &X86Opcode->Table[0x08 + OpcodeExtension];
			}

			if (X86_INVALID(X86Opcode))
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Invalid FPU opcode 0x%02X + modrm extension 0x%02X (index 0x%02X)\n", VIRTUAL_ADDRESS, Opcode, X86Instruction->modrm_b, 0x08 + OpcodeExtension);
				goto abort;
			}

			DISASM_OUTPUT(("[0x%08I64X] FPU instruction is (\"%s\"): 0x%02X + modrm 0x%02X (index 0x%02X)\n", VIRTUAL_ADDRESS, X86Opcode->Mnemonic, Opcode, X86Instruction->modrm_b, 0x08 + OpcodeExtension));
			if (!X86_OPERAND_COUNT(X86Opcode)) INSTR_INC(1); // increment Instruction->Length and address
		}
		else if (X86Opcode->MnemonicFlags & ITYPE_EXT_SUFFIX)
		{
			if (X86Instruction->HasOperandSizePrefix)
			{
				if (!Instruction->AnomalyOccurred && X86Opcode->Table == X86_3DNOW_0F)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: operand size prefix used with 3DNOW instruction\n", VIRTUAL_ADDRESS);
					Instruction->AnomalyOccurred = TRUE;
				}
				X86Instruction->HasOperandSizePrefix = FALSE;
				X86Instruction->OperandSize = 4;
			}
			Instruction->OperandCount = X86_OPERAND_COUNT(X86Opcode);
			assert(Instruction->OpcodeLength == 2 && X86Instruction->HasModRM && Instruction->OperandCount == 2);
			memcpy(&X86Instruction->Opcode, X86Opcode, sizeof(X86_OPCODE));
			Instruction->Operands[0].Flags = X86Opcode->OperandFlags[0] & X86_OPFLAGS_MASK;
			Instruction->Operands[1].Flags = X86Opcode->OperandFlags[1] & X86_OPFLAGS_MASK;
			Instruction->Operands[2].Flags = X86Opcode->OperandFlags[2] & X86_OPFLAGS_MASK;
			assert(Address == Instruction->Address + Instruction->Length);
			if (!SetOperands(Instruction, Address, Flags & DISASM_SUPPRESSERRORS)) goto abort;
			Suffix = Instruction->Address[Instruction->Length++];
			Instruction->OpcodeBytes[2] = Suffix;
			Instruction->OpcodeLength = 3;
			X86Opcode = &X86Opcode->Table[Suffix];
			
			if (X86_INVALID(X86Opcode))
			{
				if (!SuppressErrors) printf("[0x%08I64X] ERROR: Illegal opcode 0x%02X 0x%02X + suffix 0x%02X\n", VIRTUAL_ADDRESS, Instruction->OpcodeBytes[0], Instruction->OpcodeBytes[1], Suffix);
				goto abort;
			}
			assert(Instruction->Length >= 4 + Instruction->PrefixCount);
		}
		else if (X86Opcode->MnemonicFlags & ITYPE_EXT_64)
		{
			assert(X86Opcode->Table);
			if (IS_AMD64()) X86Opcode = &X86Opcode->Table[1];
			else X86Opcode = &X86Opcode->Table[0];
			assert(!X86_INVALID(X86Opcode));
		}
	}

	// Detect incompatibilities	
	if (IS_X86_16() && X86Opcode->CPU > CPU_I386)
	{
		if (!SuppressErrors) printf("[0x%08I64X] ERROR: Instruction \"%s\" (opcode 0x%02X) can't be used in 16-bit X86\n", VIRTUAL_ADDRESS, X86Opcode->Mnemonic, Instruction->LastOpcode);
		goto abort;
	}
	if (!IS_AMD64() && X86Opcode->CPU >= CPU_AMD64)
	{
		if (!SuppressErrors) printf("[0x%08I64X] ERROR: Instruction \"%s\" (opcode 0x%02X) can only be used in X86-64\n", VIRTUAL_ADDRESS, X86Opcode->Mnemonic, Instruction->LastOpcode);
		goto abort;
	}

	// Copy the opcode into the local structure and set the fields 
	assert(Instruction->OpcodeLength && !X86_INVALID(X86Opcode));
	memcpy(&X86Instruction->Opcode, X86Opcode, sizeof(X86_OPCODE));
	Instruction->Groups |= X86_GET_CATEGORY(X86Opcode);
	assert(Instruction->Groups);
	Instruction->Type |= X86_GET_TYPE(X86Opcode);
	assert((U32)Instruction->Type >= Instruction->Groups);
	Instruction->OperandCount = X86_OPERAND_COUNT(X86Opcode);

	//
	// Sanity check prefixes now that opcode is known and prefixes are resolved
	//

	// Instructions that implicitly reference the CS/DS can't have segment override prefixes
	switch (Instruction->Type)
	{
		case ITYPE_PUSHF: case ITYPE_POPF:
		case ITYPE_ENTER: case ITYPE_LEAVE:
			SANITY_CHECK_SEGMENT_OVERRIDE();
			X86Instruction->HasSegmentOverridePrefix = FALSE;
			X86Instruction->Segment = SEG_SS;
			break;
		case ITYPE_RET: case ITYPE_DEBUG: 
		case ITYPE_OFLOW: case ITYPE_TRAP: 
		case ITYPE_TRAPRET:
			SANITY_CHECK_SEGMENT_OVERRIDE();
			X86Instruction->HasSegmentOverridePrefix = FALSE;
			X86Instruction->Segment = SEG_CS;
			break;
	}

	// Check illegal prefixes used with FPU/MMX/SSEx
	if (Instruction->Groups & (ITYPE_FPU|ITYPE_MMX|ITYPE_SSE|ITYPE_SSE2|ITYPE_SSE3))
	{
		// Check for prefixes that produce unpredictable results
		for (i = 0; i < Instruction->PrefixCount; i++)
		{
			switch (Instruction->Prefixes[i])
			{
				case PREFIX_OPERAND_SIZE:
					switch (Instruction->Type)
					{
						case ITYPE_FSTOREENV: case ITYPE_FLOADENV: case ITYPE_FSAVE: case ITYPE_FRESTORE: continue;
						default: break;
					}

					if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: operand size prefix used with FPU/MMX/SSEx\n", VIRTUAL_ADDRESS);
						goto abort;
					}
					X86Instruction->HasOperandSizePrefix = FALSE;
					if (X86Instruction->OperandSize == 2) X86Instruction->OperandSize = 2;
					break;

				case PREFIX_REPNE:
				case PREFIX_REP:
					if (Instruction->Groups & ITYPE_FPU) { assert(Instruction->Repeat); continue; }
					// The Intel manual says this results in unpredictable behavior -- it's not even
					// clear which SSE prefix is used as the third opcode byte in this case
					// (e.g., is it the first or last SSE prefix?)
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: rep/repne used with MMX/SSEx\n", VIRTUAL_ADDRESS);
					goto abort;

				default:
					break;
			}
		}
	}

	// Check for conflicts involving operand size
	if (IS_AMD64())
	{
		// Check for use of rex.w=1 with an operand size prefix
		if (X86Instruction->rex.w)
		{
			assert(X86Instruction->OperandSize == 8);
			for (i = 0; i < Instruction->PrefixCount; i++)
			{
				if (Instruction->Prefixes[i] == PREFIX_OPERAND_SIZE)
				{
					X86Instruction->HasOperandSizePrefix = FALSE;
					if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: use of operand size prefix meaningless when REX.w=1\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}				
				}
			}
		}

		// Set default operand size to 64 instead of 32 for some instructions
		switch (Instruction->Type)
		{
			case ITYPE_PUSH: case ITYPE_POP: 
			case ITYPE_PUSHF: case ITYPE_POPF:
			case ITYPE_ENTER: case ITYPE_LEAVE:
			case ITYPE_CALL: case ITYPE_BRANCH:
			case ITYPE_LOOPCC: case ITYPE_RET:
				X86Instruction->HasDefault64Operand = TRUE;
				break;

			case ITYPE_SYSTEM:
				if (Instruction->OpcodeLength != 2) break;

				// lgdt/lidt/lldt/ltr
				if ((Instruction->LastOpcode == 0x00 || Instruction->LastOpcode == 0x01) && 
					(OpcodeExtension == 0x02 || OpcodeExtension == 0x03))
				{
					X86Instruction->HasDefault64Operand = TRUE;
				}
				break;

			default:
				break;
		}

		if (X86Instruction->HasDefault64Operand)
		{
			if (X86Instruction->rex.w)
			{
				if (!Instruction->AnomalyOccurred)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: use of REX.w is meaningless (default operand size is 64)\n", VIRTUAL_ADDRESS);
					Instruction->AnomalyOccurred = TRUE;
				}
				X86Instruction->rex_b &= ~8;
				X86Instruction->rex.w = 0;
			}

			if (X86Instruction->HasOperandSizePrefix)
			{
				assert(X86Instruction->OperandSize == 2);
				X86Instruction->HasDefault64Operand = FALSE;
			}
			else
			{
				assert(X86Instruction->OperandSize >= 4);
				X86Instruction->OperandSize = 8;
			}
		}
	}

	// Make sure rep/repe/repne is set correctly based on instruction
	if (Instruction->Repeat)
	{
		switch (Instruction->Type)
		{
			case ITYPE_IN:
			case ITYPE_OUT:
			case ITYPE_STRMOV:
			case ITYPE_STRSTOR:
			case ITYPE_STRLOAD:
				if (X86Instruction->HasRepeatWhileNotEqualPrefix)
				{
					if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: REPNE should only be used with cmps/scas\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					// Treat it as just a "rep"
					X86Instruction->HasRepeatWhileNotEqualPrefix = FALSE;
					X86Instruction->HasRepeatWhileEqualPrefix = TRUE;
				}
				break;
			case ITYPE_STRCMP:
				break;
			default:
				if (!Instruction->AnomalyOccurred)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Repeat prefix used with non-string instruction\n", VIRTUAL_ADDRESS);
					Instruction->AnomalyOccurred = TRUE;
				}
				Instruction->Repeat = FALSE;
				X86Instruction->HasRepeatWhileEqualPrefix = FALSE;
				X86Instruction->HasRepeatWhileNotEqualPrefix = FALSE;
				break;
		}
	}

	if (Disassemble)
	{
		assert(!Instruction->StringIndex);
		if (X86Instruction->HasRepeatWhileEqualPrefix)
		{
			if (Instruction->Type == ITYPE_STRCMP) { APPENDS("repe "); }
			else { APPENDS("rep "); }
		}
		if (X86Instruction->HasRepeatWhileNotEqualPrefix) APPENDS("repne "); 
		if (X86Instruction->HasLockPrefix) APPENDS("lock "); 
		if (X86Instruction->HasBranchTakenPrefix) APPENDS("hinttake ");
		if (X86Instruction->HasBranchNotTakenPrefix) APPENDS("hintskip ");
		APPENDPAD(12);
		APPEND(OPCSTR, SIZE_LEFT, "%s", X86Opcode->Mnemonic);
		APPENDPAD(24);
	}

	if (Instruction->OperandCount)
	{
		Instruction->Operands[0].Flags = X86Opcode->OperandFlags[0] & X86_OPFLAGS_MASK;
		Instruction->Operands[1].Flags = X86Opcode->OperandFlags[1] & X86_OPFLAGS_MASK;
		Instruction->Operands[2].Flags = X86Opcode->OperandFlags[2] & X86_OPFLAGS_MASK;
		Address = SetOperands(Instruction, Address, Flags);
		if (!Address) goto abort;
		assert(!(Instruction->Operands[0].Flags & 0x7F));
		assert(!(Instruction->Operands[1].Flags & 0x7F));
		assert(!(Instruction->Operands[2].Flags & 0x7F));
	}

	Disassembler->Stage2Count++;

#ifdef TEST_DISASM
	//////////////////////////////////////////////////////////////////////
	// Test against other disassemblers
	//////////////////////////////////////////////////////////////////////

	if (IS_X86_32())
	{
		InstructionLength = X86_GetLength(Instruction, Instruction->Address);
		if (InstructionLength && Instruction->Length != InstructionLength)
		{
			printf("[0x%08I64X] WARNING: instruction lengths differ (%d vs %d)\n", VIRTUAL_ADDRESS, Instruction->Length, InstructionLength);
			DumpInstruction(Instruction, TRUE, TRUE);
			assert(0);
		}
	}
	else if (IS_AMD64())
	{
		// TODO: need other amd64 (x86-64) disassembler to test against
	}
	else if (IS_X86_16())
	{
		// TODO: need other x86 16-bit disassembler to test against
	}
#endif

	//////////////////////////////////////////////////////////////////////
	// Post-operand sanity checks
	//////////////////////////////////////////////////////////////////////

	if (!X86Instruction->HasDstAddressing && !X86Instruction->HasSrcAddressing)
	{
		if (X86Instruction->HasAddressSizePrefix)
		{
			if (!Instruction->AnomalyOccurred)
			{
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: address size prefix used with no addressing\n", VIRTUAL_ADDRESS);
				Instruction->AnomalyOccurred = TRUE;
			}
			X86Instruction->HasAddressSizePrefix = FALSE;
		}

		if (X86Instruction->HasSegmentOverridePrefix)
		{
			if (!Instruction->AnomalyOccurred)
			{
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: segment override used with no addressing\n", VIRTUAL_ADDRESS);
				Instruction->AnomalyOccurred = TRUE;
			}
			X86Instruction->HasSegmentOverridePrefix = FALSE;
		}	
	}

	// Detect use of unusual segments
	if (!Instruction->AnomalyOccurred && !IS_X86_16())
	{
		switch (X86Instruction->Segment)
		{
			case SEG_CS: case SEG_DS: case SEG_SS:
				break;
			case SEG_ES:
				switch (Instruction->Type)
				{
 					case ITYPE_IN: case ITYPE_STRMOV: case ITYPE_STRCMP: case ITYPE_STRSTOR:
						break;
					default:
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: use of unexpected segment ES\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
						break;
				}
				break;
			case SEG_FS:
				if (IS_X86_32() && !(Instruction->Groups & ITYPE_EXEC)) break;
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: use of unexpected segment FS\n", VIRTUAL_ADDRESS);
				Instruction->AnomalyOccurred = TRUE;
				break;
			case SEG_GS:
				if (IS_AMD64() && !(Instruction->Groups & ITYPE_EXEC)) break;
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: use of unexpected segment GS\n", VIRTUAL_ADDRESS);
				Instruction->AnomalyOccurred = TRUE;
				break;
			default:
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: unexpected segment 0x%02X\n", VIRTUAL_ADDRESS, X86Instruction->Selector);
				Instruction->AnomalyOccurred = TRUE;
				break;
		}
	}

	if ((X86Opcode->OperandFlags[0] & OP_COND_EXEC) == OP_COND_EXEC)
	{
 		assert(Instruction->Type == ITYPE_BRANCHCC || Instruction->Type == ITYPE_LOOPCC);
		for (i = 0; i < Instruction->PrefixCount; i++)
		{
			switch (Instruction->Prefixes[i])
			{
				case PREFIX_BRANCH_NOT_TAKEN:
					if (!Instruction->AnomalyOccurred && X86Instruction->Segment != SEG_CS)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Segment override used with conditional branch\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					X86Instruction->HasSegmentOverridePrefix = FALSE;
					X86Instruction->Segment = SEG_CS;
					X86Instruction->HasBranchNotTakenPrefix = TRUE;
					break;
				case PREFIX_BRANCH_TAKEN:
					if (!Instruction->AnomalyOccurred && X86Instruction->Segment != SEG_DS)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Segment override used with conditional branch\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					X86Instruction->HasSegmentOverridePrefix = FALSE;
					X86Instruction->Segment = SEG_CS;
					X86Instruction->HasBranchTakenPrefix = TRUE;
					break;
			}
		}
	}

	//
	// If lock prefix is enabled, verify it is valid
	//
	if (X86Instruction->HasLockPrefix && 
		!IsValidLockPrefix(X86Instruction, Opcode, Instruction->OpcodeLength, Group, OpcodeExtension))
	{
		if (!SuppressErrors) printf("[0x%08I64X] ERROR: Illegal use of lock prefix for instruction \"%s\"\n", VIRTUAL_ADDRESS, X86Opcode->Mnemonic);
		goto abort;
	}

	//////////////////////////////////////////////////////////////////////
	// Generate disassembly output
	//////////////////////////////////////////////////////////////////////

	if (Disassemble)
	{
		if ((Flags & DISASM_SHOWFLAGS) && 
			(X86Instruction->Opcode.Preconditions || X86Instruction->Opcode.FlagsChanged || X86Instruction->Opcode.ResultsIfTrue))
		{
			APPENDPAD(124);
			if (X86Instruction->Opcode.Preconditions)
			{
				Result = X86Instruction->Opcode.Preconditions;
				APPENDS("COND:{ ");
				if (Result & COND_L) APPENDS("L ");
				if (Result & COND_NL) APPENDS("NL ");
				if (Result & COND_LE) APPENDS("LE ");
				if (Result & COND_NLE) APPENDS("NLE ");
				if (Result & COND_G) APPENDS("G ");
				if (Result & COND_NG) APPENDS("NG ");
				if (Result & COND_GE) APPENDS("GE ");
				if (Result & COND_NGE) APPENDS("NGE ");
				if (Result & COND_A) APPENDS("A ");
				if (Result & COND_NA) APPENDS("NA ");
				if (Result & COND_AE) APPENDS("AE ");
				if (Result & COND_NAE) APPENDS("NAE ");
				if (Result & COND_B) APPENDS("B ");
				if (Result & COND_NB) APPENDS("NB ");
				if (Result & COND_BE) APPENDS("BE ");
				if (Result & COND_NBE) APPENDS("NBE ");
				if (Result & COND_E) APPENDS("E ");
				if (Result & COND_NE) APPENDS("NE ");
				if (Result & COND_C) APPENDS("C ");
				if (Result & COND_NC) APPENDS("NC ");
				if (Result & COND_Z) APPENDS("Z ");
				if (Result & COND_NZ) APPENDS("NZ ");
				if (Result & COND_P) APPENDS("P ");
				if (Result & COND_NP) APPENDS("NP ");
				if (Result & COND_PE) APPENDS("PE ");
				if (Result & COND_PO) APPENDS("PO ");
				if (Result & COND_O) APPENDS("O ");
				if (Result & COND_NO) APPENDS("NO ");
				if (Result & COND_U) APPENDS("U ");
				if (Result & COND_NU) APPENDS("NU ");
				if (Result & COND_S) APPENDS("S ");
				if (Result & COND_NS) APPENDS("NS ");
				if (Result & COND_D) APPENDS("D ");
				APPENDB('}');
			}

			if (X86Instruction->Opcode.FlagsChanged)
			{
				Result = X86Instruction->Opcode.FlagsChanged;

				if (Result & FLAG_SET_MASK)
				{
					APPENDS("SET:{ ");
					if (Result & FLAG_CF_SET) APPENDS("C ");
					if (Result & FLAG_DF_SET) APPENDS("D ");
					if (Result & FLAG_IF_SET) APPENDS("I ");
					APPENDB('}');
				}

				if (Result & FLAG_CLR_MASK)
				{
					APPENDS("CLR:{ ");
					if (Result & FLAG_SF_CLR) APPENDS("S ");
					if (Result & FLAG_ZF_CLR) APPENDS("Z ");
					if (Result & FLAG_AF_CLR) APPENDS("A ");
					if (Result & FLAG_CF_CLR) APPENDS("C ");
					if (Result & FLAG_DF_CLR) APPENDS("D ");
					if (Result & FLAG_IF_CLR) APPENDS("I ");
					if (Result & FLAG_OF_CLR) APPENDS("O ");
					if ((Result & FPU_ALL_CLR) == FPU_ALL_CLR)
					{
						APPENDS("FPU_ALL ");
					}
					else
					{
						if (Result & FPU_C0_CLR) APPENDS("FPU_C0 ");
						if (Result & FPU_C1_CLR) APPENDS("FPU_C1 ");
						if (Result & FPU_C2_CLR) APPENDS("FPU_C2 ");
						if (Result & FPU_C3_CLR) APPENDS("FPU_C3 ");
					}
					APPENDB('}');
				}

				if ((Result & FLAG_MOD_MASK) == FLAG_MOD_MASK)
				{
					APPENDS("MOD:{ ");
					if ((Result & FLAG_ALL_MOD) == FLAG_ALL_MOD)
					{
						APPENDS("FLAGS_ALL ");
					}
					else if ((Result & FLAG_COMMON_MOD) == FLAG_COMMON_MOD)
					{
						APPENDS("FLAGS_COMMON ");
					}
					else
					{
						if (Result & FLAG_OF_MOD) APPENDS("O ");
						if (Result & FLAG_SF_MOD) APPENDS("S ");
						if (Result & FLAG_ZF_MOD) APPENDS("Z ");
						if (Result & FLAG_AF_MOD) APPENDS("A ");
						if (Result & FLAG_PF_MOD) APPENDS("P ");
						if (Result & FLAG_CF_MOD) APPENDS("C ");
						if (Result & FLAG_DF_MOD) APPENDS("D ");
						if (Result & FLAG_IF_MOD) APPENDS("I ");
					}
					if ((Result & FPU_ALL_MOD) == FPU_ALL_MOD)
					{
						APPENDS("FPU_ALL ");
					}
					else
					{
						if (Result & FPU_C0_MOD) APPENDS("FPU_C0 ");
						if (Result & FPU_C1_MOD) APPENDS("FPU_C1 ");
						if (Result & FPU_C2_MOD) APPENDS("FPU_C2 ");
						if (Result & FPU_C3_MOD) APPENDS("FPU_C3 ");
					}
					APPENDB('}');
				}

				if (Result & FLAG_TOG_MASK)
				{
					APPENDS("TOG:{ ");
					if (Result & FLAG_CF_TOG) APPENDS("C ");
					APPENDB('}');
				}
			}
		}

		APPENDS("\n");
	}
	else
	{
		Instruction->String[0] = '\0';
	}

	if (!Instruction->Length || Instruction->Length > X86_MAX_INSTRUCTION_LEN)
	{
		if (!SuppressErrors) printf("[0x%08I64X] ERROR: maximum instruction length reached (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
		goto abort;
	}

	if (!Decode)
	{
		Disassembler->Stage3CountNoDecode++;
		return TRUE; // all work is done
	}

	//////////////////////////////////////////////////////////////////////
	// Detect particularly interesting intructions
	//////////////////////////////////////////////////////////////////////

	Operand1 = &Instruction->Operands[0];
	if (Instruction->Groups & ITYPE_EXEC)
	{
		// If it is a negative offset with a 1-byte or 2-byte offset, assume it is a loop
		if (Operand1->Type == OPTYPE_OFFSET && 
			Operand1->Length <= 2 && X86Instruction->Displacement < 0)
		{
			Instruction->CodeBranch.IsLoop = TRUE;
			Instruction->CodeBranch.Operand = Operand1;
		}

		if (!Instruction->AnomalyOccurred &&
			Operand1->TargetAddress >= (U64)Instruction->Address &&
			Operand1->TargetAddress < (U64)Instruction->Address + Instruction->Length)
		{
			if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: branch into the middle of an instruction\n", VIRTUAL_ADDRESS);
			Instruction->AnomalyOccurred = TRUE;
		}

		switch (Instruction->Type)
		{
			case ITYPE_BRANCH:
				Operand1->Flags |= OP_ADDRESS;
				assert(Instruction->OperandCount == 1);
				if (!(Operand1->Flags & (OP_GLOBAL|OP_FAR)))
				{
					assert(!X86Instruction->HasSelector);
					X86Instruction->Segment = SEG_CS;
				}
				
				if (Operand1->TargetAddress)
				{
					assert(!Instruction->CodeBranch.AddressOffset);
					Instruction->CodeBranch.Count = 1;
					Instruction->CodeBranch.Addresses[0] = Operand1->TargetAddress;
					Instruction->CodeBranch.Operand = Operand1;
				}
				// If there is both a base and index register, the Result will probably be too wrong
				// to even guess
				else if (X86Instruction->HasFullDisplacement && 
					 ((X86Instruction->HasBaseRegister && !X86Instruction->HasIndexRegister) ||
					 (!X86Instruction->HasBaseRegister && X86Instruction->HasIndexRegister)))
				{
					assert(Operand1->Length <= 0xFF);
					if (!X86Instruction->Scale)
					{
						if (Operand1->Length) X86Instruction->Scale = (U8)Operand1->Length;
						else X86Instruction->Scale = X86Instruction->OperandSize;
					}
					assert(Operand1->Length <= 0xFF);
					tmpScale = MAX(X86Instruction->Scale, Operand1->Length);

					assert(tmpScale <= 16);
					Instruction->CodeBranch.AddressOffset = (U8)tmpScale;
					for (i = 0; i < MAX_CODE_REFERENCE_COUNT; i++) Instruction->CodeBranch.Addresses[i] = (U64)X86Instruction->Displacement + (i * tmpScale);
					Instruction->CodeBranch.Count = i;
					Instruction->CodeBranch.IsIndirect = TRUE;
					Instruction->CodeBranch.Operand = Operand1;
				}
				break;

			case ITYPE_CALL:
				Instruction->Groups |= ITYPE_STACK;
				Instruction->CodeBranch.IsCall = TRUE;
				Operand1->Flags |= OP_ADDRESS;
				assert(Instruction->OperandCount == 1);
				if (!(Operand1->Flags & (OP_GLOBAL|OP_FAR)))
				{
					assert(!X86Instruction->HasSelector);
					X86Instruction->Segment = SEG_CS;
				}
								
				if (Operand1->TargetAddress)
				{
					assert(!Instruction->CodeBranch.AddressOffset);
					Instruction->CodeBranch.Count = 1;
					Instruction->CodeBranch.Addresses[0] = Operand1->TargetAddress;
					Instruction->CodeBranch.Operand = Operand1;
				}
				// If there is both a base and index register, the Result will probably be too wrong
				// to even guess
				else if (X86Instruction->HasFullDisplacement &&
					((X86Instruction->HasBaseRegister && !X86Instruction->HasIndexRegister) ||
					 (!X86Instruction->HasBaseRegister && X86Instruction->HasIndexRegister)))
				{
					//DISASM_OUTPUT(("[0x%08I64X] Scale %d, displacement 0x%08I64x\n", VIRTUAL_ADDRESS, X86Instruction->Scale, X86Instruction->Displacement));
					if (!X86Instruction->Scale)
					{
						assert(Operand1->Length <= 0xFF);
						if (Operand1->Length) X86Instruction->Scale = (U8)Operand1->Length;
						else X86Instruction->Scale = X86Instruction->OperandSize;
					}
					tmpScale = MAX(X86Instruction->Scale, Operand1->Length);

					assert(tmpScale <= 16);
					Instruction->CodeBranch.AddressOffset = (U8)tmpScale;
					assert(X86Instruction->Scale > 1);
					for (i = 0; i < MAX_CODE_REFERENCE_COUNT; i++) Instruction->CodeBranch.Addresses[i] = (U64)X86Instruction->Displacement + (i * tmpScale);
					Instruction->CodeBranch.Count = i;
					Instruction->CodeBranch.IsIndirect = TRUE;
					Instruction->CodeBranch.Operand = Operand1;
				}
				break;

			case ITYPE_BRANCHCC:
				assert(Instruction->OperandCount == 1);
				assert(Operand1->Flags & OP_ADDRESS);
				assert(Operand1->Type == OPTYPE_OFFSET);
				if (!(Operand1->Flags & (OP_GLOBAL|OP_FAR)))
				{
					assert(!X86Instruction->HasSelector);
					X86Instruction->Segment = SEG_CS;
				}

				if (Operand1->TargetAddress)
				{
					assert(!Instruction->CodeBranch.AddressOffset);
					Instruction->CodeBranch.Count = 2;
					Instruction->CodeBranch.Addresses[0] = Operand1->TargetAddress;
					Instruction->CodeBranch.Addresses[1] = (U64)Instruction->Address + Instruction->Length;
					Instruction->CodeBranch.Operand = Operand1;
				}
				break;

			case ITYPE_LOOPCC:
				Instruction->CodeBranch.IsLoop = TRUE;
				assert(Instruction->OperandCount == 1);
				assert(Operand1->Flags & OP_ADDRESS);
				assert(Operand1->Type == OPTYPE_OFFSET);
				assert(!(Operand1->Flags & (OP_GLOBAL|OP_FAR)));
				if (Operand1->TargetAddress)
				{
					assert(!Instruction->CodeBranch.AddressOffset);
					Instruction->CodeBranch.Count = 2;
					Instruction->CodeBranch.Addresses[0] = Operand1->TargetAddress;
					Instruction->CodeBranch.Addresses[1] = (U64)Instruction->Address + Instruction->Length;
					Instruction->CodeBranch.Operand = Operand1;
				}
				break;

			case ITYPE_RET:
				Instruction->Groups |= ITYPE_STACK;
				break;

			default:
				break; // do nothing
		}
	}
	else // possible data instruction
	{
		for (i = 0, Operand = Instruction->Operands; i < Instruction->OperandCount; i++, Operand++)
		{
			if (Operand->TargetAddress)
			{
				if (Operand->Flags & OP_DST)
				{
					assert(!Instruction->DataDst.Count);
					Instruction->DataDst.Count = 1;
					Instruction->DataDst.Addresses[0] = Operand->TargetAddress;
					Instruction->DataDst.DataSize = Operand->Length;
					Instruction->DataDst.Operand = Operand;
					DISASM_OUTPUT(("[0x%08I64X] Write of size %d to 0x%04I64X\n", VIRTUAL_ADDRESS, Operand->Length, Operand->TargetAddress));
				}
				if (Operand->Flags & OP_SRC)
				{
					assert(!Instruction->DataSrc.Count);
					Instruction->DataSrc.Count = 1;
					Instruction->DataSrc.Addresses[0] = Operand->TargetAddress;
					Instruction->DataSrc.DataSize = Operand->Length;
					Instruction->DataSrc.Operand = Operand;
					DISASM_OUTPUT(("[0x%08I64X] Read of size %d to 0x%04I64X\n", VIRTUAL_ADDRESS, Operand->Length, Operand->TargetAddress));
				}
			}

			// If there is both a base and index register, the Result will probably be too wrong
			// to even guess
			else if (Operand->Flags & OP_GLOBAL && 
				((X86Instruction->HasBaseRegister && !X86Instruction->HasIndexRegister) ||
				 (!X86Instruction->HasBaseRegister && X86Instruction->HasIndexRegister)))
			{
				DISASM_OUTPUT(("[0x%08I64X] Data reference (scale %d, size %d, displacement 0x%08I64x)\n", VIRTUAL_ADDRESS, X86Instruction->Scale, Operand->Length, X86Instruction->Displacement));
				if (!X86Instruction->Scale)
				{
					assert(Operand->Length <= 0xFF);
					if (Operand->Length) X86Instruction->Scale = (U8)Operand->Length;
					else X86Instruction->Scale = X86Instruction->OperandSize;
				}
				tmpScale = MAX(X86Instruction->Scale, Operand->Length);

				assert(X86Instruction->HasFullDisplacement);
				if (Operand->Flags & OP_DST)
				{
					assert(!Instruction->DataDst.Count);
					assert(tmpScale <= 16);
					Instruction->CodeBranch.AddressOffset = (U8)tmpScale;
					for (i = 0; i < MAX_DATA_REFERENCE_COUNT; i++) Instruction->DataDst.Addresses[i] = (U64)X86Instruction->Displacement + (i * tmpScale);
					Instruction->DataDst.Count = i;
					Instruction->DataDst.DataSize = Operand->Length;
					Instruction->DataDst.Operand = Operand;
				}					
				if (Operand->Flags & OP_SRC)
				{
					assert(!Instruction->DataSrc.Count);
					assert(tmpScale <= 16);
					Instruction->CodeBranch.AddressOffset = (U8)tmpScale;
					for (i = 0; i < MAX_DATA_REFERENCE_COUNT; i++) Instruction->DataSrc.Addresses[i] = (U64)X86Instruction->Displacement + (i * tmpScale);
					Instruction->DataSrc.Count = i;
					Instruction->DataSrc.DataSize = Operand->Length;
					Instruction->DataSrc.Operand = Operand;
				}
			}
		}
	}

	if (Instruction->Groups & ITYPE_STACK)
	{
		switch (Instruction->Type)
		{
			case ITYPE_PUSH:
				assert(Instruction->OperandCount == 1 && Operand1->Length);
				Instruction->StackChange = -Operand1->Length;
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_POP:
				assert(Instruction->OperandCount == 1 && Operand1->Length);
				Instruction->StackChange = Operand1->Length;
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_PUSHA:
				Instruction->StackChange = -(X86Instruction->OperandSize * 8); // xAX, xCX, xDX, xBX, xBP, xSP, xSI, xDI
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_POPA:
				Instruction->StackChange = X86Instruction->OperandSize * 8; // xDI, xSI, xSP, xBP, xBX, xDX, xCX, xAX
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_PUSHF:			
				Instruction->StackChange = -Operand1->Length;
				Instruction->NeedsEmulation = TRUE;
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_POPF:
				Instruction->StackChange = Operand1->Length;
				Instruction->NeedsEmulation = TRUE;
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_ENTER:
				if (!Instruction->AnomalyOccurred)
				{
					if (Instruction->Operands[1].Value_U64 & 3)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: ENTER has invalid operand 2\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					if (Instruction->Operands[2].Value_U64 & ~0x1F)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: ENTER has invalid operand 3\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
				}
				SANITY_CHECK_ADDRESS_SIZE();
				Instruction->Operands[2].Value_U64 &= 0x1F;

				// frame pointer + stack space
				i = Operand1->Length + (U32)Instruction->Operands[1].Value_U64;
				Instruction->StackChange = -((LONG)i);
				i = (U32)Instruction->Operands[2].Value_U64 * Operand1->Length;
				Instruction->StackChange -= i;
				break;

			case ITYPE_LEAVE:
				// This will do "mov esp, ebp; pop ebp" so the StackChange size is dynamic
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_CALL:
				Instruction->StackChange = -X86Instruction->OperandSize;
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_RET:
				Instruction->StackChange = X86Instruction->OperandSize;

				switch (Opcode)
				{
					case 0xC3: // ret with no args
						break;

					case 0xC2: // ret with 1 arg
						if (!Instruction->AnomalyOccurred && (Operand1->Value_U64 & 3))
						{
							if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: ret has invalid operand 1\n", VIRTUAL_ADDRESS);
							Instruction->AnomalyOccurred = TRUE;
						}
						Instruction->StackChange += (LONG)Operand1->Value_U64;
						break;

					case 0xCB: // far ret with no args
						Instruction->StackChange *= 2; // account for segment
						Instruction->StackChange += (LONG)Operand1->Value_U64;
						break;

					case 0xCA: // far ret with 1 arg
						if (!Instruction->AnomalyOccurred && (Operand1->Value_U64 & 3))
						{
							if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: retf has invalid operand 1\n", VIRTUAL_ADDRESS);
							Instruction->AnomalyOccurred = TRUE;
						}
						Instruction->StackChange *= 2; // account for segment
						Instruction->StackChange += (LONG)Operand1->Value_U64;
						break;
				}
				SANITY_CHECK_ADDRESS_SIZE();
				break;

			case ITYPE_ADD:
			case ITYPE_XCHGADD:
				if (Instruction->Operands[1].Value_S64) Instruction->StackChange = (LONG)(Instruction->Operands[1].Value_S64);
				break;
			case ITYPE_SUB:
				if (Instruction->Operands[1].Value_S64) Instruction->StackChange = (LONG)(-Instruction->Operands[1].Value_S64);
				break;
			case ITYPE_MOV:
			case ITYPE_AND:
				break;

			default:
				if (!Instruction->AnomalyOccurred)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Instruction \"%s\" is modifying the stack\n", VIRTUAL_ADDRESS, X86Opcode->Mnemonic);
					Instruction->AnomalyOccurred = TRUE;
				}
				break;
		}

		if (!Instruction->AnomalyOccurred &&
			((X86Instruction->OperandSize != 2 && (Instruction->StackChange & 3)) || (Instruction->StackChange & 1)))
		{
			if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: \"%s\" has invalid stack change 0x%02X\n", VIRTUAL_ADDRESS, X86Opcode->Mnemonic, Instruction->StackChange);
			Instruction->AnomalyOccurred = TRUE;
		}
	}

	if (Instruction->Groups & ITYPE_TRAPS)
	{
		switch (Instruction->Type)
		{
			case ITYPE_TRAP:
			case ITYPE_TRAPCC:
			case ITYPE_TRAPRET:
			case ITYPE_BOUNDS:
			case ITYPE_DEBUG:
			case ITYPE_TRACE:
			case ITYPE_INVALID:
			case ITYPE_OFLOW:
				Instruction->NeedsEmulation = TRUE;
				break;
			default:
				assert(0);
				break;
		}
	}

	if (Instruction->Groups & ITYPE_SYSTEM)
	{
		switch (Instruction->Type)
		{
			case ITYPE_CPUID:
			case ITYPE_SYSCALL:
			case ITYPE_SYSCALLRET:
				// This doesn't require privileges
				break;

			case ITYPE_HALT:
			case ITYPE_IN:
			case ITYPE_OUT:
			default:
				Instruction->NeedsEmulation = TRUE;
				break;
		}
	}

	Disassembler->Stage3CountWithDecode++;
	return TRUE;

abort:
	if (!SuppressErrors)
	{
#ifdef TEST_DISASM
		printf("Dump of 0x%04I64X:\n", VIRTUAL_ADDRESS);
		__try { DumpAsBytes(stdout, Instruction->Address, (ULONG_PTR)VIRTUAL_ADDRESS, 16, TRUE); }
		__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {}
#endif
		fflush(stdout);
	}
	return FALSE;
}

// Address = address to first byte after the opcode (e.g., first byte of ModR/M byte or
// immediate value
//
// Returns the address immediately following the operand (e.g., the next operand or the
// start of the next instruction
INTERNAL U8 *SetOperands(INSTRUCTION *Instruction, U8 *Address, U32 Flags)
{
	INSTRUCTION_OPERAND *Operand;
	U32 Index, OperandIndex;
	S64 Displacement = 0;
	U8 Register;
	U32 OperandFlags, OperandType, AddressMode, Segment;
	U8 Opcode;
	MODRM modrm;
	REX rex;
	REX_MODRM rex_modrm;
	X86_OPCODE *X86Opcode;
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;
	BOOL Decode = Flags & DISASM_DECODE;
	BOOL Disassemble = Flags & DISASM_DISASSEMBLE;
	BOOL SuppressErrors = Flags & DISASM_SUPPRESSERRORS;

	Opcode = Instruction->LastOpcode;
	X86Opcode = &X86Instruction->Opcode;

	// Setup Mod R/M byte
	if (X86Instruction->HasModRM)
	{
		SET_MODRM(X86Instruction->modrm, X86Instruction->modrm_b);
		modrm = X86Instruction->modrm;
		rex = X86Instruction->rex;
		SET_REX_MODRM(X86Instruction->rex_modrm, rex, modrm);
		rex_modrm = X86Instruction->rex_modrm;
		//DISASM_OUTPUT(("[0x%08I64X] ModRM = 0x%02X (mod=%d, reg=%d, rm=%d)\n", VIRTUAL_ADDRESS, X86Instruction->modrm_b, modrm.mod, rex_modrm.reg, rex_modrm.rm));
		INSTR_INC(1); // increment Instruction->Length and address
	}
	else
	{
		// initialize them to 0
		modrm = X86Instruction->modrm;
		rex = X86Instruction->rex;
		rex_modrm = X86Instruction->rex_modrm;
	}

	for (OperandIndex = 0; OperandIndex < Instruction->OperandCount; OperandIndex++)
	{
		Operand = &Instruction->Operands[OperandIndex];
		assert(!(Operand->Flags & 0x7F));
		
		OperandFlags = X86Opcode->OperandFlags[OperandIndex] & X86_OPFLAGS_MASK;
		OperandType = X86Opcode->OperandFlags[OperandIndex] & X86_OPTYPE_MASK;
		AddressMode = X86Opcode->OperandFlags[OperandIndex] & X86_AMODE_MASK;
		if (Decode && OperandIndex != 0) APPENDS(", ");

		switch (OperandType)
		{
			////////////////////////////////////////////////////////////
			// Special operand types with no associated addressing mode
			////////////////////////////////////////////////////////////

			case OPTYPE_0:
				if (!Decode) continue;
				Operand->Value_U64 = 0;
				Operand->Type = OPTYPE_IMM;
				//DISASM_OUTPUT(("[SetOperand] const 0\n"));
				if (Disassemble)
				{
					APPENDS("<0>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_1:
				if (!Decode) continue;
				Operand->Value_U64 = 1;
				Operand->Type = OPTYPE_IMM;
				//DISASM_OUTPUT(("[SetOperand] const 1\n"));
				if (Disassemble)
				{
					APPENDS("<1>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FF:
				if (!Decode) continue;
				Operand->Value_U64 = 0xFF;
				Operand->Type = OPTYPE_IMM;
				//DISASM_OUTPUT(("[SetOperand] const 0xff\n"));
				if (Disassemble)
				{
					APPENDS("<0xFF>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_TSC:
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] TSC\n"));
				if (Disassemble)
				{
					APPENDS("<TSC_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_CS_MSR:
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] CS MSR\n"));
				if (Disassemble)
				{
					APPENDS("<CS_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_EIP_MSR:
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] EIP MSR\n"));
				if (Disassemble)
				{
					APPENDS("<EIP_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_ESP_MSR:
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] ESP MSR\n"));
				if (Disassemble)
				{
					APPENDS("<ESP_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_KERNELBASE_MSR:
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] KernelBase MSR\n"));
				if (Disassemble)
				{
					APPENDS("<KRNLBASE_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_STAR_MSR:
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] KernelBase MSR\n"));
				if (Disassemble)
				{
					APPENDS("<STAR_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_CSTAR_MSR:
				assert(!IS_AMD64());
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] CSTAR MSR\n"));
				if (Disassemble)
				{
					APPENDS("<CSTAR_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_LSTAR_MSR:
				assert(IS_AMD64());
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] LSTAR MSR\n"));
				if (Disassemble)
				{
					APPENDS("<LSTAR_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FMASK_MSR:
				if (!Decode) continue;
				Operand->Length = 8;
				Operand->Type = OPTYPE_SPECIAL;
				//DISASM_OUTPUT(("[SetOperand] FMASK MSR\n"));
				if (Disassemble)
				{
					APPENDS("<FMASK_MSR>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OP_REG:
				if (!Decode) continue;
				// The reg field is included in the opcode
				Operand->Length = X86Instruction->OperandSize;
				Register = X86_GET_REG64(Opcode);
				switch (X86Instruction->OperandSize)
				{
					case 8:
						Operand->Register = AMD64_64BIT_OFFSET + Register;
						break;
					case 4:
						Operand->Register = X86_32BIT_OFFSET + Register;
						CHECK_AMD64_REG();
						break;
					case 2:
						Operand->Register = X86_16BIT_OFFSET + Register;
						CHECK_AMD64_REG();
						break;
					case 1:
						Operand->Register = X86_8BIT_OFFSET + Register;
						if (X86Instruction->rex_b) CHECK_AMD64_REG();
						break;
					default:
						assert(0);
						return NULL;
				}
				X86_SET_REG(Register);

				//DISASM_OUTPUT(("[SetOperand] OP_REG %s\n", X86_Registers[Operand->Register]));
				if (Disassemble)
				{
					APPENDB('<'); APPENDS(X86_Registers[Operand->Register]); APPENDB('>');
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_REG8:
				if (!Decode) continue;
				// The reg field is included in the opcode
				Operand->Length = 1;
				Register = X86_GET_REG64(Opcode);
				Operand->Register = X86_8BIT_OFFSET + Register;
				CHECK_AMD64_REG();
				X86_SET_REG(Register);

				//DISASM_OUTPUT(("[SetOperand] OP_REG %s\n", X86_Registers[Operand->Register]));
				if (Disassemble)
				{
					APPENDB('<'); APPENDS(X86_Registers[Operand->Register]); APPENDB('>');
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_REG_AL:
				if (!Decode) continue;
				Operand->Length = 1;
				Operand->Register = X86_REG_AL;
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] reg AL\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_REG_CL:
				if (!Decode) continue;
				Operand->Length = 1;
				Operand->Register = X86_REG_CL;
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] reg CL\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_REG_AH:
				if (!Decode) continue;
				Operand->Length = 1;
				Operand->Register = X86_REG_AH;
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] reg AH\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_REG_AX:
				if (!Decode) continue;
				Operand->Length = 2;
				Operand->Register = X86_REG_AX;
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] reg AX\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_REG_DX:
				if (!Decode) continue;
				Operand->Length = 2;
				Operand->Register = X86_REG_DX;
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] reg DX\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_REG_ECX:
				if (!Decode) continue;
				Operand->Length = 4;
				Operand->Register = X86_REG_ECX;
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] reg ECX\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_REG_xBP:
				if (!Decode) continue;
				Operand->Length = X86Instruction->OperandSize; 
				switch (X86Instruction->OperandSize)
				{
					case 8: Operand->Register = AMD64_REG_RBP; break;
					case 4: Operand->Register = X86_REG_EBP; break;
					case 2: Operand->Register = X86_REG_BP; break;
					default: assert(0); return NULL;
				}
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] xAX_BIG (size = %d)\n", Operand->Length));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_REG_xAX_BIG:
				if (!Decode) continue;
				Operand->Length = X86Instruction->OperandSize; 
				switch (X86Instruction->OperandSize)
				{
					case 8: Operand->Register = AMD64_REG_RAX; break;
					case 4: Operand->Register = X86_REG_EAX; break;
					case 2: Operand->Register = X86_REG_AX; break;
					default: assert(0); return NULL;
				}
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] xAX_BIG (size = %d)\n", Operand->Length));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_REG_xAX_SMALL:
				if (!Decode) continue;
				Operand->Length = X86Instruction->OperandSize >> 1; 
				switch (X86Instruction->OperandSize)
				{
					case 8: Operand->Register = X86_REG_EAX; break;
					case 4: Operand->Register = X86_REG_AX; break;
					case 2: Operand->Register = X86_REG_AL; break;
					default: assert(0); return NULL;
				}
				X86_SET_REG(0);
				//DISASM_OUTPUT(("[SetOperand] xAX_SMALL (size = %d)\n", Operand->Length));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_xCX_HI_xBX_LO:
				if (!Decode) continue;
				Operand->Length = X86Instruction->OperandSize << 1; 
				if (Disassemble)
				{
					switch (X86Instruction->OperandSize)
					{
						case 8: APPENDS("<RCX:RBX>"); break;
						case 4: APPENDS("<ECX:EBX>"); break;
						case 2: APPENDS("<CX:BX>"); break;
						default: assert(0); return NULL;
					}
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] xCX_BIG:xBX_BIG (size = %d)\n", Operand->Length));
				continue;
			case OPTYPE_xDX_HI_xAX_LO:
				if (!Decode) continue;
				Operand->Length = X86Instruction->OperandSize << 1; 
				if (Disassemble)
				{
					switch (X86Instruction->OperandSize)
					{
						case 8: APPENDS("<RDX:RAX>"); break;
						case 4: APPENDS("<EDX:EAX>"); break;
						case 2: APPENDS("<DX:AX>"); break;
						default: assert(0); return NULL;
					}
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] xDX_BIG:xAX_BIG (size = %d)\n", Operand->Length));
				continue;

			case OPTYPE_EDX_HI_EAX_LO:
				if (!Decode) continue;
				Operand->Length = 8;
				//DISASM_OUTPUT(("[SetOperand] EDX:EAX\n"));
				if (Disassemble)
				{
					APPENDS("<EDX:EAX>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_EDX_ECX_EBX_EAX:
				Operand->Length = 32;
				//DISASM_OUTPUT(("[SetOperand] EDX:ECX:EBX:EAX\n"));
				if (Disassemble)
				{
					APPENDS("<EDX:ECX:EBX:EAX>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_FLAGS:
				if (!Decode) continue;
				Operand->Length = 2;
				Operand->Flags |= OP_REG;
				Operand->Register = X86_REG_FLAGS;
				//DISASM_OUTPUT(("[SetOperand] reg FLAGS\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_xFLAGS:
				if (!Decode) continue;
				Operand->Length = X86Instruction->OperandSize; 
				Operand->Flags |= OP_REG;
				switch (X86Instruction->OperandSize)
				{
					case 8: Operand->Register = AMD64_REG_RFLAGS; break;
					case 4: Operand->Register = X86_REG_EFLAGS; break;
					case 2: Operand->Register = X86_REG_FLAGS; break;
					default: assert(0); return NULL;
				}
				//DISASM_OUTPUT(("[SetOperand] reg xFLAGS (size = %d)\n", Operand->Length));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_CS:
				if (!Decode) continue;
				if (Instruction->Type != ITYPE_PUSH && Instruction->Type != ITYPE_POP) Operand->Length = 2;
				else Operand->Length = X86Instruction->OperandSize;
				Operand->Register = X86_SEG_CS;
				Operand->Flags |= OP_REG;
				//DISASM_OUTPUT(("[SetOperand] seg CS\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_DS:
				if (!Decode) continue;
				if (Instruction->Type != ITYPE_PUSH && Instruction->Type != ITYPE_POP) Operand->Length = 2;
				else Operand->Length = X86Instruction->OperandSize;
				Operand->Register = X86_SEG_DS;
				Operand->Flags |= OP_REG;
				//DISASM_OUTPUT(("[SetOperand] seg DS\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_ES:
				if (!Decode) continue;
				if (Instruction->Type != ITYPE_PUSH && Instruction->Type != ITYPE_POP) Operand->Length = 2;
				else Operand->Length = X86Instruction->OperandSize;
				Operand->Register = X86_SEG_ES;
				Operand->Flags |= OP_REG;
				//DISASM_OUTPUT(("[SetOperand] seg ES\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FS:
				if (!Decode) continue;
				if (Instruction->Type != ITYPE_PUSH && Instruction->Type != ITYPE_POP) Operand->Length = 2;
				else Operand->Length = X86Instruction->OperandSize;
				Operand->Register = X86_SEG_FS;
				Operand->Flags |= OP_REG;
				//DISASM_OUTPUT(("[SetOperand] seg FS\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_GS:
				if (!Decode) continue;
				if (Instruction->Type != ITYPE_PUSH && Instruction->Type != ITYPE_POP) Operand->Length = 2;
				else Operand->Length = X86Instruction->OperandSize;
				Operand->Register = X86_SEG_GS;
				Operand->Flags |= OP_REG;
				//DISASM_OUTPUT(("[SetOperand] seg GS\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_SS:
				if (!Decode) continue;
				if (Instruction->Type != ITYPE_PUSH && Instruction->Type != ITYPE_POP) Operand->Length = 2;
				else Operand->Length = X86Instruction->OperandSize;
				Operand->Register = X86_SEG_SS;
				Operand->Flags |= OP_REG;
				//DISASM_OUTPUT(("[SetOperand] seg SS\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_CR0:
				if (!Decode) continue;
				Operand->Length = X86Instruction->OperandSize;
				Operand->Register = X86_REG_CR0;
				Operand->Flags |= OP_REG;
				//DISASM_OUTPUT(("[SetOperand] reg CR0\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_STx:
				if (!Decode) continue;
				Operand->Length = 10;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Flags |= OP_REG;
				Register = X86_GET_REG(X86Instruction->modrm_b);
				Operand->Register = X86_FPU_OFFSET + Register;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_STx: reg st(%d)\n", Register));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_ST0:
				if (!Decode) continue;
				Operand->Length = 10;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Flags |= OP_REG;
				Operand->Register = X86_REG_ST0;
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_ST1:
				if (!Decode) continue;
				Operand->Length = 10;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Flags |= OP_REG;
				Operand->Register = X86_REG_ST1;
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "<%s>", X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_FPU_STATUS:
				if (!Decode) continue;
				Operand->Length = 2;
				if (Disassemble)
				{
					APPENDS("<FPUSTAT>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_FPU_CONTROL:
				if (!Decode) continue;
				Operand->Length = 2;
				if (Disassemble)
				{
					APPENDS("<FPUCTRL>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_FPU_TAG:
				if (!Decode) continue;
				Operand->Length = 2;
				if (Disassemble)
				{
					APPENDS("<FPUTAG>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			case OPTYPE_FLDZ:
				if (!Decode) continue;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Length = 10;
				if (Disassemble)
				{
					APPENDS("<0.0>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FLD1:
				if (!Decode) continue;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Length = 10;
				if (Disassemble)
				{
					APPENDS("<1.0>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FLDPI:
				if (!Decode) continue;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Length = 10;
				if (Disassemble)
				{
					APPENDS("<pi>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FLDL2T:
				if (!Decode) continue;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Length = 10;
				if (Disassemble)
				{
					APPENDS("<log_2 10>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FLDL2E:
				if (!Decode) continue;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Length = 10;
				if (Disassemble)
				{
					APPENDS("<log_2 e>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FLDLG2:
				if (!Decode) continue;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Length = 10;
				if (Disassemble)
				{
					APPENDS("<log_10 2>");
					X86_WRITE_OPFLAGS();
				}
				continue;
			case OPTYPE_FLDLN2:
				if (!Decode) continue;
				Operand->Type = OPTYPE_FLOAT;
				Operand->Length = 10;
				if (Disassemble)
				{
					APPENDS("<ln 2>");
					X86_WRITE_OPFLAGS();
				}
				continue;

			////////////////////////////////////////////////////////////
			// Fixed sizes regardless of operand size
			////////////////////////////////////////////////////////////

			case OPTYPE_b: // byte regardless of operand size
				Operand->Length = 1;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_b (size 1, signed %d)\n", ((OperandFlags & OP_SIGNED) != 0)));
				break;

			case OPTYPE_w: // word regardless of operand size
				Operand->Length = 2;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_w (size 2)\n"));
				break;

			case OPTYPE_d: // dword regardless of operand size
				Operand->Length = 4;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_d (size 4)\n"));
				break;

			case OPTYPE_q: // qword regardless of operand size
				Operand->Length = 8;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_q (size 8)\n"));
				break;

			case OPTYPE_o: // oword regardless of operand size
				Operand->Length = 16;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_o (size 16)\n"));
				break;

			case OPTYPE_dt: // 6-byte or 10-byte pseudo descriptor (sgdt, lgdt, sidt, lidt)
				if (IS_AMD64()) Operand->Length = 10;
				else Operand->Length = 6;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_dt (%d bytes)\n", Operand->Length));
				break;

			case OPTYPE_cpu:
				if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Undocumented loadall instruction?\n", VIRTUAL_ADDRESS);
				Instruction->AnomalyOccurred = TRUE;
				Operand->Length = 204;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_cpu (size 204)\n"));
				break;

			////////////////////////////////////////////////////////////
			// Sizes depending on the operand size
			////////////////////////////////////////////////////////////

			case OPTYPE_z: // word if operand size is 16 bits and dword otherwise
				switch (X86Instruction->OperandSize)
				{
					case 8: case 4: Operand->Length = 4; break;
					case 2: Operand->Length = 2; break;
					default: assert(0); return NULL;
				}
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_z (length %d)\n", Operand->Length));
				break;

			case OPTYPE_v: // word, dword, or qword
				Operand->Length = X86Instruction->OperandSize;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_v (size %d, signed = %d)\n", Operand->Length, ((OperandFlags & OP_SIGNED) != 0)));
				break;

			case OPTYPE_a: // two word or dword operands in memory (used only by bound)
				assert(Instruction->OpcodeBytes[0] == X86_BOUND);
				switch (X86Instruction->OperandSize)
				{
					case 8: case 4: Operand->Length = 8; break;
					case 2: Operand->Length = 4; break;
					default: assert(0); return NULL;
				}
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_a (size %d)\n", Operand->Length));
				break;

			case OPTYPE_p: // 32-bit or 48-bit pointer depending on operand size
				if (!Instruction->AnomalyOccurred && X86Instruction->HasSegmentOverridePrefix)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Segment override used when segment is explicit\n", VIRTUAL_ADDRESS);
					Instruction->AnomalyOccurred = TRUE;
				}
				switch (X86Instruction->OperandSize)
				{
					case 8: case 4: Operand->Length = 6; break;
					case 2: Operand->Length = 4; break;
					default: assert(0); return NULL;
				}
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_p (length %d)\n", Operand->Length));
				break;

			case OPTYPE_dq: // dword or qword
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_dq (size 4 or 8)\n"));
				switch (X86Instruction->OperandSize)
				{
					case 8: Operand->Length = 8; break;
					case 4: case 2: Operand->Length = 4; break;
					default: assert(0); return NULL;
				}
				break;

			case OPTYPE_mw: // a word if the destination operand is memory
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_mw (size 0)\n"));
				assert(X86Instruction->HasModRM);		
				if (modrm.mod == 3) Operand->Length = X86Instruction->OperandSize; // using register
				else Operand->Length = 2; // using memory
				break;

			case OPTYPE_lea:
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_lea (size 0)\n"));
				assert(OperandIndex == 1);
				Operand->Length = Instruction->Operands[0].Length;
				break;

			////////////////////////////////////////////////////////////
			// FPU types
			////////////////////////////////////////////////////////////

			case OPTYPE_ps: // packed single real
				Operand->Length = 4;
				Operand->Type = OPTYPE_FLOAT;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_ps (packed single real)\n"));
				break;
			case OPTYPE_pd: // packed double real
				Operand->Length = 8;
				Operand->Type = OPTYPE_FLOAT;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_pd (packed double real)\n"));
				break;
			case OPTYPE_pb: // packed BCD
				Operand->Length = 10;
				Operand->Type = OPTYPE_BCD;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_pb (packed BCD)\n"));
				break;
			case OPTYPE_ss: // scalar single real
				Operand->Length = 4;
				Operand->Type = OPTYPE_FLOAT;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_ss (single real)\n"));
				break;
			case OPTYPE_sd: // scalar double real
				Operand->Length = 8;
				Operand->Type = OPTYPE_FLOAT;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_sd (double real)\n"));
				break;
			case OPTYPE_se: // extended real
				Operand->Length = 10;
				Operand->Type = OPTYPE_FLOAT;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_se (extended real)\n"));
				break;

			case OPTYPE_fev: // FPU environment (28 bytes in 32-bit modes, 14 bytes in 16-bit real mode)
				switch (X86Instruction->OperandSize)
				{
					case 8: case 4: Operand->Length = 28; break;
					case 2: Operand->Length = 14; break;
					default: assert(0); return NULL;
				}
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_fev (FPU environment, length %d)\n", Operand->Length));
				break;

			case OPTYPE_fst1: // FPU state (108 bytes in 32-bit modes, 94 bytes in 16-bit real mode)
				switch (X86Instruction->OperandSize)
				{
					case 8: case 4: Operand->Length = 108; break;
					case 2: Operand->Length = 94; break;
					default: assert(0); return NULL;
				}
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_fst1 (FPU state, length %d)\n", Operand->Length));
				break;

			case OPTYPE_fst2: // 512 bytes for FPU state (FPU + MMX + XXM + MXCSR)
				Operand->Length = 512;
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_fst2 (FPU + MMX + XXM + MXCSR state, length 512)\n"));
				break;

			case OPTYPE_sso:
				if (modrm.mod == 3) // from register
				{
					Operand->Length = 16;
				}
				else // from memory
				{
					Operand->Length = 4;
					Operand->Type = OPTYPE_FLOAT;
				}
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_sso (single real or oword)\n"));
				break;

			case OPTYPE_sdo:
				if (modrm.mod == 3) // from register
				{
					Operand->Length = 16;
				}
				else // from memory
				{
					Operand->Length = 8;
					Operand->Type = OPTYPE_FLOAT;
				}
				//DISASM_OUTPUT(("[SetOperand] OPTYPE_sso (double real or oword)\n"));
				break;

			default:
				assert(0);
				return NULL;
		}

		switch (AddressMode)
		{
			////////////////////////////////////////////////////////////
			// Special types
			////////////////////////////////////////////////////////////

			case AMODE_xlat: // DS:[EBX+AL]
				if (!Decode) continue;
				assert(Operand->Length == 1);
				Operand->Flags |= OP_ADDRESS | OP_REG;
				Operand->Type = OPTYPE_STRING;
				
				switch (X86Instruction->AddressSize)
				{
					case 8: Operand->Register = AMD64_REG_RBX; break;
					case 4: Operand->Register = X86_REG_EBX; break;
					case 2: Operand->Register = X86_REG_BX; break;
					default: assert(0); return NULL;
				}
				X86_SET_ADDR();
				X86Instruction->Scale = 1;
				X86Instruction->BaseRegister = Operand->Register;
				X86Instruction->HasBaseRegister = TRUE;
				X86Instruction->IndexRegister = X86_REG_AL;
				X86Instruction->HasIndexRegister = TRUE;

				//DISASM_OUTPUT(("[SetOperand] AMODE_xlat (DS:[EBX+AL])\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "%s:[%s]", 
						Segments[X86Instruction->Segment], X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			////////////////////////////////////////////////////////////
			// Without mod R/M byte
			////////////////////////////////////////////////////////////

			case AMODE_I: // immediate value
				if (Decode)
				{
					Operand->Type = OPTYPE_IMM;
					switch (Operand->Length)
					{
						case 8:
							if (OperandFlags & OP_SIGNED) Operand->Value_S64 = (S64)*((S64 *)Address);
							else Operand->Value_U64 = (U64)*((U64 *)Address);
							break;
						case 4:
							if (!(OperandFlags & OP_SIGNED) && OperandIndex == 1 && 
								(Instruction->Operands[0].Flags & (OP_REG|OP_ADDRESS)) &&
								Instruction->Operands[0].Length == 8)
							{
								// For some opcodes the second operand is a sign-extended imm32 value
								assert(X86Instruction->OperandSize == 8);
								switch (Instruction->Type)
								{
									case ITYPE_AND:
									case ITYPE_ADD:
									case ITYPE_XCHGADD:
									case ITYPE_CMP:
									case ITYPE_MOV:
									case ITYPE_SUB:
									case ITYPE_TEST:
									case ITYPE_OR:
									case ITYPE_XOR:
										assert(OperandIndex == 1);
										Operand->Value_S64 = (S64)*((S32 *)Address);
										break;
									default:
										assert(0);
										if (OperandFlags & OP_SIGNED) Operand->Value_S64 = (S64)*((S32 *)Address);
										else Operand->Value_U64 = (U64)*((U32 *)Address);
										break;
								}
							}
							else
							{
								if (OperandFlags & OP_SIGNED) Operand->Value_S64 = (S64)*((S32 *)Address);
								else Operand->Value_U64 = (U64)*((U32 *)Address);
							}
							break;
						case 2:
							if (OperandFlags & OP_SIGNED) Operand->Value_S64 = (S64)*((S16 *)Address);
							else Operand->Value_U64 = (U64)*((U16 *)Address);
							break;
						case 1:
							if (OperandFlags & OP_SIGNED) Operand->Value_S64 = (S64)*((S8 *)Address);
							else Operand->Value_U64 = (U64)*((U8 *)Address);
							break;
						default:
							assert(0);
							return NULL;
					}
				}
				INSTR_INC(Operand->Length); // increment Instruction->Length and address
				assert(X86Instruction->OperandSize >= Operand->Length);
				if (Instruction->Type == ITYPE_PUSH) Operand->Length = X86Instruction->OperandSize;

				//DISASM_OUTPUT(("[SetOperand] AMODE_I (immediate data)\n"));
				if (Disassemble)
				{
					X86_WRITE_IMMEDIATE();
					X86_WRITE_OPFLAGS();
				}
				continue;

			case AMODE_J: // IP-relative jump offset
				SANITY_CHECK_ADDRESS_SIZE();
				if (Decode)
				{
					Operand->Flags |= OP_IPREL | OP_SIGNED | OP_REG | OP_ADDRESS;
					Operand->Type = OPTYPE_OFFSET;
					switch (X86Instruction->OperandSize)
					{
						case 8: Operand->Register = AMD64_REG_RIP; break;
						case 4: Operand->Register = X86_REG_EIP; break;
						case 2: Operand->Register = X86_REG_IP; break;
						default: assert(0); return NULL;
					}
					switch (Operand->Length)
					{
						case 8: X86Instruction->Displacement = *((S64 *)Address); break;
						case 4: X86Instruction->Displacement = (S64)*((S32 *)Address); break;
						case 2: X86Instruction->Displacement = (S64)*((S16 *)Address); break;
						case 1: X86Instruction->Displacement = (S64)*((S8 *)Address); break;
						default: assert(0); return NULL;
					}					

					Operand->Value_S64 = X86Instruction->Displacement;
					X86Instruction->Relative = TRUE;

					if ((Operand->Flags & OP_COND) && !X86Instruction->Displacement)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: Both conditions of branch go to same address\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
				}

				INSTR_INC(Operand->Length); // increment Instruction->Length and address
				if (!Decode) continue;

				assert((Operand->Flags & OP_EXEC) && (Instruction->Groups & ITYPE_EXEC));
				Operand->TargetAddress = ApplyDisplacement((U64)Address, Instruction);
				X86Instruction->Relative = TRUE; 
				X86_SET_ADDR();
				SANITY_CHECK_SEGMENT_OVERRIDE();
				X86Instruction->HasSegmentOverridePrefix = FALSE;
				X86Instruction->Segment = SEG_CS;
				X86Instruction->BaseRegister = Operand->Register;
				X86Instruction->HasBaseRegister = TRUE;
				assert(Instruction->OperandCount == 1);
				//DISASM_OUTPUT(("[SetOperand] AMODE_J (branch with relative offset)\n"));
				if (Disassemble)
				{
					X86_WRITE_IP_OFFSET(Operand);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case AMODE_O: // word/dword offset
				Operand->Type = OPTYPE_OFFSET;
				Operand->Flags |= OP_ADDRESS;
				SANITY_CHECK_OPERAND_SIZE();
				switch (X86Instruction->AddressSize)
				{
					case 8:
						if (Operand->Flags & OP_SIGNED) X86Instruction->Displacement = *((S64 *)Address);
						else X86Instruction->Displacement = (S64)*((U64 *)Address);
						break;
					case 4:
						if (Operand->Flags & OP_SIGNED) X86Instruction->Displacement = (S64)*((S32 *)Address);
						else X86Instruction->Displacement = (S64)*((U32 *)Address);
						break;
					case 2:
						if (Operand->Flags & OP_SIGNED) X86Instruction->Displacement = (S64)*((S16 *)Address);
						else X86Instruction->Displacement = (S64)*((U16 *)Address);
						break;
					default:
						assert(0);
						return FALSE;
				}

				INSTR_INC(X86Instruction->AddressSize); // increment Instruction->Length and address
				if (!Decode) continue;

				X86Instruction->HasFullDisplacement = TRUE;
				X86_SET_ADDR();
				X86_SET_TARGET();
				assert(X86Instruction->Segment == SEG_DS || X86Instruction->HasSegmentOverridePrefix);
				//DISASM_OUTPUT(("[SetOperand] AMODE_O (offset)\n"));
				if (Disassemble)
				{
					X86_WRITE_OFFSET(Operand);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case AMODE_A: // absolute address
				Operand->Flags |= OP_ADDRESS | OP_FAR;
				SANITY_CHECK_ADDRESS_SIZE();
				SANITY_CHECK_SEGMENT_OVERRIDE();
				X86Instruction->HasSelector = TRUE;
				X86Instruction->HasSegmentOverridePrefix = FALSE;
				switch (Operand->Length)
				{
					case 6:
						X86Instruction->Segment = *((U16 *)Address); INSTR_INC(2);
						X86Instruction->Displacement = (S64)*((S32 *)Address); INSTR_INC(4);
						break;
					case 4:
						X86Instruction->Segment = *((U16 *)Address); INSTR_INC(2);
						X86Instruction->Displacement = (S64)*((S16 *)Address); INSTR_INC(2);
						break;
					default:
						assert(0);
						return FALSE;
				}
				if (!Decode) continue;
				X86Instruction->HasFullDisplacement = TRUE;
				X86_SET_ADDR();
				X86_SET_TARGET();
				//DISASM_OUTPUT(("[SetOperand] AMODE_A (absolute address)\n"));
				if (Disassemble)
				{
					X86_WRITE_OFFSET(Operand);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case AMODE_X: // DS:[ESI]
				if (!Decode) continue;
				Operand->Flags |= OP_ADDRESS | OP_REG;
				Operand->Type = OPTYPE_STRING;
				switch (X86Instruction->AddressSize)
				{
					case 8: Operand->Register = AMD64_REG_RSI; break;
					case 4: Operand->Register = X86_REG_ESI; break;
					case 2: Operand->Register = X86_REG_SI; break;
					default: assert(0); return NULL;
				}

				X86Instruction->BaseRegister = Operand->Register;
				X86Instruction->HasBaseRegister = TRUE;
				X86_SET_ADDR();
				if (!X86Instruction->HasSegmentOverridePrefix) X86Instruction->Segment = SEG_DS;

				//DISASM_OUTPUT(("[SetOperand] AMODE_X (addressing via DS:[ESI])\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "%s:[%s]", 
						Segments[X86Instruction->Segment], X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;

			case AMODE_Y: // ES:[EDI]
				if (!Decode) continue;
				Operand->Flags |= OP_ADDRESS | OP_REG;
				Operand->Type = OPTYPE_STRING;
				switch (X86Instruction->AddressSize)
				{
					case 8: Operand->Register = AMD64_REG_RDI; break;
					case 4: Operand->Register = X86_REG_EDI; break;
					case 2: Operand->Register = X86_REG_DI; break;
					default: assert(0); return NULL;
				}

				X86Instruction->BaseRegister = Operand->Register;
				X86Instruction->HasBaseRegister = TRUE;
				X86_SET_ADDR();
				if (X86Instruction->HasSegmentOverridePrefix)
				{
					if (!Instruction->AnomalyOccurred)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ANOMALY: segment override used with AMODE_Y\n", VIRTUAL_ADDRESS);
						Instruction->AnomalyOccurred = TRUE;
					}
					Segment = X86Instruction->DstSegment = SEG_ES;
					X86Instruction->HasDstSegment = TRUE;
				}
				else
				{
					Segment = X86Instruction->Segment = SEG_ES;
				}

				//DISASM_OUTPUT(("[SetOperand] AMODE_Y (addressing via ES:[EDI])\n"));
				if (Disassemble)
				{
					APPEND(OPCSTR, SIZE_LEFT, "%s:[%s]", 
						Segments[Segment], X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				continue;
			
			////////////////////////////////////////////////////////////
			// Mod R/M byte with only registers
			// Handle that case here since it is straightforward
			////////////////////////////////////////////////////////////

			case AMODE_PR: // modrm.rm = mmx register and modrm.mod = 11
				assert(X86Instruction->HasModRM);
				if (modrm.mod != 3)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: mod != 3 for AMODE_PR (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				else if (rex_modrm.rm > 7)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: invalid mmx register %d for AMODE_PR (\"%s\")\n", VIRTUAL_ADDRESS, rex_modrm.rm, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				else if (X86Instruction->OperandSize == 2)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: AMODE_PR illegal in 16-bit mode (\"%s\")\n", VIRTUAL_ADDRESS, rex_modrm.rm, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				if (!Decode) continue;

				Operand->Flags |= OP_REG;
				Operand->Register = X86_MMX_OFFSET + rex_modrm.rm;
				X86_SET_REG(0);

				if (Disassemble)
				{
					assert(X86_Registers[Operand->Register]);
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_PR (MMX register)\n"));
				continue;

			case AMODE_VR: // modrm.rm = xmm register and modrm.mod = 11
				assert(X86Instruction->HasModRM);
				if (modrm.mod != 3)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: mod != 3 for AMODE_VR (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				else if (X86Instruction->OperandSize == 2)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: AMODE_VR illegal in 16-bit mode (\"%s\")\n", VIRTUAL_ADDRESS, rex_modrm.rm, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				if (!Decode) continue;

				Operand->Flags |= OP_REG;
				Operand->Register = X86_XMM_OFFSET + rex_modrm.rm;
				X86_SET_REG(0);

				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_VR (XMM register)\n"));
				continue;

			case AMODE_P: // modrm.reg = mmx register
				assert(X86Instruction->HasModRM);
				if (rex_modrm.reg > 7)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: invalid mmx register %d for AMODE_P (\"%s\")\n", VIRTUAL_ADDRESS, rex_modrm.reg, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				else if (X86Instruction->OperandSize == 2)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: AMODE_P illegal in 16-bit mode (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				if (!Decode) continue;

				Operand->Flags |= OP_REG;
				Operand->Register = X86_MMX_OFFSET + rex_modrm.reg;
				X86_SET_REG(0);

				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_P (MMX register)\n"));
				continue;

			case AMODE_V: // modrm.reg = xmm register
				assert(X86Instruction->HasModRM);
				if (X86Instruction->OperandSize == 2)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: AMODE_P illegal in 16-bit mode (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				if (!Decode) continue;

				Operand->Flags |= OP_REG;
				Operand->Register = X86_XMM_OFFSET + rex_modrm.reg; break;
				X86_SET_REG(0);

				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_V (XMM register)\n"));
				continue;

			case AMODE_R: // modrm.rm is general register and modrm.mod = 11
				assert(X86Instruction->HasModRM);
				if (modrm.mod != 3)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: mod != 3 for AMODE_R (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				if (!Decode) continue;
				Operand->Flags |= OP_REG;
				switch (Operand->Length)
				{
					case 8: Operand->Register = AMD64_64BIT_OFFSET + rex_modrm.rm; break;
					case 4: Operand->Register = X86_32BIT_OFFSET, rex_modrm.rm; CHECK_AMD64_REG(); break;
					case 2: Operand->Register = X86_16BIT_OFFSET, rex_modrm.rm; CHECK_AMD64_REG(); break;
					case 1: Operand->Register = X86_8BIT_OFFSET, rex_modrm.rm; if (X86Instruction->rex_b) CHECK_AMD64_REG(); break;
					default: assert(0); return NULL;
				}
				X86_SET_REG(rex_modrm.rm);
				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_R (general register)\n"));
				continue;

			case AMODE_G: // modrm.reg = general register
				assert(X86Instruction->HasModRM);
				if (!Decode) continue;
				Operand->Flags |= OP_REG;
				switch (Operand->Length)
				{
					case 8: Operand->Register = AMD64_64BIT_OFFSET + rex_modrm.reg; break;
					case 4: Operand->Register = X86_32BIT_OFFSET + rex_modrm.reg; CHECK_AMD64_REG(); break;
					case 2: Operand->Register = X86_16BIT_OFFSET + rex_modrm.reg; CHECK_AMD64_REG(); break;
					case 1: Operand->Register = X86_8BIT_OFFSET + rex_modrm.reg; if (X86Instruction->rex_b) CHECK_AMD64_REG(); break;
					default: assert(0); return NULL;
				}
				X86_SET_REG(rex_modrm.reg);
				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_G (general register)\n"));
				continue;
			
			case AMODE_S: // modrm.reg = segment register
				assert(X86Instruction->HasModRM);
				if (!Decode) continue;
				Operand->Flags |= OP_REG;
				switch (X86Instruction->OperandSize)
				{
					case 8:
					case 4:
					case 2:
						if (rex_modrm.reg <= 5) Operand->Register = X86_SEGMENT_OFFSET + rex_modrm.reg;
						break;
					default:
						assert(0);
						return NULL;
				}

				X86_SET_REG(0);
				if (Disassemble)
				{
					if (rex_modrm.reg > 5) APPEND(OPCSTR, SIZE_LEFT, "seg_%02X", rex_modrm.reg);
					else APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_S (segment register)\n"));
				continue;

			case AMODE_T: // modrm.reg = test register
				assert(X86Instruction->HasModRM);
				if (!Decode) continue;
				Instruction->Groups |= ITYPE_SYSTEM;
				Instruction->NeedsEmulation = TRUE;
				Operand->Flags |= OP_REG;
				switch (X86Instruction->OperandSize)
				{
					case 8:
					case 4:
					case 2:
						Operand->Register = X86_TEST_OFFSET + rex_modrm.reg;
						break;
					default:
						assert(0);
						return NULL;
				}

				X86_SET_REG(0);
				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_T (test register)\n"));
				continue;

			case AMODE_C: // modrm.reg = control register
				assert(X86Instruction->HasModRM);
				assert(Instruction->Type == ITYPE_MOV);
				if (!Decode) continue;
				Instruction->Groups |= ITYPE_SYSTEM;
				Instruction->NeedsEmulation = TRUE;
				Operand->Flags |= OP_REG;
				if (IS_AMD64()) X86Instruction->OperandSize = 8;
				switch (X86Instruction->OperandSize)
				{
					case 8:
					case 4:
					case 2:
						Operand->Register = X86_CONTROL_OFFSET + rex_modrm.reg;
						break;
					default:
						assert(0);
						return NULL;
				}

				X86_SET_REG(0);
				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_C (control register)\n"));
				continue;

			case AMODE_D: // modrm.reg = debug register
				assert(X86Instruction->HasModRM);
				assert(Instruction->Type == ITYPE_MOV);
				if (!Decode) continue;
				Instruction->NeedsEmulation = TRUE;
				Operand->Flags |= OP_REG;
				Instruction->Groups |= ITYPE_SYSTEM;
				Instruction->NeedsEmulation = TRUE;
				if (IS_AMD64()) X86Instruction->OperandSize = 8;
				switch (X86Instruction->OperandSize)
				{
					case 8:
					case 4:
					case 2:
						Operand->Register = X86_DEBUG_OFFSET + rex_modrm.reg;
						break;
					default:
						assert(0);
						return NULL;
				}

				X86_SET_REG(0);
				if (Disassemble)
				{
					APPENDS(X86_Registers[Operand->Register]);
					X86_WRITE_OPFLAGS();
				}
				//DISASM_OUTPUT(("[SetOperand] AMODE_D (debug register)\n"));
				continue;

			////////////////////////////////////////////////////////////
			// Mod R/M byte with memory or register
			////////////////////////////////////////////////////////////

			case AMODE_M: // memory only
				assert(X86Instruction->HasModRM);
				if (modrm.mod == 3)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: mod = 3 for AMODE_M (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}
				assert(X86Instruction->Segment == SEG_DS || X86Instruction->HasSegmentOverridePrefix);
				//DISASM_OUTPUT(("[SetOperand] AMODE_M (memory only)\n"));
				Address = SetModRM32(Instruction, Address, Operand, OperandIndex, SuppressErrors);
				if (!Address) return NULL;
				break;

			case AMODE_E: // general register or memory
				assert(X86Instruction->HasModRM);
				if (OperandType == OPTYPE_p && modrm.mod == 3)
				{
					if (!SuppressErrors) printf("[0x%08I64X] ERROR: mod = 3 for AMODE_E with OPTYPE_p (\"%s\")\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic);
					goto abort;
				}

				//DISASM_OUTPUT(("[SetOperand] AMODE_E (general register or memory)\n"));
				Address = SetModRM32(Instruction, Address, Operand, OperandIndex, SuppressErrors);
				if (!Address) return NULL;
				if (Decode && (Instruction->Type == ITYPE_PUSH || Instruction->Type == ITYPE_POP))
				{
					assert(X86Instruction->OperandSize >= Operand->Length);
					Operand->Length = X86Instruction->OperandSize;
				}
				break;

			case AMODE_Q: // mmx register or memory address
				assert(X86Instruction->HasModRM);
				//DISASM_OUTPUT(("[SetOperand] AMODE_Q (MMX register or memory address)\n"));
				if (modrm.mod == 3) // it is a register
				{
					if (rex_modrm.rm > 7)
					{
						if (!SuppressErrors) printf("[0x%08I64X] ERROR: invalid mmx register %d for AMODE_P (\"%s\")\n", VIRTUAL_ADDRESS, rex_modrm.rm, X86Instruction->Opcode.Mnemonic);
						goto abort;
					}
					Operand->Register = X86_MMX_OFFSET + rex_modrm.rm;
					Operand->Flags |= OP_REG;
					X86_SET_REG(0);
				}
				else
				{
					Address = SetModRM32(Instruction, Address, Operand, OperandIndex, SuppressErrors);
					if (!Address) return NULL;
				}
				break;

			case AMODE_W: // xmm register or memory address
				assert(X86Instruction->HasModRM);
				//DISASM_OUTPUT(("[SetOperand] AMODE_W (XMM register or memory address)\n"));
				if (modrm.mod == 3) // it is a register
				{
					Operand->Register = X86_XMM_OFFSET + rex_modrm.rm;
					Operand->Flags |= OP_REG;
					X86_SET_REG(0);
				}
				else
				{
					Address = SetModRM32(Instruction, Address, Operand, OperandIndex, SuppressErrors);
					if (!Address) return NULL;
				}
				break;

			default:
				assert(0);
				return NULL;
		}

		if (!Decode) continue;

		// If this is reached then SetModRM32 was called
		if ((Operand->Flags & OP_ADDRESS))
		{
			assert(Operand->Length);
			switch (Operand->Register)
			{
				case X86_REG_BP:
				case X86_REG_EBP:
				case AMD64_REG_RBP:
					if (X86Instruction->Displacement > 0) Operand->Flags |= OP_PARAM;
					else Operand->Flags |= OP_LOCAL;
					break;
				default:
					break;
			}
		}

		if (Disassemble)
		{
			Index = OperandType >> OPTYPE_SHIFT;
			assert(Index > 0 && Index < MAX_OPTYPE_INDEX && OptypeHandlers[Index]);
			OptypeHandlers[Index](Instruction, Operand, OperandIndex);
			X86_WRITE_OPFLAGS();
		}
	}

	return Address;

abort:
	if (!SuppressErrors)
	{
#ifdef TEST_DISASM
		printf("Dump of 0x%04I64X:\n", VIRTUAL_ADDRESS);
		__try { DumpAsBytes(stdout, Instruction->Address, (ULONG_PTR)VIRTUAL_ADDRESS, 16, TRUE); }
		__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {}
#endif
		fflush(stdout);
	}
	return NULL;
}

// NOTE: Address points one byte after ModRM
INTERNAL U8 *SetModRM16(INSTRUCTION *Instruction, U8 *Address, INSTRUCTION_OPERAND *Operand, U32 OperandIndex, BOOL SuppressErrors)
{
	MODRM modrm;
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;

	DISASM_OUTPUT(("[SetModRM16] Current instruction length = %d\n", Instruction->Length));
	modrm = X86Instruction->modrm;
	assert(!X86Instruction->rex_b);

	//
	// Both operands are registers
	// Condition: mod = 3
	//
	if (modrm.mod == 3)
	{
		//DISASM_OUTPUT(("[SetModRM16] Both regs (rm_reg %d)\n", modrm.rm));
		switch (Operand->Length)
		{
			case 4: Operand->Register = X86_32BIT_OFFSET + modrm.rm; break;
			case 2: Operand->Register = X86_16BIT_OFFSET + modrm.rm; break;
			case 1: Operand->Register = X86_8BIT_OFFSET + modrm.rm; break;
			default: assert(0); return NULL;
		}
		Operand->Flags |= OP_REG;
	}

	// 
	// Address is an absolute address (technically a 16-bit offset from DS:0)
	// Condition: mod = 0 and rm = 6
	//
	else if (modrm.mod == 0 && modrm.rm == 6)
	{
		//DISASM_OUTPUT(("[SetModRM16] Absolute addressing (displacement 0x%04X)\n", *(S16 *)Address));
		X86Instruction->Displacement = (S64)(*((S16 *)Address));
		if (IS_VALID_ADDRESS(X86Instruction->Displacement))
		{
			X86Instruction->HasFullDisplacement = TRUE;
			X86_SET_TARGET();
			Operand->Flags |= OP_GLOBAL;
		}
		X86_SET_ADDR();
		Operand->Flags |= OP_ADDRESS;
		INSTR_INC(2);
	}

	// Conditions:
	// (1) mod = 0 and rm != 6
	// (2) mod = 1-2 and rm = 0-7
	else
	{
		switch (modrm.mod)
		{
			case 0: // no displacement
				//DISASM_OUTPUT(("[SetModRM16] Indirect addressing (no displacement)\n"));
				break; 
			case 1: // 8-bit signed displacement
				//DISASM_OUTPUT(("[SetModRM16] Indirect addressing (displacement = 0x%02X, reg_rm = %d)\n", *(S8 *)Address, modrm.rm));
				X86Instruction->Displacement = (S64)(*((S8 *)Address));
				INSTR_INC(1); // increment Instruction->Length and address
				break;
			case 2: // 16-bit displacement
				//DISASM_OUTPUT(("[SetModRM16] Indirect addressing (displacement = 0x%04X, reg_rm = %d)\n", *(S16 *)Address, modrm.rm));
				X86Instruction->Displacement = (S64)(*((S16 *)Address));
				if (IS_VALID_ADDRESS(X86Instruction->Displacement))
				{
					Operand->Flags |= OP_GLOBAL;
					X86Instruction->HasFullDisplacement = TRUE;
				}
				INSTR_INC(2);
				break;
		}

		switch (modrm.rm)
		{
			case 0:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [BX+SI]\n"));
				X86Instruction->BaseRegister = X86_REG_BX;
				X86Instruction->IndexRegister = X86_REG_SI;
				X86Instruction->HasIndexRegister = TRUE;
				break;
			case 1:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [BX+DI]\n"));
				X86Instruction->BaseRegister = X86_REG_BX;
				X86Instruction->IndexRegister = X86_REG_DI;
				X86Instruction->HasIndexRegister = TRUE;
				break;
			case 2:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [BP+SI]\n"));
				X86Instruction->BaseRegister = X86_REG_BP;
				X86Instruction->IndexRegister = X86_REG_SI;
				X86Instruction->HasIndexRegister = TRUE;
				X86_SET_SEG(REG_BP);
				break;
			case 3:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [BP+DI]\n"));
				X86Instruction->BaseRegister = X86_REG_BP;
				X86Instruction->IndexRegister = X86_REG_DI;
				X86Instruction->HasIndexRegister = TRUE;
				X86_SET_SEG(REG_BP);
				break;
			case 4:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [SI]\n"));
				X86Instruction->BaseRegister = X86_REG_SI;
				break;
			case 5:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [DI]\n"));
				X86Instruction->BaseRegister = X86_REG_DI;
				break;
			case 6:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [BP]\n"));
				X86Instruction->BaseRegister = X86_REG_BP;
				break;
			case 7:
				//DISASM_OUTPUT(("[SetModRM16] Addressing mode [BX]\n"));
				X86Instruction->BaseRegister = X86_REG_BX;
				break;
		}

		X86Instruction->HasBaseRegister = TRUE;
		Operand->Flags |= OP_ADDRESS | OP_REG;
		X86_SET_ADDR();
	}

	return Address;
}

// NOTE: Address points one byte after ModRM
INTERNAL U8 *SetModRM32(INSTRUCTION *Instruction, U8 *Address, INSTRUCTION_OPERAND *Operand, U32 OperandIndex, BOOL SuppressErrors)
{
	MODRM modrm;
	REX_MODRM rex_modrm;
	U32 i, ImmediateSize = 0;
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;

	if (X86Instruction->AddressSize == 2)
	{
		return SetModRM16(Instruction, Address, Operand, OperandIndex, SuppressErrors);
	}

	//DISASM_OUTPUT(("[SetModRM32] Length %d, modrm = 0x%02X\n", Instruction->Length, X86Instruction->modrm_b));
	modrm = X86Instruction->modrm;
	rex_modrm = X86Instruction->rex_modrm;

	//
	// Both operands are registers
	// Condition: mod = 3
	//
	if (modrm.mod == 3)
	{
		switch (Operand->Length)
		{
			case 8: Operand->Register = AMD64_64BIT_OFFSET + rex_modrm.rm; break;
			case 4: Operand->Register = X86_32BIT_OFFSET + rex_modrm.rm; CHECK_AMD64_REG(); break;
			case 2: Operand->Register = X86_16BIT_OFFSET + rex_modrm.rm; CHECK_AMD64_REG(); break;
			case 1: Operand->Register = X86_8BIT_OFFSET + rex_modrm.rm; if (X86Instruction->rex_b) CHECK_AMD64_REG(); break;
			default: assert(0); return NULL;
		}
		X86_SET_REG(rex_modrm.rm);
		Operand->Flags |= OP_REG;
	}

	// 
	// Address is an absolute address (technically a 32-bit offset from DS:0)
	// mod = 0 and rm = 5
	//
	else if (modrm.mod == 0 && modrm.rm == 5)
	{
		//DISASM_OUTPUT(("[SetModRM32] Absolute addressing (displacement 0x%08lX)\n", *(S32 *)Address));
		Operand->Flags |= OP_ADDRESS;
		X86Instruction->Displacement = (S64)*((S32 *)Address);
		INSTR_INC(4); // increment Instruction->Length and address

		if (IS_AMD64())
		{
			// RIP-relative addressing always replaced Disp32, even when using a 32-bit address space
			// (via address size override prefix)
			switch (X86Instruction->OperandSize)
			{
				case 8: Operand->Register = AMD64_REG_RIP; break;
				case 4: Operand->Register = X86_REG_EIP; break;
				case 2: Operand->Register = X86_REG_IP; break;
				default: assert(0); return NULL;
			}
			X86Instruction->BaseRegister = Operand->Register;
			X86Instruction->HasBaseRegister = TRUE;
			X86Instruction->Relative = TRUE;
			Operand->Flags |= OP_IPREL | OP_SIGNED | OP_REG;
			SANITY_CHECK_SEGMENT_OVERRIDE();
			if (!X86Instruction->HasSegmentOverridePrefix) X86Instruction->Segment = SEG_CS;
			X86Instruction->HasFullDisplacement = TRUE;

			// Since there may be an immediate value to follow, it is necessary
			// to determine the length in order get the proper offset
			//
			// Maybe there is a better way to do this, since this is wasteful
			// (the size of the immediate value will have to be decoded again later
			// in SetOperands)

			for (ImmediateSize = 0, i = OperandIndex+1; i < Instruction->OperandCount; i++)
			{
				if ((X86Instruction->Opcode.OperandFlags[i] & X86_AMODE_MASK) != AMODE_I) continue;
				else assert(!ImmediateSize);
				switch (X86Instruction->Opcode.OperandFlags[i] & X86_OPTYPE_MASK)
				{
					case OPTYPE_v:
						ImmediateSize = X86Instruction->OperandSize;
						break;
					case OPTYPE_z:
						switch (X86Instruction->OperandSize)
						{
							case 8: case 4: ImmediateSize = 4; break;
							case 2: ImmediateSize = 2; break;
							default: assert(0); return NULL;
						}
						break;
					case OPTYPE_b:
						ImmediateSize = 1;
						break;
					case OPTYPE_w:
						ImmediateSize = 2;
						break;
					case OPTYPE_1:
						break;
					default:
						assert(0);
						break;
				}
			}

			Operand->TargetAddress = ApplyDisplacement((U64)Address + ImmediateSize, Instruction);
		}
		else if (IS_VALID_ADDRESS(X86Instruction->Displacement))
		{
			X86_SET_TARGET();
			Operand->Flags |= OP_GLOBAL;
			X86Instruction->HasFullDisplacement = TRUE;
		}

		X86_SET_ADDR();
	}

	//
	// Addressing mode indicated by SIB byte
	// Condition: mod = 0-2 and rm = 4
	//
	else if (modrm.rm == 4)
	{
		// The X86_SET_*() is called from within SetSIB()
		Address = SetSIB(Instruction, Address, Operand, OperandIndex, SuppressErrors);
		if (!Address)
		{
			assert(0);
			return NULL;
		}

		if (X86Instruction->sib.base != 5) // if base == 5, the displacement is handled in SetSIB
		{
			switch (modrm.mod)
			{
				case 1: // 8-bit displacement
					//DISASM_OUTPUT(("[SetModRM32] After SIB: displacement 0x%02X\n", *((S8 *)Address)));
					X86Instruction->Displacement = (S64)(*((S8 *)Address));
					INSTR_INC(1); // increment Instruction->Length and address
					break;
				case 2: // 32-bit displacement
					//DISASM_OUTPUT(("[SetModRM32] After SIB: displacement 0x%08lX\n", *((S32 *)Address)));
					X86Instruction->Displacement = (S64)*((S32 *)Address);
					if (IS_VALID_ADDRESS(X86Instruction->Displacement))
					{
						Operand->Flags |= OP_GLOBAL;
						X86Instruction->HasFullDisplacement = TRUE;
					}
					INSTR_INC(4); // increment Instruction->Length and address
					break;
			}	
		}
	}

	// Indirect addressing
	// Conditions:
	// (1) mod = 0 and (rm = 0-3 or 6-7)
	// (2) mod = 1-2 and rm != 4
	else
	{
		switch (X86Instruction->AddressSize)
		{
			case 8: Operand->Register = AMD64_64BIT_OFFSET + rex_modrm.rm; break;
			case 4: Operand->Register = X86_32BIT_OFFSET + rex_modrm.rm; CHECK_AMD64_REG(); break;
			default: assert(0); return NULL;
		}
		X86Instruction->BaseRegister = Operand->Register;
		X86Instruction->HasBaseRegister = TRUE;
		Operand->Flags |= OP_ADDRESS | OP_REG;
		X86_SET_SEG(rex_modrm.rm);
		X86_SET_ADDR();

		switch (modrm.mod)
		{
			case 0: // no displacement
				//DISASM_OUTPUT(("[SetModRM32] Indirect addressing (no displacement, reg_rm = %d)\n", rex_modrm.rm));
				break; 
			case 1: // 8-bit signed displacement
				//DISASM_OUTPUT(("[SetModRM32] Indirect addressing (displacement = 0x%02X, reg_rm = %d)\n", *(S8 *)Address, rex_modrm.rm));
				X86Instruction->Displacement = (S64)(*((S8 *)Address));
				INSTR_INC(1); // increment Instruction->Length and address
				break;
			case 2: // 32-bit displacement
				//DISASM_OUTPUT(("[SetModRM32] Indirect addressing (displacement = 0x%08lX, reg_rm = %d)\n", *(S32 *)Address, rex_modrm.rm));
				X86Instruction->Displacement = (S64)*((S32 *)Address);
				if (IS_VALID_ADDRESS(X86Instruction->Displacement))
				{
					Operand->Flags |= OP_GLOBAL;
					X86Instruction->HasFullDisplacement = TRUE;
				}
				INSTR_INC(4); // increment Instruction->Length and address
				break;
		}
	}

	return Address;
}

// NOTE: Address points at SIB
INTERNAL U8 *SetSIB(INSTRUCTION *Instruction, U8 *Address, INSTRUCTION_OPERAND *Operand, U32 OperandIndex, BOOL SuppressErrors)
{
	REX rex;
	SIB sib;
	REX_SIB rex_sib;
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;

	X86Instruction->sib_b = *Address;
	SET_SIB(X86Instruction->sib, *Address);
	sib = X86Instruction->sib;
	rex = X86Instruction->rex;
	SET_REX_SIB(X86Instruction->rex_sib, rex, sib);
	rex_sib = X86Instruction->rex_sib;

	//if (!X86Instruction->rex_b) DISASM_OUTPUT(("[0x%08I64X] SIB = 0x%02X (scale=%d, index=%d, base=%d)\n", VIRTUAL_ADDRESS, *Address, sib.scale, sib.index, sib.base)); \
	//else DISASM_OUTPUT(("[0x%08I64X] SIB = 0x%02X (scale=%d, index=%d, base=%d)\n", VIRTUAL_ADDRESS, *Address, sib.scale, rex_sib.index, rex_sib.base)); \
	//DISASM_OUTPUT(("[SetSIB] Current instruction length = %d\n", Instruction->Length));

	Operand->Flags |= OP_ADDRESS;
	X86_SET_ADDR();
	INSTR_INC(1); // increment Instruction->Length and address

	if (sib.base == 5)
	{
		switch (X86Instruction->modrm.mod)
		{
			case 0:
				X86Instruction->Displacement = (S64)*((S32 *)Address);
				if (IS_VALID_ADDRESS(X86Instruction->Displacement))
				{
					X86Instruction->HasFullDisplacement = TRUE;
					X86_SET_TARGET();
					Operand->Flags |= OP_GLOBAL;
				}
				INSTR_INC(4);
				break;
			case 1:
				X86Instruction->Displacement = (S64)(*((S8 *)Address));
				if (rex_sib.base == 5)
				{
					switch (X86Instruction->AddressSize)
					{
						case 8: Operand->Register = AMD64_REG_RBP; break;
						case 4: Operand->Register = X86_REG_EBP; break;
						default: assert(0); return NULL;
					}
					X86_SET_SEG(REG_EBP);
				}
				else
				{
					Operand->Register = AMD64_REG_R13;
				}

				X86Instruction->BaseRegister = Operand->Register;
				X86Instruction->HasBaseRegister = TRUE;
				Operand->Flags |= OP_REG;
				INSTR_INC(1);
				break;
			case 2:
				X86Instruction->Displacement = (S64)*((S32 *)Address);
				if (rex_sib.base == 5)
				{
					switch (X86Instruction->AddressSize)
					{
						case 8: Operand->Register = AMD64_REG_RBP; break;
						case 4: Operand->Register = X86_REG_EBP; break;
						default: assert(0); return NULL;
					}
					X86_SET_SEG(REG_EBP);
				}
				else
				{
					Operand->Register = AMD64_REG_R13;
				}

				if (IS_VALID_ADDRESS(X86Instruction->Displacement))
				{
					Operand->Flags |= OP_GLOBAL;
					X86Instruction->HasFullDisplacement = TRUE;
				}
				X86Instruction->BaseRegister = Operand->Register;
				X86Instruction->HasBaseRegister = TRUE;
				Operand->Flags |= OP_REG;
				INSTR_INC(4);
				break;
		}
	}
	else
	{
		switch (X86Instruction->AddressSize)
		{
			case 8: Operand->Register = AMD64_64BIT_OFFSET + rex_sib.base; break;
			case 4: Operand->Register = X86_32BIT_OFFSET + rex_sib.base; CHECK_AMD64_REG(); break;
			default: assert(0); return NULL;
		}
		X86Instruction->BaseRegister = Operand->Register;
		X86Instruction->HasBaseRegister = TRUE;
		X86_SET_SEG(rex_sib.base);
		Operand->Flags |= OP_REG;
	}

	if (rex_sib.index != 4)
	{
		switch (X86Instruction->AddressSize)
		{
			case 8:
				X86Instruction->IndexRegister = AMD64_64BIT_OFFSET + rex_sib.index;
				break;
			case 4:
				X86Instruction->IndexRegister = X86_32BIT_OFFSET + rex_sib.index;
				break;
			default:
				fflush(stdout);
				assert(0);
				return NULL;
		}

		Operand->TargetAddress = 0;
		X86Instruction->HasIndexRegister = TRUE;
		//DISASM_OUTPUT(("[SetSIB] Index register = %s\n", X86_Registers[X86_32BIT_OFFSET + rex_sib.index]));

		switch (sib.scale)
		{
			case 0: X86Instruction->Scale = 1; break;
			case 1: X86Instruction->Scale = 2; break;
			case 2: X86Instruction->Scale = 4; break;
			case 3: X86Instruction->Scale = 8; break;
		}
		//DISASM_OUTPUT(("[SetSIB] Scale = %d\n", X86Instruction->Scale));
	}

	return Address;
}

INTERNAL U64 ApplyDisplacement(U64 Address, INSTRUCTION *Instruction)
{
	X86_INSTRUCTION *X86Instruction = &Instruction->X86;

#ifdef SUPPORT_WRAPAROUND
	U64 VirtualAddress = Address + Instruction->VirtualAddressDelta;
	switch (X86Instruction->OperandSize)
	{
		case 8:
		{
			U64 PreAddr = VirtualAddress;
			U64 PostAddr = PreAddr + X86Instruction->Displacement;
			return Address + (PostAddr - PreAddr);
		}
		case 4:
		{
			// We have to do this carefully...
			// If EIP = FFFFF000 and Displacement=2000 then the final IP should be 1000
			// due to wraparound
			U32 PreAddr = (U32)VirtualAddress;
			U32 PostAddr = PreAddr + (S32)X86Instruction->Displacement;
			return Address + (PostAddr - PreAddr);
		}
		case 2:
		{
			// We have to do this carefully...
			// If IP = F000 and Displacement=2000 then the final IP should be 1000
			// due to wraparound
			U16 PreAddr = (U16)VirtualAddress;
			U16 PostAddr = PreAddr + (S16)X86Instruction->Displacement;
			return Address + (PostAddr - PreAddr);
		}
		default:
			assert(0);
			return 0;
	}
#else
	return (Address + X86Instruction->Displacement);
#endif
}



INTERNAL BOOL IsValidLockPrefix(X86_INSTRUCTION *X86Instruction, U8 Opcode, U32 OpcodeLength, U8 Group, U8 OpcodeExtension)
{
	switch (OpcodeLength)
	{
		case 1:
			switch (X86_LockPrefix_1[Opcode])
			{
				case 0: // instruction can't be locked
					return FALSE;
				case 1: // instruction can be locked
					break;
				case GR:
					assert(Group);
					if (!X86_LockPrefix_Groups[Group-1][OpcodeExtension]) return FALSE;
					break;
				default:
					assert(0);
					return FALSE;
			}
			break;

		case 2:
		case 3:
			switch (X86_LockPrefix_2[Opcode])
			{
				case 0: // lock prefix is not acceptable
					return FALSE;
				case 1: // lock prefix allowed
					break;
				case GR:
					assert(Group);
					if (!X86_LockPrefix_Groups[Group-1][OpcodeExtension]) return FALSE;
					break;
				default:
					assert(0);
					return FALSE;
			}
			break;

		default:
			assert(0);
			return FALSE;
	}

	if (!X86Instruction->HasModRM || X86Instruction->modrm.mod == 3 || !X86Instruction->HasDstAddressing)
	{
		DISASM_OUTPUT(("[0x%08I64X] ERROR: Instruction \"%s\" with LOCK prefix has invalid ModRM addressing\n", VIRTUAL_ADDRESS, X86Instruction->Opcode.Mnemonic, X86Instruction->Instruction->Address));
		return FALSE;
	}

	return TRUE;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Apriorit Inc.
United States United States
ApriorIT is a software research and development company specializing in cybersecurity and data management technology engineering. We work for a broad range of clients from Fortune 500 technology leaders to small innovative startups building unique solutions.

As Apriorit offers integrated research&development services for the software projects in such areas as endpoint security, network security, data security, embedded Systems, and virtualization, we have strong kernel and driver development skills, huge system programming expertise, and are reals fans of research projects.

Our specialty is reverse engineering, we apply it for security testing and security-related projects.

A separate department of Apriorit works on large-scale business SaaS solutions, handling tasks from business analysis, data architecture design, and web development to performance optimization and DevOps.

Official site: https://www.apriorit.com
Clutch profile: https://clutch.co/profile/apriorit
This is a Organisation

33 members

Written By
Software Developer (Senior) Apriorit Inc
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions