/*
 * Copyright (c) 2020 Bitdefender
 * SPDX-License-Identifier: Apache-2.0
 */
#ifndef TABLEDEFS_H
#define TABLEDEFS_H

//
// Types of tables.
//
typedef enum _ND_ILUT_TYPE
{
    ND_ILUT_INSTRUCTION = 0,// Table contains one entry that directly points to an instruction.
    ND_ILUT_OPCODE,         // Table contains 256 entries. Next entry is selected using an opcode.
    ND_ILUT_OPCODE_3DNOW,   // Table contains 256 entries. Next entry is selected using an opcode, but the 
                            // opcode follows the instruction.
    ND_ILUT_MODRM_MOD,      // Table contains 2 entries. Next entry is selected using modrm.mod (0 - mem, 1 - reg)
    ND_ILUT_MODRM_REG,      // Table contains 8 entries. Next entry is selected using modrm.reg.
    ND_ILUT_MODRM_RM,       // Table contains 8 entries. Next entry is selected using modrm.rm.
    ND_ILUT_MAN_PREFIX,     // Table contains 4 entries. Next entry is 0 (no prefix), 1 (0x66), 2 (0xF3), 3 (0xF2).
    ND_ILUT_MODE,           // Table contains 4 entries. Next entry is 0 (16 bit mode), 1 (32 bit mode), 2 (64 bit mode).
    ND_ILUT_DSIZE,          // Table contains 4 entries. Next entry is 0 (16 bit size), 1 (32 bit size), 2 (64 bit size).
                            // This DOES NOT take into consideration forced/default sizes.
    ND_ILUT_ASIZE,          // Default addressing mode is used to transition
    ND_ILUT_AUXILIARY,      // Table contains 8 entries. Next entry is 0 (no prefix), 1 (rex), 2 (rex.w), 3 (64 bit),
                            // 4 (default op size 16), 5 (default op size 32), 6 (default op size 64),
    ND_ILUT_VENDOR,         // Preferred vendor is used to transition. Default is entry 0. Otherwise, preferred
                            // vendor selects an alternate entry.
    ND_ILUT_FEATURE,        // Some instructions are mapped onto wide NOP space. They will be decoded only if the
                            // associated feature is set during decoding.
    ND_ILUT_VEX_MMMMM,      // Table contains 32 entries. Next entry is vex/xop/evex.mmmmm
    ND_ILUT_VEX_PP,         // Table contains 4 entries. Next entry is vex/xop/evex.pp
    ND_ILUT_VEX_L,          // Table contains 4 entries. Next entry is vex/xop.l or evex.l'l
    ND_ILUT_VEX_W,          // Table contains 2 entries. Next entry is vex/xop/evex.w
} ND_ILUT_TYPE;


// Index definitions
#define ND_ILUT_INDEX_MOD_MEM           0
#define ND_ILUT_INDEX_MOD_REG           1

#define ND_ILUT_INDEX_MAN_PREF_NONE     0
#define ND_ILUT_INDEX_MAN_PREF_66       1
#define ND_ILUT_INDEX_MAN_PREF_F3       2
#define ND_ILUT_INDEX_MAN_PREF_F2       3

#define ND_ILUT_INDEX_MODE_NONE         0
#define ND_ILUT_INDEX_MODE_16           1
#define ND_ILUT_INDEX_MODE_32           2
#define ND_ILUT_INDEX_MODE_64           3

#define ND_ILUT_INDEX_DSIZE_NONE        0
#define ND_ILUT_INDEX_DSIZE_16          1
#define ND_ILUT_INDEX_DSIZE_32          2
#define ND_ILUT_INDEX_DSIZE_64          3
#define ND_ILUT_INDEX_DSIZE_DEF64       4
#define ND_ILUT_INDEX_DSIZE_F64         5

#define ND_ILUT_INDEX_ASIZE_NONE        0
#define ND_ILUT_INDEX_ASIZE_16          1
#define ND_ILUT_INDEX_ASIZE_32          2
#define ND_ILUT_INDEX_ASIZE_64          3

