/***************************************************************************************************

  Zyan Disassembler Library (Zydis)

  Original Author : Florian Bernd

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.

***************************************************************************************************/

/**
 * @file
 * Defines the basic `ZydisDecodedInstruction` and `ZydisDecodedOperand` structs.
 */

#ifndef ZYDIS_INSTRUCTIONINFO_H
#define ZYDIS_INSTRUCTIONINFO_H

#include <Zycore/Types.h>
#include <Zydis/MetaInfo.h>
#include <Zydis/Mnemonic.h>
#include <Zydis/Register.h>
#include <Zydis/SharedTypes.h>

#ifdef __cplusplus
extern "C" {
#endif

/* ============================================================================================== */
/* Decoded operand                                                                                */
/* ============================================================================================== */

/* ---------------------------------------------------------------------------------------------- */
/* Operand attributes                                                                             */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisOperandAttributes` data-type.
 */
typedef ZyanU8 ZydisOperandAttributes;

/**
 * The operand is a `MULTISOURCE4` register operand.
 *
 * This is a special register operand-type used by `4FMAPS` instructions where the given register
 * points to the first register of a register range (4 registers in total).
 *
 * Example: ZMM3 -> [ZMM3..ZMM6]
 */
#define ZYDIS_OATTRIB_IS_MULTISOURCE4   0x01 // (1 <<  0)

/* ---------------------------------------------------------------------------------------------- */
/* Memory type                                                                                    */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisMemoryOperandType` enum.
 */
typedef enum ZydisMemoryOperandType_
{
    ZYDIS_MEMOP_TYPE_INVALID,
    /**
     * Normal memory operand.
     */
    ZYDIS_MEMOP_TYPE_MEM,
    /**
     * The memory operand is only used for address-generation. No real memory-access is
     * caused.
     */
    ZYDIS_MEMOP_TYPE_AGEN,
    /**
     * A memory operand using `SIB` addressing form, where the index register is not used
     * in address calculation and scale is ignored. No real memory-access is caused.
     */
    ZYDIS_MEMOP_TYPE_MIB,
    /**
     * A vector `SIB` memory addressing operand (`VSIB`).
     */
    ZYDIS_MEMOP_TYPE_VSIB,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_MEMOP_TYPE_MAX_VALUE = ZYDIS_MEMOP_TYPE_VSIB,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_MEMOP_TYPE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_MEMOP_TYPE_MAX_VALUE)
} ZydisMemoryOperandType;

/* ---------------------------------------------------------------------------------------------- */
/* Decoded operand                                                                                */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Extended info for register-operands.
 */
typedef struct ZydisDecodedOperandReg_
{
    /**
     * The register value.
     */
    ZydisRegister value;
} ZydisDecodedOperandReg;

/**
 * Extended info for memory-operands.
 */
typedef struct ZydisDecodedOperandMem_
{
    /**
     * The type of the memory operand.
     */
    ZydisMemoryOperandType type;
    /**
     * The segment register.
     */
    ZydisRegister segment;
    /**
     * The base register.
     */
    ZydisRegister base;
    /**
     * The index register.
     */
    ZydisRegister index;
    /**
     * The scale factor.
     */
    ZyanU8 scale;
    /**
     * Extended info for memory-operands with displacement.
     */
    struct ZydisDecodedOperandMemDisp_
    {
        /**
         * The displacement value
         */
        ZyanI64 value;
        /**
         * The offset of the displacement data, relative to the beginning of the
         * instruction, in bytes.
         */
        ZyanU8 offset;
        /**
         * The physical displacement size, in bits.
         */
        ZyanU8 size;
    } disp;
} ZydisDecodedOperandMem;

/**
 * Extended info for pointer-operands.
 */
typedef struct ZydisDecodedOperandPtr_
{
    ZyanU16 segment;
    ZyanU32 offset;
} ZydisDecodedOperandPtr;

/**
 * Extended info for immediate-operands.
 */
typedef struct ZydisDecodedOperandImm_
{
    /**
     * Signals, if the immediate value is signed.
     */
    ZyanBool is_signed;
    /**
     * Signals, if the immediate value contains a relative offset. You can use
     * `ZydisCalcAbsoluteAddress` to determine the absolute address value.
     */
    ZyanBool is_relative;
    /**
     * The immediate value.
     */
    union ZydisDecodedOperandImmValue_
    {
        ZyanU64 u;
        ZyanI64 s;
    } value;
    /**
      * The offset of the immediate data, relative to the beginning of the
      * instruction, in bytes.
      */
    ZyanU8 offset;
    /**
     * The physical immediate size, in bits.
     */
    ZyanU8 size;
} ZydisDecodedOperandImm;

/**
 * Defines the `ZydisDecodedOperand` struct.
 */
