// **************************************************************************************************
// Delphi Instruction Decode Library
// Unit InstDecode
// https://github.com/MahdiSafsafi/delphi-detours-library

// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
// ANY KIND, either express or implied. See the License for the specific language governing rights
// and limitations under the License.
//
// The Original Code is InstDecode.pas.
//
// The Initial Developer of the Original Code is Mahdi Safsafi [SMP3].
// Portions created by Mahdi Safsafi . are Copyright (C) 2013-2017 Mahdi Safsafi .
// All Rights Reserved.
//
// **************************************************************************************************

{ ===============================> CHANGE LOG <======================================================
  ==>  Dec 27,2014 , Mahdi Safsafi :
  +BugFix : IN/INS/OUT/OUTS instructions decoding.
  +BugFix : MOV with offset instructions decoding.

  ==> Version 2:
  +Updated opcodes map .
  +Added support to three byte escape Table
  +Added support to vex decoding (vex three & two byte).
  +Added support to groups opcodes instructions.
  +Added support to decode invalid opcode .
  +Added support to 16-bits ModRm .
  +Added support to handling errors.
  +Added support for mandatory prefixes.
  +Improve Decoding Process .=> Very faster than the old one !
  +Reduce memory usage .
  +Removing inused fields.
  +Better support for REX prefix.
  +Reduce OpCodesTable data size (the old : 8670 bytes => the new one : 1020 bytes !)
  +BugFix : FPU instructions length.
  +BugFix : Instructions that use two immediat .
  +BugFix : Invalid instructions .
  +BugFix : Invalid instructions for some mandatory prefixes.
  +Many Bug Fix.

  ====================================================================================================== }

unit InstDecode;

{$IFDEF FPC}
{$MODE DELPHI}
{$ENDIF FPC}

interface

{$I Defs.inc}

uses SysUtils;

type
  PInt8 = ^Int8;
  PInt16 = ^Int16;
  PInt32 = ^Int32;
  PInt64 = ^Int64;

  PUInt8 = ^UInt8;
  PUInt16 = ^UInt16;
  PUInt32 = ^UInt32;
  PUInt64 = ^UInt64;

const
  { CPUX }
  CPUX32 = $00; { x86-32 }
  CPUX64 = $01; { x86-64 }
  CPUX = {$IFDEF CPUX64}CPUX64 {$ELSE}CPUX32 {$ENDIF};

  { Address Mode }
  am16 = $01; { 16-bit addressing mode }
  am32 = $02; { 32-bit addressing mode }
  am64 = $03; { 64-bit addressing mode }
  { Default Addressing Mode Depending on CPUX (32/64)bit }
  DefAddressMode: array [0 .. 1] of Byte = (am32, am64);
  { Used to select Addressing Mode when Address Mode Prefix is used ! }
  AddressMode: array [0 .. 1] of Byte = (am16, am32);

  { Tables }
  tbOneByte = $01; { One Byte OpCodes Table }
  tbTwoByte = $02; { Two Byte OpCodes Table }
  tbThreeByte = $03; { Three Byte OpCodes Table }
  tbFPU = $04; { FPU OpCodes Table }

  { Prefixs }
  Prf_Seg_CS = $01;
  Prf_Seg_DS = $02;
  Prf_Seg_ES = $04;
  Prf_Seg_GS = $08;
  Prf_Seg_FS = $10;
  Prf_Seg_SS = $20;
  Prf_OpSize = $40;
  Prf_AddrSize = $80;
  Prf_Lock = $100;
  Prf_Repe = $200;
  Prf_Repne = $400;
  Prf_Rex = $800;
  Prf_VEX = $1000;
  Prf_Vex2 = Prf_VEX or $2000;
  Prf_Vex3 = Prf_VEX or $4000;

  { Segment Registers }
  Seg_CS = $01;
  Seg_DS = $02;
  Seg_ES = $03;
  Seg_GS = $04;
  Seg_FS = $05;
  Seg_SS = $06;

  { OpSize }
  ops8bits = $01;
  ops16bits = $02;
  ops32bits = $04;
  ops48bits = $06;
  ops64bits = $08;
  ops128bits = $10;
  ops256bits = $20;
  ops512bits = $40;

  { OpType }
  otNone = $00;
  otRET = $01; { RET Instruction }
  otCALL = $02; { CALL Instruction }
  otJMP = $04; { JMP Instruction }
  otJ = $08;
  otJcc = $10; { Conditional JUMP Instruction }

  { OpKind }
  kGrp = $01;
  // kFPU = $02; Use OpTable !

  { Options }
  DecodeVex = $01;

  { ModRm Flags }
  mfUsed = $80; { ModRm Used }

  { Sib Flags }
  sfUsed = $01; { Sib Used }

  { Displacement Flags }
  dfUsed = $01; { Disp Used }
  dfRip = $02; { RIP Disp }
  dfSigned = $04; { Displacement can be signed ! }
  dfDispOnly = $08; { Displacement Only without registers ! }
  dfOffset = $10; { Offset coded after the opcode. }

  { Immediat Flags }
  imfUsed = $01; { Imm Used }

  { Branch Flags }
  bfUsed = $01; { JUMP/CALL Used }
  bfRel = $02; { Relative Branch }
  bfAbs = $04; { Absolute Branch }
  bfIndirect = $08; { Indirect Branch }
  bfReg = $10;
  bfFar = bfAbs or $20; { Far Branch }
  bfRip = $40;

  { Operand Flags }
  opdD64 = $01;
  opdF64 = $02;
  opdDf64 = $03;
  opdDv64 = $04;

  { Options }
  UseVA = $01;

  { General Purpose Registers }
  rEAX = $00;
  rECX = $01;
  rEDX = $02;
  rEBX = $03;
  rESP = $04;
  rEBP = $05;
  rESI = $06;
  rEDI = $07;

  { Error }
  NO_ERROR = $00;
  INVALID_CPUX = $01;
  INVALID_ADDRESS = $02;
  INVALID_INSTRUCTION_LENGTH = $04;
  ERROR_DISP_SIZE = $08;
  ERROR_IMM_SIZE = $10;
  ERROR_VEX_ESCAPE = $20;
  INVALID_GROUP_OPCODE = $40;

  UnknownErrorStr = 'Unknown Error';
  InstErrorsStr: array [0 .. 7] of String = (
    { NO_ERROR }
    'No error',
    { INVALID_CPUX }
    'Invalid cpux',
    { INVALID_ADDRESS }
    'Invalid address',
    { INVALID_INSTRUCTION_LENGTH }
    'Invalid instruction length',
    { ERROR_DISP_SIZE }
    'Invalid displacement size',
    { ERROR_IMM_SIZE }
    'Invalid immediat size',
    { ERROR_VEX_ESCAPE }
    'Invalid vex mmmmm field',
    { INVALID_GROUP_OPCODE }
    'Invalid group opcode');

  _vex3_ = $03;
  _opcode_ = $01;
  _modrm_ = $01;
  _sib_ = $01;
  _disp32_ = $04;
  _imm32_ = $04;
  _imm64_ = $08;
  { Intel define instruction length as a 15 bytes !
    However , it's possible to incode instructions
    that exceed the defined length !
  }
  MAX_INST_LENGTH_X32 = _vex3_ + _opcode_ + _modrm_ + _sib_ + _disp32_ + _imm32_;
  MAX_INST_LENGTH_X64 = _vex3_ + _opcode_ + _modrm_ + _sib_ + _disp32_ + _imm64_;
  CPUX_TO_INST_LENGTH: array [0 .. 1] of ShortInt = (MAX_INST_LENGTH_X32, MAX_INST_LENGTH_X64);
{$IFDEF CPUX64}
  MAX_INST_LENGTH_N = MAX_INST_LENGTH_X64;
{$ELSE !CPUX64}
  MAX_INST_LENGTH_N = MAX_INST_LENGTH_X32;
{$ENDIF CPUX64}

var
  { Raise Exception When Error Occurs ! }
  RaiseExceptionOnError: Boolean = True;