#define ND_ILUT_INDEX_AUX_NONE          0
#define ND_ILUT_INDEX_AUX_REX           1
#define ND_ILUT_INDEX_AUX_REXW          2
#define ND_ILUT_INDEX_AUX_O64           3
#define ND_ILUT_INDEX_AUX_F3            4
#define ND_ILUT_INDEX_AUX_REP           5

#define ND_ILUT_FEATURE_NONE            0
#define ND_ILUT_FEATURE_MPX             1
#define ND_ILUT_FEATURE_CET             2
#define ND_ILUT_FEATURE_CLDEMOTE        3





typedef struct _ND_TABLE
{
    uint32_t        Type;
    const void      *Table[1];
} ND_TABLE, *PND_TABLE;

typedef struct _ND_TABLE_INSTRUCTION
{
    uint32_t        Type;
    const void      *Instruction;
} ND_TABLE_INSTRUCTION, *PND_TABLE_INSTRUCTION;

typedef struct _ND_TABLE_OPCODE
{
    uint32_t        Type;
    const void      *Table[256];
} ND_TABLE_OPCODE, *PND_TABLE_OPCODE;

typedef struct _ND_TABLE_MODRM_MOD
{
    uint32_t        Type;
    const void      *Table[2];
} ND_TABLE_MODRM_MOD, *PND_TABLE_MODRM_MOD;

typedef struct _ND_TABLE_MODRM_REG
{
    uint32_t        Type;
    const void      *Table[8];
} ND_TABLE_MODRM_REG, *PND_TABLE_MODRM_REG;

typedef struct _ND_TABLE_MODRM_RM
{
    uint32_t        Type;
    const void      *Table[8];
} ND_TABLE_MODRM_RM, *PND_TABLE_MODRM_RM;

typedef struct _ND_TABLE_MPREFIX
{
    uint32_t        Type;
    const void      *Table[4];
} ND_TABLE_MPREFIX, *PND_TABLE_MPREFIX;


typedef struct _ND_TABLE_AUXILIARY
{
    uint32_t        Type;
    const void      *Table[6];
} ND_TABLE_AUXILIARY, *PND_TABLE_AUXILIARY;

typedef struct _ND_TABLE_VENDOR
{
    uint32_t        Type;
    const void      *Table[6];
} ND_TABLE_VENDOR;

typedef struct _ND_TABLE_FEATURE
{
    uint32_t        Type;
    const void      *Table[4];
} ND_TABLE_FEATURE;

typedef struct _ND_TABLE_DSIZE
{
    uint32_t        Type;
    const void      *Table[6];
} ND_TABLE_DSIZE, *PND_TABLE_DSIZE;

typedef struct _ND_TABLE_ASIZE
{
    uint32_t        Type;
    const void      *Table[4];
} ND_TABLE_ASIZE, *PND_TABLE_ASIZE;

typedef struct _ND_TABLE_MODE
{
    uint32_t        Type;
    const void      *Table[4];
} ND_TABLE_MODE, *PND_TABLE_MODE;

typedef struct _ND_TABLE_VEX_MMMMM
{
    uint32_t        Type;
    const void      *Table[32];
} ND_TABLE_VEX_MMMMM, *PND_TABLE_VEX_MMMMM;

typedef struct _ND_TABLE_VEX_PP
{
    uint32_t        Type;
    const void      *Table[4];
} ND_TABLE_VEX_PP, *PND_TABLE_VEX_PP;

typedef struct _ND_TABLE_VEX_L
{
    uint32_t        Type;
    const void      *Table[4];
} ND_TABLE_VEX_L, *PND_TABLE_VEX_L;

typedef struct _ND_TABLE_VEX_W
{
    uint32_t        Type;
    const void      *Table[8];
} ND_TABLE_VEX_W, *PND_TABLE_VEX_W;