typedef struct ZydisDecodedOperand_
{
    /**
     * The operand-id.
     */
    ZyanU8 id;
    /**
     * The visibility of the operand.
     */
    ZydisOperandVisibility visibility;
    /**
     * The operand-actions.
     */
    ZydisOperandActions actions;
    /**
     * The operand-encoding.
     */
    ZydisOperandEncoding encoding;
    /**
     * The logical size of the operand (in bits).
     */
    ZyanU16 size;
    /**
     * The element-type.
     */
    ZydisElementType element_type;
    /**
     * The size of a single element.
     */
    ZydisElementSize element_size;
    /**
     * The number of elements.
     */
    ZyanU16 element_count;
    /*
     * Additional operand attributes.
     */
    ZydisOperandAttributes attributes;
    /**
     * The type of the operand.
     */
    ZydisOperandType type;
    /*
     * Operand type specific information.
     *
     * The enabled union variant is determined by the `type` field.
     */
    union
    {
        ZydisDecodedOperandReg reg;
        ZydisDecodedOperandMem mem;
        ZydisDecodedOperandPtr ptr;
        ZydisDecodedOperandImm imm;
    };
} ZydisDecodedOperand;

/* ---------------------------------------------------------------------------------------------- */

/* ============================================================================================== */
/* Decoded instruction                                                                            */
/* ============================================================================================== */

/* ---------------------------------------------------------------------------------------------- */
/* CPU/FPU flags                                                                                  */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisAccessedFlagsMask` data-type.
 */
typedef ZyanU32 ZydisAccessedFlagsMask;

/**
 * @defgroup decoder_cpu_flags CPU flags
 * @ingroup decoder
 *
 * Constants used for testing CPU flags accessed by an instruction.
 *
 * @{
 */

/**
 * Carry flag.
 */
#define ZYDIS_CPUFLAG_CF    (1ul <<  0)
/**
 * Parity flag.
 */
#define ZYDIS_CPUFLAG_PF    (1ul <<  2)
/**
 * Adjust flag.
 */
#define ZYDIS_CPUFLAG_AF    (1ul <<  4)
/**
 * Zero flag.
 */
#define ZYDIS_CPUFLAG_ZF    (1ul <<  6)
/**
 * Sign flag.
 */
#define ZYDIS_CPUFLAG_SF    (1ul <<  7)
/**
 * Trap flag.
 */
#define ZYDIS_CPUFLAG_TF    (1ul <<  8)
/**
 * Interrupt enable flag.
 */
#define ZYDIS_CPUFLAG_IF    (1ul <<  9)
/**
 * Direction flag.
 */
#define ZYDIS_CPUFLAG_DF    (1ul << 10)
/**
 * Overflow flag.
 */
#define ZYDIS_CPUFLAG_OF    (1ul << 11)
/**
 * I/O privilege level flag.
 */
#define ZYDIS_CPUFLAG_IOPL  (1ul << 12)
/**
 * Nested task flag.
 */
#define ZYDIS_CPUFLAG_NT    (1ul << 14)
/**
 * Resume flag.
 */
#define ZYDIS_CPUFLAG_RF    (1ul << 16)
/**
 * Virtual 8086 mode flag.
 */
#define ZYDIS_CPUFLAG_VM    (1ul << 17)
/**
 * Alignment check.
 */
#define ZYDIS_CPUFLAG_AC    (1ul << 18)
/**
 * Virtual interrupt flag.
 */
#define ZYDIS_CPUFLAG_VIF   (1ul << 19)
/**
 * Virtual interrupt pending.
 */
#define ZYDIS_CPUFLAG_VIP   (1ul << 20)
/**
 * Able to use CPUID instruction.
 */
#define ZYDIS_CPUFLAG_ID    (1ul << 21)

/**
 * @}
 */

/**
 * @defgroup decoder_fpu_flags FPU flags
 * @ingroup decoder
 *
 * Constants used for testing FPU flags accessed by an instruction.
 *
 * @{
 */

/**
 * FPU condition-code flag 0.
 */
#define ZYDIS_FPUFLAG_C0    (1ul <<  0)
/**
 * FPU condition-code flag 1.
 */
#define ZYDIS_FPUFLAG_C1    (1ul <<  1)
 /**
  * FPU condition-code flag 2.
  */
#define ZYDIS_FPUFLAG_C2    (1ul <<  2)
/**
 * FPU condition-code flag 3.
 */
#define ZYDIS_FPUFLAG_C3    (1ul <<  3)

/**
 * @}
 */

/*
 * Information about CPU/FPU flags accessed by the instruction.
 */
typedef struct ZydisAccessedFlags_
{
    /*
     * As mask containing the flags `TESTED` by the instruction.
     */
    ZydisAccessedFlagsMask tested;
    /*
     * As mask containing the flags `MODIFIED` by the instruction.
     */
    ZydisAccessedFlagsMask modified;
    /*
     * As mask containing the flags `SET_0` by the instruction.
     */
    ZydisAccessedFlagsMask set_0;
    /*
     * As mask containing the flags `SET_1` by the instruction.
     */
    ZydisAccessedFlagsMask set_1;
    /*
     * As mask containing the flags `UNDEFINED` by the instruction.
     */
    ZydisAccessedFlagsMask undefined;
} ZydisAccessedFlags;

/* ---------------------------------------------------------------------------------------------- */
/* Branch types                                                                                   */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisBranchType` enum.
 */