type
  InstException = class(Exception);

  TModRM = record
    iMod: Byte; { ModRm.Mod Field }
    Reg: Byte; { ModRm.Reg Field }
    Rm: Byte; { ModRm.Rm Field }
    Value: Byte; { ModRm Value }
    { ModRm.Flags => See ModRmFlagsTable.inc }
    Flags: Byte;
  end;

  PModRM = ^TModRM;
  LPModRM = PModRM;

  TSib = record
    Scale: Byte; { SIB.Scale Field }
    Index: Byte; { Register Index }
    Base: Byte; { Register Base }
    Value: Byte; { SIB Value }
    Flags: Byte; { SIB Flags }
  end;

  PSib = ^TSib;
  LPSib = PSib;

  TImmediat = record
    Size: Byte; { Size of Immediat => opsxxxbits }
    Value: Int64; { Immediat Value }
    Flags: Byte; { Sets of imfxxx }
  end;

  PImmediat = ^TImmediat;

  TDisplacement = record
    Size: Byte; { Size of Displacement => opsxxxbits }
    Value: Int64; { Displacement Value }
    Flags: Byte; { Sets of dfxxx }
  end;

  PDisplacement = ^TDisplacement;

  TBranch = record
    Size: Byte;
    Value: Int64;
    Target: PByte; { Destination Address }
    Falgs: Byte; { Sets of bfxxx }
  end;

  PBranch = ^TBranch;

  TRex = record
    R: Boolean; { REX.R Field }
    X: Boolean; { REX.X Field }
    B: Boolean; { REX.B Field }
    W: Boolean; { REX.W Field }
    Value: Byte; { REX Value = [$40..$4F] }
  end;

  PRex = ^TRex;

  TVex = record
    {
      ==================> N.B <==================
      1 => ALL FIELD ARE IN NO INVERTED FORM !
      2 => VEX.[R,X,B & W] ARE ACCESSIBLE THROUGH REX FIELD !

    }
    vvvv: Byte; { VEX.vvvv ==> Vector Register }
    L: Boolean; { VEX.L ==> You should use VL instead ! }
    PP: Byte; { VEX.PP ==> Implied Mandatory Prefixes }
    mmmmm: Byte; { VEX.mmmmm ==> Implied Escape }
    VL: Byte; { Vector Length }
  end;

  PVex = ^TVex;

  TInternalData = record
    MndPrf: Byte; { Mandatory Prefix }
    zOpSize: Byte; { word or dword depending on opsize prefix ! }
    vOpSize: Byte; { word or dword or qword depending on opsize & REX prefix ! }
  end;

  PInternalData = ^TInternalData;

  TInstruction = record
    Archi: Byte; { CPUX32 or CPUX64 ! }
    AddrMode: Byte; { Address Mode }
    Addr: PByte;
    VirtualAddr: PByte;
    NextInst: PByte; { Pointer to the Next Instruction }
    OpCode: Byte; { OpCode Value }
    OpType: Byte;
    OpKind: Byte;
    OpTable: Byte; { tbOneByte,tbTwoByte,... }
    OperandFlags: Byte;
    Prefixes: Word; { Sets of Prf_xxx }
    ModRm: TModRM;
    Sib: TSib;
    Disp: TDisplacement;
    Imm: TImmediat; { Primary Immediat }
    ImmEx: TImmediat; { Secondary Immediat if used ! }
    Branch: TBranch; { JMP & CALL }
    SegReg: Byte; { Segment Register }
    Rex: TRex;
    Vex: TVex;
    LID: TInternalData; { Internal Data }
    Errors: Byte;
    InstSize: Byte;
    Options: Byte;
    UserTag: UInt64;
  end;

  PInstruction = ^TInstruction;

  TDecoderProc = procedure(PInst: PInstruction);

function DecodeInst(PInst: PInstruction): ShortInt;

{ Useful ModRm Routines }
function GetModRm_Mod(const Value: Byte): Byte; {$IFDEF MustInline}inline; {$ENDIF}
function GetModRm_Reg(const Value: Byte): Byte; {$IFDEF MustInline}inline; {$ENDIF}
function GetModRm_Rm(const Value: Byte): Byte; {$IFDEF MustInline}inline; {$ENDIF}
{ Useful Sib Routines }
function GetSib_Base(const Value: Byte): Byte; {$IFDEF MustInline}inline; {$ENDIF}
function GetSib_Index(const Value: Byte): Byte; {$IFDEF MustInline}inline; {$ENDIF}
function GetSib_Scale(const Value: Byte): Byte; {$IFDEF MustInline}inline; {$ENDIF}
function IsSibBaseRegValid(PInst: PInstruction): Boolean; {$IFDEF MustInline}inline; {$ENDIF}

implementation

{$I OpCodesTables.inc}
{$I ModRmFlagsTables.inc}
{ ================================== 00 ================================== }
procedure Decode_InvalidOpCode(PInst: PInstruction); forward;
{ ================================== 01 ================================== }
procedure Decode_NA_ModRm(PInst: PInstruction); forward;
{ ================================== 02 ================================== }
procedure Decode_NA_Ib(PInst: PInstruction); forward;
{ ================================== 03 ================================== }
procedure Decode_NA_Iz(PInst: PInstruction); forward;
{ ================================== 04 ================================== }
procedure Decode_NA_I64(PInst: PInstruction); forward;
{ ================================== 05 ================================== }
procedure Decode_Escape_2_Byte(PInst: PInstruction); forward;
{ ================================== 06 ================================== }
procedure Decode_ES_Prefix(PInst: PInstruction); forward;
{ ================================== 07 ================================== }
procedure Decode_CS_Prefix(PInst: PInstruction); forward;
{ ================================== 08 ================================== }
procedure Decode_SS_Prefix(PInst: PInstruction); forward;
{ ================================== 09 ================================== }
procedure Decode_DS_Prefix(PInst: PInstruction); forward;
{ ================================== 10 ================================== }
procedure Decode_REX_Prefix(PInst: PInstruction); forward;
{ ================================== 11 ================================== }
procedure Decode_NA_D64(PInst: PInstruction); forward;
{ ================================== 12 ================================== }
procedure Decode_NA_ModRm_I64(PInst: PInstruction); forward;
{ ================================== 13 ================================== }
procedure Decode_FS_Prefix(PInst: PInstruction); forward;
{ ================================== 14 ================================== }
procedure Decode_GS_Prefix(PInst: PInstruction); forward;
{ ================================== 15 ================================== }
procedure Decode_OPSIZE_Prefix(PInst: PInstruction); forward;
{ ================================== 16 ================================== }
procedure Decode_ADSIZE_Prefix(PInst: PInstruction); forward;
{ ================================== 17 ================================== }
procedure Decode_NA_Iz_D64(PInst: PInstruction); forward;
{ ================================== 18 ================================== }
procedure Decode_NA_ModRm_Iz(PInst: PInstruction); forward;
{ ================================== 19 ================================== }
procedure Decode_NA_Ib_D64(PInst: PInstruction); forward;
{ ================================== 20 ================================== }
procedure Decode_NA_ModRm_Ib(PInst: PInstruction); forward;
{ ================================== 21 ================================== }
procedure Decode_NA(PInst: PInstruction); forward;
{ ================================== 22 ================================== }
procedure Decode_NA_Jb_Df64(PInst: PInstruction); forward;
{ ================================== 23 ================================== }
procedure Decode_Group_1(PInst: PInstruction); forward;
{ ================================== 24 ================================== }
procedure Decode_Group_1A(PInst: PInstruction); forward;
{ ================================== 25 ================================== }
procedure Decode_NA_CALL_Ap_I64(PInst: PInstruction); forward;
{ ================================== 26 ================================== }
procedure Decode_NA_OfstV(PInst: PInstruction); forward;
{ ================================== 27 ================================== }
procedure Decode_NA_Iv(PInst: PInstruction); forward;
{ ================================== 28 ================================== }
procedure Decode_Group_2(PInst: PInstruction); forward;
{ ================================== 29 ================================== }
procedure Decode_NA_RET_Iw_Df64(PInst: PInstruction); forward;
{ ================================== 30 ================================== }
procedure Decode_NA_RET_Df64(PInst: PInstruction); forward;
{ ================================== 31 ================================== }
procedure Decode_VEX3_Prefix(PInst: PInstruction); forward;
{ ================================== 32 ================================== }
procedure Decode_VEX2_Prefix(PInst: PInstruction); forward;
{ ================================== 33 ================================== }
procedure Decode_Group_11(PInst: PInstruction); forward;
{ ================================== 34 ================================== }
procedure Decode_NA_Iw_Ib_D64(PInst: PInstruction); forward;
{ ================================== 35 ================================== }
procedure Decode_NA_RET_Iw(PInst: PInstruction); forward;
{ ================================== 36 ================================== }
procedure Decode_NA_RET(PInst: PInstruction); forward;
{ ================================== 37 ================================== }
procedure Decode_NA_Ib_I64(PInst: PInstruction); forward;
{ ================================== 38 ================================== }
procedure Decode_Escape_FPU_D8(PInst: PInstruction); forward;
{ ================================== 39 ================================== }
procedure Decode_Escape_FPU_D9(PInst: PInstruction); forward;
{ ================================== 40 ================================== }
procedure Decode_Escape_FPU_DA(PInst: PInstruction); forward;
{ ================================== 41 ================================== }
procedure Decode_Escape_FPU_DB(PInst: PInstruction); forward;
{ ================================== 42 ================================== }
procedure Decode_Escape_FPU_DC(PInst: PInstruction); forward;
{ ================================== 43 ================================== }
procedure Decode_Escape_FPU_DD(PInst: PInstruction); forward;
{ ================================== 44 ================================== }
procedure Decode_Escape_FPU_DE(PInst: PInstruction); forward;
{ ================================== 45 ================================== }
procedure Decode_Escape_FPU_DF(PInst: PInstruction); forward;
{ ================================== 46 ================================== }
procedure Decode_NA_CALL_Jz_Df64(PInst: PInstruction); forward;
{ ================================== 47 ================================== }
procedure Decode_NA_JMP_Jz_Df64(PInst: PInstruction); forward;
{ ================================== 48 ================================== }
procedure Decode_NA_JMP_Ap_I64(PInst: PInstruction); forward;
{ ================================== 49 ================================== }
procedure Decode_NA_JMP_Jb_Df64(PInst: PInstruction); forward;
{ ================================== 50 ================================== }
procedure Decode_LOCK_Prefix(PInst: PInstruction); forward;
{ ================================== 51 ================================== }
procedure Decode_REPNE_Prefix(PInst: PInstruction); forward;
{ ================================== 52 ================================== }
procedure Decode_REPE_Prefix(PInst: PInstruction); forward;
{ ================================== 53 ================================== }
procedure Decode_Group_3(PInst: PInstruction); forward;
{ ================================== 54 ================================== }
procedure Decode_Group_4_INC_DEC(PInst: PInstruction); forward;
{ ================================== 55 ================================== }
procedure Decode_Group_5_INC_DEC(PInst: PInstruction); forward;
{ ================================== 56 ================================== }
procedure Decode_Group_6(PInst: PInstruction); forward;
{ ================================== 57 ================================== }
procedure Decode_Group_7(PInst: PInstruction); forward;
{ ================================== 58 ================================== }
procedure Decode_NA_CALL(PInst: PInstruction); forward;
{ ================================== 59 ================================== }
procedure Decode_NA_66_F2_F3_ModRm(PInst: PInstruction); forward;
{ ================================== 60 ================================== }
procedure Decode_NA_66_ModRm(PInst: PInstruction); forward;
{ ================================== 61 ================================== }
procedure Decode_NA_66_F3_ModRm(PInst: PInstruction); forward;
{ ================================== 62 ================================== }
procedure Decode_Group_16(PInst: PInstruction); forward;
{ ================================== 63 ================================== }
procedure Decode_NA_ModRm_F64(PInst: PInstruction); forward;
{ ================================== 64 ================================== }
procedure Decode_Escape_3_Byte(PInst: PInstruction); forward;
{ ================================== 65 ================================== }
procedure Decode_NA_F3_ModRm(PInst: PInstruction); forward;
{ ================================== 66 ================================== }
procedure Decode_66_ModRm(PInst: PInstruction); forward;
{ ================================== 67 ================================== }
procedure Decode_NA_66_F2_F3_ModRm_Ib(PInst: PInstruction); forward;
{ ================================== 68 ================================== }
procedure Decode_Group_12(PInst: PInstruction); forward;
{ ================================== 69 ================================== }
procedure Decode_Group_13(PInst: PInstruction); forward;
{ ================================== 70 ================================== }
procedure Decode_Group_14(PInst: PInstruction); forward;
{ ================================== 71 ================================== }
procedure Decode_66_F2_ModRm(PInst: PInstruction); forward;
{ ================================== 72 ================================== }
procedure Decode_NA_Jz_Df64(PInst: PInstruction); forward;
{ ================================== 73 ================================== }
procedure Decode_Group_15(PInst: PInstruction); forward;
{ ================================== 74 ================================== }
procedure Decode_F3_ModRm(PInst: PInstruction); forward;
{ ================================== 75 ================================== }
procedure Decode_Group_10_UD2(PInst: PInstruction); forward;
{ ================================== 76 ================================== }
procedure Decode_Group_8(PInst: PInstruction); forward;
{ ================================== 77 ================================== }
procedure Decode_NA_66_ModRm_Ib(PInst: PInstruction); forward;
{ ================================== 78 ================================== }
procedure Decode_Group_9(PInst: PInstruction); forward;
{ ================================== 79 ================================== }
procedure Decode_66_F2_F3_ModRm(PInst: PInstruction); forward;
{ ================================== 80 ================================== }
procedure Decode_F2_ModRm(PInst: PInstruction); forward;
{ ================================== 81 ================================== }
procedure Decode_SP_T38_F0_F7(PInst: PInstruction); forward;
{ ================================== 82 ================================== }
procedure Decode_66_ModRm_Ib(PInst: PInstruction); forward;
{ ================================== 83 ================================== }
procedure Decode_F2_ModRm_Ib(PInst: PInstruction); forward;