//
// One instruction entry. This structure should be maintained bellow 2 cache lines in size (128 bytes).
//
#ifdef _MSC_VER
__declspec(align(128))
#pragma warning(push)
#pragma warning(disable: 4214)
#else
__attribute__((aligned(128)))
#endif
typedef struct _ND_INSTRUCTION
{
    uint16_t        Instruction;    // Instruction identifier. Check ND_INS_CLASS definitions.
    uint8_t         Category;       // Instruction category. Check ND_INS_TYPE.
    uint8_t         IsaSet;         // Instruction set. Check ND_INS_SET.
    uint16_t        Mnemonic;       // Mnemonic (index inside the global mnemonic table).

    uint16_t        ValidModes;     // Valid operating modes for the instruction.
    uint16_t        ValidPrefixes;  // Accepted prefixes.
    uint8_t         ValidDecorators;// Accepted decorators (valid for EVEX instructions).

    uint8_t         OpsCount;       // Low 4 bits: explicit operands count; high 4 bits: implicit ops count.

    uint8_t         TupleType;      // EVEX tuple type.
    uint8_t         ExcType : 5;    // SSE/VEX/EVEX/OPMASK/AMX exception type.
    uint8_t         ExcClass : 3;   // Indicates the exception class (SSE/AVX, EVEX, OPMASK or AMX).
    
    uint8_t         FpuFlags;       // FPU status word C0, C1, C2 & C3 access type.

    uint8_t         Reserved2;
    uint32_t        Reserved3;

    uint32_t        Attributes;     // Instruction attributes.
    uint64_t        CpuidFlag;      // Required CPUID feature flag.

    // Per-flag access. Undefined flags will have their bit set in both the "Set" and "Cleared" mask, since a flag
    // cannot be both cleared and set.
    uint32_t        TestedFlags;    // Tested flags.
    uint32_t        ModifiedFlags;  // Modified flags.
    uint32_t        SetFlags;       // Flags that are always set to 1.
    uint32_t        ClearedFlags;   // Flags that are always cleared.

    uint64_t        Operands[10];
} ND_INSTRUCTION, *PND_INSTRUCTION;
#ifdef _MSC_VER
#pragma warning(pop)
#endif

//
// The following definitions are per-operand specific.
//
// Packed operand structure:
// Byte 0: operand type
// Byte 1: operand size
// Byte 2: operand flags
// Byte 3: operand access
// Byte 4: operand decorators
// Byte 5: operand block addressing mode - 0 if not block addressing
//
#define ND_OP(type, size, flags, acc, dec, block)       (((uint64_t)((type) & 0xFF) << 0)   |    \
                                                         ((uint64_t)((size) & 0xFF) << 8)   |    \
                                                         ((uint64_t)((flags) & 0xFF) << 16) |    \
                                                         ((uint64_t)((acc) & 0xFF) << 24)   |    \
                                                         ((uint64_t)((dec) & 0xFF) << 32)   |    \
                                                         ((uint64_t)((block) & 0xFF) << 40))

#define OP ND_OP

#define ND_OPS_CNT(expo, impo)              ((expo) | ((impo) << 4))
#define ND_EXP_OPS_CNT(cnt)                 ((cnt) & 0xF)
#define ND_IMP_OPS_CNT(cnt)                 ((cnt) >> 4)

#define ND_OP_TYPE(op)                      ((op) & 0xFF)
#define ND_OP_SIZE(op)                      (((op) >> 8) & 0xFF)
#define ND_OP_FLAGS(op)                     (((op) >> 16) & 0xFF)
#define ND_OP_ACCESS(op)                    (((op) >> 24) & 0xFF)
#define ND_OP_DECORATORS(op)                (((op) >> 32) & 0xFF)
#define ND_OP_BLOCK(op)                     (((op) >> 40) & 0xFF)




