/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (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.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @author Alexander V. Astapchuk
 */
#ifndef __ENC_PRVT_H_INCLUDED__
#define __ENC_PRVT_H_INCLUDED__

#include "enc_base.h"

ENCODER_NAMESPACE_START
/*
 * @file
 * @brief Contains some definitions/constants and other stuff used by the
 *        Encoder internally.
 */

enum OpcodeByteKind {
    //OpcodeByteKind_Opcode = 0x0000,
    OpcodeByteKind_ZeroOpcodeByte           = 0x0100,
    //
    // The names _SlashR,  _SlahsNum, _ib, _iw, etc
    // represent the appropriate abbreviations used
    // in the mnemonic descriptions in the Intel's arch manual.
    //
    OpcodeByteKind_SlashR                   = 0x0200,
    OpcodeByteKind_SlashNum                 = 0x0300,
    OpcodeByteKind_ib                       = 0x0400,
    OpcodeByteKind_iw                       = 0x0500,
    OpcodeByteKind_id                       = 0x0600,
#ifdef _EM64T_
    OpcodeByteKind_io                       = 0x0700,
#endif
    OpcodeByteKind_cb                       = 0x0800,
    OpcodeByteKind_cw                       = 0x0900,
    OpcodeByteKind_cd                       = 0x0A00,
    //OpcodeByteKind_cp                     = 0x0B00,
    //OpcodeByteKind_co                     = 0x0C00,
    //OpcodeByteKind_ct                     = 0x0D00,

    OpcodeByteKind_rb                       = 0x0E00,
    OpcodeByteKind_rw                       = 0x0F00,
    OpcodeByteKind_rd                       = 0x1000,
#ifdef _EM64T_
    OpcodeByteKind_ro                       = 0x1100,
    //OpcodeByteKind_REX                    = 0x1200,
    OpcodeByteKind_REX_W                    = 0x1300,
#endif
    OpcodeByteKind_plus_i                   = 0x1400,
    /**
        * a special marker, means 'no opcode on the given position'
        * used in opcodes array, to specify the empty slot, say
        * to fill an em64t-specific opcode on ia32.
        * last 'e' made lowercase to avoid a mess with 'F' in
        * OpcodeByteKind_LAST .
        */
    OpcodeByteKind_EMPTY                    = 0xFFFE,
    /**
        * a special marker, means 'no more opcodes in the array'
        * used in in opcodes array to show that there are no more
        * opcodes in the array for a given mnemonic.
        */
    OpcodeByteKind_LAST                     = 0xFFFF,
    /**
        * a mask to extract the OpcodeByteKind
        */
    OpcodeByteKind_KindMask                 = 0xFF00,
    /**
        * a mask to extract the opcode byte when presented
        */
    OpcodeByteKind_OpcodeMask               = 0x00FF
};

#ifdef USE_ENCODER_DEFINES

#define N           {0, 0, 0, 0 }
#define U           {1, 0, 1, OpndRole_Use }
#define D           {1, 1, 0, OpndRole_Def }
#define DU          {1, 1, 1, OpndRole_Def|OpndRole_Use }

#define U_U         {2, 0, 2, OpndRole_Use<<2 | OpndRole_Use }
#define D_U         {2, 1, 1, OpndRole_Def<<2 | OpndRole_Use }
#define D_DU        {2, 2, 1, OpndRole_Def<<2 | (OpndRole_Def|OpndRole_Use) }
#define DU_U        {2, 1, 2, ((OpndRole_Def|OpndRole_Use)<<2 | OpndRole_Use) }
#define DU_DU       {2, 2, 2, ((OpndRole_Def|OpndRole_Use)<<2 | (OpndRole_Def|OpndRole_Use)) }

#define DU_DU_DU    {3, 3, 3, ((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | (OpndRole_Def|OpndRole_Use) }
#define DU_DU_U     {3, 2, 3, (((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) }
#define D_DU_U      {3, 2, 2, (((OpndRole_Def)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) }
#define D_U_U       {3, 1, 2, (((OpndRole_Def)<<4) | ((OpndRole_Use)<<2) | OpndRole_Use) }

// Special encoding of 0x00 opcode byte. Note: it's all O-s, not zeros.
#define OxOO        OpcodeByteKind_ZeroOpcodeByte

#define Size16      InstPrefix_OpndSize

#define _r          OpcodeByteKind_SlashR

#define _0          OpcodeByteKind_SlashNum|0
#define _1          OpcodeByteKind_SlashNum|1
#define _2          OpcodeByteKind_SlashNum|2
#define _3          OpcodeByteKind_SlashNum|3
#define _4          OpcodeByteKind_SlashNum|4
#define _5          OpcodeByteKind_SlashNum|5
#define _6          OpcodeByteKind_SlashNum|6
#define _7          OpcodeByteKind_SlashNum|7

// '+i' for floating-point instructions
#define _i          OpcodeByteKind_plus_i


#define ib          OpcodeByteKind_ib
#define iw          OpcodeByteKind_iw
#define id          OpcodeByteKind_id

#define cb          OpcodeByteKind_cb
#define cw          OpcodeByteKind_cw
#define cd          OpcodeByteKind_cd

#define rb          OpcodeByteKind_rb
#define rw          OpcodeByteKind_rw
#define rd          OpcodeByteKind_rd

#define AL          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AL}
#define AH          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AH}
#define AX          {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_AX}
#define EAX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EAX}
#ifdef _EM64T_
    #define RAX     {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RAX }
#endif

#define CL          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_CL}
#define ECX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ECX}
#ifdef _EM64T_
    #define RCX         {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RCX}
#endif

#define DX          {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_DX}
#define EDX         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDX}
#ifdef _EM64T_
    #define RDX     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDX }
#endif

#define ESI         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ESI}
#ifdef _EM64T_
    #define RSI     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RSI }
#endif

#define EDI         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDI}
#ifdef _EM64T_
    #define RDI     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDI }
#endif

#define r8          {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_Null}
#define r16         {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_Null}
#define r32         {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_Null}
#ifdef _EM64T_
    #define r64     { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_Null }
#endif

#define r_m8        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Any, RegName_Null}
#define r_m16       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Any, RegName_Null}
#define r_m32       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null}

#define r_m8s        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Signed, RegName_Null}
#define r_m16s       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Signed, RegName_Null}
#define r_m32s       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Signed, RegName_Null}

#define r_m8u        {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Zero, RegName_Null}
#define r_m16u       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Zero, RegName_Null}
#define r_m32u       {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Zero, RegName_Null}

//'m' was only used in LEA mnemonic, but is replaced with
// set of exact sizes. See more comments for LEA instruction in TheTable.
//#define m           {OpndKind_Mem, OpndSize_Null, RegName_Null}
#define m8          {OpndKind_Mem, OpndSize_8, OpndExt_Any, RegName_Null}
#define m16         {OpndKind_Mem, OpndSize_16, OpndExt_Any, RegName_Null}
#define m32         {OpndKind_Mem, OpndSize_32, OpndExt_Any, RegName_Null}
#define m64         {OpndKind_Mem, OpndSize_64, OpndExt_Any, RegName_Null}
#ifdef _EM64T_
    #define r_m64   { (OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null }
#endif

#define imm8        {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null}
#define imm16       {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null}
#define imm32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}

#define imm8s        {OpndKind_Imm, OpndSize_8, OpndExt_Signed, RegName_Null}
#define imm16s       {OpndKind_Imm, OpndSize_16, OpndExt_Signed, RegName_Null}
#define imm32s       {OpndKind_Imm, OpndSize_32, OpndExt_Signed, RegName_Null}

#define imm8u        {OpndKind_Imm, OpndSize_8, OpndExt_Zero, RegName_Null}
#define imm16u       {OpndKind_Imm, OpndSize_16, OpndExt_Zero, RegName_Null}
#define imm32u       {OpndKind_Imm, OpndSize_32, OpndExt_Zero, RegName_Null}

#ifdef _EM64T_
    #define imm64   {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null }
#endif

//FIXME: moff-s are in fact memory refs, but presented as immediate.
// Need to specify this in OpndDesc.
#define moff8        {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
#define moff16       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
#define moff32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}
#ifdef _EM64T_
    #define moff64       {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null}
#endif


#define rel8        {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null}
#define rel16       {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null}
#define rel32       {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null}

#define mm64        {OpndKind_MMXReg, OpndSize_64, OpndExt_Any, RegName_Null}
#define mm_m64      {(OpndKind)(OpndKind_MMXReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null}

#define xmm64       {OpndKind_XMMReg, OpndSize_64, OpndExt_Any, RegName_Null}
#define xmm_m64     {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null}

#define xmm32       {OpndKind_XMMReg, OpndSize_32, OpndExt_Any, RegName_Null}
#define xmm_m32     {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null}

#define FP0S        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP0S}
#define FP0D        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP0D}
#define FP1S        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP1S}
#define FP1D        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP1D}
#define fp32        {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_Null}
#define fp64        {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_Null}

#ifdef _EM64T_
    #define io      OpcodeByteKind_io
    #define REX_W   OpcodeByteKind_REX_W

#endif

#endif // USE_ENCODER_DEFINES

/**
 * @brief Represents the REX part of instruction.
 */
struct  Rex {
    unsigned char b : 1;
    unsigned char x : 1;
    unsigned char r : 1;
    unsigned char w : 1;
    unsigned char dummy : 4;        // must be '0100'b
    unsigned int  :24;
};

/**
 * @brief Describes SIB (scale,index,base) byte.
 */
struct SIB {
    unsigned char base:3;
    unsigned char index:3;
    unsigned char scale:2;
    unsigned int  padding:24;
};
/**
 * @brief Describes ModRM byte.
 */
struct ModRM
{
    unsigned char rm:3;
    unsigned char reg:3;
    unsigned char mod:2;
    unsigned int  padding:24;
};



/**
* exactly the same as EncoderBase::OpcodeDesc, but also holds info about
* platform on which the opcode is applicable.
*/
struct OpcodeInfo {
    enum platform {
        /// an opcode is valid on all platforms
        all,
        // opcode is valid on IA-32 only
        em64t,
        // opcode is valid on Intel64 only
        ia32,
        // opcode is added for the sake of disassembling, should not be used in encoding
        decoder,
        // only appears in master table, replaced with 'decoder' in hashed version
        decoder32,
        // only appears in master table, replaced with 'decoder' in hashed version
        decoder64,
    };
    platform                        platf;
    unsigned                        opcode[4+1+1];
    EncoderBase::OpndDesc           opnds[3];
    EncoderBase::OpndRolesDesc      roles;
};

/**
 * @defgroup MF_ Mnemonic flags
*/

    /**
 * Operation has no special properties.
    */
#define MF_NONE             (0x00000000)
    /**
 * Operation affects flags
    */
#define MF_AFFECTS_FLAGS    (0x00000001)
    /**
 * Operation uses flags - conditional operations, ADC/SBB/ETC
    */
#define MF_USES_FLAGS       (0x00000002)
    /**
 * Operation is conditional - MOVcc/SETcc/Jcc/ETC
    */
#define MF_CONDITIONAL      (0x00000004)
/**
 * Operation is symmetric - its args can be swapped (ADD/MUL/etc).
 */
#define MF_SYMMETRIC        (0x00000008)
/**
 * Operation is XOR-like - XOR, SUB - operations of 'arg,arg' is pure def,
 * without use.
 */
#define MF_SAME_ARG_NO_USE  (0x00000010)

///@} // ~MNF

/**
 * @see same structure as EncoderBase::MnemonicDesc, but carries
 * MnemonicInfo::OpcodeInfo[] instead of OpcodeDesc[].
 * Only used during prebuilding the encoding tables, thus it's hidden under
 * the appropriate define.
 */
struct MnemonicInfo {
    /**
    * The mnemonic itself
    */
    Mnemonic    mn;
    /**
     * Various characteristics of mnemonic.
     * @see MF_
     */
    unsigned    flags;
    /**
     * Number of args/des/uses/roles for the operation. For the operations
     * which may use different number of operands (i.e. IMUL/SHL) use the
     * most common value, or leave '0' if you are sure this info is not
     * required.
     */
    EncoderBase::OpndRolesDesc              roles;
    /**
     * Print name of the mnemonic
     */
    const char *                            name;
    /**
     * Array of opcodes.
     * The terminating opcode description always have OpcodeByteKind_LAST
     * at the opcodes[i].opcode[0].
     * The size of '25' has nothing behind it, just counted the max
     * number of opcodes currently used (MOV instruction).
     */
    OpcodeInfo                              opcodes[25];
};

ENCODER_NAMESPACE_END

#endif  // ~__ENC_PRVT_H_INCLUDED__