procedure JumpError(PInst: PInstruction); forward;
procedure JumpToTableTwoByte(PInst: PInstruction); forward;
procedure JumpToTableThreeByte_38(PInst: PInstruction); forward;
procedure JumpToTableThreeByte_3A(PInst: PInstruction); forward;

procedure Decode_CALL_ModRm(PInst: PInstruction); forward;
procedure Decode_JMP_ModRm(PInst: PInstruction); forward;
procedure Decode_CALL_Mp(PInst: PInstruction); forward;
procedure Decode_JMP_Mp(PInst: PInstruction); forward;

const

  { Convert PP To Mandatory Prefixes ! }
  PPToMndPrf: array [0 .. 3] of Byte = ($00, $66, $F3, $F2);

  { Convert LL To OpSize ! }
  LLToOpSize: array [0 .. 3] of Word = (ops128bits, ops256bits, ops512bits, 0);

  { Call escaping procedure ! }
  mmmmmToEscProc: array [0 .. 4] of TDecoderProc = ( //
    JumpError, { }
    JumpToTableTwoByte, { 00001: implied 0F leading opcode byte }
    JumpToTableThreeByte_38, { 00010: implied 0F 38 leading opcode bytes }
    JumpToTableThreeByte_3A, { 00011: implied 0F 3A leading opcode bytes }
    JumpError { }
    );

  DecoderProcTable: array [0 .. $54 - 1] of TDecoderProc = ( //
    { 00 } Decode_InvalidOpCode,
    { 01 } Decode_NA_ModRm,
    { 02 } Decode_NA_Ib,
    { 03 } Decode_NA_Iz,
    { 04 } Decode_NA_I64,
    { 05 } Decode_Escape_2_Byte,
    { 06 } Decode_ES_Prefix,
    { 07 } Decode_CS_Prefix,
    { 08 } Decode_SS_Prefix,
    { 09 } Decode_DS_Prefix,
    { 10 } Decode_REX_Prefix,
    { 11 } Decode_NA_D64,
    { 12 } Decode_NA_ModRm_I64,
    { 13 } Decode_FS_Prefix,
    { 14 } Decode_GS_Prefix,
    { 15 } Decode_OPSIZE_Prefix,
    { 16 } Decode_ADSIZE_Prefix,
    { 17 } Decode_NA_Iz_D64,
    { 18 } Decode_NA_ModRm_Iz,
    { 19 } Decode_NA_Ib_D64,
    { 20 } Decode_NA_ModRm_Ib,
    { 21 } Decode_NA,
    { 22 } Decode_NA_Jb_Df64,
    { 23 } Decode_Group_1,
    { 24 } Decode_Group_1A,
    { 25 } Decode_NA_CALL_Ap_I64,
    { 26 } Decode_NA_OfstV,
    { 27 } Decode_NA_Iv,
    { 28 } Decode_Group_2,
    { 29 } Decode_NA_RET_Iw_Df64,
    { 30 } Decode_NA_RET_Df64,
    { 31 } Decode_VEX3_Prefix,
    { 32 } Decode_VEX2_Prefix,
    { 33 } Decode_Group_11,
    { 34 } Decode_NA_Iw_Ib_D64,
    { 35 } Decode_NA_RET_Iw,
    { 36 } Decode_NA_RET,
    { 37 } Decode_NA_Ib_I64,
    { 38 } Decode_Escape_FPU_D8,
    { 39 } Decode_Escape_FPU_D9,
    { 40 } Decode_Escape_FPU_DA,
    { 41 } Decode_Escape_FPU_DB,
    { 42 } Decode_Escape_FPU_DC,
    { 43 } Decode_Escape_FPU_DD,
    { 44 } Decode_Escape_FPU_DE,
    { 45 } Decode_Escape_FPU_DF,
    { 46 } Decode_NA_CALL_Jz_Df64,
    { 47 } Decode_NA_JMP_Jz_Df64,
    { 48 } Decode_NA_JMP_Ap_I64,
    { 49 } Decode_NA_JMP_Jb_Df64,
    { 50 } Decode_LOCK_Prefix,
    { 51 } Decode_REPNE_Prefix,
    { 52 } Decode_REPE_Prefix,
    { 53 } Decode_Group_3,
    { 54 } Decode_Group_4_INC_DEC,
    { 55 } Decode_Group_5_INC_DEC,
    { 56 } Decode_Group_6,
    { 57 } Decode_Group_7,
    { 58 } Decode_NA_CALL,
    { 59 } Decode_NA_66_F2_F3_ModRm,
    { 60 } Decode_NA_66_ModRm,
    { 61 } Decode_NA_66_F3_ModRm,
    { 62 } Decode_Group_16,
    { 63 } Decode_NA_ModRm_F64,
    { 64 } Decode_Escape_3_Byte,
    { 65 } Decode_NA_F3_ModRm,
    { 66 } Decode_66_ModRm,
    { 67 } Decode_NA_66_F2_F3_ModRm_Ib,
    { 68 } Decode_Group_12,
    { 69 } Decode_Group_13,
    { 70 } Decode_Group_14,
    { 71 } Decode_66_F2_ModRm,
    { 72 } Decode_NA_Jz_Df64,
    { 73 } Decode_Group_15,
    { 74 } Decode_F3_ModRm,
    { 75 } Decode_Group_10_UD2,
    { 76 } Decode_Group_8,
    { 77 } Decode_NA_66_ModRm_Ib,
    { 78 } Decode_Group_9,
    { 79 } Decode_66_F2_F3_ModRm,
    { 80 } Decode_F2_ModRm,
    { 81 } Decode_SP_T38_F0_F7,
    { 82 } Decode_66_ModRm_Ib,
    { 83 } Decode_F2_ModRm_Ib);
{$REGION 'COMMON'}
  { ========================== COMMON =============================== }