//
// Operand sizes descriptors. These are internal and stored inside the specifier. They must be interpreted
// accordingly to extract the actual size used in the instruction. These should not be used by anyone using
// the disassembler. Use the "Size" and "RawSize" fields inside the operand instead.
//
// For info on what each enum means, check out the valid_opsize field inside disasmlib.py.
//
typedef enum _ND_OPERAND_SIZE_SPEC
{
    ND_OPS_none = 0,
    ND_OPS_0,
    ND_OPS_asz,
    ND_OPS_ssz,
    ND_OPS_a,
    ND_OPS_c,
    ND_OPS_b,
    ND_OPS_w,
    ND_OPS_d,
    ND_OPS_q,
    ND_OPS_dq,
    ND_OPS_qq,
    ND_OPS_oq,
    ND_OPS_v,
    ND_OPS_y,
    ND_OPS_yf,
    ND_OPS_z,
    ND_OPS_s,
    ND_OPS_p,
    ND_OPS_fa,
    ND_OPS_fw,
    ND_OPS_fd,
    ND_OPS_fq,
    ND_OPS_ft,
    ND_OPS_fe,
    ND_OPS_fs,
    ND_OPS_l,
    ND_OPS_rx,
    ND_OPS_cl,
    ND_OPS_sd,
    ND_OPS_ss,
    ND_OPS_ps,
    ND_OPS_pd,
    ND_OPS_e,
    ND_OPS_f,
    ND_OPS_h,
    ND_OPS_x,
    ND_OPS_n,
    ND_OPS_u,
    ND_OPS_t,       // Tile register size, can be up to 1K.
    // Stack sizes - indicates number of words. Also, hybrid sizes - sizes where from a large register (say 32 bit GPR)
    // only a smaller amount of data is used (for example, 8 bit).
    ND_OPS_v2,
    ND_OPS_v3,
    ND_OPS_v4,
    ND_OPS_v8,
    // 4 + 8 bytes accessed on the shadow stack by the SAVPREVSSP instruction.
    ND_OPS_12,
    // MIB
    ND_OPS_mib,
    // VSIB sizes (for both the index and the accessed data).
    ND_OPS_vm32x,
    ND_OPS_vm32y,
    ND_OPS_vm32z,
    ND_OPS_vm32h,
    ND_OPS_vm32n,
    ND_OPS_vm64x,
    ND_OPS_vm64y,
    ND_OPS_vm64z,
    ND_OPS_vm64h,
    ND_OPS_vm64n,
    // Used for arbitrary sized buffers.
    ND_OPS_unknown,

} ND_OPERAND_SIZE_SPEC;


