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

#define printf(...) fprintf(stderr, __VA_ARGS__)

#include "disasm_x86_tables.h"

#ifdef _WIN64
#ifdef _MSC_VER
#pragma warning(disable:4311 4312)
#endif
#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%02lX\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%02lX:[", 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%02lX\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%02lX\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, 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, 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;
}