typedef enum ZydisBranchType_
{
    /**
     * The instruction is not a branch instruction.
     */
    ZYDIS_BRANCH_TYPE_NONE,
    /**
     * The instruction is a short (8-bit) branch instruction.
     */
    ZYDIS_BRANCH_TYPE_SHORT,
    /**
     * The instruction is a near (16-bit or 32-bit) branch instruction.
     */
    ZYDIS_BRANCH_TYPE_NEAR,
    /**
     * The instruction is a far (inter-segment) branch instruction.
     */
    ZYDIS_BRANCH_TYPE_FAR,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_BRANCH_TYPE_MAX_VALUE = ZYDIS_BRANCH_TYPE_FAR,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_BRANCH_TYPE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_BRANCH_TYPE_MAX_VALUE)
} ZydisBranchType;

/* ---------------------------------------------------------------------------------------------- */
/* SSE/AVX exception-class                                                                        */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisExceptionClass` enum.
 */
typedef enum ZydisExceptionClass_
{
    ZYDIS_EXCEPTION_CLASS_NONE,
    // TODO: FP Exceptions
    ZYDIS_EXCEPTION_CLASS_SSE1,
    ZYDIS_EXCEPTION_CLASS_SSE2,
    ZYDIS_EXCEPTION_CLASS_SSE3,
    ZYDIS_EXCEPTION_CLASS_SSE4,
    ZYDIS_EXCEPTION_CLASS_SSE5,
    ZYDIS_EXCEPTION_CLASS_SSE7,
    ZYDIS_EXCEPTION_CLASS_AVX1,
    ZYDIS_EXCEPTION_CLASS_AVX2,
    ZYDIS_EXCEPTION_CLASS_AVX3,
    ZYDIS_EXCEPTION_CLASS_AVX4,
    ZYDIS_EXCEPTION_CLASS_AVX5,
    ZYDIS_EXCEPTION_CLASS_AVX6,
    ZYDIS_EXCEPTION_CLASS_AVX7,
    ZYDIS_EXCEPTION_CLASS_AVX8,
    ZYDIS_EXCEPTION_CLASS_AVX11,
    ZYDIS_EXCEPTION_CLASS_AVX12,
    ZYDIS_EXCEPTION_CLASS_E1,
    ZYDIS_EXCEPTION_CLASS_E1NF,
    ZYDIS_EXCEPTION_CLASS_E2,
    ZYDIS_EXCEPTION_CLASS_E2NF,
    ZYDIS_EXCEPTION_CLASS_E3,
    ZYDIS_EXCEPTION_CLASS_E3NF,
    ZYDIS_EXCEPTION_CLASS_E4,
    ZYDIS_EXCEPTION_CLASS_E4NF,
    ZYDIS_EXCEPTION_CLASS_E5,
    ZYDIS_EXCEPTION_CLASS_E5NF,
    ZYDIS_EXCEPTION_CLASS_E6,
    ZYDIS_EXCEPTION_CLASS_E6NF,
    ZYDIS_EXCEPTION_CLASS_E7NM,
    ZYDIS_EXCEPTION_CLASS_E7NM128,
    ZYDIS_EXCEPTION_CLASS_E9NF,
    ZYDIS_EXCEPTION_CLASS_E10,
    ZYDIS_EXCEPTION_CLASS_E10NF,
    ZYDIS_EXCEPTION_CLASS_E11,
    ZYDIS_EXCEPTION_CLASS_E11NF,
    ZYDIS_EXCEPTION_CLASS_E12,
    ZYDIS_EXCEPTION_CLASS_E12NP,
    ZYDIS_EXCEPTION_CLASS_K20,
    ZYDIS_EXCEPTION_CLASS_K21,
    ZYDIS_EXCEPTION_CLASS_AMXE1,
    ZYDIS_EXCEPTION_CLASS_AMXE2,
    ZYDIS_EXCEPTION_CLASS_AMXE3,
    ZYDIS_EXCEPTION_CLASS_AMXE4,
    ZYDIS_EXCEPTION_CLASS_AMXE5,
    ZYDIS_EXCEPTION_CLASS_AMXE6,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_EXCEPTION_CLASS_MAX_VALUE = ZYDIS_EXCEPTION_CLASS_AMXE6,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_EXCEPTION_CLASS_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_EXCEPTION_CLASS_MAX_VALUE)
} ZydisExceptionClass;

/* ---------------------------------------------------------------------------------------------- */
/* AVX mask mode                                                                                  */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisMaskMode` enum.
 */