procedure SetInstError(PInst: PInstruction; Error: Byte);
var
  ErrStr: String;
begin
  ErrStr := EmptyStr;
  if Error = NO_ERROR then
  begin
    { Clear Errors ! }
    PInst^.Errors := NO_ERROR;
    Exit;
  end;
  PInst^.Errors := PInst^.Errors or Error;

  if RaiseExceptionOnError then
  begin
    if (Error > 0) and (Error < Length(InstErrorsStr)) then
      ErrStr := InstErrorsStr[Error]
    else
      ErrStr := UnknownErrorStr;
    raise InstException.Create(Format('Error %d : %s.', [Error, ErrStr]));
  end;
end;

function GetModRm_Mod(const Value: Byte): Byte;
begin
  Result := Value shr 6;
end;

function GetModRm_Reg(const Value: Byte): Byte;
begin
  Result := (Value and $38) shr $03;
end;

function GetModRm_Rm(const Value: Byte): Byte;
begin
  Result := Value and 7;
end;

function GetSib_Base(const Value: Byte): Byte;
begin
  Result := Value and 7;
end;

function GetSib_Index(const Value: Byte): Byte;
begin
  Result := (Value and $38) shr $03;
end;

function GetSib_Scale(const Value: Byte): Byte;
begin
  Result := (1 shl (Value shr 6));
end;

function IsSibBaseRegValid(PInst: PInstruction): Boolean;
begin
  Result := True;
  if PInst^.Sib.Flags and sfUsed <> 0 then
    Result := not((PInst^.ModRm.iMod = 0) and (PInst^.Sib.Base = 5));
end;

procedure SetOpCode(PInst: PInstruction); {$IFDEF MustInline}inline; {$ENDIF}
begin
  PInst^.OpCode := PInst^.NextInst^;
  Inc(PInst^.NextInst);
end;

procedure SetGroup(PInst: PInstruction); {$IFDEF MustInline}inline; {$ENDIF}
begin
  PInst^.OpKind := kGrp;
end;

procedure ForceOpSize(PInst: PInstruction); {$IFDEF MustInline}inline; {$ENDIF}
begin
  if PInst^.Archi = CPUX32 then
    Exit;
  PInst^.LID.vOpSize := ops64bits;
end;

procedure DecodeSib(PInst: PInstruction); {$IFDEF MustInline}inline; {$ENDIF}
var
  PSib: LPSib;
begin
  PSib := @PInst^.Sib;
  PSib.Flags := sfUsed;
  PSib.Value := PInst^.NextInst^;
  PSib.Base := GetSib_Base(PSib.Value);
  PSib.Index := GetSib_Index(PSib.Value);
  PSib.Scale := GetSib_Scale(PSib.Value);
  Inc(PInst^.NextInst); // Skip SIB !
end;

procedure DecodeDisp(PInst: PInstruction);
var
  Disp: Int64;
  Size: Byte;
  DispOnly: Boolean;
begin
  Disp := $00;
  Size := PInst^.Disp.Size;
  PInst^.Disp.Flags := dfUsed;
  DispOnly := (PInst^.ModRm.iMod = $00) and (PInst^.ModRm.Rm = $05);

  case Size of
    ops8bits: Disp := (PUInt8(PInst^.NextInst)^); // and $FF;
    ops16bits: Disp := (PUInt16(PInst^.NextInst)^); // and $FFFF;
    ops32bits:
      begin
        Disp := (PUInt32(PInst^.NextInst)^); // and $FFFFFFFF;
        if (PInst^.Archi = CPUX64) and DispOnly then
          { RIP disp ! }
          PInst^.Disp.Flags := PInst^.Disp.Flags or dfRip;
      end;
  else SetInstError(PInst, ERROR_DISP_SIZE);
  end;

  if DispOnly then
    PInst^.Disp.Flags := PInst^.Disp.Flags or dfDispOnly
  else
    PInst^.Disp.Flags := PInst^.Disp.Flags or dfSigned;

  PInst^.Disp.Value := Disp;
  Inc(PInst^.NextInst, Size) // Skip Disp !
end;

procedure Decode_ModRm(PInst: PInstruction);
var
  PModRM: LPModRM;
  SibUsed: Boolean;
const
  { Get Disp Size from ModRm . }
  ModRMFlagsToDispSize: array [0 .. 4] of Byte = (0, ops8bits, ops16bits, 0, ops32bits);
begin
  PModRM := @PInst^.ModRm;
  PModRM.Value := PInst^.NextInst^;
  PModRM.iMod := GetModRm_Mod(PModRM.Value);
  PModRM.Reg := GetModRm_Reg(PModRM.Value);
  PModRM.Rm := GetModRm_Rm(PModRM.Value);
  PModRM.Flags := ModRMFlags[PInst^.AddrMode][PModRM.Value];

  PInst^.Disp.Size := ModRMFlagsToDispSize[(PModRM.Flags shr 1) and 7];
  Inc(PInst^.NextInst); // Skip ModRM !

  SibUsed := (PModRM.Flags and $10 > 0); { SibUsed ! }

  if SibUsed then
  begin
    DecodeSib(PInst);
    { if the base is not valid ==> there is a disp32 .
      But the disp can be 8bit ==> we need to check first
      if the disp does not exist !
    }
    if (PInst^.Disp.Size = 0) and (not IsSibBaseRegValid(PInst)) then
      PInst^.Disp.Size := ops32bits;
  end;

  if PInst^.Disp.Size > 0 then
    DecodeDisp(PInst);

  { ModRm Exists ! }
  PModRM.Flags := PModRM.Flags or mfUsed;
end;

procedure Decode_Imm(PInst: PInstruction; immSize: Byte);
var
  Imm: Int64;
  PImm: PImmediat;
begin
  Imm := $00;
  case immSize of
    ops8bits: Imm := (PInt8(PInst^.NextInst)^);
    ops16bits: Imm := (PInt16(PInst^.NextInst)^);
    ops32bits: Imm := (PInt32(PInst^.NextInst)^);
    ops64bits: Imm := (PInt64(PInst^.NextInst)^);
  else SetInstError(PInst, ERROR_IMM_SIZE);
  end;

  {
    If Imm field already used => get the extra Imm
  }
  if PInst^.Imm.Flags and imfUsed <> $00 then
    PImm := @PInst^.ImmEx
  else
    PImm := @PInst^.Imm;

  PImm.Flags := imfUsed;
  PImm.Value := Imm;
  PImm.Size := immSize;
  Inc(PInst^.NextInst, immSize); // Skip Immediat !
end;

procedure Decode_J(PInst: PInstruction; Size: Byte);
var
  Value: Int64;
  VA: PByte;