//
// Operand types. These definitions are internal and have to be interpreted in order to find out what each
// operand represents. These should not be used by anyone using the disassembler. Use the "Type" field inside
// the operand structure instead.
//
// For more info on what each operand type means, please check out the valid_optype field inside disasmlib.py.
//
typedef enum _ND_OPERAND_TYPE_SPEC
{
    ND_OPT_A,
    ND_OPT_B,
    ND_OPT_C,
    ND_OPT_D,
    ND_OPT_E,
    ND_OPT_F,
    ND_OPT_G,
    ND_OPT_H,
    ND_OPT_I,
    ND_OPT_J,
    ND_OPT_K,

    ND_OPT_L,
    ND_OPT_M,
    ND_OPT_N,
    ND_OPT_O,
    ND_OPT_P,
    ND_OPT_Q,
    ND_OPT_R,
    ND_OPT_S,
    ND_OPT_T,
    ND_OPT_U,

    ND_OPT_V,
    ND_OPT_W,
    ND_OPT_X,
    ND_OPT_Y,
    ND_OPT_Z,
    ND_OPT_rB,
    ND_OPT_mB,
    ND_OPT_rK,
    ND_OPT_vK,
    ND_OPT_mK,
    ND_OPT_aK,
    ND_OPT_rM,
    ND_OPT_mM,
    ND_OPT_rT,
    ND_OPT_mT,
    ND_OPT_vT,
    ND_OPT_CONST_1,

    // These are implicit arguments inside instructions.

    // Special registers.
    ND_OPT_RIP,
    ND_OPT_MXCSR,
    ND_OPT_PKRU,
    ND_OPT_SSP,

    // General Purpose REgisters.
    ND_OPT_GPR_AH,
    ND_OPT_GPR_rAX,
    ND_OPT_GPR_rCX,
    ND_OPT_GPR_rDX,
    ND_OPT_GPR_rBX,
    ND_OPT_GPR_rSP,
    ND_OPT_GPR_rBP,
    ND_OPT_GPR_rSI,
    ND_OPT_GPR_rDI,
    ND_OPT_GPR_rR11,

    // Segment registers.
    ND_OPT_SEG_CS,
    ND_OPT_SEG_SS,
    ND_OPT_SEG_DS,
    ND_OPT_SEG_ES,
    ND_OPT_SEG_FS,
    ND_OPT_SEG_GS,

    // FPU registers.
    ND_OPT_FPU_ST0,
    ND_OPT_FPU_STX,

    // SSE registers.
    ND_OPT_SSE_XMM0,

    // Implicit memory operands.
    ND_OPT_MEM_rBX_AL,
    ND_OPT_MEM_rDI,
    ND_OPT_MEM_SHS,
    ND_OPT_MEM_SHSP,
    ND_OPT_MEM_SHS0,

    // Special immediates.
    ND_OPT_Im2z,

    // Misc CR/XCR/MSR/SYS registers.
    ND_OPT_CR_0,
    ND_OPT_SYS_IDTR,
    ND_OPT_SYS_GDTR,
    ND_OPT_SYS_LDTR,
    ND_OPT_SYS_TR,
    ND_OPT_X87_CONTROL,
    ND_OPT_X87_TAG,
    ND_OPT_X87_STATUS,
    ND_OPT_MSR,
    ND_OPT_XCR,
    ND_OPT_MSR_TSC,
    ND_OPT_MSR_TSCAUX,
    ND_OPT_MSR_SEIP,
    ND_OPT_MSR_SESP,
    ND_OPT_MSR_SCS,
    ND_OPT_MSR_STAR,
    ND_OPT_MSR_LSTAR,
    ND_OPT_MSR_FMASK,
    ND_OPT_MSR_FSBASE,
    ND_OPT_MSR_GSBASE,
    ND_OPT_MSR_KGSBASE,
    ND_OPT_XCR_0,
    ND_OPT_REG_BANK,

} ND_OPERAND_TYPE_SPEC;


//
// Operand flags.
//
#define ND_OPF_DEFAULT              0x01    // The operand is default, no need to show it in disassembly.
#define ND_OPF_SEX_OP1              0x02    // The operand is sign-extended to the first operands' size.
#define ND_OPF_SEX_DWS              0x04    // The operand is sign-extended to the default word size.


//
// Operand access.
//
#define ND_OPA_N                    0x00    // The operand is not accessed.
#define ND_OPA_R                    0x01    // The operand is read.
#define ND_OPA_W                    0x02    // The operand is written.
#define ND_OPA_CR                   0x04    // The operand is read conditionally.
#define ND_OPA_CW                   0x08    // The operand is written conditionally.
#define ND_OPA_RW                   0x03    // Read-Write access.
#define ND_OPA_RCW                  0x09    // Read-Conditional Write access.
#define ND_OPA_CRW                  0X06    // Conditional Read-Write access.
#define ND_OPA_CRCW                 0x0C    // Conditional Read-Conditional Write access.
#define ND_OPA_P                    0x10    // The operand is memory, and it is prefetched.


//
// Operand decorator flags.
//
#define ND_OPD_MASK                 0x01    // Mask accepted.
#define ND_OPD_Z                    0x02    // Zeroing accepted.
#define ND_OPD_B32                  0x04    // 32 bit broadcast supported.
#define ND_OPD_B64                  0x08    // 64 bit broadcast supported.
#define ND_OPD_SAE                  0x10    // Suppress all exceptions supported.
#define ND_OPD_ER                   0x20    // Embedded rounding supported.


//
// Include auto-generated stuff.
//
#include "mnemonics.h"
#include "../../inc/constants.h"
#include "instructions.h"
#include "prefixes.h"
#include "table_root.h"
#include "table_xop.h"
#include "table_vex.h"
#include "table_evex.h"

#endif // TABLEDEFS_H