typedef enum ZydisMaskMode_
{
    ZYDIS_MASK_MODE_INVALID,
    /**
     * Masking is disabled for the current instruction (`K0` register is used).
     */
    ZYDIS_MASK_MODE_DISABLED,
    /**
     * The embedded mask register is used as a merge-mask.
     */
    ZYDIS_MASK_MODE_MERGING,
    /**
     * The embedded mask register is used as a zero-mask.
     */
    ZYDIS_MASK_MODE_ZEROING,
    /**
     * The embedded mask register is used as a control-mask (element selector).
     */
    ZYDIS_MASK_MODE_CONTROL,
    /**
     * The embedded mask register is used as a zeroing control-mask (element selector).
     */
    ZYDIS_MASK_MODE_CONTROL_ZEROING,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_MASK_MODE_MAX_VALUE = ZYDIS_MASK_MODE_CONTROL_ZEROING,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_MASK_MODE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_MASK_MODE_MAX_VALUE)
} ZydisMaskMode;

/* ---------------------------------------------------------------------------------------------- */
/* AVX broadcast-mode                                                                             */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisBroadcastMode` enum.
 */
typedef enum ZydisBroadcastMode_
{
    ZYDIS_BROADCAST_MODE_INVALID,
    ZYDIS_BROADCAST_MODE_1_TO_2,
    ZYDIS_BROADCAST_MODE_1_TO_4,
    ZYDIS_BROADCAST_MODE_1_TO_8,
    ZYDIS_BROADCAST_MODE_1_TO_16,
    ZYDIS_BROADCAST_MODE_1_TO_32,
    ZYDIS_BROADCAST_MODE_1_TO_64,
    ZYDIS_BROADCAST_MODE_2_TO_4,
    ZYDIS_BROADCAST_MODE_2_TO_8,
    ZYDIS_BROADCAST_MODE_2_TO_16,
    ZYDIS_BROADCAST_MODE_4_TO_8,
    ZYDIS_BROADCAST_MODE_4_TO_16,
    ZYDIS_BROADCAST_MODE_8_TO_16,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_BROADCAST_MODE_MAX_VALUE = ZYDIS_BROADCAST_MODE_8_TO_16,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_BROADCAST_MODE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_BROADCAST_MODE_MAX_VALUE)
} ZydisBroadcastMode;

/* ---------------------------------------------------------------------------------------------- */
/* AVX rounding-mode                                                                              */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisRoundingMode` enum.
 */
typedef enum ZydisRoundingMode_
{
    ZYDIS_ROUNDING_MODE_INVALID,
    /**
     * Round to nearest.
     */
    ZYDIS_ROUNDING_MODE_RN,
    /**
     * Round down.
     */
    ZYDIS_ROUNDING_MODE_RD,
    /**
     * Round up.
     */
    ZYDIS_ROUNDING_MODE_RU,
    /**
     * Round towards zero.
     */
    ZYDIS_ROUNDING_MODE_RZ,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_ROUNDING_MODE_MAX_VALUE = ZYDIS_ROUNDING_MODE_RZ,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_ROUNDING_MODE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_ROUNDING_MODE_MAX_VALUE)
} ZydisRoundingMode;

/* ---------------------------------------------------------------------------------------------- */
/* KNC swizzle-mode                                                                               */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisSwizzleMode` enum.
 */
typedef enum ZydisSwizzleMode_
{
    ZYDIS_SWIZZLE_MODE_INVALID,
    ZYDIS_SWIZZLE_MODE_DCBA,
    ZYDIS_SWIZZLE_MODE_CDAB,
    ZYDIS_SWIZZLE_MODE_BADC,
    ZYDIS_SWIZZLE_MODE_DACB,
    ZYDIS_SWIZZLE_MODE_AAAA,
    ZYDIS_SWIZZLE_MODE_BBBB,
    ZYDIS_SWIZZLE_MODE_CCCC,
    ZYDIS_SWIZZLE_MODE_DDDD,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_SWIZZLE_MODE_MAX_VALUE = ZYDIS_SWIZZLE_MODE_DDDD,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_SWIZZLE_MODE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_SWIZZLE_MODE_MAX_VALUE)
} ZydisSwizzleMode;

/* ---------------------------------------------------------------------------------------------- */
/* KNC conversion-mode                                                                            */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisConversionMode` enum.
 */
typedef enum ZydisConversionMode_
{
    ZYDIS_CONVERSION_MODE_INVALID,
    ZYDIS_CONVERSION_MODE_FLOAT16,
    ZYDIS_CONVERSION_MODE_SINT8,
    ZYDIS_CONVERSION_MODE_UINT8,
    ZYDIS_CONVERSION_MODE_SINT16,
    ZYDIS_CONVERSION_MODE_UINT16,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_CONVERSION_MODE_MAX_VALUE = ZYDIS_CONVERSION_MODE_UINT16,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_CONVERSION_MODE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_CONVERSION_MODE_MAX_VALUE)
} ZydisConversionMode;

