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

  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
 * Functions for decoding instructions.
 */

#ifndef ZYDIS_DECODER_H
#define ZYDIS_DECODER_H

#include <Zycore/Types.h>
#include <Zycore/Defines.h>
#include <Zydis/DecoderTypes.h>
#include <Zydis/Status.h>

#ifdef __cplusplus
extern "C" {
#endif

/* ============================================================================================== */
/* Enums and types                                                                                */
/* ============================================================================================== */

/* ---------------------------------------------------------------------------------------------- */
/* Decoder mode                                                                                   */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisDecoderMode` enum.
 */
typedef enum ZydisDecoderMode_
{
    /**
     * Enables minimal instruction decoding without semantic analysis.
     *
     * This mode provides access to the mnemonic, the instruction-length, the effective
     * operand-size, the effective address-width, some attributes (e.g. `ZYDIS_ATTRIB_IS_RELATIVE`)
     * and all of the information in the `raw` field of the `ZydisDecodedInstruction` struct.
     *
     * Operands, most attributes and other specific information (like `AVX` info) are not
     * accessible in this mode.
     *
     * This mode is NOT enabled by default.
     */
    ZYDIS_DECODER_MODE_MINIMAL,
    /**
     * Enables the `AMD`-branch mode.
     *
     * Intel ignores the operand-size override-prefix (`0x66`) for all branches with 32-bit
     * immediates and forces the operand-size of the instruction to 64-bit in 64-bit mode.
     * In `AMD`-branch mode `0x66` is not ignored and changes the operand-size and the size of the
     * immediate to 16-bit.
     *
     * This mode is NOT enabled by default.
     */
    ZYDIS_DECODER_MODE_AMD_BRANCHES,
    /**
     * Enables `KNC` compatibility-mode.
     *
     * `KNC` and `KNL+` chips are sharing opcodes and encodings for some mask-related instructions.
     * Enable this mode to use the old `KNC` specifications (different mnemonics, operands, ..).
     *
     * This mode is NOT enabled by default.
     */
    ZYDIS_DECODER_MODE_KNC,
    /**
     * Enables the `MPX` mode.
     *
     * The `MPX` isa-extension reuses (overrides) some of the widenop instruction opcodes.
     *
     * This mode is enabled by default.
     */
    ZYDIS_DECODER_MODE_MPX,
    /**
     * Enables the `CET` mode.
     *
     * The `CET` isa-extension reuses (overrides) some of the widenop instruction opcodes.
     *
     * This mode is enabled by default.
     */
    ZYDIS_DECODER_MODE_CET,
    /**
     * Enables the `LZCNT` mode.
     *
     * The `LZCNT` isa-extension reuses (overrides) some of the widenop instruction opcodes.
     *
     * This mode is enabled by default.
     */
    ZYDIS_DECODER_MODE_LZCNT,
    /**
     * Enables the `TZCNT` mode.
     *
     * The `TZCNT` isa-extension reuses (overrides) some of the widenop instruction opcodes.
     *
     * This mode is enabled by default.
     */
    ZYDIS_DECODER_MODE_TZCNT,
    /**
     * Enables the `WBNOINVD` mode.
     *
     * The `WBINVD` instruction is interpreted as `WBNOINVD` on ICL chips, if a `F3` prefix is
     * used.
     *
     * This mode is disabled by default.
     */
    ZYDIS_DECODER_MODE_WBNOINVD,
     /**
     * Enables the `CLDEMOTE` mode.
     *
     * The `CLDEMOTE` isa-extension reuses (overrides) some of the widenop instruction opcodes.
     *
     * This mode is enabled by default.
     */
    ZYDIS_DECODER_MODE_CLDEMOTE,
    /**
     * Enables the `IPREFETCH` mode.
     *
     * The `IPREFETCH` isa-extension reuses (overrides) some of the widenop instruction opcodes.
     *
     * This mode is enabled by default.
     */
    ZYDIS_DECODER_MODE_IPREFETCH,
    /**
     * Enables the `UD0` compatibility mode.
     *
     * Some processors decode the `UD0` instruction without a ModR/M byte. Enable this decoder mode
     * to mimic this behavior.
     *
     * This mode is disabled by default.
     */
    ZYDIS_DECODER_MODE_UD0_COMPAT,

    /**
     * Maximum value of this enum.
     */
    ZYDIS_DECODER_MODE_MAX_VALUE = ZYDIS_DECODER_MODE_UD0_COMPAT,
    /**
     * The minimum number of bits required to represent all values of this enum.
     */
    ZYDIS_DECODER_MODE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_DECODER_MODE_MAX_VALUE)
} ZydisDecoderMode;

/* ---------------------------------------------------------------------------------------------- */
/* Decoder struct                                                                                 */
/* ---------------------------------------------------------------------------------------------- */

/**
 * Defines the `ZydisDecoder` struct.
 *
 * All fields in this struct should be considered as "private". Any changes may lead to unexpected
 * behavior.
 */
typedef struct ZydisDecoder_
{
    /**
     * The machine mode.
     */
    ZydisMachineMode machine_mode;
    /**
     * The stack width.
     */
    ZydisStackWidth stack_width;
    /**
     * The decoder mode bitmap.
     */
    ZyanU32 decoder_mode;
} ZydisDecoder;

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

/* ============================================================================================== */
/* Exported functions                                                                             */
/* ============================================================================================== */

/**
 * @addtogroup decoder Decoder
 * Functions allowing decoding of instruction bytes to a machine interpretable struct.
 * @{
 */

/**
 * Initializes the given `ZydisDecoder` instance.
 *
 * @param   decoder         A pointer to the `ZydisDecoder` instance.
 * @param   machine_mode    The machine mode.
 * @param   stack_width     The stack width.
 *
 * @return  A zyan status code.
 */
ZYDIS_EXPORT ZyanStatus ZydisDecoderInit(ZydisDecoder* decoder, ZydisMachineMode machine_mode,
    ZydisStackWidth stack_width);

/**
 * Enables or disables the specified decoder-mode.
 *
 * @param   decoder A pointer to the `ZydisDecoder` instance.
 * @param   mode    The decoder mode.
 * @param   enabled `ZYAN_TRUE` to enable, or `ZYAN_FALSE` to disable the specified decoder-mode.
 *
 * @return  A zyan status code.
 */
ZYDIS_EXPORT ZyanStatus ZydisDecoderEnableMode(ZydisDecoder* decoder, ZydisDecoderMode mode,
    ZyanBool enabled);

/**
 * Decodes the instruction in the given input `buffer` and returns all details (e.g. operands).
 *
 * @param   decoder         A pointer to the `ZydisDecoder` instance.
 * @param   buffer          A pointer to the input buffer.
 * @param   length          The length of the input buffer. Note that this can be bigger than the
 *                          actual size of the instruction -- you don't have to know the size up
 *                          front. This length is merely used to prevent Zydis from doing
 *                          out-of-bounds reads on your buffer.
 * @param   instruction     A pointer to the `ZydisDecodedInstruction` struct receiving the details
 *                          about the decoded instruction.
 * @param   operands        A pointer to an array with `ZYDIS_MAX_OPERAND_COUNT` entries that
 *                          receives the decoded operands. The number of operands decoded is
 *                          determined by the `instruction.operand_count` field. Excess entries are
 *                          zeroed.
 *
 * This is a convenience function that combines the following functions into one call:
 * 
 *   - `ZydisDecoderDecodeInstruction`
 *   - `ZydisDecoderDecodeOperands`
 * 
 * Please refer to `ZydisDecoderDecodeInstruction` if operand decoding is not required or should
 * be done separately (`ZydisDecoderDecodeOperands`).
 *
 * This function is not available in MINIMAL_MODE.
 *
 * @return  A zyan status code.
 */
ZYDIS_EXPORT ZyanStatus ZydisDecoderDecodeFull(const ZydisDecoder* decoder,
    const void* buffer, ZyanUSize length, ZydisDecodedInstruction* instruction,
    ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]);

/**
 * Decodes the instruction in the given input `buffer`.
 *
 * @param   decoder     A pointer to the `ZydisDecoder` instance.
 * @param   context     A pointer to a decoder context struct which is required for further
 *                      decoding (e.g. operand decoding using `ZydisDecoderDecodeOperands`) or
 *                      `ZYAN_NULL` if not needed.
 * @param   buffer      A pointer to the input buffer.
 * @param   length      The length of the input buffer. Note that this can be bigger than the
 *                      actual size of the instruction -- you don't have to know the size up
 *                      front. This length is merely used to prevent Zydis from doing
 *                      out-of-bounds reads on your buffer.
 * @param   instruction A pointer to the `ZydisDecodedInstruction` struct, that receives the
 *                      details about the decoded instruction.
 *
 * @return  A zyan status code.
 */
ZYDIS_EXPORT ZyanStatus ZydisDecoderDecodeInstruction(const ZydisDecoder* decoder,
    ZydisDecoderContext* context, const void* buffer, ZyanUSize length,
    ZydisDecodedInstruction* instruction);

/**
 * Decodes the instruction operands.
 *
 * @param   decoder         A pointer to the `ZydisDecoder` instance.
 * @param   context         A pointer to the `ZydisDecoderContext` struct.
 * @param   instruction     A pointer to the `ZydisDecodedInstruction` struct.
 * @param   operands        The array that receives the decoded operands.
 *                          Refer to `ZYDIS_MAX_OPERAND_COUNT` or `ZYDIS_MAX_OPERAND_COUNT_VISIBLE`
 *                          when allocating space for the array to ensure that the buffer size is
 *                          sufficient to always fit all instruction operands.
 *                          Refer to `instruction.operand_count` or
 *                          `instruction.operand_count_visible' when allocating space for the array
 *                          to ensure that the buffer size is sufficient to fit all operands of
 *                          the given instruction.
 * @param   operand_count   The length of the `operands` array.
 *                          This argument as well limits the maximum amount of operands to decode.
 *                          If this value is `0`, no operands will be decoded and `ZYAN_NULL` will
 *                          be accepted for the `operands` argument.
 *
 * This function fails, if `operand_count` is larger than the total number of operands for the
 * given instruction (`instruction.operand_count`).
 *
 * This function is not available in MINIMAL_MODE.
 *
 * @return  A zyan status code.
 */
ZYDIS_EXPORT ZyanStatus ZydisDecoderDecodeOperands(const ZydisDecoder* decoder,
    const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction,
    ZydisDecodedOperand* operands, ZyanU8 operand_count);

/** @} */

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

#ifdef __cplusplus
}
#endif

#endif /* ZYDIS_DECODER_H */