begin
  Value := $00;
  case Size of
    ops8bits: Value := (PInt8(PInst^.NextInst)^);
    ops16bits: Value := (PInt16(PInst^.NextInst)^);
    ops32bits: Value := (PInt32(PInst^.NextInst)^);
    ops64bits: Value := (PInt64(PInst^.NextInst)^);
  end;
  Inc(PInst^.NextInst, Size);
  if PInst^.OpType = otNone then
    PInst^.OpType := otJ;
  if PInst^.OpCode in [$70 .. $8F] then
    PInst^.OpType := otJ or otJcc;
  if Assigned(PInst^.VirtualAddr) then
    VA := PInst^.VirtualAddr + (PInst^.NextInst - PInst^.Addr)
  else
    VA := PInst^.NextInst;
  PInst^.Branch.Size := Size;
  PInst^.Branch.Falgs := bfUsed or bfRel;
  PInst^.Branch.Value := Value;
  PInst^.Branch.Target := VA + Value;
end;

procedure Decode_Branch_ModRm(PInst: PInstruction);
var
  P: PByte;
  VA: PByte;
begin
  SetOpCode(PInst);
  Decode_ModRm(PInst);
  PInst^.Branch.Value := PInst^.Disp.Value;
  PInst^.Branch.Size := PInst^.Disp.Size;
  PInst^.Branch.Falgs := bfUsed or bfIndirect or bfAbs;
  if Assigned(PInst^.VirtualAddr) then
    VA := PInst^.VirtualAddr + (PInst^.NextInst - PInst^.Addr)
  else
    VA := PInst^.NextInst;
  if (PInst^.ModRm.iMod = $00) and (PInst^.ModRm.Rm = $05) then
  begin
    { Memory = Displacement }
    if PInst^.Archi = CPUX64 then
    begin
      if PInst^.Prefixes and Prf_AddrSize <> 0 then
        { Displacement = EIP + Offset }
        VA := PByte(UInt64(VA) and $FFFFFFFF);
      { Displacement = RIP + Offset }
      PInst^.Branch.Falgs := PInst^.Branch.Falgs or bfRip;
      P := VA + Int32(PInst^.Disp.Value);
      { Memory 64-bits }
      PInst^.Branch.Target := PByte(PUInt64(P)^);
    end
    else
    begin
      { No RIP }
      P := PByte(UInt32(PInst^.Disp.Value));
      if PInst^.Prefixes and Prf_OpSize <> 0 then
        { Memory 16-bits }
        PInst^.Branch.Target := PByte(PUInt16(P)^)
      else
        { Memory 32-bits }
        PInst^.Branch.Target := PByte(PUInt32(P)^);
    end;
  end
  else
  begin
    { Memory = Displacement + Register }
    PInst^.Branch.Falgs := PInst^.Branch.Falgs or bfReg;
    PInst^.Branch.Target := nil;
  end;
end;

procedure Decode_Ap(PInst: PInstruction);
begin
  SetOpCode(PInst);
  PInst^.Branch.Falgs := bfUsed or bfFar;
  { We must clear the upper word ! }
  PInst^.Branch.Value := PUInt64(PInst^.NextInst)^ and $FFFFFFFFFFFF;
  PInst^.Branch.Size := ops48bits;
  PInst^.Branch.Target := nil;
  Inc(PInst^.NextInst, ops48bits);
end;

procedure Decode_Mp(PInst: PInstruction);
begin
  SetOpCode(PInst);
  PInst^.Branch.Falgs := bfUsed or bfFar;
  Decode_ModRm(PInst);
  PInst^.Branch.Value := PInst^.Disp.Value;
  PInst^.Branch.Size := PInst^.Disp.Size;
  PInst^.Branch.Target := nil;
end;

procedure Decode_InvalidOpCode(PInst: PInstruction); {$IFDEF MustInline}inline;
{$ENDIF}
begin
  SetOpCode(PInst);
end;

procedure Decode_Invalid_Group(PInst: PInstruction); {$IFDEF MustInline}inline;
{$ENDIF}
begin
  SetOpCode(PInst);
  Inc(PInst^.NextInst);
end;

procedure Decode_Invalid_FPU(PInst: PInstruction); {$IFDEF MustInline}inline;
{$ENDIF}
begin
  SetOpCode(PInst);
  Inc(PInst^.NextInst);
end;

{$ENDREGION}
{$REGION 'PREFIXES'}
{ ========================== PREFIXES =============================== }

procedure Decode_ES_Prefix(PInst: PInstruction);
begin
  { ES Segment Override Prefix }
  Inc(PInst^.NextInst);
  PInst^.Prefixes := PInst^.Prefixes or Prf_Seg_ES;
  PInst^.SegReg := Seg_ES;
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_CS_Prefix(PInst: PInstruction);
begin
  { CS Segment Override Prefix }
  Inc(PInst^.NextInst);
  PInst^.Prefixes := PInst^.Prefixes or Prf_Seg_CS;
  PInst^.SegReg := Seg_CS;
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_SS_Prefix(PInst: PInstruction);
begin
  { SS Segment Override Prefix }
  Inc(PInst^.NextInst);
  PInst^.Prefixes := PInst^.Prefixes or Prf_Seg_SS;
  PInst^.SegReg := Seg_SS;
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_DS_Prefix(PInst: PInstruction);
begin
  { DS Segment Override Prefix }
  Inc(PInst^.NextInst);
  PInst^.Prefixes := PInst^.Prefixes or Prf_Seg_DS;
  PInst^.SegReg := Seg_DS;
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_REX_Prefix(PInst: PInstruction);
begin
  { REX Prefix valid only on PM64! }
  if PInst^.Archi = CPUX32 then
  begin
    { INC/DEC REG }
    Decode_NA(PInst);
    Exit;
  end;
  PInst^.Prefixes := PInst^.Prefixes or Prf_Rex;
  PInst^.Rex.Value := PInst^.NextInst^;
  PInst^.Rex.B := (PInst^.Rex.Value and 1 <> 0);
  PInst^.Rex.X := (PInst^.Rex.Value and 2 <> 0);
  PInst^.Rex.R := (PInst^.Rex.Value and 4 <> 0);
  PInst^.Rex.W := (PInst^.Rex.Value and 8 <> 0);

  if PInst^.Rex.W then
    PInst^.LID.vOpSize := ops64bits;

  Inc(PInst^.NextInst); // Skip Rex .
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_FS_Prefix(PInst: PInstruction);
begin
  { FS Segment Override Prefix }
  Inc(PInst^.NextInst);
  PInst^.Prefixes := PInst^.Prefixes or Prf_Seg_FS;
  PInst^.SegReg := Seg_FS;
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_GS_Prefix(PInst: PInstruction);
begin
  { GS Segment Override Prefix }
  Inc(PInst^.NextInst);
  PInst^.Prefixes := PInst^.Prefixes or Prf_Seg_GS;
  PInst^.SegReg := Seg_GS;
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_OPSIZE_Prefix(PInst: PInstruction);
begin
  PInst^.Prefixes := PInst^.Prefixes or Prf_OpSize;
  PInst^.LID.vOpSize := ops16bits;
  PInst^.LID.zOpSize := ops16bits;
  PInst^.LID.MndPrf := $66;
  Inc(PInst^.NextInst);
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_ADSIZE_Prefix(PInst: PInstruction);
begin
  PInst^.Prefixes := PInst^.Prefixes or Prf_AddrSize;
  Inc(PInst^.NextInst);
  PInst^.AddrMode := AddressMode[PInst^.Archi];
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_VEX3_Prefix(PInst: PInstruction);
var
  P: Byte;
  Q: PByte;
  R, X: Boolean;
begin
  if PInst^.Options and DecodeVex = 0 then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  if PInst^.Archi = CPUX32 then
  begin
    Q := PInst^.NextInst;
    Inc(Q);
    R := (Q^ and $80 <> 0);
    X := (Q^ and $40 <> 0);
    {
      if R & X are set ==> Vex prefix is valid !
      otherwise the instruction is LES .
    }
    if not(R and X) then
    begin
      { LES instruction }
      Decode_NA_ModRm(PInst);
      Exit;
    end;
  end;

  Inc(PInst^.NextInst); // Skip $C4 !
  PInst^.Prefixes := PInst^.Prefixes or Prf_Vex3;
  P := PInst^.NextInst^; // P0
  PInst^.Rex.R := not(P and $80 <> 0);
  PInst^.Rex.X := not(P and $40 <> 0);
  PInst^.Rex.B := not(P and $20 <> 0);
  PInst^.Vex.mmmmm := (P and $1F);
  Inc(PInst^.NextInst); // Skip P0
  P := PInst^.NextInst^; // P1
  Inc(PInst^.NextInst); // Skip P1
  PInst^.Rex.W := (P and $80 <> 0);
  PInst^.Vex.vvvv := $0F - ((P and $78) shr 3);
  PInst^.Vex.L := (P and 4 <> 0);
  PInst^.Vex.PP := (P and 3);
  PInst^.Vex.VL := LLToOpSize[Byte(PInst^.Vex.L)];
  PInst^.LID.MndPrf := PPToMndPrf[PInst^.Vex.PP];
  mmmmmToEscProc[PInst^.Vex.mmmmm](PInst);