/* ---------------------------------------------------------------------------------------------- */
/* Legacy prefix type                                                                             */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisPrefixType` enum.
 */
typedef enum ZydisPrefixType_
{
    /**
     * The prefix is ignored by the instruction.
     *
     * This applies to all prefixes that are not accepted by the instruction in general or the
     * ones that are overwritten by a prefix of the same group closer to the instruction opcode.
     */
    ZYDIS_PREFIX_TYPE_IGNORED,
    /**
     * The prefix is effectively used by the instruction.
     */
    ZYDIS_PREFIX_TYPE_EFFECTIVE,
    /**
     * The prefix is used as a mandatory prefix.
     *
     * A mandatory prefix is interpreted as an opcode extension and has no further effect on the
     * instruction.
     */
    ZYDIS_PREFIX_TYPE_MANDATORY,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_PREFIX_TYPE_MAX_VALUE = ZYDIS_PREFIX_TYPE_MANDATORY,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_PREFIX_TYPE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_PREFIX_TYPE_MAX_VALUE)
} ZydisPrefixType;

// TODO: Check effective for 66/67 prefixes (currently defaults to EFFECTIVE)

/* ---------------------------------------------------------------------------------------------- */
/* Decoded instruction                                                                            */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Detailed info about the `REX` prefix.
 */
typedef struct ZydisDecodedInstructionRawRex_
{
    /**
     * 64-bit operand-size promotion.
     */
    ZyanU8 W;
    /**
     * Extension of the `ModRM.reg` field.
     */
    ZyanU8 R;
    /**
     * Extension of the `SIB.index` field.
     */
    ZyanU8 X;
    /**
     * Extension of the `ModRM.rm`, `SIB.base`, or `opcode.reg` field.
     */
    ZyanU8 B;
    /**
     * The offset of the effective `REX` byte, relative to the beginning of the
     * instruction, in bytes.
     *
     * This offset always points to the "effective" `REX` prefix (the one closest to the
     * instruction opcode), if multiple `REX` prefixes are present.
     *
     * Note that the `REX` byte can be the first byte of the instruction, which would lead
     * to an offset of `0`. Please refer to the instruction attributes to check for the
     * presence of the `REX` prefix.
     */
    ZyanU8 offset;
} ZydisDecodedInstructionRawRex;

/**
 * Detailed info about the `XOP` prefix.
 */
typedef struct ZydisDecodedInstructionRawXop_
{
    /**
     * Extension of the `ModRM.reg` field (inverted).
     */
    ZyanU8 R;
    /**
     * Extension of the `SIB.index` field (inverted).
     */
    ZyanU8 X;
    /**
     * Extension of the `ModRM.rm`, `SIB.base`, or `opcode.reg` field (inverted).
     */
    ZyanU8 B;
    /**
     * Opcode-map specifier.
     */
    ZyanU8 m_mmmm;
    /**
     * 64-bit operand-size promotion or opcode-extension.
     */
    ZyanU8 W;
    /**
     * `NDS`/`NDD` (non-destructive-source/destination) register
     * specifier (inverted).
     */
    ZyanU8 vvvv;
    /**
     * Vector-length specifier.
     */
    ZyanU8 L;
    /**
     * Compressed legacy prefix.
     */
    ZyanU8 pp;
    /**
     * The offset of the first xop byte, relative to the beginning of
     * the instruction, in bytes.
     */
    ZyanU8 offset;
} ZydisDecodedInstructionRawXop;

/**
 * Detailed info about the `VEX` prefix.
 */
typedef struct ZydisDecodedInstructionRawVex_
{
    /**
     * Extension of the `ModRM.reg` field (inverted).
     */
    ZyanU8 R;
    /**
     * Extension of the `SIB.index` field (inverted).
     */
    ZyanU8 X;
    /**
     * Extension of the `ModRM.rm`, `SIB.base`, or `opcode.reg` field (inverted).
     */
    ZyanU8 B;
    /**
     * Opcode-map specifier.
     */
    ZyanU8 m_mmmm;
    /**
     * 64-bit operand-size promotion or opcode-extension.
     */
    ZyanU8 W;
    /**
     * `NDS`/`NDD` (non-destructive-source/destination) register specifier
     *  (inverted).
     */
    ZyanU8 vvvv;
    /**
     * Vector-length specifier.
     */
    ZyanU8 L;
    /**
     * Compressed legacy prefix.
     */
    ZyanU8 pp;
    /**
     * The offset of the first `VEX` byte, relative to the beginning of the instruction, in
     * bytes.
     */
    ZyanU8 offset;
    /**
     * The size of the `VEX` prefix, in bytes.
     */
    ZyanU8 size;
} ZydisDecodedInstructionRawVex;

/**
 * Detailed info about the `EVEX` prefix.
 */
typedef struct ZydisDecodedInstructionRawEvex_
{
    /**
     * Extension of the `ModRM.reg` field (inverted).
     */
    ZyanU8 R;
    /**
     * Extension of the `SIB.index/vidx` field (inverted).
     */
    ZyanU8 X;
    /**
     * Extension of the `ModRM.rm` or `SIB.base` field (inverted).
     */
    ZyanU8 B;
    /**
     * High-16 register specifier modifier (inverted).
     */
    ZyanU8 R2;
    /**
     * Opcode-map specifier.
     */
    ZyanU8 mmm;
    /**
     * 64-bit operand-size promotion or opcode-extension.
     */
    ZyanU8 W;
    /**
     * `NDS`/`NDD` (non-destructive-source/destination) register specifier
     * (inverted).
     */
    ZyanU8 vvvv;
    /**
     * Compressed legacy prefix.
     */
    ZyanU8 pp;
    /**
     * Zeroing/Merging.
     */
    ZyanU8 z;
    /**
     * Vector-length specifier or rounding-control (most significant bit).
     */
    ZyanU8 L2;
    /**
     * Vector-length specifier or rounding-control (least significant bit).
     */
    ZyanU8 L;
    /**
     * Broadcast/RC/SAE context.
     */
    ZyanU8 b;
    /**
     * High-16 `NDS`/`VIDX` register specifier.
     */
    ZyanU8 V2;
    /**
     * Embedded opmask register specifier.
     */
    ZyanU8 aaa;
    /**
     * The offset of the first evex byte, relative to the beginning of the
     * instruction, in bytes.
     */
    ZyanU8 offset;
} ZydisDecodedInstructionRawEvex;

/**
 * Detailed info about the `MVEX` prefix.
 */
typedef struct ZydisDecodedInstructionRawMvex_
{
    /**
     * Extension of the `ModRM.reg` field (inverted).
     */
    ZyanU8 R;
    /**
     * Extension of the `SIB.index/vidx` field (inverted).
     */
    ZyanU8 X;
    /**
     * Extension of the `ModRM.rm` or `SIB.base` field (inverted).
     */
    ZyanU8 B;
    /**
     * High-16 register specifier modifier (inverted).
     */
    ZyanU8 R2;
    /**
     * Opcode-map specifier.
     */
    ZyanU8 mmmm;
    /**
     * 64-bit operand-size promotion or opcode-extension.
     */
    ZyanU8 W;
    /**
     * `NDS`/`NDD` (non-destructive-source/destination) register specifier
     *  (inverted).
     */
    ZyanU8 vvvv;
    /**
     * Compressed legacy prefix.
     */
    ZyanU8 pp;
    /**
     * Non-temporal/eviction hint.
     */
    ZyanU8 E;
    /**
     * Swizzle/broadcast/up-convert/down-convert/static-rounding controls.
     */
    ZyanU8 SSS;
    /**
     * High-16 `NDS`/`VIDX` register specifier.
     */
    ZyanU8 V2;
    /**
     * Embedded opmask register specifier.
     */
    ZyanU8 kkk;
    /**
     * The offset of the first mvex byte, relative to the beginning of the
     * instruction, in bytes.
     */
    ZyanU8 offset;
} ZydisDecodedInstructionRawMvex;

/**
 * Extended info for `AVX` instructions.
 */
typedef struct ZydisDecodedInstructionAvx_
{
    /**
     * The `AVX` vector-length.
     */
    ZyanU16 vector_length;
    /**
     * Info about the embedded writemask-register (`AVX-512` and `KNC` only).
     */
    struct ZydisDecodedInstructionAvxMask_
    {
        /**
         * The masking mode.
         */
        ZydisMaskMode mode;
        /**
         * The mask register.
         */
        ZydisRegister reg;
    } mask;
    /**
     * Contains info about the `AVX` broadcast.
     */
    struct ZydisDecodedInstructionAvxBroadcast_
    {
        /**
         * Signals, if the broadcast is a static broadcast.
         *
         * This is the case for instructions with inbuilt broadcast functionality, which is
         * always active and not controlled by the `EVEX/MVEX.RC` bits.
         */
        ZyanBool is_static;
        /**
         * The `AVX` broadcast-mode.
         */
        ZydisBroadcastMode mode;
    } broadcast;
    /**
     * Contains info about the `AVX` rounding.
     */
    struct ZydisDecodedInstructionAvxRounding_
    {
        /**
         * The `AVX` rounding-mode.
         */
        ZydisRoundingMode mode;
    } rounding;
    /**
     * Contains info about the `AVX` register-swizzle (`KNC` only).
     */
    struct ZydisDecodedInstructionAvxSwizzle_
    {
        /**
         * The `AVX` register-swizzle mode.
         */
        ZydisSwizzleMode mode;
    } swizzle;
    /**
     * Contains info about the `AVX` data-conversion (`KNC` only).
     */
    struct ZydisDecodedInstructionAvxConversion_
    {
        /**
         * The `AVX` data-conversion mode.
         */
        ZydisConversionMode mode;
    } conversion;
    /**
     * Signals, if the `SAE` (suppress-all-exceptions) functionality is
     * enabled for the instruction.
     */
    ZyanBool has_sae;
    /**
     * Signals, if the instruction has a memory-eviction-hint (`KNC` only).
     */
    ZyanBool has_eviction_hint;
    // TODO: publish EVEX tuple-type and MVEX functionality
} ZydisDecodedInstructionAvx;

/**
 * Instruction meta info.
 */
typedef struct ZydisDecodedInstructionMeta_
{
    /**
     * The instruction category.
     */
    ZydisInstructionCategory category;
    /**
     * The ISA-set.
     */
    ZydisISASet isa_set;
    /**
     * The ISA-set extension.
     */
    ZydisISAExt isa_ext;
    /**
     * The branch type.
     */
    ZydisBranchType branch_type;
    /**
     * The exception class.
     */
    ZydisExceptionClass exception_class;
} ZydisDecodedInstructionMeta;

/**
 * Detailed info about different instruction-parts like `ModRM`, `SIB` or
 * encoding-prefixes.
 */
typedef struct ZydisDecodedInstructionRaw_
{
    /**
     * The number of legacy prefixes.
     */
    ZyanU8 prefix_count;
    /**
     * Detailed info about the legacy prefixes (including `REX`).
     */
    struct ZydisDecodedInstructionRawPrefixes_
    {
        /**
         * The prefix type.
         */
        ZydisPrefixType type;
        /**
         * The prefix byte.
         */
        ZyanU8 value;
    } prefixes[ZYDIS_MAX_INSTRUCTION_LENGTH];

    /*
     * Copy of the `encoding` field.
     *
     * This is here to allow the Rust bindings to treat the following union as an `enum`,
     * sparing us a lot of unsafe code. Prefer using the regular `encoding` field in C/C++ code.
     */
    ZydisInstructionEncoding encoding2;
    /*
     * Union for things from various mutually exclusive encodings.
     */
    union
    {
        ZydisDecodedInstructionRawRex rex;
        ZydisDecodedInstructionRawXop xop;
        ZydisDecodedInstructionRawVex vex;
        ZydisDecodedInstructionRawEvex evex;
        ZydisDecodedInstructionRawMvex mvex;
    };

    /**
     * Detailed info about the `ModRM` byte.
     */
    struct ZydisDecodedInstructionModRm_
    {
        /**
         * The addressing mode.
         */
        ZyanU8 mod;
        /**
         * Register specifier or opcode-extension.
         */
        ZyanU8 reg;
        /**
         * Register specifier or opcode-extension.
         */
        ZyanU8 rm;
        /**
         * The offset of the `ModRM` byte, relative to the beginning of the
         * instruction, in bytes.
         */
        ZyanU8 offset;
    } modrm;
    /**
     * Detailed info about the `SIB` byte.
     */
    struct ZydisDecodedInstructionRawSib_
    {
        /**
         * The scale factor.
         */
        ZyanU8 scale;
        /**
         * The index-register specifier.
         */
        ZyanU8 index;
        /**
         * The base-register specifier.
         */
        ZyanU8 base;
        /**
         * The offset of the `SIB` byte, relative to the beginning of the
         * instruction, in bytes.
         */
        ZyanU8 offset;
    } sib;
    /**
     * Detailed info about displacement-bytes.
     */
    struct ZydisDecodedInstructionRawDisp_
    {
        /**
         * The displacement value
         */
        ZyanI64 value;
        /**
         * The physical displacement size, in bits.
         */
        ZyanU8 size;
        // TODO: publish cd8 scale
        /**
         * The offset of the displacement data, relative to the beginning of the
         * instruction, in bytes.
         */
        ZyanU8 offset;
    } disp;
    /**
     * Detailed info about immediate-bytes.
     */
    struct ZydisDecodedInstructionRawImm_
    {
        /**
         * Signals, if the immediate value is signed.
         */
        ZyanBool is_signed;
        /**
         * Signals, if the immediate value contains a relative offset. You can use
         * `ZydisCalcAbsoluteAddress` to determine the absolute address value.
         */
        ZyanBool is_relative;
        /**
         * The immediate value.
         */
        union ZydisDecodedInstructionRawImmValue_
        {
            ZyanU64 u;
            ZyanI64 s;
        } value;
        /**
         * The physical immediate size, in bits.
         */
        ZyanU8 size;
        /**
         * The offset of the immediate data, relative to the beginning of the
         * instruction, in bytes.
         */
        ZyanU8 offset;
    } imm[2];
} ZydisDecodedInstructionRaw;

/**
 * Information about a decoded instruction.
 */
typedef struct ZydisDecodedInstruction_
{
    /**
     * The machine mode used to decode this instruction.
     */
    ZydisMachineMode machine_mode;
    /**
     * The instruction-mnemonic.
     */
    ZydisMnemonic mnemonic;
    /**
     * The length of the decoded instruction.
     */
    ZyanU8 length;
    /**
     * The instruction-encoding (`LEGACY`, `3DNOW`, `VEX`, `EVEX`, `XOP`).
     */
    ZydisInstructionEncoding encoding;
    /**
     * The opcode-map.
     */
    ZydisOpcodeMap opcode_map;
    /**
     * The instruction-opcode.
     */
    ZyanU8 opcode;
    /**
     * The stack width.
     */
    ZyanU8 stack_width;
    /**
     * The effective operand width.
     */
    ZyanU8 operand_width;
    /**
     * The effective address width.
     */
    ZyanU8 address_width;
    /**
     * The number of instruction-operands.
     *
     * Explicit and implicit operands are guaranteed to be in the front and ordered as they are
     * printed by the formatter in `Intel` mode. No assumptions can be made about the order of
     * hidden operands, except that they always located behind the explicit and implicit operands.
     */
    ZyanU8 operand_count;
    /**
     * The number of explicit (visible) instruction-operands.
     *
     * Explicit and implicit operands are guaranteed to be in the front and ordered as they are
     * printed by the formatter in `Intel` mode.
     */
    ZyanU8 operand_count_visible;
    /**
     * See @ref instruction_attributes.
     */
    ZydisInstructionAttributes attributes;
    /**
     * Information about CPU flags accessed by the instruction.
     *
     * The bits in the masks correspond to the actual bits in the `FLAGS/EFLAGS/RFLAGS`
     * register. See @ref decoder_cpu_flags.
     */
    const ZydisAccessedFlags* cpu_flags;
    /**
     * Information about FPU flags accessed by the instruction.
     * 
     * See @ref decoder_fpu_flags.
     */
    const ZydisAccessedFlags* fpu_flags;
    /**
     * Extended info for `AVX` instructions.
     */
    ZydisDecodedInstructionAvx avx;
    /**
     * Meta info.
     */
    ZydisDecodedInstructionMeta meta;
    /**
     * Detailed info about different instruction-parts like `ModRM`, `SIB` or
     * encoding-prefixes.
     */
    ZydisDecodedInstructionRaw raw;
} ZydisDecodedInstruction;

/* ---------------------------------------------------------------------------------------------- */
/* Decoder context                                                                                */
/* ---------------------------------------------------------------------------------------------- */

/**
 * The decoder context is used to preserve some internal state between subsequent decode
 * operations for THE SAME instruction.
 *
 * The context is initialized by @c ZydisDecoderDecodeInstruction and required by e.g.
 * @c ZydisDecoderDecodeOperands.
 *
 * All fields in this struct should be considered as "private". Any changes may lead to unexpected
 * behavior.
 *
 * This struct is neither ABI nor API stable!
 */
typedef struct ZydisDecoderContext_
{
    /**
     * A pointer to the internal instruction definition.
     */
    const void* definition;
    /**
     * Contains the effective operand-size index.
     *
     * 0 = 16 bit, 1 = 32 bit, 2 = 64 bit
     */
    ZyanU8 eosz_index;
    /**
     * Contains the effective address-size index.
     *
     * 0 = 16 bit, 1 = 32 bit, 2 = 64 bit
     */
    ZyanU8 easz_index;
    /**
     * Contains some cached REX/XOP/VEX/EVEX/MVEX values to provide uniform access.
     */
    struct
    {
        ZyanU8 W;
        ZyanU8 R;
        ZyanU8 X;
        ZyanU8 B;
        ZyanU8 L;
        ZyanU8 LL;
        ZyanU8 R2;
        ZyanU8 V2;
        ZyanU8 vvvv;
        ZyanU8 mask;
    } vector_unified;
    /**
     * Information about encoded operand registers.
     */
    struct
    {
        /**
         * Signals if the `modrm.mod == 3` or `reg` form is forced for the instruction.
         */
        ZyanBool is_mod_reg;
        /**
         * The final register id for the `reg` encoded register.
         */
        ZyanU8 id_reg;
        /**
         * The final register id for the `rm` encoded register.
         *
         * This value is only set, if a register is encoded in `modrm.rm`.
         */
        ZyanU8 id_rm;
        /**
         * The final register id for the `ndsndd` (`.vvvv`) encoded register.
         */
        ZyanU8 id_ndsndd;
        /**
         * The final register id for the base register.
         *
         * This value is only set, if a memory operand is encoded in `modrm.rm`.
         */
        ZyanU8 id_base;
        /**
         * The final register id for the index register.
         *
         * This value is only set, if a memory operand is encoded in `modrm.rm` and the `SIB` byte
         * is present.
         */
        ZyanU8 id_index;
    } reg_info;
    /**
     * Internal EVEX-specific information.
     */
    struct
    {
        /**
         * The EVEX tuple-type.
         */
        ZyanU8 tuple_type;
        /**
         * The EVEX element-size.
         */
        ZyanU8 element_size;
    } evex;
    /**
     * Internal MVEX-specific information.
     */
    struct
    {
        /**
         * The MVEX functionality.
         */
        ZyanU8 functionality;
    } mvex;
    /**
     * The scale factor for EVEX/MVEX compressed 8-bit displacement values.
     */
    ZyanU8 cd8_scale; // TODO: Could make sense to expose this in the ZydisDecodedInstruction
} ZydisDecoderContext;

/* ---------------------------------------------------------------------------------------------- */

/* ============================================================================================== */

#ifdef __cplusplus
}
#endif

#endif /* ZYDIS_INSTRUCTIONINFO_H */
