text: #define _CRT_SECURE_NO_WARNINGS
  #include <inttypes.h>
  #include <stdio.h>
  #include <string.h>
  #include <sstream>
  #include "binaryninjaapi.h"
  #include "il.h"
  extern "C" {
      #include "xedInc/xed-interface.h"
  }
  #include "arch_x86_common_architecture.h"
  
  using namespace BinaryNinja;
  using namespace std;
  
  enum Elfx86RelocationType : uint32_t
  {
  	R_386_NONE              = 0,  // No relocation.
  	R_386_32                = 1,  // Add symbol value.
  	R_386_PC32              = 2,  // Add PC-relative symbol value.
  	R_386_GOT32             = 3,  // Add PC-relative GOT offset.
  	R_386_PLT32             = 4,  // Add PC-relative PLT offset.
  	R_386_COPY              = 5,  // Copy data from shared object.
  	R_386_GLOB_DAT          = 6,  // Set GOT entry to data address.
  	R_386_JUMP_SLOT         = 7,  // Set GOT entry to code address.
  	R_386_RELATIVE          = 8,  // Add load address of shared object.
  	R_386_GOTOFF            = 9,  // Add GOT-relative symbol address.
  	R_386_GOTPC             = 10, // Add PC-relative GOT table address.
  	R_386_TLS_TPOFF         = 14, // Negative offset in static TLS block
  	R_386_TLS_IE            = 15, // Absolute address of GOT for -ve static TLS
  	R_386_TLS_GOTIE         = 16, // GOT entry for negative static TLS block
  	R_386_TLS_LE            = 17, // Negative offset relative to static TLS
  	R_386_TLS_GD            = 18, // 32 bit offset to GOT (index,off) pair
  	R_386_TLS_LDM           = 19, // 32 bit offset to GOT (index,zero) pair
  	R_386_16                = 20, //
  	R_386_PC16              = 21, //
  	R_386_8                 = 22, //
  	R_386_PC8               = 23, //
  	R_386_TLS_GD_32         = 24, // 32 bit offset to GOT (index,off) pair
  	R_386_TLS_GD_PUSH       = 25, // pushl instruction for Sun ABI GD sequence
  	R_386_TLS_GD_CALL       = 26, // call instruction for Sun ABI GD sequence
  	R_386_TLS_GD_POP        = 27, // popl instruction for Sun ABI GD sequence
  	R_386_TLS_LDM_32        = 28, // 32 bit offset to GOT (index,zero) pair
  	R_386_TLS_LDM_PUSH      = 29, // pushl instruction for Sun ABI LD sequence
  	R_386_TLS_LDM_CALL      = 30, // call instruction for Sun ABI LD sequence
  	R_386_TLS_LDM_POP       = 31, // popl instruction for Sun ABI LD sequence
  	R_386_TLS_LDO_32        = 32, // 32 bit offset from start of TLS block
  	R_386_TLS_IE_32         = 33, // 32 bit offset to GOT static TLS offset entry
  	R_386_TLS_LE_32         = 34, // 32 bit offset within static TLS block
  	R_386_TLS_DTPMOD32      = 35, // GOT entry containing TLS index
  	R_386_TLS_DTPOFF32      = 36, // GOT entry containing TLS offset
  	R_386_TLS_TPOFF32       = 37, // GOT entry of -ve static TLS offset
  	R_386_SIZE32            = 38, //
  	R_386_TLS_GOTDESC       = 39, //
  	R_386_TLS_DESC_CALL     = 40, //
  	R_386_TLS_DESC          = 41, //
  	R_386_IRELATIVE         = 42, // PLT entry resolved indirectly at runtime
  	MAX_ELF_X86_RELOCATION
  };
  
  enum Elfx64RelocationType : uint32_t
  {
  	R_X86_64_NONE            = 0,   // No reloc
  	R_X86_64_64              = 1,   // Direct 64 bit
  	R_X86_64_PC32            = 2,   // PC relative 32 bit signed
  	R_X86_64_GOT32           = 3,   // 32 bit GOT entry
  	R_X86_64_PLT32           = 4,   // 32 bit PLT address
  	R_X86_64_COPY            = 5,   // Copy symbol at runtime
  	R_X86_64_GLOB_DAT        = 6,   // Create GOT entry
  	R_X86_64_JUMP_SLOT       = 7,   // Create PLT entry
  	R_X86_64_RELATIVE        = 8,   // Adjust by program base
  	R_X86_64_GOTPCREL        = 9,   // 32 bit signed pc relative offset to GOT
  	R_X86_64_32              = 10,  // Direct 32 bit zero extended
  	R_X86_64_32S             = 11,  // Direct 32 bit sign extended
  	R_X86_64_16              = 12,  // Direct 16 bit zero extended
  	R_X86_64_PC16            = 13,  // 16 bit sign extended pc relative
  	R_X86_64_8               = 14,  // Direct 8 bit sign extended
  	R_X86_64_PC8             = 15,  // 8 bit sign extended pc relative
  	R_X86_64_DTPMOD64        = 16,
  	R_X86_64_DTPOFF64        = 17,
  	R_X86_64_TPOFF64         = 18,
  	R_X86_64_TLSGD           = 19,
  	R_X86_64_TLSLD           = 20,
  	R_X86_64_DTPOFF32        = 21,
  	R_X86_64_GOTTPOFF        = 22,
  	R_X86_64_TPOFF32         = 23,
  	R_X86_64_PC64            = 24,
  	R_X86_64_GOTOFF64        = 25,
  	R_X86_64_GOTPC32         = 26,
  	R_X86_64_UNKNOWN27       = 27,
  	R_X86_64_UNKNOWN28       = 28,
  	R_X86_64_UNKNOWN29       = 29,
  	R_X86_64_UNKNOWN30       = 30,
  	R_X86_64_UNKNOWN31       = 31,
  	R_X86_64_SIZE32          = 32,
  	R_X86_64_SIZE64          = 33,
  	R_X86_64_GOTPC32_TLSDESC = 34,
  	R_X86_64_TLSDESC_CALL    = 35,
  	R_X86_64_TLSDESC         = 36,
  	R_X86_64_IRELATIVE       = 37,
  	R_X86_64_RELATIVE64      = 38,
  	R_X86_64_PC32_BND        = 39,
  	R_X86_64_PLT32_BND       = 40,
  	R_X86_64_GOTPCRELX       = 41,
  	R_X86_64_REX_GOTPCRELX   = 42,
  	MAX_ELF_X64_RELOCATION
  };
  
  enum Machox86RelocationType : uint32_t
  {
  	GENERIC_RELOC_VANILLA = 0,
  	GENERIC_RELOC_PAIR = 1,
  	GENERIC_RELOC_SECTDIFF = 2,
  	GENERIC_RELOC_PB_LA_PTR = 3,
  	GENERIC_RELOC_LOCAL_SECTDIFF = 4,
  	GENERIC_RELOC_TLV = 5,
  	MACHO_MAX_X86_RELOCATION
  };
  
  enum Machox64RelocationType : uint32_t
  {
  	X86_64_RELOC_UNSIGNED = 0,
  	X86_64_RELOC_SIGNED = 1,
  	X86_64_RELOC_BRANCH = 2,
  	X86_64_RELOC_GOT_LOAD = 3,
  	X86_64_RELOC_GOT = 4,
  	X86_64_RELOC_SUBTRACTOR = 5,
  	X86_64_RELOC_SIGNED_1 = 6,
  	X86_64_RELOC_SIGNED_2 = 7,
  	X86_64_RELOC_SIGNED_4 = 8,
  	X86_64_RELOC_TLV = 9,
  	MACHO_MAX_X86_64_RELOCATION
  };
  
  enum COFFx86RelocationType : uint32_t
  {
  	PE_IMAGE_REL_I386_ABSOLUTE = 0x0000,  // The relocation is ignored.
  	PE_IMAGE_REL_I386_DIR16    = 0x0001,  // Not supported.
  	PE_IMAGE_REL_I386_REL16    = 0x0002,  // Not supported.
  	PE_IMAGE_REL_I386_DIR32    = 0x0006,  // The target's 32-bit VA.
  	PE_IMAGE_REL_I386_DIR32NB  = 0x0007,  // The target's 32-bit RVA.
  	PE_IMAGE_REL_I386_SEG12    = 0x0009,  // Not supported.
  	PE_IMAGE_REL_I386_SECTION  = 0x000A,  // The 16-bit section index of the section that contains the target. This is used to support debugging information.
  	PE_IMAGE_REL_I386_SECREL   = 0x000B,  // The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage.
  	PE_IMAGE_REL_I386_TOKEN    = 0x000C,  // The CLR token.
  	PE_IMAGE_REL_I386_SECREL7  = 0x000D,  // A 7-bit offset from the base of the section that contains the target.
  	PE_IMAGE_REL_I386_REL32    = 0x0014,  // The 32-bit relative displacement to the target. This supports the x86 relative branch and call instructions.
  	MAX_PE_X86_RELOCATION
  };
  
  enum COFFx64RelocationType : uint32_t
  {
  	PE_IMAGE_REL_AMD64_ABSOLUTE = 0x0000, // The relocation is ignored.
  	PE_IMAGE_REL_AMD64_ADDR64   = 0x0001, // The 64-bit VA of the relocation target.
  	PE_IMAGE_REL_AMD64_ADDR32   = 0x0002, // The 32-bit VA of the relocation target.
  	PE_IMAGE_REL_AMD64_ADDR32NB = 0x0003, // The 32-bit address without an image base (RVA).
  	PE_IMAGE_REL_AMD64_REL32    = 0x0004, // The 32-bit relative address from the byte following the relocation.
  	PE_IMAGE_REL_AMD64_REL32_1  = 0x0005, // The 32-bit address relative to byte distance 1 from the relocation.
  	PE_IMAGE_REL_AMD64_REL32_2  = 0x0006, // The 32-bit address relative to byte distance 2 from the relocation.
  	PE_IMAGE_REL_AMD64_REL32_3  = 0x0007, // The 32-bit address relative to byte distance 3 from the relocation.
  	PE_IMAGE_REL_AMD64_REL32_4  = 0x0008, // The 32-bit address relative to byte distance 4 from the relocation.
  	PE_IMAGE_REL_AMD64_REL32_5  = 0x0009, // The 32-bit address relative to byte distance 5 from the relocation.
  	PE_IMAGE_REL_AMD64_SECTION  = 0x000A, // The 16-bit section index of the section that contains the target. This is used to support debugging information.
  	PE_IMAGE_REL_AMD64_SECREL   = 0x000B, // The 32-bit offset of the target from the beginning of its section. This is used to support debugging information and static thread local storage.
  	PE_IMAGE_REL_AMD64_SECREL7  = 0x000C, // A 7-bit unsigned offset from the base of the section that contains the target.
  	PE_IMAGE_REL_AMD64_TOKEN    = 0x000D, // CLR tokens.
  	PE_IMAGE_REL_AMD64_SREL32   = 0x000E, // A 32-bit signed span-dependent value emitted into the object.
  	PE_IMAGE_REL_AMD64_PAIR     = 0x000F, // A pair that must immediately follow every span-dependent value.
  	PE_IMAGE_REL_AMD64_SSPAN32  = 0x0010, // A 32-bit signed span-dependent value that is applied at link time.
  	MAX_PE_X64_RELOCATION
  };
  
  enum PeRelocationType : uint32_t
  {
  	PE_IMAGE_REL_BASED_ABSOLUTE       = 0,  // The base relocation is skipped. This type can be used to pad a block.
  	PE_IMAGE_REL_BASED_HIGH           = 1,  // The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word.
  	PE_IMAGE_REL_BASED_LOW            = 2,  // The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the low half of a 32-bit word.
  	PE_IMAGE_REL_BASED_HIGHLOW        = 3,  // The base relocation applies all 32 bits of the difference to the 32-bit field at offset.
  	PE_IMAGE_REL_BASED_HIGHADJ        = 4,  // The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word. The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation. This means that this base relocation occupies two slots.
  	PE_IMAGE_REL_BASED_MIPS_JMPADDR   = 5,  // The relocation interpretation is dependent on the machine type. When the machine type is MIPS, the base relocation applies to a MIPS jump instruction.
  	PE_IMAGE_REL_BASED_ARM_MOV32      = 5,  // This relocation is meaningful only when the machine type is ARM or Thumb. The base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair.
  	PE_IMAGE_REL_BASED_RISCV_HIGH20   = 5,  // This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the high 20 bits of a 32-bit absolute address.
  	PE_IMAGE_REL_BASE_RESERVED        = 6,  // Reserved, must be zero.
  	PE_IMAGE_REL_BASED_THUMB_MOV32    = 7,  // This relocation is meaningful only when the machine type is Thumb. The base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair.
  	PE_IMAGE_REL_BASED_RISCV_LOW12I   = 7,  // This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format.
  	PE_IMAGE_REL_BASED_RISCV_LOW12S   = 8,  // This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format.
  	PE_IMAGE_REL_BASED_MIPS_JMPADDR16 = 9,  // The relocation is only meaningful when the machine type is MIPS. The base relocation applies to a MIPS16 jump instruction.
  	PE_IMAGE_REL_BASED_DIR64          = 10, // The base relocation applies the difference to the 64-bit field at offset.
  	MAX_PE_RELOCATION
  };
  
  
  static const char* GetRelocationString(PeRelocationType relocType)
  {
  	static const char* relocTable[] =
  	{
  		"PE_IMAGE_REL_BASED_ABSOLUTE",
  		"PE_IMAGE_REL_BASED_HIGH",
  		"PE_IMAGE_REL_BASED_LOW",
  		"PE_IMAGE_REL_BASED_HIGHLOW",
  		"PE_IMAGE_REL_BASED_HIGHADJ",
  		"PE_IMAGE_REL_BASED_MIPS_JMPADDR",
  		"PE_IMAGE_REL_BASED_ARM_MOV32",
  		"PE_IMAGE_REL_BASED_RISCV_HIGH20",
  		"PE_IMAGE_REL_BASE_RESERVED",
  		"PE_IMAGE_REL_BASED_THUMB_MOV32",
  		"PE_IMAGE_REL_BASED_RISCV_LOW12I",
  		"PE_IMAGE_REL_BASED_RISCV_LOW12S",
  		"PE_IMAGE_REL_BASED_MIPS_JMPADDR16",
  		"PE_IMAGE_REL_BASED_DIR64"
  	};
  
  	if (relocType < MAX_PE_RELOCATION)
  		return relocTable[relocType];
  	return "Unknown relocation";
  }
  
  
  static const char* GetRelocationString(Elfx86RelocationType relocType)
  {
  	static const char* relocTable[] =
  	{
  		"R_386_NONE",
  		"R_386_32",
  		"R_386_PC32",
  		"R_386_GOT32",
  		"R_386_PLT32",
  		"R_386_COPY",
  		"R_386_GLOB_DAT",
  		"R_386_JUMP_SLOT",
  		"R_386_RELATIVE",
  		"R_386_GOTOFF",
  		"R_386_GOTPC",
  		"",
  		"",
  		"",
  		"R_386_TLS_TPOFF",
  		"R_386_TLS_IE",
  		"R_386_TLS_GOTIE",
  		"R_386_TLS_LE",
  		"R_386_TLS_GD",
  		"R_386_TLS_LDM",
  		"R_386_16",
  		"R_386_PC16",
  		"R_386_8",
  		"R_386_PC8",
  		"R_386_TLS_GD_32",
  		"R_386_TLS_GD_PUSH",
  		"R_386_TLS_GD_CALL",
  		"R_386_TLS_GD_POP",
  		"R_386_TLS_LDM_32",
  		"R_386_TLS_LDM_PUSH",
  		"R_386_TLS_LDM_CALL",
  		"R_386_TLS_LDM_POP",
  		"R_386_TLS_LDO_32",
  		"R_386_TLS_IE_32",
  		"R_386_TLS_LE_32",
  		"R_386_TLS_DTPMOD32",
  		"R_386_TLS_DTPOFF32",
  		"R_386_TLS_TPOFF32",
  		"R_386_SIZE32",
  		"R_386_TLS_GOTDESC",
  		"R_386_TLS_DESC_CALL",
  		"R_386_TLS_DESC",
  		"R_386_IRELATIVE",
  	};
  	if (relocType < MAX_ELF_X86_RELOCATION)
  		return relocTable[relocType];
  	return "Unknown x86 relocation";
  }
  
  static const char* GetRelocationString(Elfx64RelocationType relocType)
  {
  	static const char* relocTable[] = {
  		"R_X86_64_NONE",
  		"R_X86_64_64",
  		"R_X86_64_PC32",
  		"R_X86_64_GOT32",
  		"R_X86_64_PLT32",
  		"R_X86_64_COPY",
  		"R_X86_64_GLOB_DAT",
  		"R_X86_64_JUMP_SLOT",
  		"R_X86_64_RELATIVE",
  		"R_X86_64_GOTPCREL",
  		"R_X86_64_32",
  		"R_X86_64_32S",
  		"R_X86_64_16",
  		"R_X86_64_PC16",
  		"R_X86_64_8",
  		"R_X86_64_PC8",
  		"R_X86_64_DTPMOD64",
  		"R_X86_64_DTPOFF64",
  		"R_X86_64_TPOFF64",
  		"R_X86_64_TLSGD",
  		"R_X86_64_TLSLD",
  		"R_X86_64_DTPOFF32",
  		"R_X86_64_GOTTPOFF",
  		"R_X86_64_TPOFF32",
  		"R_X86_64_PC64",
  		"R_X86_64_GOTOFF64",
  		"R_X86_64_GOTPC32",
  		"R_X86_64_UNKNOWN27",
  		"R_X86_64_UNKNOWN28",
  		"R_X86_64_UNKNOWN29",
  		"R_X86_64_UNKNOWN30",
  		"R_X86_64_UNKNOWN31",
  		"R_X86_64_SIZE32",
  		"R_X86_64_SIZE64",
  		"R_X86_64_GOTPC32_TLSDESC",
  		"R_X86_64_TLSDESC_CALL",
  		"R_X86_64_TLSDESC",
  		"R_X86_64_IRELATIVE",
  		"R_X86_64_RELATIVE64",
  		"R_X86_64_PC32_BND",
  		"R_X86_64_PLT32_BND",
  		"R_X86_64_GOTPCRELX",
  		"R_X86_64_REX_GOTPCRELX"};
  	if (relocType < MAX_ELF_X64_RELOCATION)
  		return relocTable[relocType];
  	return "Unknown x86_64 relocation";
  }
  
  static const char* GetRelocationString(Machox86RelocationType relocType)
  {
  	static const char* relocTable[] = {
  		"GENERIC_RELOC_VANILLA",
  		"GENERIC_RELOC_PAIR",
  		"GENERIC_RELOC_SECTDIFF",
  		"GENERIC_RELOC_PB_LA_PTR",
  		"GENERIC_RELOC_LOCAL_SECTDIFF",
  		"GENERIC_RELOC_TLV",
  	};
  	if (relocType < MACHO_MAX_X86_RELOCATION)
  		return relocTable[relocType];
  	return "Unknown x86 relocation";
  }
  
  static const char* GetRelocationString(Machox64RelocationType relocType)
  {
  	static const char* relocTable[] = {
  		"X86_64_RELOC_UNSIGNED",
  		"X86_64_RELOC_SIGNED",
  		"X86_64_RELOC_BRANCH",
  		"X86_64_RELOC_GOT_LOAD",
  		"X86_64_RELOC_GOT",
  		"X86_64_RELOC_SUBTRACTOR",
  		"X86_64_RELOC_SIGNED_1",
  		"X86_64_RELOC_SIGNED_2",
  		"X86_64_RELOC_SIGNED_4",
  		"X86_64_RELOC_TLV"
  	};
  	if (relocType < MACHO_MAX_X86_64_RELOCATION)
  		return relocTable[relocType];
  	return "Unknown x64 relocation";
  }
  
  // static const char* GetRelocationString(COFFx86RelocationType relocType)
  // {
  // 	static const char* relocTable[] =
  // 	{
  // 		"PE_IMAGE_REL_I386_ABSOLUTE",
  // 		"PE_IMAGE_REL_I386_DIR16",
  // 		"PE_IMAGE_REL_I386_REL16",
  // 		"PE_IMAGE_REL_I386_DIR32",
  // 		"PE_IMAGE_REL_I386_DIR32NB",
  // 		"PE_IMAGE_REL_I386_SEG12",
  // 		"PE_IMAGE_REL_I386_SECTION",
  // 		"PE_IMAGE_REL_I386_SECREL",
  // 		"PE_IMAGE_REL_I386_TOKEN",
  // 		"PE_IMAGE_REL_I386_SECREL7",
  // 		"PE_IMAGE_REL_I386_REL32"
  // 	};
  // 	if (relocType < MAX_PE_X86_RELOCATION)
  // 		return relocTable[relocType];
  // 	return "Unknown x86 relocation";
  // }
  
  // static const char* GetRelocationString(COFFx64RelocationType relocType)
  // {
  // 	static const char* relocTable[] =
  // 	{
  // 		"PE_IMAGE_REL_AMD64_ABSOLUTE",
  // 		"PE_IMAGE_REL_AMD64_ADDR64",
  // 		"PE_IMAGE_REL_AMD64_ADDR32",
  // 		"PE_IMAGE_REL_AMD64_ADDR32NB",
  // 		"PE_IMAGE_REL_AMD64_REL32",
  // 		"PE_IMAGE_REL_AMD64_REL32_1",
  // 		"PE_IMAGE_REL_AMD64_REL32_2",
  // 		"PE_IMAGE_REL_AMD64_REL32_3",
  // 		"PE_IMAGE_REL_AMD64_REL32_4",
  // 		"PE_IMAGE_REL_AMD64_REL32_5",
  // 		"PE_IMAGE_REL_AMD64_SECTION",
  // 		"PE_IMAGE_REL_AMD64_SECREL",
  // 		"PE_IMAGE_REL_AMD64_SECREL7",
  // 		"PE_IMAGE_REL_AMD64_TOKEN",
  // 		"PE_IMAGE_REL_AMD64_SREL32",
  // 		"PE_IMAGE_REL_AMD64_PAIR",
  // 		"PE_IMAGE_REL_AMD64_SSPAN32"
  // 	};
  // 	if (relocType < MAX_PE_X64_RELOCATION)
  // 		return relocTable[relocType];
  // 	return "Unknown x86_64 relocation";
  // }
  
  bool X86CommonArchitecture::Decode(const uint8_t* data, size_t len, xed_decoded_inst_t* xedd)
  {
  	// Zero out structure data, and keep the current destructuring mode (32/64/etc)
  	xed_decoded_inst_zero_keep_mode(xedd);
  
  	xed3_operand_set_cet(xedd, 1);
  	xed3_operand_set_mpxmode(xedd, 1);
  
  	// Decode the data and check for errors
  	xed_error_enum_t xed_error = xed_decode(xedd, data, (unsigned)len);
  	switch(xed_error)
  	{
  	case XED_ERROR_NONE:
  		return true;
  	default:
  		return false;
  	}
  }
  
  size_t X86CommonArchitecture::GetAddressSizeBits()  const
  {
  	return GetAddressSize() * 8;
  }
  
  uint64_t X86CommonArchitecture::GetAddressMask() const
  {
  	if (GetAddressSizeBits() == 64)
  		return (uint64_t)-1;
  	return (((uint64_t)1) << GetAddressSizeBits()) - 1;
  }
  
  void X86CommonArchitecture::SetInstructionInfoForInstruction(uint64_t addr, InstructionInfo& result, xed_decoded_inst_t* xedd)
  {
  	result.length = xed_decoded_inst_get_length(xedd);
  
  	const uint64_t               abs_br = xed_decoded_inst_get_branch_displacement(xedd) + addr + xed_decoded_inst_get_length(xedd);
  	const xed_iform_enum_t   xedd_iForm = xed_decoded_inst_get_iform_enum(xedd);
  	const xed_iclass_enum_t xedd_iClass = xed_decoded_inst_get_iclass(xedd);
  
  	// 1. First parse 'generally', by instruction category, then
  	// 2. break down to special cases and impliment iclass and possibly iform-specific cases
  
  	switch (xed_decoded_inst_get_category(xedd))
  	{
  	case XED_CATEGORY_CALL:
  	// CALL instruction with an immediate as the first operand and it's not the next instruction
  		if ((abs_br != addr+result.length) && ((xedd_iForm == XED_IFORM_CALL_NEAR_RELBRz) || (xedd_iForm == XED_IFORM_CALL_NEAR_RELBRd)))
  			result.AddBranch(CallDestination, abs_br);
  		break;
  
  	case XED_CATEGORY_UNCOND_BR:
  		if (xed_operand_name(xed_inst_operand(xed_decoded_inst_inst(xedd), 0)) == XED_OPERAND_RELBR)
  			result.AddBranch(UnconditionalBranch, abs_br);
  		else
  			result.AddBranch(UnresolvedBranch);
  		break;
  
  	case XED_CATEGORY_COND_BR:
  		result.AddBranch(TrueBranch, abs_br);
  		result.AddBranch(FalseBranch, addr + result.length);
  		break;
  
  	case XED_CATEGORY_INTERRUPT:
  		if (xed_decoded_inst_get_unsigned_immediate(xedd) == 0x80)
  			result.AddBranch(SystemCall);
  		else if (xedd_iClass == XED_ICLASS_INT3)
  			result.AddBranch(ExceptionBranch);
  		break;
  
  	case XED_CATEGORY_SYSCALL:
  		result.AddBranch(SystemCall);
  		break;
  
  	case XED_CATEGORY_SYSRET:
  		result.AddBranch(FunctionReturn);
  		break;
  
  	case XED_CATEGORY_RET:
  		result.AddBranch(FunctionReturn);
  		break;
  
  	default:
  		switch (xedd_iClass)
  		{
  		// case XED_ICLASS_UD0:
  		// case XED_ICLASS_UD1:
  		case XED_ICLASS_UD2:
  		case XED_ICLASS_HLT:
  			result.AddBranch(ExceptionBranch);
  			break;
  
  		default:
  			break;
  		}
  		break;
  	}
  }
  
  bool X86CommonArchitecture::IsConditionalJump(xed_decoded_inst_t* xedd)
  {
  	return (xed_decoded_inst_get_category(xedd) == XED_CATEGORY_COND_BR);
  }
  
  string X86CommonArchitecture::GetSizeString(const size_t size) const
  {
  	switch (size)
  	{
  	case 1:
  		return "byte ";
  	case 2:
  		return "word ";
  	case 4:
  		return "dword ";
  	case 8:
  		return "qword ";
  	case 10:
  		return "tword ";
  	case 16:
  		return "oword ";
  	default:
  		return "";
  	}
  }
  
  BNRegisterInfo X86CommonArchitecture::RegisterInfo(xed_reg_enum_t fullWidthReg, size_t offset, size_t size, bool zeroExtend)
  {
  	BNRegisterInfo result;
  	result.fullWidthRegister = fullWidthReg;
  	result.offset = offset;
  	result.size = size;
  	result.extend = zeroExtend ? ZeroExtendToFullWidth : NoExtend;
  	return result;
  }
  
  void X86CommonArchitecture::GetAddressSizeToken(const short bytes, vector<InstructionTextToken>& result, const bool lowerCase)
  {
  	// Size
  	switch (bytes)
  	{
  	case 1:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"byte ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"BYTE ");
  		break;
  	case 2:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"word ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"WORD ");
  		break;
  	case 4:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"dword ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"DWORD ");
  		break;
  	case 8:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"qword ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"QWORD ");
  		break;
  	case 10:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"tword ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"TWORD ");
  		break;
  	case 16:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"xmmword ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"XMMWORD ");
  		break;
  	case 32:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"ymmword ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"YMMWORD ");
  		break;
  	case 64:
  		if (lowerCase)
  			result.emplace_back(BeginMemoryOperandToken,"zmmword ");
  		else
  			result.emplace_back(BeginMemoryOperandToken,"ZMMWORD ");
  		break;
  	default:
  		break;
  	}
  }
  
  unsigned short X86CommonArchitecture::GetInstructionOpcode(const xed_decoded_inst_t* const xedd, const xed_operand_values_t* const ov, vector<InstructionTextToken>& result) const
  {
  	string opcode = "";
  	if (xed_decoded_inst_has_mpx_prefix(xedd))
  		opcode += "BND ";
  	if (xed_decoded_inst_is_xacquire(xedd))
  		opcode += "XACQUIRE ";
  	if (xed_decoded_inst_is_xrelease(xedd))
  		opcode += "XRELEASE ";
  	if (xed_operand_values_has_lock_prefix(ov))
  		opcode += "LOCK ";
  	if (xed_operand_values_has_real_rep(ov))
  	{
  		if (xed_operand_values_has_rep_prefix(ov))
  			opcode += "REP ";
  		if (xed_operand_values_has_repne_prefix(ov))
  			opcode += "REPNE ";
  	}
  	else if (xed_operand_values_branch_not_taken_hint(ov))
  		opcode += "HINT-NOT-TAKEN ";
  	else if (xed_operand_values_branch_taken_hint(ov))
  		opcode += "HINT-TAKEN ";
  
  	switch (m_disassembly_options.df)
  	{
  	case DF_INTEL:
  		opcode += string(xed_iform_to_iclass_string_intel(xed_decoded_inst_get_iform_enum(xedd)));
  		break;
  	case DF_BN_INTEL:
  		// To match asmx86 disassembly
  		switch (xed_decoded_inst_get_iclass(xedd))
  		{
  		case XED_ICLASS_RET_NEAR:
  			opcode += "RETN";
  			break;
  		case XED_ICLASS_JZ:
  			opcode += "JE";
  			break;
  		case XED_ICLASS_JNZ:
  			opcode += "JNE";
  			break;
  		case XED_ICLASS_JNB:
  			opcode += "JAE";
  			break;
  		case XED_ICLASS_JNBE:
  			opcode += "JA";
  			break;
  		case XED_ICLASS_JP:
  			opcode += "JPE";
  			break;
  		case XED_ICLASS_JNP:
  			opcode += "JPO";
  			break;
  		case XED_ICLASS_JNL:
  			opcode += "JGE";
  			break;
  		case XED_ICLASS_JNLE:
  			opcode += "JG";
  			break;
  
  		case XED_ICLASS_SETNB:
  			opcode += "SETAE";
  			break;
  		case XED_ICLASS_SETZ:
  			opcode += "SETE";
  			break;
  		case XED_ICLASS_SETNZ:
  			opcode += "SETNE";
  			break;
  		case XED_ICLASS_SETNBE:
  			opcode += "SETA";
  			break;
  		case XED_ICLASS_SETP:
  			opcode += "SETPE";
  			break;
  		case XED_ICLASS_SETNP:
  			opcode += "SETPO";
  			break;
  		case XED_ICLASS_SETNL:
  			opcode += "SETGE";
  			break;
  		case XED_ICLASS_SETNLE:
  			opcode += "SETG";
  			break;
  
  		case XED_ICLASS_CMOVNB:
  			opcode += "CMOVAE";
  			break;
  		case XED_ICLASS_CMOVZ:
  			opcode += "CMOVE";
  			break;
  		case XED_ICLASS_CMOVNZ:
  			opcode += "CMOVNE";
  			break;
  		case XED_ICLASS_CMOVNBE:
  			opcode += "CMOVA";
  			break;
  		case XED_ICLASS_CMOVP:
  			opcode += "CMOVPE";
  			break;
  		case XED_ICLASS_CMOVNP:
  			opcode += "CMOVPO";
  			break;
  		case XED_ICLASS_CMOVNL:
  			opcode += "CMOVGE";
  			break;
  		case XED_ICLASS_CMOVNLE:
  			opcode += "CMOVG";
  			break;
  
  		default:
  			opcode += string(xed_iform_to_iclass_string_intel(xed_decoded_inst_get_iform_enum(xedd)));
  		}
  		break;
  	case DF_ATT:
  		opcode += string(xed_iform_to_iclass_string_att(xed_decoded_inst_get_iform_enum(xedd)));
  		break;
  	case DF_XED:
  		opcode += string(xed_iclass_enum_t2str(xed_decoded_inst_get_iclass(xedd)));
  		break;
  	default:
  		LogError("Invalid Disassembly Flavor");
  	}
  
  	if (m_disassembly_options.lowerCase)
  		for (char& c : opcode)
  			c = tolower(c);
  	else
  		for (char& c : opcode)
  			c = toupper(c);
  
  	result.emplace_back(InstructionToken, opcode);
  
  	return (unsigned short)opcode.length();
  }
  
  void X86CommonArchitecture::GetInstructionPadding(const unsigned int instruction_name_length, vector<InstructionTextToken>& result) const
  {
  	string padding = "";
  	const short min = 7 < instruction_name_length ? 7 : instruction_name_length;
  	for (unsigned short delim = 0; delim < (8 - min); ++delim)
  		padding += ' ';
  	result.emplace_back(TextToken, padding);
  }
  
  // (in theory) Exactly how XED wants the world to see x86
  void X86CommonArchitecture::GetOperandTextIntel(const xed_decoded_inst_t* const xedd, const uint64_t addr, const size_t len, const xed_operand_values_t* const ov, const xed_inst_t* const xi, vector<InstructionTextToken>& result) const
  {
  	xed_reg_enum_t extra_index_operand = XED_REG_INVALID;
  
  	// Get operands
  	for (unsigned int opIndex = 0; opIndex < xed_inst_noperands(xi); ++opIndex)
  	{
  		const xed_operand_t*          op = xed_inst_operand(xi, opIndex);
  		const xed_operand_enum_t op_name = xed_operand_name(op);
  
  		// XED's suppressed operands shouln't be represented in Intel syntax
  		if (xed_operand_operand_visibility(op) == XED_OPVIS_SUPPRESSED)
  		{
  			if ((xed_decoded_inst_get_category(xedd) == XED_CATEGORY_STRINGOP) &&
  				(op_name == XED_OPERAND_MEM0 || op_name == XED_OPERAND_MEM1))
  			{
  				if (op_name == XED_OPERAND_MEM1)
  					result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  			}
  			else
  				continue;
  		}
  
  		switch(op_name)
  		{
  		case XED_OPERAND_REG0:
  		case XED_OPERAND_REG1:
  		case XED_OPERAND_REG2:
  		case XED_OPERAND_REG3:
  		case XED_OPERAND_REG4:
  		case XED_OPERAND_REG5:
  		case XED_OPERAND_REG6:
  		case XED_OPERAND_REG7:
  		case XED_OPERAND_REG8:
  		{
  			string reg = "";
  
  			const xed_reg_enum_t xedReg = xed_decoded_inst_get_reg(xedd, op_name);
  			if ((xedReg >= XED_REG_X87_FIRST) && (xedReg <=  XED_REG_X87_LAST))
  			{
  				reg += "ST";
  				reg += ('0' + (xedReg-XED_REG_X87_FIRST));
  			}
  			else
  			{
  				reg += xed_reg_enum_t2str(xedReg);
  			}
  			if (m_disassembly_options.lowerCase)
  				for (char& c : reg)
  					c = tolower(c);
  			result.emplace_back(RegisterToken, reg);
  			break;
  		}
  		case XED_OPERAND_AGEN:
  		case XED_OPERAND_MEM0:
  		{
  			GetAddressSizeToken(xed_decoded_inst_operand_length_bits(xedd, opIndex) / 8, result, m_disassembly_options.lowerCase);
  
  			if (m_disassembly_options.lowerCase)
  				result.emplace_back(TextToken, "ptr ");
  			else
  				result.emplace_back(TextToken, "PTR ");
  			result.emplace_back(TextToken, "[");
  
  			// Segment
  			const xed_reg_enum_t seg = xed_decoded_inst_get_seg_reg(xedd, 0);
  			const bool validSegment = (seg != XED_REG_INVALID && !xed_operand_values_using_default_segment(ov, 0));
  			if (validSegment)
  			{
  				string seg_str(xed_reg_enum_t2str(seg));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : seg_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, seg_str);
  				result.emplace_back(TextToken, ":");
  			}
  
  			bool started = false;
  			const xed_reg_enum_t base = xed_decoded_inst_get_base_reg(xedd, 0);
  
  			int64_t disp = xed_decoded_inst_get_memory_displacement(xedd, 0);
  
  			if ((base != XED_REG_INVALID) && !((base == XED_REG_RIP) || (base == XED_REG_EIP) || (base == XED_REG_IP)))
  			{
  				string base_str(xed_reg_enum_t2str(base));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : base_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, base_str);
  				started = true;
  			}
  			else if ((base == XED_REG_RIP) || (base == XED_REG_EIP) || (base == XED_REG_IP))
  			{
  				if (xed_operand_values_has_memory_displacement(ov))
  					disp += addr + len;
  				else
  					disp = addr + len;
  
  				stringstream sstream;
  				if (m_disassembly_options.lowerCase)
  					sstream << "0x" << hex << nouppercase << disp;
  				else
  					sstream << "0x" << hex << uppercase << disp;
  
  				result.emplace_back(CodeRelativeAddressToken, sstream.str(), disp, GetAddressSize());
  
  				result.emplace_back(EndMemoryOperandToken, "]");
  				break;
  			}
  
  			const xed_reg_enum_t index = xed_decoded_inst_get_index_reg(xedd, 0);
  			if (index != XED_REG_INVALID)
  			{
  				if (xed_decoded_inst_get_attribute(xedd, XED_ATTRIBUTE_INDEX_REG_IS_POINTER))
  				{
  					// MPX BNDLDX/BNDSTX instr are unusual in that they use
  					// the index reg as distinct operand.
  					extra_index_operand = index;
  				}
  				else  // normal path
  				{
  					if (started)
  						result.emplace_back(TextToken, "+");
  					started = true;
  
  					string index_str(xed_reg_enum_t2str(index));
  					if (m_disassembly_options.lowerCase)
  						for (char& c : index_str)
  							c = tolower(c);
  
  					result.emplace_back(RegisterToken, index_str);
  
  					const unsigned int scale = xed_decoded_inst_get_scale(xedd, 0);
  					if (scale != 1)
  					{
  						result.emplace_back(TextToken, "*");
  						stringstream sstream;
  						sstream << scale;
  						result.emplace_back(IntegerToken, sstream.str(), scale, 1);
  					}
  				}
  			}
  
  			const unsigned short disp_bytes = xed_decoded_inst_get_memory_displacement_width_bits(xedd, 0) / 8;
  
  			if (xed_operand_values_has_memory_displacement(ov) &&
  				((disp != 0) || (!started && validSegment)))
  			{
  				stringstream sstream;
  				sstream << "0x" << hex;
  				if (m_disassembly_options.lowerCase)
  					sstream << nouppercase;
  				else
  					sstream << uppercase;
  
  				if (started)
  				{
  					if (disp < 0)
  					{
  						result.emplace_back(TextToken, "-");
  						disp = -disp;
  					}
  					else
  						result.emplace_back(TextToken, "+");
  
  					if (disp_bytes == 2)
  						sstream << (uint16_t)disp;
  					else if (disp_bytes == 4)
  						sstream << (uint32_t)disp;
  					else if (disp_bytes == 8)
  						sstream << (uint64_t)disp;
  					else
  						sstream << disp;
  					result.emplace_back(IntegerToken, sstream.str(), disp, disp_bytes);
  				}
  				else
  				{
  					sstream << disp;
  
  					if (validSegment)
  						result.emplace_back(IntegerToken, sstream.str(), disp, GetAddressSize());
  					else
  						result.emplace_back(PossibleAddressToken, sstream.str(), disp, GetAddressSize());
  				}
  			}
  			else if (xed_operand_values_has_memory_displacement(ov) &&
  				((disp == 0) && (!started)))
  			{
  				result.emplace_back(IntegerToken, "0x0", disp, GetAddressSize());
  			}
  			result.emplace_back(EndMemoryOperandToken, "]");
  
  			break;
  		}
  		case XED_OPERAND_MEM1:
  		{
  			GetAddressSizeToken(xed_decoded_inst_operand_length_bits(xedd, opIndex) / 8, result, m_disassembly_options.lowerCase);
  
  			if (m_disassembly_options.lowerCase)
  				result.emplace_back(TextToken, "ptr ");
  			else
  				result.emplace_back(TextToken, "PTR ");
  
  			// Segment
  			const xed_reg_enum_t seg = xed_decoded_inst_get_seg_reg(xedd, 1);
  			if (seg != XED_REG_INVALID && !xed_operand_values_using_default_segment(ov, 1))
  			{
  				string seg_str(xed_reg_enum_t2str(seg));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : seg_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, seg_str);
  
  				result.emplace_back(TextToken, ":");
  			}
  
  			result.emplace_back(TextToken, "[");
  			const xed_reg_enum_t base = xed_decoded_inst_get_base_reg(xedd, 1);
  			if (base != XED_REG_INVALID)
  			{
  				string base_str(xed_reg_enum_t2str(base));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : base_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, base_str);
  			}
  			result.emplace_back(EndMemoryOperandToken, "]");
  
  			break;
  		}
  		case XED_OPERAND_IMM0:
  		{
  			const size_t            addrSize = xed_decoded_inst_get_machine_mode_bits(xedd) / 8;
  			const unsigned int immediateSize = xed_decoded_inst_get_operand_width(xedd) / 8;
  
  			stringstream sstream;
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			if (xed_decoded_inst_get_immediate_is_signed(xedd) && (immediateSize != 1))
  			{
  				const int64_t immediateValue = xed_decoded_inst_get_signed_immediate(xedd);
  
  				if (immediateValue >= 0)
  					sstream << immediateValue;  // Don't zero extend
  				else
  				{
  					// I'm not proud of this switch statement... It was the product of a lot of pain.
  					switch (immediateSize)  // For sign extentions
  					{
  					case 1:
  						sstream << (int8_t)immediateValue;
  						break;
  					case 2:
  						sstream << (int16_t)immediateValue;
  						break;
  					case 4:
  						sstream << (int32_t)immediateValue;
  						break;
  					default:
  						sstream << (int64_t)immediateValue;
  					}
  				}
  
  				if (immediateSize == addrSize)
  					result.emplace_back(PossibleAddressToken, sstream.str(), immediateValue, immediateSize);
  				else
  					result.emplace_back(IntegerToken, sstream.str(), immediateValue, immediateSize);
  			}
  			else
  			{
  				const uint64_t immediateValue = xed_decoded_inst_get_unsigned_immediate(xedd);
  				sstream << immediateValue;
  				if (immediateSize == addrSize)
  					result.emplace_back(PossibleAddressToken, sstream.str(), immediateValue, immediateSize);
  				else
  					result.emplace_back(IntegerToken, sstream.str(), immediateValue, immediateSize);
  			}
  			break;
  		}
  		case XED_OPERAND_IMM1:  // The ENTER instruction
  		{
  			stringstream sstream;
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << (uint16_t)xed_decoded_inst_get_second_immediate(xedd);
  			result.emplace_back(IntegerToken, sstream.str(), xed_decoded_inst_get_second_immediate(xedd), 1);
  			break;
  		}
  		case XED_OPERAND_PTR:  // TODO...remove?
  		{
  			stringstream sstream;
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << xed_decoded_inst_get_branch_displacement(xedd);
  			result.emplace_back(PossibleAddressToken, sstream.str(), xed_decoded_inst_get_branch_displacement(xedd), 8);
  			break;
  		}
  		case XED_OPERAND_RELBR:
  		{
  			const int64_t relbr = xed_decoded_inst_get_branch_displacement(xedd) + addr + xed_decoded_inst_get_length(xedd);
  
  			stringstream sstream;
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << relbr;
  			result.emplace_back(CodeRelativeAddressToken, sstream.str(), relbr, 8);
  			break;
  		}
  		default:
  		{
  			result.emplace_back(TextToken, "unimplimented");
  		}  // default case of outer switch
  		}  // outer switch
  
  		// If there is another operand and it is visable, print delimiter
  		if ((opIndex != xed_inst_noperands(xi)-1) &&
  			((xed_operand_operand_visibility(xed_inst_operand(xi, opIndex+1)) == XED_OPVIS_EXPLICIT) ||
  			(xed_operand_operand_visibility(xed_inst_operand(xi, opIndex+1)) == XED_OPVIS_IMPLICIT)))
  				result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  	}
  
  	if (extra_index_operand != XED_REG_INVALID)
  	{
  		result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  		string reg = xed_reg_enum_t2str(extra_index_operand);
  		if (m_disassembly_options.lowerCase)
  			for (char& c : reg)
  				c = tolower(c);
  		result.emplace_back(RegisterToken, reg);
  	}
  }
  
  // The syntax used by asmx86, that users are used to (also the only one that should be garenteed to roundtrip with asm)
  void X86CommonArchitecture::GetOperandTextBNIntel(const xed_decoded_inst_t* const xedd, const uint64_t addr, const size_t len, const xed_operand_values_t* const ov, const xed_inst_t* const xi, vector<InstructionTextToken>& result) const
  {
  	xed_reg_enum_t extra_index_operand = XED_REG_INVALID;
  
  	// Get operands
  	for (unsigned int opIndex = 0; opIndex < xed_inst_noperands(xi); ++opIndex)
  	{
  		const xed_operand_t*          op = xed_inst_operand(xi, opIndex);
  		const xed_operand_enum_t op_name = xed_operand_name(op);
  
  		// XED's suppressed operands shouln't be represented in Intel syntax
  		if (xed_operand_operand_visibility(op) == XED_OPVIS_SUPPRESSED)
  		{
  			if ((xed_decoded_inst_get_category(xedd) == XED_CATEGORY_STRINGOP) &&
  				(op_name == XED_OPERAND_MEM0 || op_name == XED_OPERAND_MEM1))
  			{
  				if (op_name == XED_OPERAND_MEM1)
  					result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  			}
  			else
  				continue;
  		}
  
  		if ((xed_operand_operand_visibility(op) == XED_OPVIS_IMPLICIT) && (xed_decoded_inst_get_reg(xedd, op_name) == XED_REG_ST0))
  			continue;
  
  		switch(op_name)
  		{
  		case XED_OPERAND_REG0:
  		case XED_OPERAND_REG1:
  		case XED_OPERAND_REG2:
  		case XED_OPERAND_REG3:
  		case XED_OPERAND_REG4:
  		case XED_OPERAND_REG5:
  		case XED_OPERAND_REG6:
  		case XED_OPERAND_REG7:
  		case XED_OPERAND_REG8:
  		{
  			string reg = "";
  
  			const xed_reg_enum_t xedReg = xed_decoded_inst_get_reg(xedd, op_name);
  			if ((xedReg >= XED_REG_X87_FIRST) && (xedReg <=  XED_REG_X87_LAST))
  			{
  				reg += "ST";
  				reg += ('0' + (xedReg - XED_REG_X87_FIRST));
  			}
  			// As of July 2020, the XED now outputs these registers as mm0, etc.
  			// However, to maintain backward-compatibility, we need to make them mmx0, etc,
  			// as they previously are
  			else if ((xedReg >= XED_REG_MMX0) && (xedReg <=  XED_REG_MMX1))
  			{
  				reg += "MMX";
  				reg += ('0' + (xedReg - XED_REG_MMX0));
  			}
  			else
  			{
  				reg += xed_reg_enum_t2str(xedReg);
  			}
  
  			if (m_disassembly_options.lowerCase)
  				for (char& c : reg)
  					c = tolower(c);
  
  			result.emplace_back(RegisterToken, reg);
  
  			// handle the {z} modifier
  			if (XED_REG_K1 <= xedReg && xedReg <= XED_REG_K7)
  			{
  				if(xed_decoded_inst_zeroing(xedd))
  				{
  					if (m_disassembly_options.lowerCase)
  						result.emplace_back(TextToken, " {z}");
  					else
  						result.emplace_back(TextToken, " {Z}");
  				}
  			}
  
  			break;
  		}
  		case XED_OPERAND_AGEN:
  		case XED_OPERAND_MEM0:
  		{
  			// Size
  			if (xed_inst_iclass(xi) != XED_ICLASS_LEA)
  				GetAddressSizeToken(xed_decoded_inst_operand_length_bits(xedd, opIndex) / 8, result, m_disassembly_options.lowerCase);
  
  			result.emplace_back(TextToken, "[");
  			// Segment
  			const xed_reg_enum_t seg = xed_decoded_inst_get_seg_reg(xedd, 0);
  			const bool validSegment = (seg != XED_REG_INVALID && !xed_operand_values_using_default_segment(ov, 0));
  			if (validSegment)
  			{
  				string seg_str(xed_reg_enum_t2str(seg));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : seg_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, seg_str);
  				result.emplace_back(TextToken, ":");
  			}
  
  			bool started = false;
  			xed_reg_enum_t base = xed_decoded_inst_get_base_reg(xedd, 0);
  
  			int64_t disp = xed_decoded_inst_get_memory_displacement(xedd, 0);
  
  			if ((base != XED_REG_INVALID) && !((base == XED_REG_RIP) || (base == XED_REG_EIP) || (base == XED_REG_IP)))
  			{
  				string base_str(xed_reg_enum_t2str(base));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : base_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, base_str);
  				started = true;
  			}
  			else if ((base == XED_REG_RIP) || (base == XED_REG_EIP) || (base == XED_REG_IP))
  			{
  				if (m_disassembly_options.lowerCase)
  					result.emplace_back(TextToken, "rel ");
  				else
  					result.emplace_back(TextToken, "REL ");
  
  				if (xed_operand_values_has_memory_displacement(ov))
  					disp += addr + len;
  				else
  					disp = addr + len;
  
  				stringstream sstream;
  				if (m_disassembly_options.lowerCase)
  					sstream << "0x" << hex << nouppercase << disp;
  				else
  					sstream << "0x" << hex << uppercase << disp;
  
  				result.emplace_back(CodeRelativeAddressToken, sstream.str(), disp, GetAddressSize());
  
  				result.emplace_back(EndMemoryOperandToken, "]");
  				break;
  			}
  
  			const xed_reg_enum_t index = xed_decoded_inst_get_index_reg(xedd, 0);
  			if (index != XED_REG_INVALID)
  			{
  				if (xed_decoded_inst_get_attribute(xedd, XED_ATTRIBUTE_INDEX_REG_IS_POINTER))
  				{
  					// MPX BNDLDX/BNDSTX instr are unusual in that they use
  					// the index reg as distinct operand.
  					extra_index_operand = index;
  				}
  				else  // normal path
  				{
  					if (started)
  						result.emplace_back(TextToken, "+");
  					started = true;
  
  					string index_str(xed_reg_enum_t2str(index));
  					if (m_disassembly_options.lowerCase)
  						for (char& c : index_str)
  							c = tolower(c);
  
  					result.emplace_back(RegisterToken, index_str);
  
  					const unsigned int scale = xed_decoded_inst_get_scale(xedd, 0);
  					if (scale != 1)
  					{
  						result.emplace_back(TextToken, "*");
  						stringstream sstream;
  						sstream << scale;
  						result.emplace_back(IntegerToken, sstream.str(), scale, 1);
  					}
  				}
  			}
  
  			const unsigned short disp_bytes = xed_decoded_inst_get_memory_displacement_width_bits(xedd, 0) / 8;
  
  			if (xed_operand_values_has_memory_displacement(ov) &&
  				((disp != 0) || (!started && validSegment)))
  			{
  				stringstream sstream;
  				sstream << "0x" << hex;
  				if (m_disassembly_options.lowerCase)
  					sstream << nouppercase;
  				else
  					sstream << uppercase;
  
  				if (started)
  				{
  					if (disp < 0)
  					{
  						result.emplace_back(TextToken, "-");
  						disp = -disp;
  					}
  					else
  						result.emplace_back(TextToken, "+");
  
  					if (disp_bytes == 2)
  						sstream << (uint16_t)disp;
  					else if (disp_bytes == 4)
  						sstream << (uint32_t)disp;
  					else if (disp_bytes == 8)
  						sstream << (uint64_t)disp;
  					else
  						sstream << disp;
  					result.emplace_back(IntegerToken, sstream.str(), disp, disp_bytes);
  				}
  				else
  				{
  					sstream << disp;
  
  					if (validSegment)
  						result.emplace_back(IntegerToken, sstream.str(), disp, GetAddressSize());
  					else
  						result.emplace_back(PossibleAddressToken, sstream.str(), disp, GetAddressSize());
  				}
  			}
  			else if (xed_operand_values_has_memory_displacement(ov) &&
  				((disp == 0) && (!started)))
  			{
  				result.emplace_back(IntegerToken, "0x0", disp, GetAddressSize());
  			}
  
  			result.emplace_back(EndMemoryOperandToken, "]");
  
  			break;
  		}
  		case XED_OPERAND_MEM1:
  		{
  
  
  			// Segment
  			const xed_reg_enum_t seg = xed_decoded_inst_get_seg_reg(xedd, 1);
  			if (seg != XED_REG_INVALID && !xed_operand_values_using_default_segment(ov, 1))
  			{
  				string seg_str(xed_reg_enum_t2str(seg));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : seg_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, seg_str);
  
  				result.emplace_back(TextToken, ":");
  			}
  
  			result.emplace_back(TextToken, "[");
  			const xed_reg_enum_t base = xed_decoded_inst_get_base_reg(xedd, 1);
  			if (base != XED_REG_INVALID)
  			{
  				string base_str(xed_reg_enum_t2str(base));
  				if (m_disassembly_options.lowerCase)
  					for (char& c : base_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, base_str);
  			}
  			result.emplace_back(EndMemoryOperandToken, "]");
  
  			break;
  		}
  		case XED_OPERAND_IMM0:
  		{
  			const size_t            addrSize = xed_decoded_inst_get_machine_mode_bits(xedd) / 8;
  			const unsigned int immediateSize = xed_decoded_inst_get_operand_width(xedd) / 8;
  
  			stringstream sstream;
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			if (xed_decoded_inst_get_immediate_is_signed(xedd)  && (immediateSize != 1))
  			{
  				const int64_t immediateValue = xed_decoded_inst_get_signed_immediate(xedd);
  
  				if (immediateValue >= 0)
  					sstream << immediateValue;  // Don't zero extend
  				else
  				{
  					// I'm not proud of this switch statement... It was the product of a lot of pain.
  					switch (immediateSize)  // For sign extentions
  					{
  					case 1:
  						sstream << (int8_t)immediateValue;
  						break;
  					case 2:
  						sstream << (int16_t)immediateValue;
  						break;
  					case 4:
  						sstream << (int32_t)immediateValue;
  						break;
  					default:
  						sstream << (int64_t)immediateValue;
  					}
  				}
  
  				if (immediateSize == addrSize)
  					result.emplace_back(PossibleAddressToken, sstream.str(), immediateValue, immediateSize);
  				else
  					result.emplace_back(IntegerToken, sstream.str(), immediateValue, immediateSize);
  			}
  			else
  			{
  				const uint64_t immediateValue = xed_decoded_inst_get_unsigned_immediate(xedd);
  				sstream << immediateValue;
  				if (immediateSize == addrSize)
  					result.emplace_back(PossibleAddressToken, sstream.str(), immediateValue, immediateSize);
  				else
  					result.emplace_back(IntegerToken, sstream.str(), immediateValue, immediateSize);
  			}
  			break;
  		}
  		case XED_OPERAND_IMM1:  // The ENTER instruction
  		{
  			stringstream sstream;
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << (uint16_t)xed_decoded_inst_get_second_immediate(xedd);
  			result.emplace_back(IntegerToken, sstream.str(), xed_decoded_inst_get_second_immediate(xedd), 1);
  			break;
  		}
  		case XED_OPERAND_PTR:
  		{
  			stringstream sstream;
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << xed_decoded_inst_get_branch_displacement(xedd);
  			result.emplace_back(PossibleAddressToken, sstream.str(), xed_decoded_inst_get_branch_displacement(xedd), 8);
  			break;
  		}
  		case XED_OPERAND_RELBR:
  		{
  			const int64_t relbr = xed_decoded_inst_get_branch_displacement(xedd) + addr + xed_decoded_inst_get_length(xedd);
  
  			stringstream sstream;
  			if ((xed_decoded_inst_get_iclass(xedd) == XED_ICLASS_CALL_NEAR) && (relbr == (int64_t)(addr + xed_decoded_inst_get_length(xedd))))
  			{
  				sstream << "$+" << xed_decoded_inst_get_length(xedd);
  				result.emplace_back(TextToken, sstream.str());
  				break;
  			}
  
  			sstream << "0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << relbr;
  			result.emplace_back(CodeRelativeAddressToken, sstream.str(), relbr, 8);
  			break;
  		}
  		default:
  		{
  			if (m_disassembly_options.lowerCase)
  				result.emplace_back(TextToken, "unimplimented ");
  			else
  				result.emplace_back(TextToken, "UNIMPLIMENTED ");
  		}  // default case of outer switch
  		}  // outer switch
  
  		// If there is another operand and it is visable, print delimiter
  		if ((opIndex != xed_inst_noperands(xi)-1) &&
  			((xed_operand_operand_visibility(xed_inst_operand(xi, opIndex+1)) == XED_OPVIS_EXPLICIT) ||
  				((xed_operand_operand_visibility(xed_inst_operand(xi, opIndex+1)) == XED_OPVIS_IMPLICIT) &&
  				(xed_decoded_inst_get_reg(xedd, xed_operand_name(xed_inst_operand(xi, opIndex+1))) != XED_REG_ST0))))
  				result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  	}
  
  	if (extra_index_operand != XED_REG_INVALID)
  	{
  		result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  		string reg = xed_reg_enum_t2str(extra_index_operand);
  		if (m_disassembly_options.lowerCase)
  			for (char& c : reg)
  				c = tolower(c);
  		result.emplace_back(RegisterToken, reg);
  	}
  }
  
  void X86CommonArchitecture::GetOperandTextATT(const xed_decoded_inst_t* const xedd, const uint64_t addr, const size_t len, const xed_operand_values_t* const ov, const xed_inst_t* const xi, vector<InstructionTextToken>& result) const
  {
  	unsigned i,j;
  	unsigned noperands = xed_inst_noperands(xi);
  
  	bool intel_way = false;
  
  	if (xed_inst_get_attribute(xi, XED_ATTRIBUTE_ATT_OPERAND_ORDER_EXCEPTION))
  		intel_way = true;
  
  	// if (xed_decoded_inst_get_attribute(xedd, XED_ATTRIBUTE_INDEX_REG_IS_POINTER))
  	// {
  	// 	for(j=0; j<noperands; ++j)
  	// 	{
  	// 		i = noperands - j - 1;  // never intel_way
  
  	// 		if(xed_operand_name(xed_inst_operand(xi,i)) == XED_OPERAND_MEM0)
  	// 			cout << "%" + xed_reg_enum_t2str(xed3_operand_get_index(xedd));
  	// 	}
  	// }
  
  	for(j=0; j<noperands; ++j)
  	{
  		if (intel_way)
  			i = j;
  		else
  			i = noperands - j - 1;
  
  		const xed_operand_t*          op = xed_inst_operand(xi, i);
  		const xed_operand_enum_t op_name = xed_operand_name(op);
  
  		// XED's suppressed operands shouln't be represented in Intel syntax
  		if (xed_operand_operand_visibility(op) == XED_OPVIS_SUPPRESSED)
  		{
  			if ((xed_decoded_inst_get_category(xedd) == XED_CATEGORY_STRINGOP) &&
  				(op_name == XED_OPERAND_MEM0 || op_name == XED_OPERAND_MEM1))
  			{
  				if (op_name == XED_OPERAND_MEM1)
  					result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  			}
  			else
  			{
  				continue;
  			}
  		}
  
  		switch(xed_operand_name(op))
  		{
  		case XED_OPERAND_REG0:
  		case XED_OPERAND_REG1:
  		case XED_OPERAND_REG2:
  		case XED_OPERAND_REG3:
  		case XED_OPERAND_REG4:
  		case XED_OPERAND_REG5:
  		case XED_OPERAND_REG6:
  		case XED_OPERAND_REG7:
  		case XED_OPERAND_REG8:
  		{
  			string reg = "%";
  
  			const xed_reg_enum_t xedReg = xed_decoded_inst_get_reg(xedd, op_name);
  			if ((xedReg >= XED_REG_X87_FIRST) && (xedReg <=  XED_REG_X87_LAST))
  			{
  				reg += "ST";
  				reg += ('0' + (xedReg-XED_REG_X87_FIRST));
  			}
  			else
  			{
  				reg += xed_reg_enum_t2str(xedReg);
  			}
  			if (m_disassembly_options.lowerCase)
  				for (char& c : reg)
  					c = tolower(c);
  			result.emplace_back(RegisterToken, reg);
  			break;
  		}
  		case XED_OPERAND_AGEN:
  		case XED_OPERAND_MEM0:
  		{
  			GetAddressSizeToken(xed_decoded_inst_operand_length_bits(xedd, i) / 8, result, m_disassembly_options.lowerCase);
  
  			if (m_disassembly_options.lowerCase)
  				result.emplace_back(TextToken, "ptr ");
  			else
  				result.emplace_back(TextToken, "PTR ");
  			result.emplace_back(TextToken, "[");
  
  			// Segment
  			const xed_reg_enum_t seg = xed_decoded_inst_get_seg_reg(xedd, 0);
  			const bool validSegment = (seg != XED_REG_INVALID && !xed_operand_values_using_default_segment(ov, 0));
  			if (validSegment)
  			{
  				string seg_str(xed_reg_enum_t2str(seg));
  				seg_str = "%" + seg_str;
  				if (m_disassembly_options.lowerCase)
  					for (char& c : seg_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, seg_str);
  				result.emplace_back(TextToken, ":");
  			}
  
  			bool started = false;
  			xed_reg_enum_t base = xed_decoded_inst_get_base_reg(xedd, 0);
  
  			int64_t disp = xed_decoded_inst_get_memory_displacement(xedd, 0);
  
  			if ((base != XED_REG_INVALID) && !((base == XED_REG_RIP) || (base == XED_REG_EIP) || (base == XED_REG_IP)))
  			{
  				string base_str(xed_reg_enum_t2str(base));
  				base_str = "%" + base_str;
  				if (m_disassembly_options.lowerCase)
  					for (char& c : base_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, base_str);
  				started = true;
  			}
  			else if ((base == XED_REG_RIP) || (base == XED_REG_EIP) || (base == XED_REG_IP))
  			{
  				if (xed_operand_values_has_memory_displacement(ov))
  					disp += addr + len;
  				else
  					disp = addr + len;
  
  				stringstream sstream;
  				if (m_disassembly_options.lowerCase)
  					sstream << "0x" << hex << nouppercase << disp;
  				else
  					sstream << "0x" << hex << uppercase << disp;
  
  				result.emplace_back(CodeRelativeAddressToken, sstream.str(), disp, GetAddressSize());
  
  				result.emplace_back(EndMemoryOperandToken, "]");
  				break;
  			}
  
  			const xed_reg_enum_t index = xed_decoded_inst_get_index_reg(xedd, 0);
  			if (index != XED_REG_INVALID)
  			{
  				if (started)
  					result.emplace_back(TextToken, "+");
  				started = true;
  
  				string index_str(xed_reg_enum_t2str(index));
  				index_str = "%" + index_str;
  				if (m_disassembly_options.lowerCase)
  					for (char& c : index_str)
  						c = tolower(c);
  
  				result.emplace_back(RegisterToken, index_str);
  
  				const unsigned int scale = xed_decoded_inst_get_scale(xedd, 0);
  				if (scale != 1)
  				{
  					result.emplace_back(TextToken, "*");
  					stringstream sstream;
  					sstream << scale;
  					result.emplace_back(IntegerToken, sstream.str(), scale, 1);
  				}
  			}
  
  			const unsigned short disp_bytes = xed_decoded_inst_get_memory_displacement_width_bits(xedd, 0) / 8;
  
  			if (xed_operand_values_has_memory_displacement(ov) &&
  				((disp != 0) || (!started && validSegment)))
  			{
  				stringstream sstream;
  				sstream << "0x" << hex;
  				if (m_disassembly_options.lowerCase)
  					sstream << nouppercase;
  				else
  					sstream << uppercase;
  
  				if (started)
  				{
  					if (disp < 0)
  					{
  						result.emplace_back(TextToken, "-");
  						disp = -disp;
  					}
  					else
  						result.emplace_back(TextToken, "+");
  
  					if (disp_bytes == 2)
  						sstream << (uint16_t)disp;
  					else if (disp_bytes == 4)
  						sstream << (uint32_t)disp;
  					else if (disp_bytes == 8)
  						sstream << (uint64_t)disp;
  					else
  						sstream << disp;
  					result.emplace_back(IntegerToken, sstream.str(), disp, disp_bytes);
  				}
  				else
  				{
  					sstream << disp;
  
  					if (validSegment)
  						result.emplace_back(IntegerToken, sstream.str(), disp, GetAddressSize());
  					else
  						result.emplace_back(PossibleAddressToken, sstream.str(), disp, GetAddressSize());
  				}
  			}
  			else if (xed_operand_values_has_memory_displacement(ov) &&
  				((disp == 0) && (!started)))
  			{
  				result.emplace_back(IntegerToken, "0x0", disp, GetAddressSize());
  			}
  			result.emplace_back(EndMemoryOperandToken, "]");
  
  			break;
  		}
  
  		case XED_OPERAND_MEM1:
  		{
  			GetAddressSizeToken(xed_decoded_inst_operand_length_bits(xedd, i) / 8, result, m_disassembly_options.lowerCase);
  
  			if (m_disassembly_options.lowerCase)
  				result.emplace_back(TextToken, "ptr ");
  			else
  				result.emplace_back(TextToken, "PTR ");
  
  			// Segment
  			const xed_reg_enum_t seg = xed_decoded_inst_get_seg_reg(xedd, 1);
  			if (seg != XED_REG_INVALID && !xed_operand_values_using_default_segment(ov, 1))
  			{
  				string seg_str(xed_reg_enum_t2str(seg));
  				seg_str = "%" + seg_str;
  				if (m_disassembly_options.lowerCase)
  					for (char& c : seg_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, seg_str);
  
  				result.emplace_back(TextToken, ":");
  			}
  
  			result.emplace_back(TextToken, "[");
  			const xed_reg_enum_t base = xed_decoded_inst_get_base_reg(xedd, 1);
  			if (base != XED_REG_INVALID)
  			{
  				string base_str(xed_reg_enum_t2str(base));
  				base_str = "%" + base_str;
  				if (m_disassembly_options.lowerCase)
  					for (char& c : base_str)
  						c = tolower(c);
  				result.emplace_back(RegisterToken, base_str);
  			}
  			result.emplace_back(EndMemoryOperandToken, "]");
  
  			break;
  		}
  
  		case XED_OPERAND_IMM0:
  		{
  			const size_t            addrSize = xed_decoded_inst_get_machine_mode_bits(xedd) / 8;
  			const unsigned int immediateSize = xed_decoded_inst_get_operand_width(xedd) / 8;
  
  			stringstream sstream;
  			sstream << "$0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			if (xed_decoded_inst_get_immediate_is_signed(xedd) && (immediateSize != 1))
  			{
  				const int64_t immediateValue = xed_decoded_inst_get_signed_immediate(xedd);
  
  				if (immediateValue >= 0)
  					sstream << immediateValue;  // Don't zero extend
  				else
  				{
  					// I'm not proud of this switch statement... It was the product of a lot of pain.
  					switch (immediateSize)  // For sign extentions
  					{
  					case 1:
  						sstream << (int8_t)immediateValue;
  						break;
  					case 2:
  						sstream << (int16_t)immediateValue;
  						break;
  					case 4:
  						sstream << (int32_t)immediateValue;
  						break;
  					default:
  						sstream << (int64_t)immediateValue;
  					}
  				}
  
  				if (immediateSize == addrSize)
  					result.emplace_back(PossibleAddressToken, sstream.str(), immediateValue, immediateSize);
  				else
  					result.emplace_back(IntegerToken, sstream.str(), immediateValue, immediateSize);
  			}
  			else
  			{
  				const uint64_t immediateValue = xed_decoded_inst_get_unsigned_immediate(xedd);
  				sstream << immediateValue;
  				if (immediateSize == addrSize)
  					result.emplace_back(PossibleAddressToken, sstream.str(), immediateValue, immediateSize);
  				else
  					result.emplace_back(IntegerToken, sstream.str(), immediateValue, immediateSize);
  			}
  			break;
  		}
  
  		case XED_OPERAND_IMM1:
  		{
  			stringstream sstream;
  			sstream << "$0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << (uint16_t)xed_decoded_inst_get_second_immediate(xedd);
  			result.emplace_back(IntegerToken, sstream.str(), xed_decoded_inst_get_second_immediate(xedd), 1);
  			break;
  		}
  
  		case XED_OPERAND_PTR:
  		{
  			stringstream sstream;
  			sstream << "$0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << xed_decoded_inst_get_branch_displacement(xedd);
  			result.emplace_back(PossibleAddressToken, sstream.str(), xed_decoded_inst_get_branch_displacement(xedd), 8);
  			break;
  
  		}
  
  		case XED_OPERAND_RELBR:
  		{
  			const int64_t relbr = xed_decoded_inst_get_branch_displacement(xedd) + addr + xed_decoded_inst_get_length(xedd);
  
  			stringstream sstream;
  			sstream << "$0x" << hex;
  			if (m_disassembly_options.lowerCase)
  				sstream << nouppercase;
  			else
  				sstream << uppercase;
  
  			sstream << relbr;
  			result.emplace_back(CodeRelativeAddressToken, sstream.str(), relbr, 8);
  			break;
  		}
  
  		default:
  		{
  			result.emplace_back(TextToken, "unimplimented");
  		}  // default case of outer switch
  		}  // outer switch
  
  		// If there is another operand and it is visable, print delimiter
  		if (intel_way)
  		{
  			if ((i != (size_t)xed_inst_noperands(xi)-1) &&
  				((xed_operand_operand_visibility(xed_inst_operand(xi, i+1)) == XED_OPVIS_EXPLICIT) ||
  				(xed_operand_operand_visibility(xed_inst_operand(xi, i+1)) == XED_OPVIS_IMPLICIT)))
  			{
  					result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  			}
  		}
  		else
  		{
  			if ((i != 0) &&
  				((xed_operand_operand_visibility(xed_inst_operand(xi, noperands - j - 2)) == XED_OPVIS_EXPLICIT) ||
  				(xed_operand_operand_visibility(xed_inst_operand(xi, noperands - j - 2)) == XED_OPVIS_IMPLICIT)))
  			{
  					result.emplace_back(OperandSeparatorToken, m_disassembly_options.separator);
  			}
  		}
  
  	}
  }
  
  void X86CommonArchitecture::GetOperandTextXED(const xed_decoded_inst_t* const xedd, const uint64_t addr, const size_t, const xed_operand_values_t* const, const xed_inst_t* const, vector<InstructionTextToken>& result) const
  {
  	char out_buffer[100];
  	if (!xed_format_context(XED_SYNTAX_XED, xedd, out_buffer, 100, addr, 0, 0))
  	{
  		LogError("Can't disassemble");
  		return;
  	}
  
  	// Find space
  	int i = 0;
  	for (; ; ++i)
  		if ((out_buffer[i] == ' ') && (out_buffer[i+1] != ' '))
  			break;
  
  	// Convert To String
  	string outstring(out_buffer+i);
  
  	result.emplace_back(TextToken, outstring);
  }
  
  void X86CommonArchitecture::GetOperandText(const xed_decoded_inst_t* const xedd, const uint64_t addr, const size_t len, const xed_operand_values_t* const ov, const xed_inst_t* const xi, vector<InstructionTextToken>& result) const
  {
  	switch (m_disassembly_options.df)
  	{
  	case DF_INTEL:
  		GetOperandTextIntel(xedd, addr, len, ov, xi, result);
  		break;
  
  	case DF_BN_INTEL:
  		GetOperandTextBNIntel(xedd, addr, len, ov, xi, result);
  		break;
  
  	case DF_ATT:
  		GetOperandTextATT(xedd, addr, len, ov, xi, result);
  		break;
  
  	case DF_XED:
  		GetOperandTextXED(xedd, addr, len, ov, xi, result);
  		break;
  
  	default:
  		LogError("Invalid Disassembly Flavor");
  	}
  }
  
  X86CommonArchitecture::X86CommonArchitecture(const string& name, size_t bits): Architecture(name), m_bits(bits)
  {
  	Ref<Settings> settings = Settings::Instance();
  	const bool lowercase = settings->Get<bool>("arch.x86.disassembly.lowercase");
  	const string flavor = settings->Get<string>("arch.x86.disassembly.syntax");
  	const string separator = settings->Get<string>("arch.x86.disassembly.separator");
  
  	DISASSEMBLY_FLAVOR_ENUM flavorEnum;
  
  	if (flavor == "BN_INTEL")
  		flavorEnum = DF_BN_INTEL;
  	else if (flavor == "INTEL")
  		flavorEnum = DF_INTEL;
  	else if (flavor == "AT&T")
  		flavorEnum = DF_ATT;
  	// else if (flavor == "XED")
  		// flavorEnum = DF_XED;
  	else
  		flavorEnum = DF_BN_INTEL;
  
  	m_disassembly_options = DISASSEMBLY_OPTIONS(flavorEnum, lowercase, separator);
  }
  
  BNEndianness X86CommonArchitecture::GetEndianness() const
  {
  	return LittleEndian;
  }
  
  vector<uint32_t> X86CommonArchitecture::GetGlobalRegisters()
  {
  	return vector< uint32_t> {
  		XED_REG_CS,
  		XED_REG_DS,
  		XED_REG_ES,
  		XED_REG_SS,
  		XED_REG_FS,
  		XED_REG_GS,
  		XED_REG_FSBASE,
  		XED_REG_GSBASE,
  		XED_REG_FLAGS,
  		XED_REG_EFLAGS,
  		XED_REG_RFLAGS
  	};
  }
  
  vector<uint32_t> X86CommonArchitecture::GetSystemRegisters()
  {
  	return vector< uint32_t> {
  		XED_REG_BND0,
  		XED_REG_BND1,
  		XED_REG_BND2,
  		XED_REG_BND3,
  
  		XED_REG_CR0,
  		XED_REG_CR1,
  		XED_REG_CR2,
  		XED_REG_CR3,
  		XED_REG_CR4,
  		XED_REG_CR5,
  		XED_REG_CR6,
  		XED_REG_CR7,
  		XED_REG_CR8,
  		XED_REG_CR9,
  		XED_REG_CR10,
  		XED_REG_CR11,
  		XED_REG_CR12,
  		XED_REG_CR13,
  		XED_REG_CR14,
  		XED_REG_CR15,
  
  		XED_REG_DR0,
  		XED_REG_DR1,
  		XED_REG_DR2,
  		XED_REG_DR3,
  		XED_REG_DR4,
  		XED_REG_DR5,
  		XED_REG_DR6,
  		XED_REG_DR7,
  
  		XED_REG_GDTR,
  		XED_REG_LDTR,
  		XED_REG_IDTR,
  		XED_REG_TR,
  
  		XED_REG_TSC,
  		XED_REG_TSCAUX,
  		XED_REG_MSRS
  	};
  }
  
  bool X86CommonArchitecture::GetInstructionInfo(const uint8_t* data, uint64_t addr, size_t maxLen, InstructionInfo& result)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  	if (!Decode(data, maxLen, &xedd))
  		return false;
  
  	SetInstructionInfoForInstruction(addr, result, &xedd);
  	return true;
  }
  
  
  bool X86CommonArchitecture::GetInstructionText(const uint8_t* data, uint64_t addr, size_t& len, vector<InstructionTextToken>& result)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  
  	if (Decode(data, len, &xedd))
  	{
  		len = xed_decoded_inst_get_length(&xedd);
  
  		// If there's no instruction
  		const xed_inst_t* xi = xed_decoded_inst_inst(&xedd);
  		if (!xi)
  			return false;
  
  		// Opcodes
  		const xed_operand_values_t* const   ov = xed_decoded_inst_operands_const(&xedd);
  		unsigned short instruction_name_length = GetInstructionOpcode(&xedd, ov, result);
  
  		// Padding
  		GetInstructionPadding(instruction_name_length, result);
  
  		// Operands
  		GetOperandText(&xedd, addr, len, ov, xi, result);
  
  		return true;
  	}
  
  	return false;
  }
  
  bool X86CommonArchitecture::GetInstructionLowLevelIL(const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  	if (!Decode(data, len, &xedd))
  	{
  		il.AddInstruction(il.Undefined());
  		return false;
  	}
  
  	len = xed_decoded_inst_get_length(&xedd);
  	return GetLowLevelILForInstruction(this, addr, il, &xedd);
  }
  
  size_t X86CommonArchitecture::GetFlagWriteLowLevelIL(BNLowLevelILOperation op, size_t size, uint32_t flagWriteType,
  	uint32_t flag, BNRegisterOrConstant* operands, size_t operandCount, LowLevelILFunction& il)
  {
  	switch (op)
  	{
  	case LLIL_NEG:
  		switch (flag)
  		{
  		case IL_FLAG_O:
  			return il.AddExpr(LLIL_CMP_E, size, 0, il.GetExprForRegisterOrConstantOperation(op, size, operands, operandCount),
  				il.AddExpr(LLIL_CONST, size, 0, (1LL << ((size * 8) - 1))));
  		}
  		break;
  	case LLIL_XOR:
  		switch (flag)
  		{
  		case IL_FLAG_C:
  		case IL_FLAG_O:
  			return il.Const(0, 0);
  		}
  		break;
  	case LLIL_MULU_DP:
  		switch (flag)
  		{
  		case IL_FLAG_C:
  		case IL_FLAG_O:
  			return il.AddExpr(LLIL_CMP_NE, size * 2, 0, il.AddExpr(LLIL_LSR, size * 2, 0,
  				il.GetExprForRegisterOrConstantOperation(op, size, operands, operandCount),
  				il.AddExpr(LLIL_CONST, 1, 0, size * 8)), il.AddExpr(LLIL_CONST, size * 2, 0, 0));
  		}
  	default:
  		break;
  	}
  	if (((flagWriteType == IL_FLAGWRITE_X87COM) || (flagWriteType == IL_FLAGWRITE_X87C1Z)) && (flag == IL_FLAG_C1))
  		return il.Const(0, 0);
  	return Architecture::GetFlagWriteLowLevelIL(op, size, flagWriteType, flag, operands, operandCount, il);
  }
  
  size_t X86CommonArchitecture::GetSemanticFlagGroupLowLevelIL(uint32_t semGroup, LowLevelILFunction& il)
  {
  	switch (semGroup)
  	{
  	case IL_FLAG_GROUP_E:
  		return GetFlagConditionLowLevelIL(LLFC_E, IL_FLAG_CLASS_INT, il);
  	case IL_FLAG_GROUP_NE:
  		return GetFlagConditionLowLevelIL(LLFC_NE, IL_FLAG_CLASS_INT, il);
  	case IL_FLAG_GROUP_LT:
  		return GetFlagConditionLowLevelIL(LLFC_ULT, IL_FLAG_CLASS_INT, il);
  	case IL_FLAG_GROUP_LE:
  		return GetFlagConditionLowLevelIL(LLFC_ULE, IL_FLAG_CLASS_INT, il);
  	case IL_FLAG_GROUP_GE:
  		return GetFlagConditionLowLevelIL(LLFC_UGE, IL_FLAG_CLASS_INT, il);
  	case IL_FLAG_GROUP_GT:
  		return GetFlagConditionLowLevelIL(LLFC_UGT, IL_FLAG_CLASS_INT, il);
  	case IL_FLAG_GROUP_PE:
  		return il.Flag(IL_FLAG_P);
  	case IL_FLAG_GROUP_PO:
  		return il.Not(0, il.Flag(IL_FLAG_P));
  	default:
  		return il.Unimplemented();
  	}
  }
  
  string X86CommonArchitecture::GetRegisterName(uint32_t reg)
  {
  	string reg_str = "";
  	if (m_disassembly_options.df == DF_ATT)
  		reg_str += "%";
  
  	if ((reg >= REG_X87_r(0)) && (reg <= REG_X87_r(7)))
  		reg_str += "X87_R" + to_string(reg - REG_X87_r(0));
  	else if (reg == REG_X87_TOP)
  		reg_str += "TOP";
  	else if ((reg >= XED_REG_X87_FIRST) && (reg <= XED_REG_X87_LAST))
  		reg_str += "ST" + to_string(reg - XED_REG_X87_FIRST);
  	else
  		reg_str += xed_reg_enum_t2str((xed_reg_enum_t)reg);
  
  	if (m_disassembly_options.lowerCase)
  		for (char& c : reg_str)
  			c = tolower(c);
  
  	return reg_str;
  }
  
  string X86CommonArchitecture::GetFlagName(uint32_t flag)
  {
  	char result[32];
  	switch (flag)
  	{
  	case IL_FLAG_C:
  		return "c";
  	case IL_FLAG_P:
  		return "p";
  	case IL_FLAG_A:
  		return "a";
  	case IL_FLAG_Z:
  		return "z";
  	case IL_FLAG_S:
  		return "s";
  	case IL_FLAG_D:
  		return "d";
  	case IL_FLAG_O:
  		return "o";
  	case IL_FLAG_C0:
  		return "c0";
  	case IL_FLAG_C1:
  		return "c1";
  	case IL_FLAG_C2:
  		return "c2";
  	case IL_FLAG_C3:
  		return "c3";
  	default:
  		sprintf(result, "flag%" PRIu32, flag);
  		return result;
  	}
  }
  
  vector<uint32_t> X86CommonArchitecture::GetAllFlags()
  {
  	return vector<uint32_t> {IL_FLAG_C, IL_FLAG_P, IL_FLAG_A, IL_FLAG_Z, IL_FLAG_S, IL_FLAG_D, IL_FLAG_O,
  		IL_FLAG_C0, IL_FLAG_C1, IL_FLAG_C2, IL_FLAG_C3};
  }
  
  string X86CommonArchitecture::GetSemanticFlagClassName(uint32_t semClass)
  {
  	switch (semClass)
  	{
  	case IL_FLAG_CLASS_X87COM:
  		return "x87com";
  	case IL_FLAG_CLASS_X87COMI:
  		return "x87comi";
  	default:
  		return "";
  	}
  }
  
  vector<uint32_t> X86CommonArchitecture::GetAllSemanticFlagClasses()
  {
  	return vector<uint32_t> {IL_FLAG_CLASS_X87COM, IL_FLAG_CLASS_X87COMI};
  }
  
  string X86CommonArchitecture::GetSemanticFlagGroupName(uint32_t semGroup)
  {
  	switch (semGroup)
  	{
  	case IL_FLAG_GROUP_E:
  		return "e";
  	case IL_FLAG_GROUP_NE:
  		return "ne";
  	case IL_FLAG_GROUP_LT:
  		return "lt";
  	case IL_FLAG_GROUP_LE:
  		return "le";
  	case IL_FLAG_GROUP_GE:
  		return "ge";
  	case IL_FLAG_GROUP_GT:
  		return "gt";
  	case IL_FLAG_GROUP_PE:
  		return "pe";
  	case IL_FLAG_GROUP_PO:
  		return "po";
  	default:
  		return "";
  	}
  }
  
  vector<uint32_t> X86CommonArchitecture::GetAllSemanticFlagGroups()
  {
  	return vector<uint32_t> {IL_FLAG_GROUP_E, IL_FLAG_GROUP_NE, IL_FLAG_GROUP_LT, IL_FLAG_GROUP_LE,
  		IL_FLAG_GROUP_GE, IL_FLAG_GROUP_GT, IL_FLAG_GROUP_PE, IL_FLAG_GROUP_PO};
  }
  
  string X86CommonArchitecture::GetFlagWriteTypeName(uint32_t flags)
  {
  	switch (flags)
  	{
  	case IL_FLAGWRITE_ALL:
  		return "*";
  	case IL_FLAGWRITE_NOCARRY:
  		return "!c";
  	case IL_FLAGWRITE_CO:
  		return "co";
  	case IL_FLAGWRITE_X87COM:
  		return "x87com";
  	case IL_FLAGWRITE_X87COMI:
  		return "x87comi";
  	case IL_FLAGWRITE_X87C1Z:
  		return "x87c1z";
  	case IL_FLAGWRITE_X87RND:
  		return "x87rnd";
  	default:
  		return "";
  	}
  }
  
  uint32_t X86CommonArchitecture::GetSemanticClassForFlagWriteType(uint32_t writeType)
  {
  	switch (writeType)
  	{
  	case IL_FLAGWRITE_X87COM:
  	case IL_FLAGWRITE_X87C1Z:
  	case IL_FLAGWRITE_X87RND:
  		return IL_FLAG_CLASS_X87COM;
  	case IL_FLAGWRITE_X87COMI:
  		return IL_FLAG_CLASS_X87COMI;
  	default:
  		return IL_FLAG_CLASS_INT;
  	}
  }
  
  vector<uint32_t> X86CommonArchitecture::GetAllFlagWriteTypes()
  {
  	return vector<uint32_t> {IL_FLAGWRITE_ALL, IL_FLAGWRITE_NOCARRY, IL_FLAGWRITE_CO,
  		IL_FLAGWRITE_X87COM, IL_FLAGWRITE_X87COMI, IL_FLAGWRITE_X87C1Z, IL_FLAGWRITE_X87RND};
  }
  
  BNFlagRole X86CommonArchitecture::GetFlagRole(uint32_t flag, uint32_t semClass)
  {
  	if (semClass == IL_FLAG_CLASS_X87COM)
  	{
  		switch (flag)
  		{
  		case IL_FLAG_C0:
  			return CarryFlagRole;
  		case IL_FLAG_C2:
  			return UnorderedFlagRole;
  		case IL_FLAG_C3:
  			return ZeroFlagRole;
  		default:
  			return SpecialFlagRole;
  		}
  	}
  
  	switch (flag)
  	{
  	case IL_FLAG_C:
  		return CarryFlagRole;
  	case IL_FLAG_P:
  		if (semClass == IL_FLAG_CLASS_X87COMI)
  			return UnorderedFlagRole;
  		return EvenParityFlagRole;
  	case IL_FLAG_A:
  		return HalfCarryFlagRole;
  	case IL_FLAG_Z:
  		return ZeroFlagRole;
  	case IL_FLAG_S:
  		return NegativeSignFlagRole;
  	case IL_FLAG_O:
  		return OverflowFlagRole;
  	default:
  		return SpecialFlagRole;
  	}
  }
  
  vector<uint32_t> X86CommonArchitecture::GetFlagsRequiredForFlagCondition(BNLowLevelILFlagCondition cond, uint32_t semClass)
  {
  	if (semClass == IL_FLAG_CLASS_X87COM)
  	{
  		switch (cond)
  		{
  		case LLFC_FE:
  		case LLFC_FNE:
  			return vector<uint32_t>{ IL_FLAG_C3 };
  		case LLFC_FLT:
  		case LLFC_FGE:
  			return vector<uint32_t>{ IL_FLAG_C0 };
  		case LLFC_FLE:
  		case LLFC_FGT:
  			return vector<uint32_t>{ IL_FLAG_C0, IL_FLAG_C3 };
  		case LLFC_FO:
  		case LLFC_FUO:
  			return vector<uint32_t>{ IL_FLAG_C2 };
  		default:
  			return vector<uint32_t>();
  		}
  	}
  
  	switch (cond)
  	{
  	case LLFC_E:
  	case LLFC_NE:
  	case LLFC_FE:
  	case LLFC_FNE:
  		return vector<uint32_t>{ IL_FLAG_Z };
  	case LLFC_SLT:
  	case LLFC_SGE:
  		return vector<uint32_t>{ IL_FLAG_S, IL_FLAG_O };
  	case LLFC_ULT:
  	case LLFC_UGE:
  	case LLFC_FLT:
  	case LLFC_FGE:
  		return vector<uint32_t>{ IL_FLAG_C };
  	case LLFC_SLE:
  	case LLFC_SGT:
  		return vector<uint32_t>{ IL_FLAG_Z, IL_FLAG_S, IL_FLAG_O };
  	case LLFC_ULE:
  	case LLFC_UGT:
  	case LLFC_FLE:
  	case LLFC_FGT:
  		return vector<uint32_t>{ IL_FLAG_C, IL_FLAG_Z };
  	case LLFC_NEG:
  	case LLFC_POS:
  		return vector<uint32_t>{ IL_FLAG_S };
  	case LLFC_O:
  	case LLFC_NO:
  		return vector<uint32_t>{ IL_FLAG_O };
  	case LLFC_FO:
  	case LLFC_FUO:
  		return vector<uint32_t>{ IL_FLAG_P };
  	default:
  		return vector<uint32_t>();
  	}
  }
  
  vector<uint32_t> X86CommonArchitecture::GetFlagsRequiredForSemanticFlagGroup(uint32_t semGroup)
  {
  	switch (semGroup)
  	{
  	case IL_FLAG_GROUP_E:
  	case IL_FLAG_GROUP_NE:
  		return vector<uint32_t>{ IL_FLAG_Z };
  	case IL_FLAG_GROUP_LT:
  	case IL_FLAG_GROUP_GE:
  		return vector<uint32_t>{ IL_FLAG_C };
  	case IL_FLAG_GROUP_LE:
  	case IL_FLAG_GROUP_GT:
  		return vector<uint32_t>{ IL_FLAG_C, IL_FLAG_Z };
  	case IL_FLAG_GROUP_PE:
  	case IL_FLAG_GROUP_PO:
  		return vector<uint32_t>{ IL_FLAG_P };
  	default:
  		return vector<uint32_t>();
  	}
  }
  
  map<uint32_t, BNLowLevelILFlagCondition> X86CommonArchitecture::GetFlagConditionsForSemanticFlagGroup(uint32_t semGroup)
  {
  	switch (semGroup)
  	{
  	case IL_FLAG_GROUP_E:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_INT, LLFC_E},
  			{IL_FLAG_CLASS_X87COMI, LLFC_FE}
  		};
  	case IL_FLAG_GROUP_NE:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_INT, LLFC_NE},
  			{IL_FLAG_CLASS_X87COMI, LLFC_FNE}
  		};
  	case IL_FLAG_GROUP_LT:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_INT, LLFC_ULT},
  			{IL_FLAG_CLASS_X87COMI, LLFC_FLT}
  		};
  	case IL_FLAG_GROUP_LE:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_INT, LLFC_ULE},
  			{IL_FLAG_CLASS_X87COMI, LLFC_FLE}
  		};
  	case IL_FLAG_GROUP_GE:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_INT, LLFC_UGE},
  			{IL_FLAG_CLASS_X87COMI, LLFC_FGE}
  		};
  	case IL_FLAG_GROUP_GT:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_INT, LLFC_UGT},
  			{IL_FLAG_CLASS_X87COMI, LLFC_FGT}
  		};
  	case IL_FLAG_GROUP_PE:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_X87COMI, LLFC_FUO}
  		};
  	case IL_FLAG_GROUP_PO:
  		return map<uint32_t, BNLowLevelILFlagCondition> {
  			{IL_FLAG_CLASS_X87COMI, LLFC_FO}
  		};
  	default:
  		return map<uint32_t, BNLowLevelILFlagCondition>();
  	}
  }
  
  vector<uint32_t> X86CommonArchitecture::GetFlagsWrittenByFlagWriteType(uint32_t writeType)
  {
  	switch (writeType)
  	{
  	case IL_FLAGWRITE_ALL:
  		return vector<uint32_t>{ IL_FLAG_C, IL_FLAG_P, IL_FLAG_A, IL_FLAG_Z, IL_FLAG_S, IL_FLAG_O };
  	case IL_FLAGWRITE_NOCARRY:
  		return vector<uint32_t>{ IL_FLAG_P, IL_FLAG_A, IL_FLAG_Z, IL_FLAG_S, IL_FLAG_O };
  	case IL_FLAGWRITE_CO:
  		return vector<uint32_t>{ IL_FLAG_C, IL_FLAG_O };
  	case IL_FLAGWRITE_X87COM:
  		return vector<uint32_t>{ IL_FLAG_C0, IL_FLAG_C1, IL_FLAG_C2, IL_FLAG_C3 };
  	case IL_FLAGWRITE_X87COMI:
  		return vector<uint32_t>{ IL_FLAG_C, IL_FLAG_Z, IL_FLAG_P };
  	case IL_FLAGWRITE_X87C1Z:
  	case IL_FLAGWRITE_X87RND:
  		return vector<uint32_t>{ IL_FLAG_C1 };
  	default:
  		return vector<uint32_t>();
  	}
  }
  
  string X86CommonArchitecture::GetRegisterStackName(uint32_t regStack)
  {
  	if (regStack == REG_STACK_X87)
  		return "x87";
  	return "";
  }
  
  vector<uint32_t> X86CommonArchitecture::GetAllRegisterStacks()
  {
  	return vector<uint32_t>{REG_STACK_X87};
  }
  
  BNRegisterStackInfo X86CommonArchitecture::GetRegisterStackInfo(uint32_t regStack)
  {
  	if (regStack == REG_STACK_X87)
  	{
  		BNRegisterStackInfo result;
  		result.firstStorageReg = REG_X87_r(0);
  		result.storageCount = 8;
  		result.firstTopRelativeReg = XED_REG_ST0;
  		result.topRelativeCount = 8;
  		result.stackTopReg = REG_X87_TOP;
  		return result;
  	}
  	return Architecture::GetRegisterStackInfo(regStack);
  }
  
  bool X86CommonArchitecture::CanAssemble()
  {
  	return true;
  }
  
  bool X86CommonArchitecture::Assemble(const string& code, uint64_t addr, DataBuffer& result, string& errors)
  {
  	string finalCode;
  
  	if (GetAddressSizeBits() == 32)
  		finalCode = "\tsection .text align=1\n\tbits 32\n";
  	else
  		finalCode = "\tsection .text align=1\n\tbits 64\n";
  
  	char orgStr[32];
  	sprintf(orgStr, "\torg 0x%" PRIx64 "\n", addr);
  	finalCode += orgStr;
  
  	finalCode += "%line 0 input\n";
  	finalCode += code;
  
  	Ref<TemporaryFile> inputFile = new TemporaryFile(finalCode);
  	Ref<TemporaryFile> outputFile = new TemporaryFile();
  	if (!inputFile->IsValid())
  	{
  		errors = "Unable to create temporary file for input\n";
  		return false;
  	}
  	if (!outputFile->IsValid())
  	{
  		errors = "Unable to create temporary file for output\n";
  		return false;
  	}
  
  	#ifdef WIN32
  		string yasmPath = GetPathRelativeToBundledPluginDirectory("yasm.exe");
  	#else
  		string yasmPath = GetPathRelativeToBundledPluginDirectory("yasm");
  	#endif
  
  	string inputPath = inputFile->GetPath();
  	string outputPath = outputFile->GetPath();
  
  	vector<string> args = vector<string> { yasmPath, "-fbin", "-w", "-Worphan-labels", "-Werror", "-o", outputPath, inputPath };
  
  	string output;
  	bool ok = ExecuteWorkerProcess(yasmPath, args, DataBuffer(),
  		output,  // _binary_ stdout is ignored
  		errors,  // _text_ stderr becomes the error message
  		false,  // yasm stdout is ignored (stay with default: no newline translate)
  		true  // yasm stderr is known to be text (translate newlines)
  	);
  
  	if(!ok)
  	{
  		/* when there was a problem creating the yasm process
  			OR the yasm process return code was nonzero */
  		if(errors.size() == 0)
  		{
  			errors = yasmPath + " returned nonzero\n";
  		}
  	}
  	else
  	{
  		result = outputFile->GetContents(); /* assembled bytes */
  		if(result.GetLength() == 0)
  		{
  			errors = "Empty output from assembler\n";
  			ok = false;
  		}
  	}
  
  	return ok;
  }
  
  bool X86CommonArchitecture::IsNeverBranchPatchAvailable(const uint8_t* data, uint64_t, size_t len)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  
  	if (!Decode(data, len, &xedd))
  		return false;
  	return IsConditionalJump(&xedd);
  }
  
  bool X86CommonArchitecture::IsAlwaysBranchPatchAvailable(const uint8_t* data, uint64_t, size_t len)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  	if (!Decode(data, len, &xedd))
  		return false;
  	return IsConditionalJump(&xedd);
  }
  
  bool X86CommonArchitecture::IsInvertBranchPatchAvailable(const uint8_t* data, uint64_t, size_t len)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  	if (!Decode(data, len, &xedd))
  		return false;
  	return IsConditionalJump(&xedd);
  }
  
  bool X86CommonArchitecture::IsSkipAndReturnZeroPatchAvailable(const uint8_t* data, uint64_t, size_t len)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  	if (!Decode(data, len, &xedd))
  		return false;
  	return xed_decoded_inst_get_category(&xedd) == XED_CATEGORY_CALL;
  }
  
  bool X86CommonArchitecture::IsSkipAndReturnValuePatchAvailable(const uint8_t* data, uint64_t, size_t len)
  {
  	xed_decoded_inst_t xedd;
  	switch (m_bits)
  	{
  	case 64:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LONG_64, XED_ADDRESS_WIDTH_64b);
  		break;
  	case 32:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32, XED_ADDRESS_WIDTH_32b);
  		break;
  	case 16:
  		xed_decoded_inst_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_16, XED_ADDRESS_WIDTH_16b);
  		break;
  	default:
  		LogError("Invalid Processor Mode");
  		return false;
  	}
  	if (!Decode(data, len, &xedd))
  		return false;
  	return (xed_decoded_inst_get_category(&xedd) == XED_CATEGORY_CALL) && (xed_decoded_inst_get_length(&xedd) >= 5);
  }
  
  bool X86CommonArchitecture::ConvertToNop(uint8_t* data, uint64_t, size_t len)
  {
  	memset(data, 0x90, len);
  	return true;
  }
  
  size_t X86CommonArchitecture::FindOpcodeOffset(const uint8_t* data, size_t len)
  {
  	size_t i;
  	for (i = 0; i < len; i++)
  	{
  		if ((data[0] >= 0x26) && (data[0] <= 0x3e) && ((data[0] & 7) == 6)) // Segment prefix
  			continue;
  		if ((data[0] >= 0x64) && (data[0] <= 0x67)) // FS/GS prefix and size overrides
  			continue;
  		if (data[0] == 0xf0) // Lock prefix
  			continue;
  		if ((data[0] == 0xf2) || (data[0] == 0xf3)) // Rep prefixes
  			continue;
  		if ((GetAddressSizeBits() == 64) && (data[0] >= 0x40) && (data[0] <= 0x4f)) // REX prefix
  			continue;
  		break;
  	}
  	return i;
  }
  
  bool X86CommonArchitecture::AlwaysBranch(uint8_t* data, uint64_t, size_t len)
  {
  	size_t i = FindOpcodeOffset(data, len);
  	if (i >= len)
  		return false;
  
  	if ((len - i) == 2)
  	{
  		data[i] = 0xeb;
  		return true;
  	}
  
  	if ((len - i) == 5)
  	{
  		data[i] = 0xe9;
  		return true;
  	}
  
  	if ((len - i) > 5)
  	{
  		memmove(&data[(len - i) - 5], data, i);
  		memset(data, 0x90, (len - i) - 5);
  		data[len - 5] = 0xe9;
  		return true;
  	}
  
  	return false;
  }
  
  bool X86CommonArchitecture::InvertBranch(uint8_t* data, uint64_t, size_t len)
  {
  	size_t i = FindOpcodeOffset(data, len);
  	if (i >= len)
  		return false;
  
  	if (data[i] == 0x0f)
  	{
  		if ((i + 1) >= len)
  			return false;
  		data[i + 1] ^= 1;
  		return true;
  	}
  
  	data[i] ^= 1;
  	return true;
  }
  
  bool X86CommonArchitecture::SkipAndReturnValue(uint8_t* data, uint64_t, size_t len, uint64_t value)
  {
  	if (len >= 5)
  	{
  		data[0] = 0xb8;
  		*(uint32_t*)&data[1] = (uint32_t)value;
  		memset(&data[5], 0x90, len - 5);
  		return true;
  	}
  
  	if ((value == 0) && (len >= 2))
  	{
  		// xor eax, eax
  		data[0] = 0x31;
  		data[1] = 0xc0;
  		memset(&data[2], 0x90, len - 2);
  		return true;
  	}
  
  	return false;
  }
  
  
  class X86Architecture: public X86CommonArchitecture
  {
  protected:
  	virtual size_t GetAddressSize() const override
  	{
  		return 4;
  	}
  
  public:
  	X86Architecture(): X86CommonArchitecture("x86", 32)
  	{}
  
  	virtual vector<uint32_t> GetFullWidthRegisters() override
  	{
  		return vector<uint32_t>{
  			// 16-Bit
  			XED_REG_CS, XED_REG_DS, XED_REG_ES, XED_REG_SS, XED_REG_FS, XED_REG_GS, XED_REG_FSBASE, XED_REG_GSBASE,  // 16+
  
  			// 32-Bit
  			XED_REG_EIP,  // 32+
  			XED_REG_ESP, XED_REG_EBP, XED_REG_ESI, XED_REG_EDI,  // 32+
  			XED_REG_EFLAGS,  // 32+
  
  			XED_REG_EAX, XED_REG_ECX, XED_REG_EDX, XED_REG_EBX,  // 32+
  
  			XED_REG_TSC, XED_REG_TSCAUX,  // 32+ (32 on 32, 64 on 64) Timestamp Counters
  			XED_REG_TR,  // 16+ (16 on 16, 32 on 32, 64 on 64) Task Register
  
  			XED_REG_CR0, XED_REG_CR1, XED_REG_CR2, XED_REG_CR3, XED_REG_CR4, XED_REG_CR5, XED_REG_CR6, XED_REG_CR7, XED_REG_CR8, XED_REG_CR9, XED_REG_CR10, XED_REG_CR11, XED_REG_CR12, XED_REG_CR13, XED_REG_CR14, XED_REG_CR15,  // 32+ (32 on 32, 64 on 64) Control Registers
  			XED_REG_DR0, XED_REG_DR1, XED_REG_DR2, XED_REG_DR3, XED_REG_DR4, XED_REG_DR5, XED_REG_DR6, XED_REG_DR7,  // 32+ (starting in later revisions of 32) (32 on 32, 64 on 64) Debug registers
  
     			XED_REG_MXCSR,  // 32+ SSE (MMX) Control Reg (32 on 32, 64 on 64)
  
  			// x87 FPU related
  			REG_X87_TOP, XED_REG_X87CONTROL, XED_REG_X87STATUS, XED_REG_X87TAG, XED_REG_X87OPCODE, XED_REG_X87LASTCS,
  			XED_REG_X87LASTDS, XED_REG_X87LASTIP, XED_REG_X87LASTDP, XED_REG_X87PUSH, XED_REG_X87POP, XED_REG_X87POP2,
  
  			// 48-Bit (All 32+)
  			XED_REG_GDTR,  // Global Descriptor Table Register
  			XED_REG_LDTR,  // Local Descriptor Table Register
  			XED_REG_IDTR,  // Interrupt Descriptor Table Register
  
  			// 64-Bit
  			XED_REG_XCR0,  // 32+ (64 on 32, 64 on 64)
  
  			// 80-Bit
  			XED_REG_ST0, XED_REG_ST1, XED_REG_ST2, XED_REG_ST3, XED_REG_ST4, XED_REG_ST5, XED_REG_ST6, XED_REG_ST7,  // 32+ Floating point
  			REG_X87_r(0), REG_X87_r(1), REG_X87_r(2), REG_X87_r(3), REG_X87_r(4), REG_X87_r(5), REG_X87_r(6), REG_X87_r(7),
  
  			// 128-Bit
  			XED_REG_XMM0, XED_REG_XMM1, XED_REG_XMM2, XED_REG_XMM3, XED_REG_XMM4, XED_REG_XMM5, XED_REG_XMM6, XED_REG_XMM7,  // 32+ SSE
  		};
  	}
  
  	virtual vector<uint32_t> GetAllRegisters() override
  	{
  		return vector<uint32_t>{
  			// 8-Bit
  			XED_REG_AH, XED_REG_CH, XED_REG_DH, XED_REG_BH, XED_REG_AL, XED_REG_CL, XED_REG_DL, XED_REG_BL,  // 16+
  
  			// 16-Bit
  			XED_REG_IP,  // 16+
  			XED_REG_CS, XED_REG_DS, XED_REG_ES, XED_REG_SS, XED_REG_FS, XED_REG_GS, XED_REG_FSBASE, XED_REG_GSBASE,  // 16+
  			XED_REG_SP, XED_REG_BP, XED_REG_SI, XED_REG_DI,  // 16+
  			XED_REG_FLAGS,  // 16+
  
  			XED_REG_AX, XED_REG_CX, XED_REG_DX, XED_REG_BX,  // 16+
  
  			REG_X87_TOP, // 32+
  			XED_REG_X87CONTROL, XED_REG_X87STATUS, XED_REG_X87TAG, XED_REG_X87PUSH, XED_REG_X87POP,
  			XED_REG_X87POP2, XED_REG_X87OPCODE, XED_REG_X87LASTCS, XED_REG_X87LASTIP, XED_REG_X87LASTDS, XED_REG_X87LASTDP,
  
  			// 32-Bit
  			XED_REG_EIP,  // 32+
  			XED_REG_ESP, XED_REG_EBP, XED_REG_ESI, XED_REG_EDI,  // 32+
  			XED_REG_EFLAGS,  // 32+
  
  			XED_REG_EAX, XED_REG_ECX, XED_REG_EDX, XED_REG_EBX,  // 32+
  
  			XED_REG_TSC, XED_REG_TSCAUX,  // 32+ (32 on 32, 64 on 64) Timestamp Counters
  			XED_REG_TR,  // 16+ (16 on 16, 32 on 32, 64 on 64) Task Register
  
  			XED_REG_CR0, XED_REG_CR1, XED_REG_CR2, XED_REG_CR3, XED_REG_CR4, XED_REG_CR5, XED_REG_CR6, XED_REG_CR7, XED_REG_CR8, XED_REG_CR9, XED_REG_CR10, XED_REG_CR11, XED_REG_CR12, XED_REG_CR13, XED_REG_CR14, XED_REG_CR15,  // 32+ (32 on 32, 64 on 64) Control Registers
  			XED_REG_DR0, XED_REG_DR1, XED_REG_DR2, XED_REG_DR3, XED_REG_DR4, XED_REG_DR5, XED_REG_DR6, XED_REG_DR7,  // 32+ (starting in later revisions of 32) (32 on 32, 64 on 64) Debug registers
  
     			XED_REG_MXCSR,  // 32+ SSE (MMX) Control Reg (32 on 32, 64 on 64)
  
  			// 48-Bit (All 32+)
  			XED_REG_GDTR,  // Global Descriptor Table Register
  			XED_REG_LDTR,  // Local Descriptor Table Register
  			XED_REG_IDTR,  // Interrupt Descriptor Table Register
  
  			// 64-Bit
  			XED_REG_MMX0, XED_REG_MMX1, XED_REG_MMX2, XED_REG_MMX3, XED_REG_MMX4, XED_REG_MMX5, XED_REG_MMX6, XED_REG_MMX7,  // 32+ Floating point, bottom of st regs
  			XED_REG_XCR0,  // 32+ (64 on 32, 64 on 64)
  
  			// 80-Bit
  			XED_REG_ST0, XED_REG_ST1, XED_REG_ST2, XED_REG_ST3, XED_REG_ST4, XED_REG_ST5, XED_REG_ST6, XED_REG_ST7,  // 32+ Floating point
  			REG_X87_r(0), REG_X87_r(1), REG_X87_r(2), REG_X87_r(3), REG_X87_r(4), REG_X87_r(5), REG_X87_r(6), REG_X87_r(7),
  
  			// 128-Bit
  			XED_REG_XMM0, XED_REG_XMM1, XED_REG_XMM2, XED_REG_XMM3, XED_REG_XMM4, XED_REG_XMM5, XED_REG_XMM6, XED_REG_XMM7,  // 32+ SSE
  		};
  	}
  
  	virtual BNRegisterInfo GetRegisterInfo(const uint32_t reg) override
  	{
  		switch (reg)
  		{
  		// 8-Bit
  		case XED_REG_AH:     return RegisterInfo(XED_REG_EAX, 1, 1);
  		case XED_REG_CH:     return RegisterInfo(XED_REG_ECX, 1, 1);
  		case XED_REG_DH:     return RegisterInfo(XED_REG_EDX, 1, 1);
  		case XED_REG_BH:     return RegisterInfo(XED_REG_EBX, 1, 1);
  		case XED_REG_AL:     return RegisterInfo(XED_REG_EAX, 0, 1);
  		case XED_REG_CL:     return RegisterInfo(XED_REG_ECX, 0, 1);
  		case XED_REG_DL:     return RegisterInfo(XED_REG_EDX, 0, 1);
  		case XED_REG_BL:     return RegisterInfo(XED_REG_EBX, 0, 1);
  
  		// 16-Bit
  		case XED_REG_IP:     return RegisterInfo(XED_REG_EIP, 0, 2);
  
  		case XED_REG_CS:     return RegisterInfo(XED_REG_CS, 0, 2);
  		case XED_REG_DS:     return RegisterInfo(XED_REG_DS, 0, 2);
  		case XED_REG_ES:     return RegisterInfo(XED_REG_ES, 0, 2);
  		case XED_REG_SS:     return RegisterInfo(XED_REG_SS, 0, 2);
  		case XED_REG_FS:     return RegisterInfo(XED_REG_FS, 0, 2);
  		case XED_REG_GS:     return RegisterInfo(XED_REG_GS, 0, 2);
  
  		case XED_REG_SP:     return RegisterInfo(XED_REG_ESP, 0, 2);
  		case XED_REG_BP:     return RegisterInfo(XED_REG_EBP, 0, 2);
  		case XED_REG_SI:     return RegisterInfo(XED_REG_ESI, 0, 2);
  		case XED_REG_DI:     return RegisterInfo(XED_REG_EDI, 0, 2);
  
  		case XED_REG_FLAGS:  return RegisterInfo(XED_REG_EFLAGS, 0, 2);
  
  		case XED_REG_AX:     return RegisterInfo(XED_REG_EAX, 0, 2);
  		case XED_REG_CX:     return RegisterInfo(XED_REG_ECX, 0, 2);
  		case XED_REG_DX:     return RegisterInfo(XED_REG_EDX, 0, 2);
  		case XED_REG_BX:     return RegisterInfo(XED_REG_EBX, 0, 2);
  
  		// 32-Bit
  		case XED_REG_EIP:    return RegisterInfo(XED_REG_EIP, 0, 4);
  		case XED_REG_FSBASE: return RegisterInfo(XED_REG_FSBASE, 0, 4);
  		case XED_REG_GSBASE: return RegisterInfo(XED_REG_GSBASE, 0, 4);
  
  		case XED_REG_ESP:    return RegisterInfo(XED_REG_ESP, 0, 4);
  		case XED_REG_EBP:    return RegisterInfo(XED_REG_EBP, 0, 4);
  		case XED_REG_ESI:    return RegisterInfo(XED_REG_ESI, 0, 4);
  		case XED_REG_EDI:    return RegisterInfo(XED_REG_EDI, 0, 4);
  
  		case XED_REG_EFLAGS: return RegisterInfo(XED_REG_EFLAGS, 0, 4);
  
  		case XED_REG_EAX:    return RegisterInfo(XED_REG_EAX, 0, 4);
  		case XED_REG_ECX:    return RegisterInfo(XED_REG_ECX, 0, 4);
  		case XED_REG_EDX:    return RegisterInfo(XED_REG_EDX, 0, 4);
  		case XED_REG_EBX:    return RegisterInfo(XED_REG_EBX, 0, 4);
  
  		case XED_REG_TSC:    return RegisterInfo(XED_REG_TSC, 0, 4);
  		case XED_REG_TSCAUX: return RegisterInfo(XED_REG_TSCAUX, 0, 4);
  
  		case XED_REG_TR:     return RegisterInfo(XED_REG_TR, 0, 4);
  
  		case REG_X87_TOP:              return RegisterInfo((xed_reg_enum_t)REG_X87_TOP, 0, 2);
  		case XED_REG_X87CONTROL:       return RegisterInfo(XED_REG_X87CONTROL, 0, 2);
  		case XED_REG_X87STATUS:        return RegisterInfo(XED_REG_X87STATUS, 0, 2);
  		case XED_REG_X87TAG:           return RegisterInfo(XED_REG_X87TAG, 0, 2);
  		case XED_REG_X87OPCODE:        return RegisterInfo(XED_REG_X87OPCODE, 0, 2);
  		case XED_REG_X87LASTCS:        return RegisterInfo(XED_REG_X87LASTCS, 0, 2);
  		case XED_REG_X87LASTDS:        return RegisterInfo(XED_REG_X87LASTDS, 0, 2);
  		case XED_REG_X87LASTIP:        return RegisterInfo(XED_REG_X87LASTIP, 0, 4);
  		case XED_REG_X87LASTDP:        return RegisterInfo(XED_REG_X87LASTDP, 0, 4);
  		case XED_REG_X87PUSH:          return RegisterInfo(XED_REG_X87PUSH, 0, 4);
  		case XED_REG_X87POP:           return RegisterInfo(XED_REG_X87POP, 0, 4);
  		case XED_REG_X87POP2:          return RegisterInfo(XED_REG_X87POP2, 0, 4);
  
  		case XED_REG_CR0:    return RegisterInfo(XED_REG_CR0, 0, 4);
  		case XED_REG_CR1:    return RegisterInfo(XED_REG_CR1, 0, 4);
  		case XED_REG_CR2:    return RegisterInfo(XED_REG_CR2, 0, 4);
  		case XED_REG_CR3:    return RegisterInfo(XED_REG_CR3, 0, 4);
  		case XED_REG_CR4:    return RegisterInfo(XED_REG_CR4, 0, 4);
  		case XED_REG_CR5:    return RegisterInfo(XED_REG_CR5, 0, 4);
  		case XED_REG_CR6:    return RegisterInfo(XED_REG_CR6, 0, 4);
  		case XED_REG_CR7:    return RegisterInfo(XED_REG_CR7, 0, 4);
  		case XED_REG_CR8:    return RegisterInfo(XED_REG_CR8, 0, 4);
  		case XED_REG_CR9:    return RegisterInfo(XED_REG_CR9, 0, 4);
  		case XED_REG_CR10:   return RegisterInfo(XED_REG_CR10, 0, 4);
  		case XED_REG_CR11:   return RegisterInfo(XED_REG_CR11, 0, 4);
  		case XED_REG_CR12:   return RegisterInfo(XED_REG_CR12, 0, 4);
  		case XED_REG_CR13:   return RegisterInfo(XED_REG_CR13, 0, 4);
  		case XED_REG_CR14:   return RegisterInfo(XED_REG_CR14, 0, 4);
  		case XED_REG_CR15:   return RegisterInfo(XED_REG_CR15, 0, 4);
  
  		case XED_REG_DR0:    return RegisterInfo(XED_REG_DR0, 0, 4);
  		case XED_REG_DR1:    return RegisterInfo(XED_REG_DR1, 0, 4);
  		case XED_REG_DR2:    return RegisterInfo(XED_REG_DR2, 0, 4);
  		case XED_REG_DR3:    return RegisterInfo(XED_REG_DR3, 0, 4);
  		case XED_REG_DR4:    return RegisterInfo(XED_REG_DR4, 0, 4);
  		case XED_REG_DR5:    return RegisterInfo(XED_REG_DR5, 0, 4);
  		case XED_REG_DR6:    return RegisterInfo(XED_REG_DR6, 0, 4);
  		case XED_REG_DR7:    return RegisterInfo(XED_REG_DR7, 0, 4);
  
  		case XED_REG_MXCSR:  return RegisterInfo(XED_REG_MXCSR, 0, 4);
  
  		// 48-Bit
  		case XED_REG_GDTR:   return RegisterInfo(XED_REG_GDTR, 0, 6);
  		case XED_REG_LDTR:   return RegisterInfo(XED_REG_LDTR, 0, 6);
  		case XED_REG_IDTR:   return RegisterInfo(XED_REG_IDTR, 0, 6);
  
  		// 64-Bit
  		case XED_REG_MMX0:   return RegisterInfo(XED_REG_ST0, 0, 8);
  		case XED_REG_MMX1:   return RegisterInfo(XED_REG_ST1, 0, 8);
  		case XED_REG_MMX2:   return RegisterInfo(XED_REG_ST2, 0, 8);
  		case XED_REG_MMX3:   return RegisterInfo(XED_REG_ST3, 0, 8);
  		case XED_REG_MMX4:   return RegisterInfo(XED_REG_ST4, 0, 8);
  		case XED_REG_MMX5:   return RegisterInfo(XED_REG_ST5, 0, 8);
  		case XED_REG_MMX6:   return RegisterInfo(XED_REG_ST6, 0, 8);
  		case XED_REG_MMX7:   return RegisterInfo(XED_REG_ST7, 0, 8);
  
  		case XED_REG_XCR0:   return RegisterInfo(XED_REG_XCR0, 0, 8);
  
  		// 80-Bit
  		case XED_REG_ST0:    return RegisterInfo(XED_REG_ST0, 0, 10);
  		case XED_REG_ST1:    return RegisterInfo(XED_REG_ST1, 0, 10);
  		case XED_REG_ST2:    return RegisterInfo(XED_REG_ST2, 0, 10);
  		case XED_REG_ST3:    return RegisterInfo(XED_REG_ST3, 0, 10);
  		case XED_REG_ST4:    return RegisterInfo(XED_REG_ST4, 0, 10);
  		case XED_REG_ST5:    return RegisterInfo(XED_REG_ST5, 0, 10);
  		case XED_REG_ST6:    return RegisterInfo(XED_REG_ST6, 0, 10);
  		case XED_REG_ST7:    return RegisterInfo(XED_REG_ST7, 0, 10);
  
  		case REG_X87_r(0):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(0), 0, 10);
  		case REG_X87_r(1):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(1), 0, 10);
  		case REG_X87_r(2):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(2), 0, 10);
  		case REG_X87_r(3):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(3), 0, 10);
  		case REG_X87_r(4):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(4), 0, 10);
  		case REG_X87_r(5):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(5), 0, 10);
  		case REG_X87_r(6):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(6), 0, 10);
  		case REG_X87_r(7):   return RegisterInfo((xed_reg_enum_t)REG_X87_r(7), 0, 10);
  
  		// 128-Bit
  		case XED_REG_XMM0:   return RegisterInfo(XED_REG_XMM0, 0, 16);
  		case XED_REG_XMM1:   return RegisterInfo(XED_REG_XMM1, 0, 16);
  		case XED_REG_XMM2:   return RegisterInfo(XED_REG_XMM2, 0, 16);
  		case XED_REG_XMM3:   return RegisterInfo(XED_REG_XMM3, 0, 16);
  		case XED_REG_XMM4:   return RegisterInfo(XED_REG_XMM4, 0, 16);
  		case XED_REG_XMM5:   return RegisterInfo(XED_REG_XMM5, 0, 16);
  		case XED_REG_XMM6:   return RegisterInfo(XED_REG_XMM6, 0, 16);
  		case XED_REG_XMM7:   return RegisterInfo(XED_REG_XMM7, 0, 16);
  
  		default:
  			return RegisterInfo(XED_REG_INVALID, 0, 0);
  		}
  	}
  
  	virtual uint32_t GetStackPointerRegister() override
  	{
  		return XED_REG_ESP;
  	}
  };
  
  
  class X16Architecture: public X86CommonArchitecture
  {
  protected:
  	virtual size_t GetAddressSize() const override
  	{
  		return 2;
  	}
  
  public:
  	X16Architecture(): X86CommonArchitecture("x86_16", 16)
  	{}
  
  	virtual vector<uint32_t> GetFullWidthRegisters() override
  	{
  		return vector<uint32_t> {
  			// 16-Bit
  			XED_REG_IP,  // 16+
  			XED_REG_CS, XED_REG_DS, XED_REG_ES, XED_REG_SS, XED_REG_FS, XED_REG_GS, XED_REG_FSBASE, XED_REG_GSBASE,  // 16+
  			XED_REG_SP, XED_REG_BP, XED_REG_SI, XED_REG_DI,  // 16+
  			XED_REG_FLAGS,  // 16+
  
  			XED_REG_AX, XED_REG_CX, XED_REG_DX, XED_REG_BX,  // 16+
  
  			XED_REG_TR,  // 16+ (16 on 16, 32 on 32, 64 on 64) Task Register
  		};
  	}
  
  	virtual vector<uint32_t> GetAllRegisters() override
  	{
  		return vector<uint32_t>{
  			// 8-Bit
  			XED_REG_AH, XED_REG_CH, XED_REG_DH, XED_REG_BH, XED_REG_AL, XED_REG_CL, XED_REG_DL, XED_REG_BL,  // 16+
  
  			// 16-Bit
  			XED_REG_IP,  // 16+
  			XED_REG_CS, XED_REG_DS, XED_REG_ES, XED_REG_SS, XED_REG_FS, XED_REG_GS, XED_REG_FSBASE, XED_REG_GSBASE,  // 16+
  			XED_REG_SP, XED_REG_BP, XED_REG_SI, XED_REG_DI,  // 16+
  			XED_REG_FLAGS,  // 16+
  
  			XED_REG_AX, XED_REG_CX, XED_REG_DX, XED_REG_BX,  // 16+
  
  			XED_REG_TR,  // 16+ (16 on 16, 32 on 32, 64 on 64) Task Register
  		};
  	}
  
  	virtual BNRegisterInfo GetRegisterInfo(const uint32_t reg) override
  	{
  		switch (reg)
  		{
  		// 8-Bit
  		case XED_REG_AH:     return RegisterInfo(XED_REG_AX, 1, 1);
  		case XED_REG_CH:     return RegisterInfo(XED_REG_CX, 1, 1);
  		case XED_REG_DH:     return RegisterInfo(XED_REG_DX, 1, 1);
  		case XED_REG_BH:     return RegisterInfo(XED_REG_BX, 1, 1);
  		case XED_REG_AL:     return RegisterInfo(XED_REG_AX, 0, 1);
  		case XED_REG_CL:     return RegisterInfo(XED_REG_CX, 0, 1);
  		case XED_REG_DL:     return RegisterInfo(XED_REG_DX, 0, 1);
  		case XED_REG_BL:     return RegisterInfo(XED_REG_BX, 0, 1);
  
  		// 16-Bit
  		case XED_REG_IP:     return RegisterInfo(XED_REG_IP, 0, 2);
  		case XED_REG_CS:     return RegisterInfo(XED_REG_CS, 0, 2);
  		case XED_REG_DS:     return RegisterInfo(XED_REG_DS, 0, 2);
  		case XED_REG_ES:     return RegisterInfo(XED_REG_ES, 0, 2);
  		case XED_REG_SS:     return RegisterInfo(XED_REG_SS, 0, 2);
  		case XED_REG_FS:     return RegisterInfo(XED_REG_FS, 0, 2);
  		case XED_REG_GS:     return RegisterInfo(XED_REG_GS, 0, 2);
  		case XED_REG_FSBASE: return RegisterInfo(XED_REG_FSBASE, 0, 2);
  		case XED_REG_GSBASE: return RegisterInfo(XED_REG_GSBASE, 0, 2);
  		case XED_REG_SP:     return RegisterInfo(XED_REG_SP, 0, 2);
  		case XED_REG_BP:     return RegisterInfo(XED_REG_BP, 0, 2);
  		case XED_REG_SI:     return RegisterInfo(XED_REG_SI, 0, 2);
  		case XED_REG_DI:     return RegisterInfo(XED_REG_DI, 0, 2);
  		case XED_REG_FLAGS:  return RegisterInfo(XED_REG_FLAGS, 0, 2);
  		case XED_REG_AX:     return RegisterInfo(XED_REG_AX, 0, 2);
  		case XED_REG_CX:     return RegisterInfo(XED_REG_CX, 0, 2);
  		case XED_REG_DX:     return RegisterInfo(XED_REG_DX, 0, 2);
  		case XED_REG_BX:     return RegisterInfo(XED_REG_BX, 0, 2);
  		case XED_REG_TR:     return RegisterInfo(XED_REG_TR, 0, 2);
  
  		default:             return RegisterInfo(XED_REG_INVALID, 0, 0);
  		}
  	}
  
  	virtual uint32_t GetStackPointerRegister() override
  	{
  		return XED_REG_SP;
  	}
  };
  
  
  class X64Architecture: public X86CommonArchitecture
  {
  protected:
  	virtual size_t GetAddressSize() const override
  	{
  		return 8;
  	}
  
  public:
  	X64Architecture(): X86CommonArchitecture("x86_64", 64)
  	{}
  
  	virtual vector<uint32_t> GetFullWidthRegisters() override
  	{
  		return vector< uint32_t>  {
  			// 16-Bit
  			XED_REG_CS, XED_REG_DS, XED_REG_ES, XED_REG_SS, XED_REG_FS, XED_REG_GS, XED_REG_FSBASE, XED_REG_GSBASE,  // 16+
  			XED_REG_SP, XED_REG_BP, XED_REG_SI, XED_REG_DI,  // 16+
  
  			// x87 FPU related
  			REG_X87_TOP, XED_REG_X87CONTROL, XED_REG_X87STATUS, XED_REG_X87TAG, XED_REG_X87OPCODE, XED_REG_X87LASTCS,
  			XED_REG_X87LASTDS, XED_REG_X87LASTIP, XED_REG_X87LASTDP, XED_REG_X87PUSH, XED_REG_X87POP, XED_REG_X87POP2,
  
  			// 48-Bit (All 32+)
  			XED_REG_GDTR,  // Global Descriptor Table Register
  			XED_REG_LDTR,  // Local Descriptor Table Register
  			XED_REG_IDTR,  // Interrupt Descriptor Table Register
  
  			// 64-Bit
  			XED_REG_TR,  // 16+ (16 on 16, 32 on 32, 64 on 64) Task Register
  			XED_REG_TSC, XED_REG_TSCAUX,  // 32+ (32 on 32, 64 on 64) Timestamp Counters
  			XED_REG_RIP,  // 64+
  			XED_REG_RSP, XED_REG_RBP, XED_REG_RSI, XED_REG_RDI,  // 64+
  			XED_REG_RFLAGS,  // 64+
  			XED_REG_MXCSR,  // 32+ SSE (MMX) Control Reg (32 on 32, 64 on 64)
  			XED_REG_XCR0,  // 32+ (64 on 32, 64 on 64)
  			XED_REG_SSP,  // 64+ Shadow Stack Reg
  
  			XED_REG_RAX, XED_REG_RCX, XED_REG_RDX, XED_REG_RBX,  // 64+
  			XED_REG_R8, XED_REG_R9, XED_REG_R10, XED_REG_R11, XED_REG_R12, XED_REG_R13, XED_REG_R14, XED_REG_R15,  // 64+
  
  			XED_REG_BNDCFGU, XED_REG_BNDSTATUS,  // 64 briefly. MPX control registers
  			XED_REG_K0, XED_REG_K1, XED_REG_K2, XED_REG_K3, XED_REG_K4, XED_REG_K5, XED_REG_K6, XED_REG_K7,  // 64+ AVX bit-masking registers (also not confident in size)
  
  			XED_REG_CR0, XED_REG_CR1, XED_REG_CR2, XED_REG_CR3, XED_REG_CR4, XED_REG_CR5, XED_REG_CR6, XED_REG_CR7, XED_REG_CR8, XED_REG_CR9, XED_REG_CR10, XED_REG_CR11, XED_REG_CR12, XED_REG_CR13, XED_REG_CR14, XED_REG_CR15,  // 32+ (32 on 32, 64 on 64) Control Registers
  			XED_REG_DR0, XED_REG_DR1, XED_REG_DR2, XED_REG_DR3, XED_REG_DR4, XED_REG_DR5, XED_REG_DR6, XED_REG_DR7,  // 32+ (starting in later revisions of 32) (32 on 32, 64 on 64) Debug registers
  
  			// 80-Bit
  			XED_REG_ST0, XED_REG_ST1, XED_REG_ST2, XED_REG_ST3, XED_REG_ST4, XED_REG_ST5, XED_REG_ST6, XED_REG_ST7,  // 32+ Floating point
  			REG_X87_r(0), REG_X87_r(1), REG_X87_r(2), REG_X87_r(3), REG_X87_r(4), REG_X87_r(5), REG_X87_r(6), REG_X87_r(7),
  
  			// 128-Bit
  			XED_REG_BND0, XED_REG_BND1, XED_REG_BND2, XED_REG_BND3,  // 64 briefly. MPX registers
  
  			// 512-Bit
  			XED_REG_ZMM0, XED_REG_ZMM1, XED_REG_ZMM2, XED_REG_ZMM3, XED_REG_ZMM4, XED_REG_ZMM5, XED_REG_ZMM6, XED_REG_ZMM7, XED_REG_ZMM8, XED_REG_ZMM9, XED_REG_ZMM10, XED_REG_ZMM11, XED_REG_ZMM12, XED_REG_ZMM13, XED_REG_ZMM14, XED_REG_ZMM15, XED_REG_ZMM16, XED_REG_ZMM17, XED_REG_ZMM18, XED_REG_ZMM19, XED_REG_ZMM20, XED_REG_ZMM21, XED_REG_ZMM22, XED_REG_ZMM23, XED_REG_ZMM24, XED_REG_ZMM25, XED_REG_ZMM26, XED_REG_ZMM27, XED_REG_ZMM28, XED_REG_ZMM29, XED_REG_ZMM30, XED_REG_ZMM31  // 64+ AVX
  		};
  	}
  
  	virtual vector<uint32_t> GetAllRegisters() override
  	{
  		return vector< uint32_t> {
  			// 8-Bit
  			XED_REG_AH, XED_REG_CH, XED_REG_DH, XED_REG_BH, XED_REG_AL, XED_REG_CL, XED_REG_DL, XED_REG_BL,  // 16+
  			XED_REG_SPL, XED_REG_BPL, XED_REG_SIL, XED_REG_DIL,  // 64+
  			XED_REG_R8B, XED_REG_R9B, XED_REG_R10B, XED_REG_R11B, XED_REG_R12B, XED_REG_R13B, XED_REG_R14B, XED_REG_R15B,  // 64+
  
  			// 16-Bit
  			XED_REG_IP,  // 16+
  			XED_REG_CS, XED_REG_DS, XED_REG_ES, XED_REG_SS, XED_REG_FS, XED_REG_GS, XED_REG_FSBASE, XED_REG_GSBASE,  // 16+
  			XED_REG_SP, XED_REG_BP, XED_REG_SI, XED_REG_DI,  // 16+
  			XED_REG_FLAGS,  // 16+
  
  			REG_X87_TOP, // 32+
  			XED_REG_X87CONTROL, XED_REG_X87STATUS, XED_REG_X87TAG, XED_REG_X87PUSH, XED_REG_X87POP,
  			XED_REG_X87POP2, XED_REG_X87OPCODE, XED_REG_X87LASTCS, XED_REG_X87LASTIP, XED_REG_X87LASTDS, XED_REG_X87LASTDP,
  
  			XED_REG_AX, XED_REG_CX, XED_REG_DX, XED_REG_BX,  // 16+
  			XED_REG_R8W, XED_REG_R9W, XED_REG_R10W, XED_REG_R11W, XED_REG_R12W, XED_REG_R13W, XED_REG_R14W, XED_REG_R15W,  // 64+
  
  			// 32-Bit
  			XED_REG_EIP,  // 32+
  			XED_REG_ESP, XED_REG_EBP, XED_REG_ESI, XED_REG_EDI,  // 32+
  			XED_REG_EFLAGS,  // 32+
  
  			XED_REG_EAX, XED_REG_ECX, XED_REG_EDX, XED_REG_EBX,  // 32+
  			XED_REG_R8D, XED_REG_R9D, XED_REG_R10D, XED_REG_R11D, XED_REG_R12D, XED_REG_R13D, XED_REG_R14D, XED_REG_R15D,  // 64+
  
  			// 48-Bit (All 32+)
  			XED_REG_GDTR,  // Global Descriptor Table Register
  			XED_REG_LDTR,  // Local Descriptor Table Register
  			XED_REG_IDTR,  // Interrupt Descriptor Table Register
  
  			// 64-Bit
  			XED_REG_TR,  // 16+ (16 on 16, 32 on 32, 64 on 64) Task Register
  			XED_REG_TSC, XED_REG_TSCAUX,  // 32+ (32 on 32, 64 on 64) Timestamp Counters
  			XED_REG_MMX0, XED_REG_MMX1, XED_REG_MMX2, XED_REG_MMX3, XED_REG_MMX4, XED_REG_MMX5, XED_REG_MMX6, XED_REG_MMX7,  // 32+ Floating point, bottom of st regs
  			XED_REG_RIP,  // 64+
  			XED_REG_RSP, XED_REG_RBP, XED_REG_RSI, XED_REG_RDI,  // 64+
  			XED_REG_RFLAGS,  // 64+
  			XED_REG_MXCSR,  // 32+ SSE (MMX) Control Reg (32 on 32, 64 on 64)
  			XED_REG_XCR0,  // 32+ (64 on 32, 64 on 64)
  			XED_REG_SSP,  // 64+ Shadow Stack Reg
  
  			XED_REG_RAX, XED_REG_RCX, XED_REG_RDX, XED_REG_RBX,  // 64+
  			XED_REG_R8, XED_REG_R9, XED_REG_R10, XED_REG_R11, XED_REG_R12, XED_REG_R13, XED_REG_R14, XED_REG_R15,  // 64+
  
  			XED_REG_BNDCFGU, XED_REG_BNDSTATUS,  // 64 briefly. MPX control registers
  			XED_REG_K0, XED_REG_K1, XED_REG_K2, XED_REG_K3, XED_REG_K4, XED_REG_K5, XED_REG_K6, XED_REG_K7,  // 64+ AVX bit-masking registers (also not confident in size)
  
  			XED_REG_CR0, XED_REG_CR1, XED_REG_CR2, XED_REG_CR3, XED_REG_CR4, XED_REG_CR5, XED_REG_CR6, XED_REG_CR7, XED_REG_CR8, XED_REG_CR9, XED_REG_CR10, XED_REG_CR11, XED_REG_CR12, XED_REG_CR13, XED_REG_CR14, XED_REG_CR15,  // 32+ (32 on 32, 64 on 64) Control Registers
  			XED_REG_DR0, XED_REG_DR1, XED_REG_DR2, XED_REG_DR3, XED_REG_DR4, XED_REG_DR5, XED_REG_DR6, XED_REG_DR7,  // 32+ (starting in later revisions of 32) (32 on 32, 64 on 64) Debug registers
  
  			// 80-Bit
  			XED_REG_ST0, XED_REG_ST1, XED_REG_ST2, XED_REG_ST3, XED_REG_ST4, XED_REG_ST5, XED_REG_ST6, XED_REG_ST7,  // 32+ Floating point
  			REG_X87_r(0), REG_X87_r(1), REG_X87_r(2), REG_X87_r(3), REG_X87_r(4), REG_X87_r(5), REG_X87_r(6), REG_X87_r(7),
  
  			// 128-Bit
  			XED_REG_XMM0, XED_REG_XMM1, XED_REG_XMM2, XED_REG_XMM3, XED_REG_XMM4, XED_REG_XMM5, XED_REG_XMM6, XED_REG_XMM7,  // 32+ SSE
  			XED_REG_XMM8, XED_REG_XMM9, XED_REG_XMM10, XED_REG_XMM11, XED_REG_XMM12, XED_REG_XMM13, XED_REG_XMM14, XED_REG_XMM15,  // 64+ SSE
  			XED_REG_XMM16, XED_REG_XMM17, XED_REG_XMM18, XED_REG_XMM19, XED_REG_XMM20, XED_REG_XMM21, XED_REG_XMM22, XED_REG_XMM23, XED_REG_XMM24, XED_REG_XMM25, XED_REG_XMM26, XED_REG_XMM27, XED_REG_XMM28, XED_REG_XMM29, XED_REG_XMM30, XED_REG_XMM31,  // 64+ AVX
  			XED_REG_BND0, XED_REG_BND1, XED_REG_BND2, XED_REG_BND3,  // 64 briefly. MPX registers
  
  			// 256-Bit
  			XED_REG_YMM0, XED_REG_YMM1, XED_REG_YMM2, XED_REG_YMM3, XED_REG_YMM4, XED_REG_YMM5, XED_REG_YMM6, XED_REG_YMM7, XED_REG_YMM8, XED_REG_YMM9, XED_REG_YMM10, XED_REG_YMM11, XED_REG_YMM12, XED_REG_YMM13, XED_REG_YMM14, XED_REG_YMM15, XED_REG_YMM16, XED_REG_YMM17, XED_REG_YMM18, XED_REG_YMM19, XED_REG_YMM20, XED_REG_YMM21, XED_REG_YMM22, XED_REG_YMM23, XED_REG_YMM24, XED_REG_YMM25, XED_REG_YMM26, XED_REG_YMM27, XED_REG_YMM28, XED_REG_YMM29, XED_REG_YMM30, XED_REG_YMM31,  // 64+ AVX
  
  			// 512-Bit
  			XED_REG_ZMM0, XED_REG_ZMM1, XED_REG_ZMM2, XED_REG_ZMM3, XED_REG_ZMM4, XED_REG_ZMM5, XED_REG_ZMM6, XED_REG_ZMM7, XED_REG_ZMM8, XED_REG_ZMM9, XED_REG_ZMM10, XED_REG_ZMM11, XED_REG_ZMM12, XED_REG_ZMM13, XED_REG_ZMM14, XED_REG_ZMM15, XED_REG_ZMM16, XED_REG_ZMM17, XED_REG_ZMM18, XED_REG_ZMM19, XED_REG_ZMM20, XED_REG_ZMM21, XED_REG_ZMM22, XED_REG_ZMM23, XED_REG_ZMM24, XED_REG_ZMM25, XED_REG_ZMM26, XED_REG_ZMM27, XED_REG_ZMM28, XED_REG_ZMM29, XED_REG_ZMM30, XED_REG_ZMM31  // 64+ AVX
  		};
  	}
  
  	virtual BNRegisterInfo GetRegisterInfo(const uint32_t reg) override
  	{
  		switch (reg)
  		{
  		// 8-Bit
  		case XED_REG_AH:        return RegisterInfo(XED_REG_RAX, 1, 1);
  		case XED_REG_CH:        return RegisterInfo(XED_REG_RCX, 1, 1);
  		case XED_REG_DH:        return RegisterInfo(XED_REG_RDX, 1, 1);
  		case XED_REG_BH:        return RegisterInfo(XED_REG_RBX, 1, 1);
  		case XED_REG_AL:        return RegisterInfo(XED_REG_RAX, 0, 1);
  		case XED_REG_CL:        return RegisterInfo(XED_REG_RCX, 0, 1);
  		case XED_REG_DL:        return RegisterInfo(XED_REG_RDX, 0, 1);
  		case XED_REG_BL:        return RegisterInfo(XED_REG_RBX, 0, 1);
  
  		case XED_REG_SPL:       return RegisterInfo(XED_REG_RSP, 0, 1);
  		case XED_REG_BPL:       return RegisterInfo(XED_REG_RBP, 0, 1);
  		case XED_REG_SIL:       return RegisterInfo(XED_REG_RSI, 0, 1);
  		case XED_REG_DIL:       return RegisterInfo(XED_REG_RDI, 0, 1);
  
  		case XED_REG_R8B:       return RegisterInfo(XED_REG_R8, 0, 1);
  		case XED_REG_R9B:       return RegisterInfo(XED_REG_R9, 0, 1);
  		case XED_REG_R10B:      return RegisterInfo(XED_REG_R10, 0, 1);
  		case XED_REG_R11B:      return RegisterInfo(XED_REG_R11, 0, 1);
  		case XED_REG_R12B:      return RegisterInfo(XED_REG_R12, 0, 1);
  		case XED_REG_R13B:      return RegisterInfo(XED_REG_R13, 0, 1);
  		case XED_REG_R14B:      return RegisterInfo(XED_REG_R14, 0, 1);
  		case XED_REG_R15B:      return RegisterInfo(XED_REG_R15, 0, 1);
  
  		// 16-Bit
  		case XED_REG_IP:        return RegisterInfo(XED_REG_RIP, 0, 2);
  
  		case XED_REG_CS:        return RegisterInfo(XED_REG_CS, 0, 2);
  		case XED_REG_DS:        return RegisterInfo(XED_REG_DS, 0, 2);
  		case XED_REG_ES:        return RegisterInfo(XED_REG_ES, 0, 2);
  		case XED_REG_SS:        return RegisterInfo(XED_REG_SS, 0, 2);
  		case XED_REG_FS:        return RegisterInfo(XED_REG_FS, 0, 2);
  		case XED_REG_GS:        return RegisterInfo(XED_REG_GS, 0, 2);
  
  		case XED_REG_SP:        return RegisterInfo(XED_REG_RSP, 0, 2);
  		case XED_REG_BP:        return RegisterInfo(XED_REG_RBP, 0, 2);
  		case XED_REG_SI:        return RegisterInfo(XED_REG_RSI, 0, 2);
  		case XED_REG_DI:        return RegisterInfo(XED_REG_RDI, 0, 2);
  
  		case XED_REG_FLAGS:     return RegisterInfo(XED_REG_RFLAGS, 0, 2);
  
  		case XED_REG_AX:        return RegisterInfo(XED_REG_RAX, 0, 2);
  		case XED_REG_CX:        return RegisterInfo(XED_REG_RCX, 0, 2);
  		case XED_REG_DX:        return RegisterInfo(XED_REG_RDX, 0, 2);
  		case XED_REG_BX:        return RegisterInfo(XED_REG_RBX, 0, 2);
  		case XED_REG_R8W:       return RegisterInfo(XED_REG_R8, 0, 2);
  		case XED_REG_R9W:       return RegisterInfo(XED_REG_R9, 0, 2);
  		case XED_REG_R10W:      return RegisterInfo(XED_REG_R10, 0, 2);
  		case XED_REG_R11W:      return RegisterInfo(XED_REG_R11, 0, 2);
  		case XED_REG_R12W:      return RegisterInfo(XED_REG_R12, 0, 2);
  		case XED_REG_R13W:      return RegisterInfo(XED_REG_R13, 0, 2);
  		case XED_REG_R14W:      return RegisterInfo(XED_REG_R14, 0, 2);
  		case XED_REG_R15W:      return RegisterInfo(XED_REG_R15, 0, 2);
  
  		// 32-Bit
  		case XED_REG_EIP:       return RegisterInfo(XED_REG_RIP, 0, 4);
  
  		case XED_REG_ESP:       return RegisterInfo(XED_REG_RSP, 0, 4, true);
  		case XED_REG_EBP:       return RegisterInfo(XED_REG_RBP, 0, 4, true);
  		case XED_REG_ESI:       return RegisterInfo(XED_REG_RSI, 0, 4, true);
  		case XED_REG_EDI:       return RegisterInfo(XED_REG_RDI, 0, 4, true);
  
  		case REG_X87_TOP:              return RegisterInfo((xed_reg_enum_t)REG_X87_TOP, 0, 2);
  		case XED_REG_X87CONTROL:       return RegisterInfo(XED_REG_X87CONTROL, 0, 2);
  		case XED_REG_X87STATUS:        return RegisterInfo(XED_REG_X87STATUS, 0, 2);
  		case XED_REG_X87TAG:           return RegisterInfo(XED_REG_X87TAG, 0, 2);
  		case XED_REG_X87OPCODE:        return RegisterInfo(XED_REG_X87OPCODE, 0, 2);
  		case XED_REG_X87LASTCS:        return RegisterInfo(XED_REG_X87LASTCS, 0, 2);
  		case XED_REG_X87LASTDS:        return RegisterInfo(XED_REG_X87LASTDS, 0, 2);
  		case XED_REG_X87LASTIP:        return RegisterInfo(XED_REG_X87LASTIP, 0, 8);
  		case XED_REG_X87LASTDP:        return RegisterInfo(XED_REG_X87LASTDP, 0, 8);
  		case XED_REG_X87PUSH:          return RegisterInfo(XED_REG_X87PUSH, 0, 8);
  		case XED_REG_X87POP:           return RegisterInfo(XED_REG_X87POP, 0, 8);
  		case XED_REG_X87POP2:          return RegisterInfo(XED_REG_X87POP2, 0, 8);
  
  		case XED_REG_EFLAGS:    return RegisterInfo(XED_REG_RFLAGS, 0, 4, true);
  
  		case XED_REG_EAX:       return RegisterInfo(XED_REG_RAX, 0, 4, true);
  		case XED_REG_ECX:       return RegisterInfo(XED_REG_RCX, 0, 4, true);
  		case XED_REG_EDX:       return RegisterInfo(XED_REG_RDX, 0, 4, true);
  		case XED_REG_EBX:       return RegisterInfo(XED_REG_RBX, 0, 4, true);
  
  		case XED_REG_R8D:       return RegisterInfo(XED_REG_R8, 0, 4, true);
  		case XED_REG_R9D:       return RegisterInfo(XED_REG_R9, 0, 4, true);
  		case XED_REG_R10D:      return RegisterInfo(XED_REG_R10, 0, 4, true);
  		case XED_REG_R11D:      return RegisterInfo(XED_REG_R11, 0, 4, true);
  		case XED_REG_R12D:      return RegisterInfo(XED_REG_R12, 0, 4, true);
  		case XED_REG_R13D:      return RegisterInfo(XED_REG_R13, 0, 4, true);
  		case XED_REG_R14D:      return RegisterInfo(XED_REG_R14, 0, 4, true);
  		case XED_REG_R15D:      return RegisterInfo(XED_REG_R15, 0, 4, true);
  
  		// 48-Bit
  		case XED_REG_GDTR:      return RegisterInfo(XED_REG_GDTR, 0, 6);
  		case XED_REG_LDTR:      return RegisterInfo(XED_REG_LDTR, 0, 6);
  		case XED_REG_IDTR:      return RegisterInfo(XED_REG_IDTR, 0, 6);
  
  		// 64-Bit
  		case XED_REG_FSBASE:    return RegisterInfo(XED_REG_FSBASE, 0, 8);
  		case XED_REG_GSBASE:    return RegisterInfo(XED_REG_GSBASE, 0, 8);
  		case XED_REG_TSC:       return RegisterInfo(XED_REG_TSC, 0, 8);
  		case XED_REG_TSCAUX:    return RegisterInfo(XED_REG_TSCAUX, 0, 8);
  		case XED_REG_TR:        return RegisterInfo(XED_REG_TR, 0, 8);
  
  		case XED_REG_MMX0:      return RegisterInfo(XED_REG_ST0, 0, 8);
  		case XED_REG_MMX1:      return RegisterInfo(XED_REG_ST1, 0, 8);
  		case XED_REG_MMX2:      return RegisterInfo(XED_REG_ST2, 0, 8);
  		case XED_REG_MMX3:      return RegisterInfo(XED_REG_ST3, 0, 8);
  		case XED_REG_MMX4:      return RegisterInfo(XED_REG_ST4, 0, 8);
  		case XED_REG_MMX5:      return RegisterInfo(XED_REG_ST5, 0, 8);
  		case XED_REG_MMX6:      return RegisterInfo(XED_REG_ST6, 0, 8);
  		case XED_REG_MMX7:      return RegisterInfo(XED_REG_ST7, 0, 8);
  
  		case XED_REG_RIP:       return RegisterInfo(XED_REG_RIP, 0, 8);
  
  		case XED_REG_RSP:       return RegisterInfo(XED_REG_RSP, 0, 8);
  		case XED_REG_RBP:       return RegisterInfo(XED_REG_RBP, 0, 8);
  		case XED_REG_RSI:       return RegisterInfo(XED_REG_RSI, 0, 8);
  		case XED_REG_RDI:       return RegisterInfo(XED_REG_RDI, 0, 8);
  
  		case XED_REG_RFLAGS:    return RegisterInfo(XED_REG_RFLAGS, 0, 8);
  
  		case XED_REG_MXCSR:     return RegisterInfo(XED_REG_MXCSR, 0, 8);
  		case XED_REG_XCR0:      return RegisterInfo(XED_REG_XCR0, 0, 8);
  		case XED_REG_SSP:       return RegisterInfo(XED_REG_SSP, 0, 8);
  
  		case XED_REG_RAX:       return RegisterInfo(XED_REG_RAX, 0, 8);
  		case XED_REG_RCX:       return RegisterInfo(XED_REG_RCX, 0, 8);
  		case XED_REG_RDX:       return RegisterInfo(XED_REG_RDX, 0, 8);
  		case XED_REG_RBX:       return RegisterInfo(XED_REG_RBX, 0, 8);
  		case XED_REG_R8:        return RegisterInfo(XED_REG_R8, 0, 8);
  		case XED_REG_R9:        return RegisterInfo(XED_REG_R9, 0, 8);
  		case XED_REG_R10:       return RegisterInfo(XED_REG_R10, 0, 8);
  		case XED_REG_R11:       return RegisterInfo(XED_REG_R11, 0, 8);
  		case XED_REG_R12:       return RegisterInfo(XED_REG_R12, 0, 8);
  		case XED_REG_R13:       return RegisterInfo(XED_REG_R13, 0, 8);
  		case XED_REG_R14:       return RegisterInfo(XED_REG_R14, 0, 8);
  		case XED_REG_R15:       return RegisterInfo(XED_REG_R15, 0, 8);
  
  		case XED_REG_BNDCFGU:   return RegisterInfo(XED_REG_BNDCFGU, 0, 8);
  		case XED_REG_BNDSTATUS: return RegisterInfo(XED_REG_BNDSTATUS, 0, 8);
  
  		case XED_REG_K0:        return RegisterInfo(XED_REG_K0, 0, 8);
  		case XED_REG_K1:        return RegisterInfo(XED_REG_K1, 0, 8);
  		case XED_REG_K2:        return RegisterInfo(XED_REG_K2, 0, 8);
  		case XED_REG_K3:        return RegisterInfo(XED_REG_K3, 0, 8);
  		case XED_REG_K4:        return RegisterInfo(XED_REG_K4, 0, 8);
  		case XED_REG_K5:        return RegisterInfo(XED_REG_K5, 0, 8);
  		case XED_REG_K6:        return RegisterInfo(XED_REG_K6, 0, 8);
  		case XED_REG_K7:        return RegisterInfo(XED_REG_K7, 0, 8);
  
  		case XED_REG_CR0:       return RegisterInfo(XED_REG_CR0, 0, 8);
  		case XED_REG_CR1:       return RegisterInfo(XED_REG_CR1, 0, 8);
  		case XED_REG_CR2:       return RegisterInfo(XED_REG_CR2, 0, 8);
  		case XED_REG_CR3:       return RegisterInfo(XED_REG_CR3, 0, 8);
  		case XED_REG_CR4:       return RegisterInfo(XED_REG_CR4, 0, 8);
  		case XED_REG_CR5:       return RegisterInfo(XED_REG_CR5, 0, 8);
  		case XED_REG_CR6:       return RegisterInfo(XED_REG_CR6, 0, 8);
  		case XED_REG_CR7:       return RegisterInfo(XED_REG_CR7, 0, 8);
  		case XED_REG_CR8:       return RegisterInfo(XED_REG_CR8, 0, 8);
  		case XED_REG_CR9:       return RegisterInfo(XED_REG_CR9, 0, 8);
  		case XED_REG_CR10:      return RegisterInfo(XED_REG_CR10, 0, 8);
  		case XED_REG_CR11:      return RegisterInfo(XED_REG_CR11, 0, 8);
  		case XED_REG_CR12:      return RegisterInfo(XED_REG_CR12, 0, 8);
  		case XED_REG_CR13:      return RegisterInfo(XED_REG_CR13, 0, 8);
  		case XED_REG_CR14:      return RegisterInfo(XED_REG_CR14, 0, 8);
  		case XED_REG_CR15:      return RegisterInfo(XED_REG_CR15, 0, 8);
  
  		case XED_REG_DR0:       return RegisterInfo(XED_REG_DR0, 0, 8);
  		case XED_REG_DR1:       return RegisterInfo(XED_REG_DR1, 0, 8);
  		case XED_REG_DR2:       return RegisterInfo(XED_REG_DR2, 0, 8);
  		case XED_REG_DR3:       return RegisterInfo(XED_REG_DR3, 0, 8);
  		case XED_REG_DR4:       return RegisterInfo(XED_REG_DR4, 0, 8);
  		case XED_REG_DR5:       return RegisterInfo(XED_REG_DR5, 0, 8);
  		case XED_REG_DR6:       return RegisterInfo(XED_REG_DR6, 0, 8);
  		case XED_REG_DR7:       return RegisterInfo(XED_REG_DR7, 0, 8);
  
  		// 80-Bit
  		case XED_REG_ST0:       return RegisterInfo(XED_REG_ST0, 0, 10);
  		case XED_REG_ST1:       return RegisterInfo(XED_REG_ST1, 0, 10);
  		case XED_REG_ST2:       return RegisterInfo(XED_REG_ST2, 0, 10);
  		case XED_REG_ST3:       return RegisterInfo(XED_REG_ST3, 0, 10);
  		case XED_REG_ST4:       return RegisterInfo(XED_REG_ST4, 0, 10);
  		case XED_REG_ST5:       return RegisterInfo(XED_REG_ST5, 0, 10);
  		case XED_REG_ST6:       return RegisterInfo(XED_REG_ST6, 0, 10);
  		case XED_REG_ST7:       return RegisterInfo(XED_REG_ST7, 0, 10);
  
  		case REG_X87_r(0):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(0), 0, 10);
  		case REG_X87_r(1):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(1), 0, 10);
  		case REG_X87_r(2):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(2), 0, 10);
  		case REG_X87_r(3):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(3), 0, 10);
  		case REG_X87_r(4):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(4), 0, 10);
  		case REG_X87_r(5):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(5), 0, 10);
  		case REG_X87_r(6):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(6), 0, 10);
  		case REG_X87_r(7):      return RegisterInfo((xed_reg_enum_t)REG_X87_r(7), 0, 10);
  
  		// 128-Bit
  		case XED_REG_XMM0:      return RegisterInfo(XED_REG_ZMM0, 0, 16);
  		case XED_REG_XMM1:      return RegisterInfo(XED_REG_ZMM1, 0, 16);
  		case XED_REG_XMM2:      return RegisterInfo(XED_REG_ZMM2, 0, 16);
  		case XED_REG_XMM3:      return RegisterInfo(XED_REG_ZMM3, 0, 16);
  		case XED_REG_XMM4:      return RegisterInfo(XED_REG_ZMM4, 0, 16);
  		case XED_REG_XMM5:      return RegisterInfo(XED_REG_ZMM5, 0, 16);
  		case XED_REG_XMM6:      return RegisterInfo(XED_REG_ZMM6, 0, 16);
  		case XED_REG_XMM7:      return RegisterInfo(XED_REG_ZMM7, 0, 16);
  		case XED_REG_XMM8:      return RegisterInfo(XED_REG_ZMM8, 0, 16);
  		case XED_REG_XMM9:      return RegisterInfo(XED_REG_ZMM9, 0, 16);
  		case XED_REG_XMM10:     return RegisterInfo(XED_REG_ZMM10, 0, 16);
  		case XED_REG_XMM11:     return RegisterInfo(XED_REG_ZMM11, 0, 16);
  		case XED_REG_XMM12:     return RegisterInfo(XED_REG_ZMM12, 0, 16);
  		case XED_REG_XMM13:     return RegisterInfo(XED_REG_ZMM13, 0, 16);
  		case XED_REG_XMM14:     return RegisterInfo(XED_REG_ZMM14, 0, 16);
  		case XED_REG_XMM15:     return RegisterInfo(XED_REG_ZMM15, 0, 16);
  		case XED_REG_XMM16:     return RegisterInfo(XED_REG_ZMM16, 0, 16);
  		case XED_REG_XMM17:     return RegisterInfo(XED_REG_ZMM17, 0, 16);
  		case XED_REG_XMM18:     return RegisterInfo(XED_REG_ZMM18, 0, 16);
  		case XED_REG_XMM19:     return RegisterInfo(XED_REG_ZMM19, 0, 16);
  		case XED_REG_XMM20:     return RegisterInfo(XED_REG_ZMM20, 0, 16);
  		case XED_REG_XMM21:     return RegisterInfo(XED_REG_ZMM21, 0, 16);
  		case XED_REG_XMM22:     return RegisterInfo(XED_REG_ZMM22, 0, 16);
  		case XED_REG_XMM23:     return RegisterInfo(XED_REG_ZMM23, 0, 16);
  		case XED_REG_XMM24:     return RegisterInfo(XED_REG_ZMM24, 0, 16);
  		case XED_REG_XMM25:     return RegisterInfo(XED_REG_ZMM25, 0, 16);
  		case XED_REG_XMM26:     return RegisterInfo(XED_REG_ZMM26, 0, 16);
  		case XED_REG_XMM27:     return RegisterInfo(XED_REG_ZMM27, 0, 16);
  		case XED_REG_XMM28:     return RegisterInfo(XED_REG_ZMM28, 0, 16);
  		case XED_REG_XMM29:     return RegisterInfo(XED_REG_ZMM29, 0, 16);
  		case XED_REG_XMM30:     return RegisterInfo(XED_REG_ZMM30, 0, 16);
  		case XED_REG_XMM31:     return RegisterInfo(XED_REG_ZMM31, 0, 16);
  
  		case XED_REG_BND0:      return RegisterInfo(XED_REG_BND0, 0, 16);
  		case XED_REG_BND1:      return RegisterInfo(XED_REG_BND1, 0, 16);
  		case XED_REG_BND2:      return RegisterInfo(XED_REG_BND2, 0, 16);
  		case XED_REG_BND3:      return RegisterInfo(XED_REG_BND3, 0, 16);
  
  		// 256-Bit
  		case XED_REG_YMM0:      return RegisterInfo(XED_REG_ZMM0, 0, 32);
  		case XED_REG_YMM1:      return RegisterInfo(XED_REG_ZMM1, 0, 32);
  		case XED_REG_YMM2:      return RegisterInfo(XED_REG_ZMM2, 0, 32);
  		case XED_REG_YMM3:      return RegisterInfo(XED_REG_ZMM3, 0, 32);
  		case XED_REG_YMM4:      return RegisterInfo(XED_REG_ZMM4, 0, 32);
  		case XED_REG_YMM5:      return RegisterInfo(XED_REG_ZMM5, 0, 32);
  		case XED_REG_YMM6:      return RegisterInfo(XED_REG_ZMM6, 0, 32);
  		case XED_REG_YMM7:      return RegisterInfo(XED_REG_ZMM7, 0, 32);
  		case XED_REG_YMM8:      return RegisterInfo(XED_REG_ZMM8, 0, 32);
  		case XED_REG_YMM9:      return RegisterInfo(XED_REG_ZMM9, 0, 32);
  		case XED_REG_YMM10:     return RegisterInfo(XED_REG_ZMM10, 0, 32);
  		case XED_REG_YMM11:     return RegisterInfo(XED_REG_ZMM11, 0, 32);
  		case XED_REG_YMM12:     return RegisterInfo(XED_REG_ZMM12, 0, 32);
  		case XED_REG_YMM13:     return RegisterInfo(XED_REG_ZMM13, 0, 32);
  		case XED_REG_YMM14:     return RegisterInfo(XED_REG_ZMM14, 0, 32);
  		case XED_REG_YMM15:     return RegisterInfo(XED_REG_ZMM15, 0, 32);
  		case XED_REG_YMM16:     return RegisterInfo(XED_REG_ZMM16, 0, 32);
  		case XED_REG_YMM17:     return RegisterInfo(XED_REG_ZMM17, 0, 32);
  		case XED_REG_YMM18:     return RegisterInfo(XED_REG_ZMM18, 0, 32);
  		case XED_REG_YMM19:     return RegisterInfo(XED_REG_ZMM19, 0, 32);
  		case XED_REG_YMM20:     return RegisterInfo(XED_REG_ZMM20, 0, 32);
  		case XED_REG_YMM21:     return RegisterInfo(XED_REG_ZMM21, 0, 32);
  		case XED_REG_YMM22:     return RegisterInfo(XED_REG_ZMM22, 0, 32);
  		case XED_REG_YMM23:     return RegisterInfo(XED_REG_ZMM23, 0, 32);
  		case XED_REG_YMM24:     return RegisterInfo(XED_REG_ZMM24, 0, 32);
  		case XED_REG_YMM25:     return RegisterInfo(XED_REG_ZMM25, 0, 32);
  		case XED_REG_YMM26:     return RegisterInfo(XED_REG_ZMM26, 0, 32);
  		case XED_REG_YMM27:     return RegisterInfo(XED_REG_ZMM27, 0, 32);
  		case XED_REG_YMM28:     return RegisterInfo(XED_REG_ZMM28, 0, 32);
  		case XED_REG_YMM29:     return RegisterInfo(XED_REG_ZMM29, 0, 32);
  		case XED_REG_YMM30:     return RegisterInfo(XED_REG_ZMM30, 0, 32);
  		case XED_REG_YMM31:     return RegisterInfo(XED_REG_ZMM31, 0, 32);
  
  		// 512-Bit
  		case XED_REG_ZMM0:      return RegisterInfo(XED_REG_ZMM0, 0, 64);
  		case XED_REG_ZMM1:      return RegisterInfo(XED_REG_ZMM1, 0, 64);
  		case XED_REG_ZMM2:      return RegisterInfo(XED_REG_ZMM2, 0, 64);
  		case XED_REG_ZMM3:      return RegisterInfo(XED_REG_ZMM3, 0, 64);
  		case XED_REG_ZMM4:      return RegisterInfo(XED_REG_ZMM4, 0, 64);
  		case XED_REG_ZMM5:      return RegisterInfo(XED_REG_ZMM5, 0, 64);
  		case XED_REG_ZMM6:      return RegisterInfo(XED_REG_ZMM6, 0, 64);
  		case XED_REG_ZMM7:      return RegisterInfo(XED_REG_ZMM7, 0, 64);
  		case XED_REG_ZMM8:      return RegisterInfo(XED_REG_ZMM8, 0, 64);
  		case XED_REG_ZMM9:      return RegisterInfo(XED_REG_ZMM9, 0, 64);
  		case XED_REG_ZMM10:     return RegisterInfo(XED_REG_ZMM10, 0, 64);
  		case XED_REG_ZMM11:     return RegisterInfo(XED_REG_ZMM11, 0, 64);
  		case XED_REG_ZMM12:     return RegisterInfo(XED_REG_ZMM12, 0, 64);
  		case XED_REG_ZMM13:     return RegisterInfo(XED_REG_ZMM13, 0, 64);
  		case XED_REG_ZMM14:     return RegisterInfo(XED_REG_ZMM14, 0, 64);
  		case XED_REG_ZMM15:     return RegisterInfo(XED_REG_ZMM15, 0, 64);
  		case XED_REG_ZMM16:     return RegisterInfo(XED_REG_ZMM16, 0, 64);
  		case XED_REG_ZMM17:     return RegisterInfo(XED_REG_ZMM17, 0, 64);
  		case XED_REG_ZMM18:     return RegisterInfo(XED_REG_ZMM18, 0, 64);
  		case XED_REG_ZMM19:     return RegisterInfo(XED_REG_ZMM19, 0, 64);
  		case XED_REG_ZMM20:     return RegisterInfo(XED_REG_ZMM20, 0, 64);
  		case XED_REG_ZMM21:     return RegisterInfo(XED_REG_ZMM21, 0, 64);
  		case XED_REG_ZMM22:     return RegisterInfo(XED_REG_ZMM22, 0, 64);
  		case XED_REG_ZMM23:     return RegisterInfo(XED_REG_ZMM23, 0, 64);
  		case XED_REG_ZMM24:     return RegisterInfo(XED_REG_ZMM24, 0, 64);
  		case XED_REG_ZMM25:     return RegisterInfo(XED_REG_ZMM25, 0, 64);
  		case XED_REG_ZMM26:     return RegisterInfo(XED_REG_ZMM26, 0, 64);
  		case XED_REG_ZMM27:     return RegisterInfo(XED_REG_ZMM27, 0, 64);
  		case XED_REG_ZMM28:     return RegisterInfo(XED_REG_ZMM28, 0, 64);
  		case XED_REG_ZMM29:     return RegisterInfo(XED_REG_ZMM29, 0, 64);
  		case XED_REG_ZMM30:     return RegisterInfo(XED_REG_ZMM30, 0, 64);
  		case XED_REG_ZMM31:     return RegisterInfo(XED_REG_ZMM31, 0, 64);
  
  		default:                return RegisterInfo(XED_REG_INVALID, 0, 0);
  		}
  	}
  
  	virtual uint32_t GetStackPointerRegister() override
  	{
  		return XED_REG_RSP;
  	}
  };
  
  
  class X86BaseCallingConvention: public CallingConvention
  {
  public:
  	X86BaseCallingConvention(Architecture* arch, const string& name): CallingConvention(arch, name)
  	{
  	}
  
  	virtual vector<uint32_t> GetCallerSavedRegisters() override
  	{
  		return vector<uint32_t>{ XED_REG_EAX, XED_REG_ECX, XED_REG_EDX };
  	}
  
  	virtual vector<uint32_t> GetCalleeSavedRegisters() override
  	{
  		return vector<uint32_t>{ XED_REG_EBX, XED_REG_EBP, XED_REG_ESI, XED_REG_EDI };
  	}
  
  	virtual uint32_t GetGlobalPointerRegister() override
  	{
  		return XED_REG_EBX;
  	}
  
  	virtual uint32_t GetIntegerReturnValueRegister() override
  	{
  		return XED_REG_EAX;
  	}
  
  	virtual uint32_t GetHighIntegerReturnValueRegister() override
  	{
  		return XED_REG_EDX;
  	}
  
  	virtual uint32_t GetFloatReturnValueRegister() override
  	{
  		return XED_REG_ST0;
  	}
  
  	virtual RegisterValue GetIncomingFlagValue(uint32_t flag, Function*) override
  	{
  		RegisterValue result;
  		if (flag == IL_FLAG_D)
  		{
  			result.state = ConstantValue;
  			result.value = 0;
  		}
  		return result;
  	}
  };
  
  
  class X86CdeclCallingConvention: public X86BaseCallingConvention
  {
  public:
  	X86CdeclCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "cdecl")
  	{
  	}
  };
  
  
  class X86StdcallCallingConvention: public X86BaseCallingConvention
  {
  public:
  	X86StdcallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "stdcall")
  	{
  	}
  
  	virtual bool IsStackAdjustedOnReturn() override
  	{
  		return true;
  	}
  };
  
  
  class X86RegParmCallingConvention: public X86BaseCallingConvention
  {
  public:
  	X86RegParmCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "regparm")
  	{
  	}
  
  	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
  	{
  		return vector<uint32_t>{ XED_REG_EAX, XED_REG_EDX, XED_REG_ECX };
  	}
  };
  
  
  class X86FastcallCallingConvention: public X86BaseCallingConvention
  {
  public:
  	X86FastcallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "fastcall")
  	{
  	}
  
  	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
  	{
  		return vector<uint32_t>{ XED_REG_ECX, XED_REG_EDX };
  	}
  };
  
  
  class X86ThiscallCallingConvention: public X86BaseCallingConvention
  {
  public:
  	X86ThiscallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "thiscall")
  	{
  	}
  
  	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
  	{
  		return vector<uint32_t>{ XED_REG_ECX };
  	}
  };
  
  
  class X86LinuxSystemCallConvention: public CallingConvention
  {
  public:
  	X86LinuxSystemCallConvention(Architecture* arch): CallingConvention(arch, "linux-syscall")
  	{
  	}
  
  	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_EAX, XED_REG_EBX, XED_REG_ECX, XED_REG_EDX, XED_REG_ESI, XED_REG_EDI, XED_REG_EBP };
  	}
  
  	virtual vector<uint32_t> GetCallerSavedRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_EAX, XED_REG_EDX };
  	}
  
  	virtual vector<uint32_t> GetCalleeSavedRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_EBX, XED_REG_EBP, XED_REG_ESI, XED_REG_EDI };
  	}
  
  	virtual uint32_t GetIntegerReturnValueRegister() override
  	{
  		return XED_REG_EAX;
  	}
  
  	virtual uint32_t GetHighIntegerReturnValueRegister() override
  	{
  		return XED_REG_EDX;
  	}
  
  	virtual uint32_t GetFloatReturnValueRegister() override
  	{
  		return XED_REG_ST0;
  	}
  
  	virtual bool IsEligibleForHeuristics() override
  	{
  		return false;
  	}
  };
  
  
  class X64BaseCallingConvention: public CallingConvention
  {
  public:
  	X64BaseCallingConvention(Architecture* arch, const string& name): CallingConvention(arch, name)
  	{
  	}
  
  	virtual uint32_t GetIntegerReturnValueRegister() override
  	{
  		return XED_REG_RAX;
  	}
  
  	virtual uint32_t GetFloatReturnValueRegister() override
  	{
  		return XED_REG_XMM0;
  	}
  
  	virtual RegisterValue GetIncomingFlagValue(uint32_t flag, Function*) override
  	{
  		RegisterValue result;
  		if (flag == IL_FLAG_D)
  		{
  			result.state = ConstantValue;
  			result.value = 0;
  		}
  		return result;
  	}
  };
  
  
  class X64SystemVCallingConvention: public X64BaseCallingConvention
  {
  public:
  	X64SystemVCallingConvention(Architecture* arch): X64BaseCallingConvention(arch, "sysv")
  	{
  	}
  
  	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_RDI, XED_REG_RSI, XED_REG_RDX, XED_REG_RCX, XED_REG_R8, XED_REG_R9 };
  	}
  
  	virtual vector<uint32_t> GetFloatArgumentRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_XMM0, XED_REG_XMM1, XED_REG_XMM2, XED_REG_XMM3, XED_REG_XMM4, XED_REG_XMM5, XED_REG_XMM6, XED_REG_XMM7 };
  	}
  
  	virtual vector<uint32_t> GetCallerSavedRegisters() override
  	{
  		return vector<uint32_t> {
  			XED_REG_RAX, XED_REG_RCX, XED_REG_RDX,
  			XED_REG_RSI, XED_REG_RDI,
  			XED_REG_R8, XED_REG_R9, XED_REG_R10, XED_REG_R11,
  			XED_REG_XMM0, XED_REG_XMM1, XED_REG_XMM2, XED_REG_XMM3, XED_REG_XMM4, XED_REG_XMM5, XED_REG_XMM6, XED_REG_XMM7, XED_REG_XMM8, XED_REG_XMM9, XED_REG_XMM10, XED_REG_XMM11, XED_REG_XMM12, XED_REG_XMM13, XED_REG_XMM14, XED_REG_XMM15 };
  	}
  
  	virtual vector<uint32_t> GetCalleeSavedRegisters() override
  	{
  		return vector<uint32_t> {
  			XED_REG_RBX, XED_REG_RBP,
  			XED_REG_R12, XED_REG_R13, XED_REG_R14, XED_REG_R15 };
  	}
  };
  
  
  class X64WindowsCallingConvention: public X64BaseCallingConvention
  {
  public:
  	X64WindowsCallingConvention(Architecture* arch): X64BaseCallingConvention(arch, "win64")
  	{
  	}
  
  	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_RCX, XED_REG_RDX, XED_REG_R8, XED_REG_R9 };
  	}
  
  	virtual vector<uint32_t> GetFloatArgumentRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_XMM0, XED_REG_XMM1, XED_REG_XMM2, XED_REG_XMM3 };
  	}
  
  	virtual vector<uint32_t> GetCallerSavedRegisters() override
  	{
  		return vector<uint32_t> {
  			XED_REG_RAX, XED_REG_RCX, XED_REG_RDX,
  			XED_REG_R8, XED_REG_R9, XED_REG_R10, XED_REG_R11,
  			XED_REG_XMM4, XED_REG_XMM5 };
  	}
  
  	virtual vector<uint32_t> GetCalleeSavedRegisters() override
  	{
  		return vector<uint32_t> {
  			XED_REG_RBX, XED_REG_RBP, XED_REG_RSI, XED_REG_RDI,
  			XED_REG_R12, XED_REG_R13, XED_REG_R14, XED_REG_R15 };
  	}
  
  	virtual bool AreArgumentRegistersSharedIndex() override
  	{
  		return true;
  	}
  
  	virtual bool IsStackReservedForArgumentRegisters() override
  	{
  		return true;
  	}
  };
  
  
  class X64LinuxSystemCallConvention: public CallingConvention
  {
  public:
  	X64LinuxSystemCallConvention(Architecture* arch): CallingConvention(arch, "linux-syscall")
  	{
  	}
  
  	virtual vector<uint32_t> GetIntegerArgumentRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_RAX, XED_REG_RDI, XED_REG_RSI, XED_REG_RDX, XED_REG_R10, XED_REG_R8, XED_REG_R9 };
  	}
  
  	virtual vector<uint32_t> GetCallerSavedRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_RAX, XED_REG_RCX, XED_REG_R11 };
  	}
  
  	virtual vector<uint32_t> GetCalleeSavedRegisters() override
  	{
  		return vector<uint32_t> { XED_REG_RBX, XED_REG_RBP, XED_REG_RSI, XED_REG_RDI,
  			XED_REG_R12, XED_REG_R13, XED_REG_R14, XED_REG_R15 };
  	}
  
  	virtual uint32_t GetIntegerReturnValueRegister() override
  	{
  		return XED_REG_RAX;
  	}
  
  	virtual bool IsEligibleForHeuristics() override
  	{
  		return false;
  	}
  };
  
  
  class x86MachoRelocationHandler: public RelocationHandler
  {
  public:
  
  	virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest,
  		size_t len) override
  	{
  		(void)view;
  		(void)arch;
  		(void)len;
  		auto info = reloc->GetInfo();
  		uint64_t pcRelAddr = info.pcRelative ? reloc->GetAddress() : 0;
  		// TODO these need tested
  		// uint8_t* dest8 = (uint8_t*)dest;
  		// uint16_t* dest16 = (uint16_t*)dest;
  		uint32_t* dest32 = (uint32_t*)dest;
  		uint32_t target = (uint32_t)reloc->GetTarget();
  
  		switch (info.nativeType)
  		{
  		case (uint64_t)-1: // Magic number defined in MachOView.cpp
  			// We need to write a jump absolute `jmp target`
  			dest[0] = '\xe9';
  			((uint32_t*)&dest[1])[0] = target - (uint32_t)reloc->GetAddress() - 5;
  			break;
  		case (uint64_t)-2: // Magic number defined in MachOView.cpp
  			dest32[0] = target;
  			break;
  		case GENERIC_RELOC_VANILLA:
  			switch (info.size)
  			{
  				// TODO these need tested
  				// case 1: *dest8 = target - pcRelAddr; break;
  				// case 2: *dest16 = target - pcRelAddr; break;
  				case 4: *dest32 = (uint32_t)(target - pcRelAddr); break;
  				default: break;
  			}
  			// TODO rebasing
  			break;
  
  		default:
  			break;
  		}
  
  		return true;
  	}
  
  	virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
  	{
  		(void)view; (void)arch;
  		set<uint64_t> relocTypes;
  		for (size_t i = 0; i < result.size(); i++)
  		{
  			result[i].type = StandardRelocationType;
  			switch (result[i].nativeType)
  			{
  			case GENERIC_RELOC_VANILLA:
  				if (result[i].size != 4)
  				{
  					result[i].type = IgnoredRelocation;
  					relocTypes.insert(result[i].nativeType);
  				}
  				break;
  			default:
  				result[i].type = UnhandledRelocation;
  				relocTypes.insert(result[i].nativeType);
  				break;
  			}
  		}
  
  		for (auto& reloc : relocTypes)
  			LogWarn("Unsupported Mach-O relocation type: %s", GetRelocationString((Machox86RelocationType)reloc));
  		return true;
  	}
  };
  
  class x86ElfRelocationHandler: public RelocationHandler
  {
  public:
  	virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
  	{
  		(void)view; (void)arch;
  		set<uint64_t> relocTypes;
  		for (auto& reloc : result)
  		{
  			reloc.type = StandardRelocationType;
  			switch (reloc.nativeType)
  			{
  			case R_386_NONE:
  				reloc.type = IgnoredRelocation;
  				break;
  			case R_386_32:
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				break;
  			case R_386_PC32:
  			case R_386_GOT32:
  			case R_386_PLT32:
  				reloc.pcRelative = true;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				break;
  			case R_386_RELATIVE:
  				reloc.pcRelative = false;
  				reloc.baseRelative = true;
  				reloc.hasSign = false;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				reloc.implicitAddend = true;
  				reloc.addend = 0;
  				break;
  			case R_386_COPY:
  				reloc.type = ELFCopyRelocationType;
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				break;
  			case R_386_GLOB_DAT:
  				reloc.type = ELFGlobalRelocationType;
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				reloc.implicitAddend = false;
  				break;
  			case R_386_JUMP_SLOT:
  				reloc.type = ELFJumpSlotRelocationType;
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				reloc.implicitAddend = false;
  				break;
  			default:
  				reloc.type = UnhandledRelocation;
  				relocTypes.insert(reloc.nativeType);
  			}
  		}
  		for (auto& reloc : relocTypes)
  			LogWarn("Unsupported ELF relocation type: %s", GetRelocationString((Elfx86RelocationType)reloc));
  		return true;
  	}
  };
  
  class x64MachoRelocationHandler: public RelocationHandler
  {
  public:
  	virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override
  	{
  		(void)view;
  		(void)arch;
  		auto info = reloc->GetInfo();
  		uint64_t pcRelAddr = info.pcRelative ? reloc->GetAddress() : 0;
  		if (len < info.size)
  			return false;
  		uint32_t target = (uint32_t)info.target;
  		uint32_t* dest32 = (uint32_t*)dest;
  		uint64_t* dest64 = (uint64_t*)dest;
  		switch (info.nativeType)
  		{
  		case X86_64_RELOC_BRANCH:
  			if (info.size == 4)
  				dest32[0] = dest32[0] - 4 + target - (uint32_t)pcRelAddr;
  			break;
  		case X86_64_RELOC_GOT_LOAD:
  			dest32[0] = dest32[0] - 4 + target  - (uint32_t)pcRelAddr;
  			break;
  		case X86_64_RELOC_SIGNED:
  			dest32[0] = dest32[0] + target - (uint32_t)pcRelAddr;
  			break;
  		case X86_64_RELOC_SIGNED_1:
  			dest32[0] = dest32[0] + 1 + target - (uint32_t)pcRelAddr;
  			break;
  		case X86_64_RELOC_SIGNED_2:
  			dest32[0] = dest32[0] + 2 + target - (uint32_t)pcRelAddr;
  			break;
  		case X86_64_RELOC_SIGNED_4:
  			dest32[0] = dest32[0] + 4 + target - (uint32_t)pcRelAddr;
  			break;
  		case X86_64_RELOC_GOT:
  			dest32[0] = dest32[0] + target - (uint32_t)pcRelAddr;
  			break;
  		case X86_64_RELOC_UNSIGNED:
  			if (!info.external)
  			{
  				switch (info.size)
  				{
  					case 4: *dest32 += target - (uint32_t)pcRelAddr; break;
  					case 8: *dest64 += info.target - pcRelAddr; break;
  					default: break;
  				}
  			}
  			// TODO rebasing
  			break;
  		case X86_64_RELOC_SUBTRACTOR:
  			if (!info.next)
  				break;
  			dest64[0] = dest64[0] + info.next->target - target;
  			break;
  		case (uint64_t) -2:
  			dest64[0] = reloc->GetTarget();
  			break;
  		}
  		return true;
  	}
  
  
  	virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
  	{
  		(void)view; (void)arch;
  		set<uint64_t> relocTypes;
  		for (size_t i = 0; i < result.size(); i++)
  		{
  			result[i].type = StandardRelocationType;
  			switch (result[i].nativeType)
  			{
  			case X86_64_RELOC_UNSIGNED:
  				result[i].hasSign = false;
  				break;
  			case X86_64_RELOC_BRANCH:
  				result[i].pcRelative = true;
  				result[i].size = 4;
  				result[i].implicitAddend = true;
  				break;
  			case X86_64_RELOC_GOT:
  			case X86_64_RELOC_GOT_LOAD:
  				result[i].pcRelative = true;
  				result[i].size = 4;
  				result[i].implicitAddend = true;
  				break;
  			case X86_64_RELOC_SIGNED:
  				result[i].implicitAddend = true;
  				result[i].size = 4;
  				result[i].hasSign = true;
  				break;
  			case X86_64_RELOC_SIGNED_1:
  				result[i].implicitAddend = true;
  				result[i].addend = 1;
  				result[i].size = 4;
  				result[i].hasSign = true;
  				break;
  			case X86_64_RELOC_SIGNED_2:
  				result[i].implicitAddend = true;
  				result[i].addend = 2;
  				result[i].size = 4;
  				result[i].hasSign = true;
  				break;
  			case X86_64_RELOC_SIGNED_4:
  				result[i].implicitAddend = true;
  				result[i].addend = 4;
  				result[i].size = 4;
  				result[i].hasSign = true;
  				break;
  			case X86_64_RELOC_SUBTRACTOR:
  				// X86_64_RELOC_SUBTRACTOR should always be followed by a X86_64_RELOC_UNSIGNED
  				if (i == result.size() - 1)
  					result[i].type = IgnoredRelocation;
  				else if ((Machox64RelocationType)result[i + 1].type != X86_64_RELOC_UNSIGNED)
  					result[i].type = IgnoredRelocation;
  				else
  				{
  					result[i].next = &result[i + 1];
  					result[i + 1].type = IgnoredRelocation;
  					i++;
  				}
  				break;
  			default:
  				result[i].type = UnhandledRelocation;
  				relocTypes.insert(result[i].nativeType);
  				break;
  			}
  		}
  
  		for (auto& reloc : relocTypes)
  			LogWarn("Unsupported Mach-O relocation: %s", GetRelocationString((Machox64RelocationType)reloc));
  		return true;
  	}
  };
  
  class x64ElfRelocationHandler: public RelocationHandler
  {
  public:
  	virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override
  	{
  		BNRelocationInfo info = reloc->GetInfo();
  		switch (info.nativeType)
  		{
  		case R_X86_64_REX_GOTPCRELX: {
  			// When we're actually applying this we don't need to change the 3 first bytes,
  			// just apply it to the immediate and it's fine. However, we track all seven
  			// variable bytes so we don't lie to the user.
  			dest += 3;
  			uint64_t pc = reloc->GetAddress() + 3;
  			uint64_t addend = 0;
  			if (info.implicitAddend && info.size < sizeof(addend))
  				memcpy(&addend, dest, reloc->GetInfo().size);
  			else
  				addend = info.addend;
  			uint32_t write = (uint32_t)(reloc->GetTarget() + addend - pc);
  			memcpy(dest, (uint8_t*)&write, sizeof(uint32_t));
  			return true;
  		}
  		case R_X86_64_IRELATIVE: {
  			// What???????
  			uint64_t write = info.addend;
  			memcpy(dest, (uint8_t*)&write, sizeof(uint64_t));
  			return true;
  		}
  		default:
  			return RelocationHandler::ApplyRelocation(view, arch, reloc, dest, len);
  		}
  	}
  
  	virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
  	{
  		/*
  		From the Intel AMD64 ELF Linux ABI
  		A Represents the addend used to compute the value of the relocatable field.
  		B Represents the base address at which a shared object has been loaded into memory during execution.
  			Generally, a shared object is built with a 0 base virtual address, but the execution address will be different.
  		G Represents the offset into the global offset table at which the relocation entry’s symbol
  			will reside during execution.
  		GOT Represents the address of the global offset table.
  		L Represents the place (section offset or address) of the Procedure Linkage Table
  			entry for a symbol.
  		P Represents the place (section offset or address) of the storage unit being relocated (computed using r_offset).
  		S Represents the value of the symbol whose index resides in the relocation entry.
  		Z Represents the size of the symbol whose index resides in the relocation entry.
  		The AMD64 LP64 ABI architecture uses only Elf64_Rela relocation entries with explicit addends. Theg
  			r_addend member serves as the relocation addend.
  		The AMD64 ILP32 ABI architecture uses only Elf32_Rela relocation entries in relocatable files. Relocations
  			contained within executable files or shared objects may use either Elf32_Rela relocation or Elf32_Rel relocation.
  		*/
  		(void)view; (void)arch;
  		set<uint64_t> relocTypes;
  		for (auto& reloc : result)
  		{
  			reloc.type = StandardRelocationType;
  			switch (reloc.nativeType)
  			{
  			case R_X86_64_NONE:
  				reloc.type = IgnoredRelocation;
  				break;
  			case R_X86_64_COPY:
  				reloc.type = ELFCopyRelocationType;
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.size = 8;
  				reloc.truncateSize = 8;
  				break;
  			case R_X86_64_GLOB_DAT:
  				reloc.type = ELFGlobalRelocationType;
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.size = 8;
  				reloc.truncateSize = 8;
  				break;
  			case R_X86_64_JUMP_SLOT:
  				reloc.type = ELFJumpSlotRelocationType;
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.size = 8;
  				reloc.truncateSize = 8;
  				break;
  			case R_X86_64_8:
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 1;
  				reloc.truncateSize = 1;
  				break;
  			case R_X86_64_16:
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 2;
  				reloc.truncateSize = 2;
  				break;
  			case R_X86_64_32S:
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.hasSign = true;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				break;
  			case R_X86_64_32:
  			case R_X86_64_GOT32:
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 8;
  				reloc.truncateSize = 4;
  				break;
  			case R_X86_64_64:
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 8;
  				reloc.truncateSize = 8;
  				break;
  			case R_X86_64_PC32:
  			case R_X86_64_PLT32:
  			case R_X86_64_GOTPCREL:
  			case R_X86_64_GOTPCRELX:
  				// These are pointers into .got and .plt sections which aren't present
  				// At some point in the future we may need to create these sections
  				reloc.pcRelative = true;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 4;
  				reloc.truncateSize = 4;
  				break;
  			case R_X86_64_PC64:
  				reloc.pcRelative = true;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 8;
  				reloc.truncateSize = 8;
  				break;
  			case R_X86_64_REX_GOTPCRELX:
  				// The 3 bytes before the immediate that specify the registers and operand
  				// encoding are variable!!! Example: 49c7c400000000 vs 4c8b25d5140000
  				reloc.address -= 3;
  				reloc.pcRelative = true;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 7;
  				reloc.truncateSize = 7;
  				break;
  			case R_X86_64_RELATIVE:
  				reloc.pcRelative = false;
  				reloc.baseRelative = true;
  				reloc.hasSign = false;
  				reloc.size = 8;
  				reloc.truncateSize = 8;
  				reloc.implicitAddend = true;
  				break;
  			case R_X86_64_PC16:
  				reloc.pcRelative = true;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 8;
  				reloc.truncateSize = 2;
  				break;
  			case R_X86_64_PC8:
  				reloc.pcRelative = true;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 8;
  				reloc.truncateSize = 1;
  				break;
  			case R_X86_64_IRELATIVE:
  				reloc.pcRelative = false;
  				reloc.baseRelative = false;
  				reloc.hasSign = false;
  				reloc.size = 8;
  				reloc.truncateSize = 8;
  				break;
  			default:
  				reloc.type = UnhandledRelocation;
  				relocTypes.insert(reloc.nativeType);
  				break;
  			}
  		}
  		for (auto& reloc : relocTypes)
  			LogWarn("Unsupported ELF relocation: %s", GetRelocationString((Elfx64RelocationType)reloc));
  		return true;
  	}
  };
  
  class PeRelocationHandler: public RelocationHandler
  {
  public:
  	virtual bool ApplyRelocation(Ref<BinaryView> view, Ref<Architecture> arch, Ref<Relocation> reloc, uint8_t* dest, size_t len) override
  	{
  		// Note: info.base contains preferred base address and the base where the image is actually loaded
  		(void)view;
  		(void)arch;
  		(void)len;
  		uint64_t* data64 = (uint64_t*)dest;
  		uint32_t* data32 = (uint32_t*)dest;
  		uint16_t* data16 = (uint16_t*)dest;
  		auto info = reloc->GetInfo();
  		if (info.size == 8)
  		{
  			data64[0] += info.base;
  		}
  		else if (info.size == 4)
  		{
  			data32[0] += (uint32_t)info.base;
  		}
  		else if (info.size == 2)
  		{
  			if (info.nativeType == PE_IMAGE_REL_BASED_HIGH)
  			{
  				data16[0] = data16[0] + (uint16_t)(info.base >> 16);
  			}
  			else if (info.nativeType == PE_IMAGE_REL_BASED_LOW)
  			{
  				data16[0] = data16[0] + (uint16_t)(info.base & 0xffff);
  			}
  		}
  		return true;
  	}
  
  	virtual bool GetRelocationInfo(Ref<BinaryView> view, Ref<Architecture> arch, vector<BNRelocationInfo>& result) override
  	{
  		(void)view; (void)arch;
  		set<uint64_t> relocTypes;
  		for (auto& reloc : result)
  		{
  			switch (reloc.nativeType)
  			{
  			case PE_IMAGE_REL_BASED_ABSOLUTE:
  				reloc.type = IgnoredRelocation;
  				break;
  			case PE_IMAGE_REL_BASED_HIGHLOW:
  				reloc.size = 4;
  				break;
  			case PE_IMAGE_REL_BASED_DIR64:
  				reloc.size = 8;
  				break;
  			case PE_IMAGE_REL_BASED_HIGH:
  				reloc.size = 2;
  				break;
  			case PE_IMAGE_REL_BASED_LOW:
  				reloc.size = 2;
  				break;
  			default:
  				// By default, PE relocations are correct when not rebased.
  				// Upon rebasing, support would need to be added to correctly process the relocation
  				reloc.type = UnhandledRelocation;
  				relocTypes.insert(reloc.nativeType);
  			}
  		}
  
  		for (auto& reloc : relocTypes)
  			LogWarn("Unsupported PE relocation: %s", GetRelocationString((PeRelocationType)reloc));
  		return false;
  	}
  
  	virtual size_t GetOperandForExternalRelocation(const uint8_t* data, uint64_t addr, size_t length,
  		Ref<LowLevelILFunction> il, Ref<Relocation> relocation) override
  	{
  		(void)data;
  		(void)addr;
  		(void)length;
  		(void)il;
  		(void)relocation;
  		return BN_AUTOCOERCE_EXTERN_PTR;
  	}
  };
  
  
  static void InitX86Settings()
  {
  	Ref<Settings> settings = Settings::Instance();
  	settings->RegisterSetting("arch.x86.disassembly.syntax",
  			R"({
  			"title" : "x86 Disassembly Syntax",
  			"type" : "string",
  			"default" : "BN_INTEL",
  			"aliases" : ["arch.x86.disassemblyFlavor"],
  			"description" : "Specify disassembly syntax for the x86/x86_64 architectures.",
  			"enum" : ["BN_INTEL", "INTEL", "AT&T"],
  			"enumDescriptions" : [
  				"Sets the disassembly syntax to a simplified Intel format. (TBD) ",
  				"Sets the disassembly syntax to Intel format. (Destination on the left) ",
  				"Sets the disassembly syntax to AT&T format. (Destination on the right) "],
  			"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
  			})");
  
  	settings->RegisterSetting("arch.x86.disassembly.separator",
  			R"({
  			"title" : "x86 Disassembly Separator",
  			"type" : "string",
  			"default" : ", ",
  			"aliases" : ["arch.x86.disassemblySeperator", "arch.x86.disassemblySeparator"],
  			"description" : "Specify the token separator between operands.",
  			"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
  			})");
  
  	settings->RegisterSetting("arch.x86.disassembly.lowercase",
  			R"({
  			"title" : "x86 Disassembly Case",
  			"type" : "boolean",
  			"default" : true,
  			"aliases" : ["arch.x86.disassemblyLowercase"],
  			"description" : "Specify the case for opcodes, operands, and registers.",
  			"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
  			})");
  }
  
  
  extern "C"
  {
  	BN_DECLARE_CORE_ABI_VERSION
  
  #ifdef DEMO_VERSION
  	bool X86PluginInit()
  #else
  	BINARYNINJAPLUGIN bool CorePluginInit()
  #endif
  	{
  		InitX86Settings();
  
  		// XED Setup
  		xed_tables_init();
  
  		// Register the architectures in the global list of available architectures
  		Architecture* x16 = new X16Architecture();
  		Architecture::Register(x16);
  
  		Architecture* x86 = new X86Architecture();
  		Architecture::Register(x86);
  
  		Architecture* x64 = new X64Architecture();
  		Architecture::Register(x64);
  
  		// Register calling conventions
  		Ref<CallingConvention> conv;
  		conv = new X86CdeclCallingConvention(x86);
  		x86->RegisterCallingConvention(conv);
  		x86->SetDefaultCallingConvention(conv);
  		x86->SetCdeclCallingConvention(conv);
  		conv = new X86StdcallCallingConvention(x86);
  		x86->RegisterCallingConvention(conv);
  		x86->SetStdcallCallingConvention(conv);
  		conv = new X86RegParmCallingConvention(x86);
  		x86->RegisterCallingConvention(conv);
  		conv = new X86FastcallCallingConvention(x86);
  		x86->RegisterCallingConvention(conv);
  		conv = new X86ThiscallCallingConvention(x86);
  		x86->RegisterCallingConvention(conv);
  		conv = new X86LinuxSystemCallConvention(x86);
  		x86->RegisterCallingConvention(conv);
  
  		x86->RegisterRelocationHandler("Mach-O", new x86MachoRelocationHandler());
  		x86->RegisterRelocationHandler("ELF", new x86ElfRelocationHandler());
  		x86->RegisterRelocationHandler("PE", new PeRelocationHandler());
  
  		conv = new X64SystemVCallingConvention(x64);
  		x64->RegisterCallingConvention(conv);
  		x64->SetDefaultCallingConvention(conv);
  		x64->SetCdeclCallingConvention(conv);
  		x64->SetFastcallCallingConvention(conv);
  		x64->SetStdcallCallingConvention(conv);
  		conv = new X64WindowsCallingConvention(x64);
  		x64->RegisterCallingConvention(conv);
  		conv = new X64LinuxSystemCallConvention(x64);
  		x64->RegisterCallingConvention(conv);
  
  		x64->RegisterRelocationHandler("Mach-O", new x64MachoRelocationHandler());
  		x64->RegisterRelocationHandler("ELF", new x64ElfRelocationHandler());
  		x64->RegisterRelocationHandler("PE", new PeRelocationHandler());
  
  		// Register the architectures with the binary format parsers so that they know when to use
  		// these architectures for disassembling an executable file
  		BinaryViewType::RegisterArchitecture("ELF", 3, LittleEndian, x86);
  		BinaryViewType::RegisterArchitecture("PE", 0x14c, LittleEndian, x86);
  		BinaryViewType::RegisterArchitecture("Mach-O", 0x00000007, LittleEndian, x86);
  		x86->SetBinaryViewTypeConstant("ELF", "R_COPY", 5);
  		x86->SetBinaryViewTypeConstant("ELF", "R_JUMP_SLOT", 7);
  
  		BinaryViewType::RegisterArchitecture("ELF", 62, LittleEndian, x64);
  		BinaryViewType::RegisterArchitecture("PE", 0x8664, LittleEndian, x64);
  		BinaryViewType::RegisterArchitecture("Mach-O", 0x01000007, LittleEndian, x64);
  		x64->SetBinaryViewTypeConstant("ELF", "R_COPY", 5);
  		x64->SetBinaryViewTypeConstant("ELF", "R_JUMP_SLOT", 7);
  
  		return true;
  	}
  }
  