end;

procedure Decode_VEX2_Prefix(PInst: PInstruction);
var
  P: Byte;
  Q: PByte;
  R: Boolean;
begin
  if PInst^.Options and DecodeVex = 0 then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  if PInst^.Archi = CPUX32 then
  begin
    Q := PInst^.NextInst;
    Inc(Q);
    R := (Q^ and $80 <> 0);
    {
      if R is set ==> Vex prefix is valid !
      otherwise the instruction is LDS.
    }
    if not R then
    begin
      { LDS instruction }
      Decode_NA_ModRm(PInst);
      Exit;
    end;
  end;

  Inc(PInst^.NextInst); // Skip $C5 !
  PInst^.Prefixes := PInst^.Prefixes or Prf_Vex2;
  P := PInst^.NextInst^;
  PInst^.Rex.R := not(P and $80 <> 0);
  PInst^.Vex.vvvv := $0F - ((P and $78) shr 3);
  PInst^.Vex.L := (P and 4 <> 0);
  PInst^.Vex.PP := (P and 3);
  PInst^.Vex.VL := LLToOpSize[Byte(PInst^.Vex.L)];
  PInst^.LID.MndPrf := PPToMndPrf[PInst^.Vex.PP];
  Inc(PInst^.NextInst); // Skip P0 !
  DecoderProcTable[TwoByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_LOCK_Prefix(PInst: PInstruction);
begin
  PInst^.Prefixes := PInst^.Prefixes or Prf_Lock;
  Inc(PInst^.NextInst);
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_REPNE_Prefix(PInst: PInstruction);
begin
  PInst^.Prefixes := PInst^.Prefixes or Prf_Repne;
  PInst^.LID.MndPrf := $F2;
  Inc(PInst^.NextInst);
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_REPE_Prefix(PInst: PInstruction);
begin
  PInst^.Prefixes := PInst^.Prefixes or Prf_Repe;
  PInst^.LID.MndPrf := $F3;
  Inc(PInst^.NextInst);
  DecoderProcTable[OneByteTable[PInst^.NextInst^]](PInst);
end;
{$ENDREGION}
{$REGION 'ESCAPE'}
{ ========================== ESCAPE =============================== }

procedure JumpError(PInst: PInstruction);
begin
  { Wrong Vex.mmmmm value ! }
  SetInstError(PInst, ERROR_VEX_ESCAPE);
end;

procedure JumpToTableTwoByte(PInst: PInstruction);
begin
  { Implied $0F OpCode => Jump to table TwoByteTable }
  PInst^.OpTable := tbTwoByte;
  DecoderProcTable[TwoByteTable[PInst^.NextInst^]](PInst);
end;

procedure JumpToTableThreeByte_38(PInst: PInstruction);
begin
  { Implied $0F$38 OpCodes => Jump to table ThreeByteTable.38 }
  PInst^.OpTable := tbThreeByte;
  DecoderProcTable[ThreeByteTable38[PInst^.NextInst^]](PInst);
end;

procedure JumpToTableThreeByte_3A(PInst: PInstruction);
begin
  { Implied $0F$3A OpCodes => Jump to table ThreeByteTable.3A }
  PInst^.OpTable := tbThreeByte;
  DecoderProcTable[ThreeByteTable3A[PInst^.NextInst^]](PInst);
end;

procedure Decode_Escape_2_Byte(PInst: PInstruction);
begin
  { Two Byte OpCode Escape ! }
  PInst^.OpTable := tbTwoByte;
  Inc(PInst^.NextInst);
  DecoderProcTable[TwoByteTable[PInst^.NextInst^]](PInst);
end;

procedure Decode_Escape_3_Byte(PInst: PInstruction);
var
  P: Byte;
begin
  { Three Byte OpCode Escape ! }
  PInst^.OpTable := tbThreeByte;
  P := PInst^.NextInst^;
  Inc(PInst^.NextInst);
  if P = $38 then
    DecoderProcTable[ThreeByteTable38[PInst^.NextInst^]](PInst)
  else
    DecoderProcTable[ThreeByteTable3A[PInst^.NextInst^]](PInst);
end;

{$ENDREGION}
{$REGION 'FPU'}
{ ========================== FPU =============================== }

procedure Decode_Escape_FPU_D8(PInst: PInstruction);
begin
  { All OpCode are valid for $D8 ! }
  PInst^.OpTable := tbFPU;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Escape_FPU_D9(PInst: PInstruction);
var
  P: PByte;
  ModRm: Byte;
  Reg: Byte;
begin
  PInst^.OpTable := tbFPU;
  P := PInst^.NextInst;
  Inc(P);
  ModRm := P^;
  Reg := GetModRm_Reg(ModRm);
  if ModRm < $C0 then
  begin
    if Reg = $01 then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end
  else
  begin
    if ModRm in [$D1 .. $DF, $E2, $E3, $EF] then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Escape_FPU_DA(PInst: PInstruction);
var
  P: PByte;
  ModRm: Byte;
begin
  PInst^.OpTable := tbFPU;
  P := PInst^.NextInst;
  Inc(P);
  ModRm := P^;
  if ModRm > $C0 then
  begin
    if ModRm in [$E0 .. $E8, $EA .. $EF, $F0 .. $FF] then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Escape_FPU_DB(PInst: PInstruction);
var
  P: PByte;
  ModRm: Byte;
  Reg: Byte;
begin
  PInst^.OpTable := tbFPU;
  P := PInst^.NextInst;
  Inc(P);
  ModRm := P^;
  Reg := GetModRm_Reg(ModRm);
  if ModRm < $C0 then
  begin
    if (Reg = $04) or (Reg = $06) then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end
  else
  begin
    if ModRm in [$E0, $E1, $E4 .. $E7, $F8 .. $FF] then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Escape_FPU_DC(PInst: PInstruction);
var
  P: PByte;
  ModRm: Byte;
begin
  PInst^.OpTable := tbFPU;
  P := PInst^.NextInst;
  Inc(P);
  ModRm := P^;
  if ModRm > $C0 then
  begin
    if ModRm in [$D0 .. $DF] then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Escape_FPU_DD(PInst: PInstruction);
var
  P: PByte;
  ModRm: Byte;
  Reg: Byte;
begin
  PInst^.OpTable := tbFPU;
  P := PInst^.NextInst;
  Inc(P);
  ModRm := P^;
  Reg := GetModRm_Reg(ModRm);
  if ModRm < $C0 then
  begin
    if (Reg = $05) then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end
  else
  begin
    if ModRm in [$C8 .. $CF, $F0 .. $FF] then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Escape_FPU_DE(PInst: PInstruction);
var
  P: PByte;
  ModRm: Byte;
begin
  PInst^.OpTable := tbFPU;
  P := PInst^.NextInst;
  Inc(P);
  ModRm := P^;
  if ModRm > $C0 then
  begin
    if ModRm in [$D0 .. $D8, $DA .. $DF] then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Escape_FPU_DF(PInst: PInstruction);
var
  P: PByte;
  ModRm: Byte;
begin
  PInst^.OpTable := tbFPU;
  P := PInst^.NextInst;
  Inc(P);
  ModRm := P^;
  if ModRm > $C0 then
  begin
    if ModRm in [$C0 .. $CF, $D0 .. $DF, $E1 .. $E7, $F8 .. $FF] then
    begin
      Decode_Invalid_FPU(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

{$ENDREGION}
{$REGION 'GROUPS'}
{ ========================== GROUPS =============================== }

procedure Decode_Group_1(PInst: PInstruction);
begin
  SetGroup(PInst);
  if not(PInst^.NextInst^ in [$80 .. $83]) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  if PInst^.NextInst^ = $81 then
    Decode_NA_ModRm_Iz(PInst)
  else
    Decode_NA_ModRm_Ib(PInst);
end;

procedure Decode_Group_1A(PInst: PInstruction);
var
  P: PByte;
  Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.NextInst^ <> $8F) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P); // ModRm !
  Reg := GetModRm_Reg(P^);
  if (Reg = $00) then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_2(PInst: PInstruction);
begin
  SetGroup(PInst);
  if not(PInst^.NextInst^ in [$C0 .. $C1, $D0 .. $D3]) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  if (PInst^.NextInst^ in [$C0, $C1]) then
    Decode_NA_ModRm_Ib(PInst)
  else
    Decode_NA_ModRm(PInst);
end;

procedure Decode_Group_3(PInst: PInstruction);
var
  P: PByte;
  Reg: Byte;
begin
  SetGroup(PInst);
  if not(PInst^.NextInst^ in [$F6, $F7]) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  Reg := GetModRm_Reg(P^);
  if (Reg < $02) then
  begin
    { [TEST Reg,Immb] & [TEST Reg,Immz] }
    if PInst^.NextInst^ = $F6 then
      Decode_NA_ModRm_Ib(PInst)
    else
      Decode_NA_ModRm_Iz(PInst);
    Exit;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Group_4_INC_DEC(PInst: PInstruction);
var
  P: PByte;
  Reg: Byte;
begin
  SetGroup(PInst);
  Assert(PInst^.NextInst^ = $FE);
  P := PInst^.NextInst;
  Inc(P); // ModRm
  Reg := GetModRm_Reg(P^);
  if (Reg < $02) then
  begin
    { INC/DEC REG }
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_5_INC_DEC(PInst: PInstruction);
var
  Reg: Byte;
  P: PByte;
const
  GroupProc: array [0 .. 7] of TDecoderProc = ( //
    { 00 } Decode_NA_ModRm, { INC Ev }
    { 01 } Decode_NA_ModRm, { DEC Ev }
    { 02 } Decode_CALL_ModRm, { CALL Ev }
    { 03 } Decode_CALL_Mp, { CALL Mp }
    { 04 } Decode_JMP_ModRm, { JMP Ev }
    { 05 } Decode_JMP_Mp, { JMP Mp }
    { 06 } Decode_NA_ModRm, { PUSH Ev }
    { 07 } Decode_Invalid_Group { InvalidOpCode }
    );
begin
  SetGroup(PInst);
  if (PInst^.NextInst^ <> $FF) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P); // ModRm
  Reg := GetModRm_Reg(P^);
  GroupProc[Reg](PInst);
end;

procedure Decode_Group_6(PInst: PInstruction);
var
  P: PByte;
  Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.OpTable <> tbTwoByte) and (PInst^.NextInst^ <> $00) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  Reg := GetModRm_Reg(P^);
  if Reg = $07 then
  begin
    Decode_Invalid_Group(PInst);
    Exit;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_Group_7(PInst: PInstruction);
var
  P: PByte;
  iMod, Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.OpTable <> tbTwoByte) and (PInst^.NextInst^ <> $01) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  iMod := GetModRm_Mod(P^);
  Reg := GetModRm_Reg(P^);
  if (Reg = $04) or (Reg = $06) then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end
  else if Reg = $05 then
  begin
    Decode_Invalid_Group(PInst);
    Exit;
  end;
  if iMod <> $03 then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_8(PInst: PInstruction);
var
  P: PByte;
  Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.OpTable <> tbTwoByte) and (PInst^.NextInst^ <> $BA) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  Reg := GetModRm_Reg(P^);
  if Reg > $03 then
  begin
    Decode_NA_ModRm_Ib(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_9(PInst: PInstruction);
var
  P: PByte;
  iMod, Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.OpTable <> tbTwoByte) and (PInst^.NextInst^ <> $C7) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  iMod := GetModRm_Mod(P^);
  Reg := GetModRm_Reg(P^);
  if (iMod = $03) and (Reg > $05) then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end;

  if (iMod <> $03) then
  begin
    { Mod = Mem }
    if (((PInst^.LID.MndPrf = $00) and (Reg = $01)) or //
      ((PInst^.LID.MndPrf = $66) and (Reg = $06)) or //
      ((PInst^.LID.MndPrf = $F3) and (Reg > $05))) then
    begin
      Decode_NA_ModRm(PInst);
      Exit;
    end;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_10_UD2(PInst: PInstruction);
begin
  SetGroup(PInst);
  Decode_InvalidOpCode(PInst);
end;

procedure Decode_Group_11(PInst: PInstruction);
var
  P: PByte;
  Reg: Byte;
begin
  SetGroup(PInst);
  if not(PInst^.NextInst^ in [$C6, $C7]) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  Reg := GetModRm_Reg(P^);
  if PInst^.NextInst^ = $C6 then
  begin
    if (Reg = $00) then
    begin
      { XABORT Instruction }
      Decode_NA_ModRm_Ib(PInst);
      Exit;
    end
    else if (Reg = $07) then
    begin
      Decode_NA_Ib(PInst);
      Exit;
    end
  end
  else if PInst^.NextInst^ = $C7 then
  begin
    if Reg = $00 then
    begin
      Decode_NA_ModRm_Iz(PInst);
      Exit;
    end
    else if Reg = $07 then
    begin
      { XBEGIN Instruction }
      SetOpCode(PInst);
      Inc(PInst^.NextInst);
      Decode_J(PInst, PInst^.LID.zOpSize);
      PInst^.OpType := $00;
      Exit;
    end;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_12(PInst: PInstruction);
var
  P: PByte;
  iMod, Reg: Byte;
begin
  SetGroup(PInst);
  { Group 12 & 13 }
  if (PInst^.OpTable <> tbTwoByte) and not(PInst^.NextInst^ in [$71, $72]) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  iMod := GetModRm_Mod(P^);
  Reg := GetModRm_Reg(P^);
  if (iMod = $03) and (Reg in [$02, $04, $06]) then
  begin
    Decode_NA_ModRm_Ib(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_13(PInst: PInstruction);
begin
  SetGroup(PInst);
  { Group 13 has the same instructions signature as Group 12 ! }
  Decode_Group_12(PInst);
end;

procedure Decode_Group_14(PInst: PInstruction);
var
  P: PByte;
  iMod, Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.OpTable <> tbTwoByte) and (PInst^.NextInst^ <> $73) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  iMod := GetModRm_Mod(P^);
  Reg := GetModRm_Reg(P^);
  if iMod = $03 then
  begin
    if (Reg = $02) or (Reg = $06) then
    begin
      Decode_NA_ModRm_Ib(PInst);
      Exit;
    end;
    if (PInst^.LID.MndPrf = $66) and ((Reg = $03) or (Reg = $07)) then
    begin
      Decode_NA_ModRm_Ib(PInst);
      Exit;
    end;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_15(PInst: PInstruction);
var
  P: PByte;
  iMod, Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.OpTable <> tbTwoByte) and (PInst^.NextInst^ <> $AE) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  iMod := GetModRm_Mod(P^);
  Reg := GetModRm_Reg(P^);
  if (iMod = $03) and (PInst^.LID.MndPrf = $F3) and (Reg < $04) then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_16(PInst: PInstruction);
var
  P: PByte;
  iMod, Reg: Byte;
begin
  SetGroup(PInst);
  if (PInst^.OpTable <> tbTwoByte) and (PInst^.NextInst^ <> $18) then
    SetInstError(PInst, INVALID_GROUP_OPCODE);
  P := PInst^.NextInst;
  Inc(P);
  iMod := GetModRm_Mod(P^);
  Reg := GetModRm_Reg(P^);
  if (iMod <> $03) and (Reg < $04) then
  begin
    { Prefetch group instructions. }
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

procedure Decode_Group_17(PInst: PInstruction);
var
  P: PByte;
  Reg: Byte;
begin
  SetGroup(PInst);
  P := PInst^.NextInst;
  Inc(P);
  Reg := GetModRm_Reg(P^);
  if (Reg > $00) and (Reg < $04) then
  begin
    Decode_NA_ModRm(PInst);
    Exit;
  end;
  Decode_Invalid_Group(PInst);
end;

{$ENDREGION}
{$REGION 'DECODERS'}
{ ========================== DECODERS PROC =============================== }

procedure Decode_NA_CALL_Ap_I64(PInst: PInstruction);
begin
  { Instruction is only valid for x32 ! }
  if PInst^.Archi = CPUX64 then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  PInst^.OpType := otCALL;
  Decode_Ap(PInst);
end;

procedure Decode_NA_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_NA_Ib(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_NA_Iz(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_Imm(PInst, PInst^.LID.zOpSize);
end;

procedure Decode_NA_I64(PInst: PInstruction);
begin
  { Instruction is invalid on PM64 }
  { Only valid when mandatory prefix is : $00 }
  if (PInst^.Archi = CPUX64) or ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
end;

procedure Decode_NA(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
end;

procedure Decode_NA_ModRm_I64(PInst: PInstruction);
begin
  { Instruction is invalid on PM64 }
  { Only valid when mandatory prefix is : $00 }
  if (PInst^.Archi = CPUX64) or ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_NA_ModRm_Iz(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
  Decode_Imm(PInst, PInst^.LID.zOpSize);
end;

procedure Decode_NA_ModRm_Ib(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_NA_Jb_Df64(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf <> $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdDf64;
  Decode_J(PInst, ops8bits);
end;

procedure Decode_NA_RET(PInst: PInstruction);
begin
  SetOpCode(PInst);
  PInst^.OpType := otRET;
  if PInst^.OpCode in [$C2, $CA] then
    Decode_Imm(PInst, ops16bits);
end;

procedure Decode_NA_Ib_I64(PInst: PInstruction);
begin
  { Instruction is invalid on PM64 }
  { Only valid when mandatory prefix is : $00 }
  if (PInst^.Archi = CPUX64) or ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_NA_CALL_Jz_Df64(PInst: PInstruction);
begin
  SetOpCode(PInst);
  PInst^.OpType := otCALL;
  PInst^.OperandFlags := opdDf64;
  Decode_J(PInst, PInst^.LID.zOpSize);
end;

procedure Decode_NA_JMP_Jz_Df64(PInst: PInstruction);
begin
  SetOpCode(PInst);
  PInst^.OpType := otJMP;
  PInst^.OperandFlags := opdDf64;
  Decode_J(PInst, PInst^.LID.zOpSize);
end;

procedure Decode_NA_JMP_Ap_I64(PInst: PInstruction);
begin
  if PInst^.Archi = CPUX64 then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  PInst^.OpType := otJMP;
  Decode_Ap(PInst);
end;

procedure Decode_NA_JMP_Jb_Df64(PInst: PInstruction);
begin
  SetOpCode(PInst);
  PInst^.OpType := otJMP;
  PInst^.OperandFlags := opdDf64;
  Decode_J(PInst, ops8bits);
end;

procedure Decode_CALL_ModRm(PInst: PInstruction);
begin
  PInst^.OpType := otCALL;
  Decode_Branch_ModRm(PInst);
end;

procedure Decode_CALL_Mp(PInst: PInstruction);
begin
  PInst^.OpType := otCALL;
  Decode_Mp(PInst);
end;

procedure Decode_JMP_ModRm(PInst: PInstruction);
begin
  PInst^.OpType := otJMP;
  Decode_Branch_ModRm(PInst);
end;

procedure Decode_JMP_Mp(PInst: PInstruction);
begin
  PInst^.OpType := otJMP;
  Decode_Mp(PInst);
end;

procedure Decode_NA_CALL(PInst: PInstruction);
begin
  { SYSCALL! }
  SetOpCode(PInst);
end;

procedure Decode_NA_66_F2_F3_ModRm(PInst: PInstruction);
begin
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_NA_66_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 or $66 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf in [$00, $66])) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_NA_66_F3_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 or $66 or $F3 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf = $F2)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_NA_F3_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 or $F3 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf in [$00, $F3])) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_66_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $66 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $66)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_NA_66_F2_F3_ModRm_Ib(PInst: PInstruction);
begin
  SetOpCode(PInst);
  Decode_ModRm(PInst);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_66_F2_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $66 or $F2 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf in [$66, $F2])) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_F3_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $F3 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $F3)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_NA_66_ModRm_Ib(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 or $66 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf in [$00, $66])) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_66_F2_F3_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $66 or $F2 or $F3 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_F2_ModRm(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $F2 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $F2)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
end;

procedure Decode_SP_T38_F0_F7(PInst: PInstruction);
var
  Prf66F2: Boolean;
begin
  if PInst^.NextInst^ = $F3 then
  begin
    Decode_Group_17(PInst);
    Exit;
  end;

  { 66 & F2 }
  Prf66F2 := PInst^.Prefixes and (Prf_OpSize or Prf_Repne) = (Prf_OpSize or Prf_Repne);

  if Prf66F2 then
  begin
    { Valid only for CRC32 instruction ! }
    if (PInst^.NextInst^ = $F0) or (PInst^.NextInst^ = $F1) then
    begin
      Decode_NA_ModRm(PInst);
      Exit;
    end
    else
    begin
      Decode_InvalidOpCode(PInst);
      Exit;
    end;
  end
  else if PInst^.LID.MndPrf = $00 then
  begin
    if (PInst^.NextInst^ = $F4) or (PInst^.NextInst^ = $F6) then
    begin
      Decode_InvalidOpCode(PInst);
      Exit;
    end;
  end
  else if PInst^.LID.MndPrf = $66 then
  begin
    if (PInst^.NextInst^ in [$F2 .. $F5]) then
    begin
      Decode_InvalidOpCode(PInst);
      Exit;
    end;
  end
  else if PInst^.LID.MndPrf = $F3 then
  begin
    if (PInst^.NextInst^ < $F5) then
    begin
      Decode_InvalidOpCode(PInst);
      Exit;
    end;
  end
  else if PInst^.LID.MndPrf = $F2 then
  begin
    if (PInst^.NextInst^ in [$F2 .. $F4]) then
    begin
      Decode_InvalidOpCode(PInst);
      Exit;
    end;
  end;
  Decode_NA_ModRm(PInst);
end;

procedure Decode_66_ModRm_Ib(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $66 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $66)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_F2_ModRm_Ib(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $F2 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $F2)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_ModRm(PInst);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_NA_RET_Iw_Df64(PInst: PInstruction);
begin
  PInst^.OpType := otRET;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdDf64;
  Decode_Imm(PInst, ops16bits);
end;

procedure Decode_NA_D64(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf <> $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdD64;
end;

procedure Decode_NA_Iz_D64(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf <> $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdD64;
  Decode_Imm(PInst, PInst^.LID.zOpSize);
end;

procedure Decode_NA_Ib_D64(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf <> $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdD64;
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_NA_RET_Df64(PInst: PInstruction);
begin
  PInst^.OpType := otRET;
  PInst^.OperandFlags := opdDf64;
  SetOpCode(PInst);
end;

procedure Decode_NA_RET_Iw(PInst: PInstruction);
begin
  PInst^.OpType := otRET;
  SetOpCode(PInst);
  Decode_Imm(PInst, ops16bits);
end;

procedure Decode_NA_Iw_Ib_D64(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf <> $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdD64;
  Decode_Imm(PInst, ops16bits);
  Decode_Imm(PInst, ops8bits);
end;

procedure Decode_NA_ModRm_F64(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf <> $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdF64;
  Decode_ModRm(PInst);
end;

procedure Decode_NA_Jz_Df64(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and (PInst^.LID.MndPrf <> $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  PInst^.OperandFlags := opdDf64;
  Decode_J(PInst, PInst^.LID.zOpSize);
end;

procedure Decode_NA_OfstV(PInst: PInstruction);
begin
  SetOpCode(PInst);
  Decode_Imm(PInst, PInst.LID.vOpSize);
  PInst.Disp.Value := PInst.Imm.Value;
  PInst.Disp.Size := PInst.Imm.Size;
  PInst.Disp.Flags := dfUsed or dfOffset;
  PInst.Imm.Size := $00;
  PInst.Imm.Value := $00;
  PInst.Imm.Flags := $00;
end;

procedure Decode_NA_Iv(PInst: PInstruction);
begin
  { Only valid when mandatory prefix is : $00 }
  if ((PInst^.OpTable <> tbOneByte) and not(PInst^.LID.MndPrf = $00)) then
  begin
    Decode_InvalidOpCode(PInst);
    Exit;
  end;
  SetOpCode(PInst);
  Decode_Imm(PInst, PInst^.LID.vOpSize);
end;
{$ENDREGION}

function DecodeInst(PInst: PInstruction): ShortInt;
var
  P: PByte;
  LArchi: Byte;
  LAddr: PByte;
  LErrors: Byte;
  LVA: PByte;
  LOptions: Byte;
begin
  { No Errors }
  SetInstError(PInst, NO_ERROR);

  if not(PInst^.Archi in [CPUX32, CPUX64]) then
    SetInstError(PInst, INVALID_CPUX);

  if not Assigned(PInst^.Addr) then
    SetInstError(PInst, INVALID_ADDRESS);

  { Init Instruction Structure }
  LArchi := PInst^.Archi;
  LAddr := PInst^.Addr;
  LVA := PInst^.VirtualAddr;
  LErrors := PInst^.Errors;
  LOptions := PInst^.Options;

  FillChar(PInst^, SizeOf(TInstruction), #0);

  PInst^.Archi := LArchi;
  PInst^.Addr := LAddr;
  PInst^.Errors := LErrors;
  PInst^.VirtualAddr := LVA;
  PInst.Options := LOptions;

  P := PInst^.Addr;
  PInst^.NextInst := P;

  PInst^.LID.zOpSize := ops32bits;
  PInst^.LID.vOpSize := ops32bits;
  PInst^.AddrMode := DefAddressMode[PInst^.Archi];

  { Default Op Table is One Byte ! }
  PInst^.OpTable := tbOneByte;

  DecoderProcTable[OneByteTable[P^]](PInst);
  Result := PInst^.NextInst - P;
  PInst^.InstSize := Result;

  if Result > CPUX_TO_INST_LENGTH[PInst^.Archi] then
    SetInstError(PInst, INVALID_INSTRUCTION_LENGTH);
end;

end.
