/*
 *  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
 */
/**
 * @file
 * @brief Simple interface for generating processor instructions.
 *
 * The interface works for both IA32 and EM64T. By default, only IA32
 * capabilities are presented. To enable EM64T feature, the _EM64T_ macro
 * must be defined (and, of course, a proper library version to be used).
 *
 * The interface is based on the original ia32.h encoder interface,
 * with some simplifications and add-ons - EM64T-specific, SSE and SSE2.
 *
 * The interface mostly intended for existing legacy code like LIL code
 * generator. From the implementation point of view, it's just a wrapper
 * around the EncoderBase functionality.
 */

#ifndef _VM_ENCODER_H_
#define _VM_ENCODER_H_

#include <limits.h>
#include "enc_base.h"
//#include "open/types.h"

#ifdef _EM64T_
// size of general-purpose value on the stack in bytes
#define GR_STACK_SIZE 8
// size of floating-point value on the stack in bytes
#define FR_STACK_SIZE 8

#if defined(WIN32) || defined(_WIN64)
    // maximum number of GP registers for inputs
    const int MAX_GR = 4;
    // maximum number of FP registers for inputs
    const int MAX_FR = 4;
    // WIN64 reserves 4 words for shadow space
    const int SHADOW = 4 * GR_STACK_SIZE;
#else
    // maximum number of GP registers for inputs
    const int MAX_GR = 6;
    // maximum number of FP registers for inputs
    const int MAX_FR = 8;
    // Linux x64 doesn't reserve shadow space
    const int SHADOW = 0;
#endif

#else
// size of general-purpose value on the stack in bytes
#define GR_STACK_SIZE 4
// size of general-purpose value on the stack in bytes
#define FR_STACK_SIZE 8

// maximum number of GP registers for inputs
const int MAX_GR = 0;
// maximum number of FP registers for inputs
const int MAX_FR = 0;
#endif

typedef enum Reg_No {
#ifdef _EM64T_
    rax_reg = 0,rbx_reg,    rcx_reg,    rdx_reg,
    rdi_reg,    rsi_reg,    rsp_reg,    rbp_reg,
    r8_reg,     r9_reg,     r10_reg,    r11_reg,
    r12_reg,    r13_reg,    r14_reg,    r15_reg,
    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
    xmm8_reg,   xmm9_reg,   xmm10_reg,  xmm11_reg,
    xmm12_reg,  xmm13_reg,  xmm14_reg,  xmm15_reg,

#else   // !defined(_EM64T_)

    eax_reg = 0,ebx_reg,    ecx_reg,    edx_reg,
    edi_reg,    esi_reg,    esp_reg,    ebp_reg,
    xmm0_reg,   xmm1_reg,   xmm2_reg,   xmm3_reg,
    xmm4_reg,   xmm5_reg,   xmm6_reg,   xmm7_reg,
    fs_reg,
#endif
    /** @brief Total number of registers.*/
    n_reg
} Reg_No;
//
// instruction operand sizes: 8,16,32,64 bits
//
typedef enum Opnd_Size {
    size_8 = 0,
    size_16,
    size_32,
    size_64,
    n_size,
#ifdef _EM64T_
    size_platf = size_64
#else
    size_platf = size_32
#endif
} Opnd_Size;

//
// opcodes for alu instructions
//
typedef enum ALU_Opcode {
    add_opc = 0,or_opc,     adc_opc,    sbb_opc,
    and_opc,    sub_opc,    xor_opc,    cmp_opc,
    n_alu
} ALU_Opcode;

//
// opcodes for shift instructions
//
typedef enum Shift_Opcode {
    shld_opc,   shrd_opc,   shl_opc,    shr_opc,
    sar_opc,    ror_opc, max_shift_opcode=6,     n_shift = 6
} Shift_Opcode;

typedef enum ConditionCode {
    Condition_O     = 0,
    Condition_NO    = 1,
    Condition_B     = 2,
    Condition_NAE   = Condition_B,
    Condition_C     = Condition_B,
    Condition_NB    = 3,
    Condition_AE    = Condition_NB,
    Condition_NC    = Condition_NB,
    Condition_Z     = 4,
    Condition_E     = Condition_Z,
    Condition_NZ    = 5,
    Condition_NE    = Condition_NZ,
    Condition_BE    = 6,
    Condition_NA    = Condition_BE,
    Condition_NBE   = 7,
    Condition_A     = Condition_NBE,

    Condition_S     = 8,
    Condition_NS    = 9,
    Condition_P     = 10,
    Condition_PE    = Condition_P,
    Condition_NP    = 11,
    Condition_PO    = Condition_NP,
    Condition_L     = 12,
    Condition_NGE   = Condition_L,
    Condition_NL    = 13,
    Condition_GE    = Condition_NL,
    Condition_LE    = 14,
    Condition_NG    = Condition_LE,
    Condition_NLE   = 15,
    Condition_G     = Condition_NLE,
    Condition_Count = 16
} ConditionCode;

//
// prefix code
//
typedef enum InstrPrefix {
    no_prefix,
    lock_prefix                     = 0xF0,
    hint_branch_taken_prefix        = 0x2E,
    hint_branch_not_taken_prefix    = 0x3E,
    prefix_repne                    = 0xF2,
    prefix_repnz                    = prefix_repne,
    prefix_repe                     = 0xF3,
    prefix_repz                     = prefix_repe,
    prefix_rep                      = 0xF3,
    prefix_cs                       = 0x2E,
    prefix_ss                       = 0x36,
    prefix_ds                       = 0x3E,
    prefix_es                       = 0x26,
    prefix_fs                       = 0x64,
    prefix_gs                       = 0x65
} InstrPrefix;


//
// an instruction operand
//
class Opnd {

protected:
    enum Tag { SignedImm, UnsignedImm, Reg, Mem, FP, XMM };

    const Tag  tag;

    Opnd(Tag t): tag(t) {}

public:
    void * operator new(size_t, void * mem) {
        return mem;
    }

    void operator delete(void *) {}

    void operator delete(void *, void *) {}

private:
    // disallow copying
    Opnd(const Opnd &): tag(Mem) { assert(false); }
    Opnd& operator=(const Opnd &) { assert(false); return *this; }
};
typedef int I_32;
class Imm_Opnd: public Opnd {

protected:
    union {
#ifdef _EM64T_
        int64           value;
        unsigned char   bytes[8];
#else
        I_32           value;
        unsigned char   bytes[4];
#endif
    };
    Opnd_Size           size;

public:
    Imm_Opnd(I_32 val, bool isSigned = true):
        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(size_32) {
        if (isSigned) {
            if (CHAR_MIN <= val && val <= CHAR_MAX) {
                size = size_8;
            } else if (SHRT_MIN <= val && val <= SHRT_MAX) {
                size = size_16;
            }
        } else {
            assert(val >= 0);
            if (val <= UCHAR_MAX) {
                size = size_8;
            } else if (val <= USHRT_MAX) {
                size = size_16;
            }
        }
    }
    Imm_Opnd(const Imm_Opnd& that): Opnd(that.tag), value(that.value), size(that.size) {};

#ifdef _EM64T_
    Imm_Opnd(Opnd_Size sz, int64 val, bool isSigned = true):
        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) {
#ifndef NDEBUG
        switch (size) {
        case size_8:
            assert(val == (int64)(I_8)val);
            break;
        case size_16:
            assert(val == (int64)(int16)val);
            break;
        case size_32:
            assert(val == (int64)(I_32)val);
            break;
        case size_64:
            break;
        case n_size:
            assert(false);
            break;
        }
#endif // NDEBUG
    }

    int64 get_value() const { return value; }

#else

    Imm_Opnd(Opnd_Size sz, I_32 val, int isSigned = true):
        Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) {
#ifndef NDEBUG
        switch (size) {
        case size_8:
            assert((I_32)val == (I_32)(I_8)val);
            break;
        case size_16:
            assert((I_32)val == (I_32)(int16)val);
            break;
        case size_32:
            break;
        case size_64:
        case n_size:
            assert(false);
            break;
        }
#endif // NDEBUG
    }

    I_32 get_value() const { return value; }

#endif
    Opnd_Size get_size() const { return size; }
    bool      is_signed() const { return tag == SignedImm; }
};

class RM_Opnd: public Opnd {

public:
    bool is_reg() const { return tag != SignedImm && tag != UnsignedImm && tag != Mem; }

protected:
    RM_Opnd(Tag t): Opnd(t) {}

private:
    // disallow copying
    RM_Opnd(const RM_Opnd &): Opnd(Reg) { assert(false); }
};

class R_Opnd: public RM_Opnd {

protected:
    Reg_No      _reg_no;

public:
    R_Opnd(Reg_No r): RM_Opnd(Reg), _reg_no(r) {}
    Reg_No  reg_no() const { return _reg_no; }

private:
    // disallow copying
    R_Opnd(const R_Opnd &): RM_Opnd(Reg) { assert(false); }
};

//
// a memory operand with displacement
// Can also serve as a full memory operand with base,index, displacement and scale.
// Use n_reg to specify 'no register', say, for index.
class M_Opnd: public RM_Opnd {

protected:
    Imm_Opnd        m_disp;
    Imm_Opnd        m_scale;
    R_Opnd          m_index;
    R_Opnd          m_base;

public:
    //M_Opnd(Opnd_Size sz): RM_Opnd(Mem, K_M, sz), m_disp(0), m_scale(0), m_index(n_reg), m_base(n_reg) {}
    M_Opnd(I_32 disp):
        RM_Opnd(Mem), m_disp(disp), m_scale(0), m_index(n_reg), m_base(n_reg) {}
    M_Opnd(Reg_No rbase, I_32 rdisp):
        RM_Opnd(Mem), m_disp(rdisp), m_scale(0), m_index(n_reg), m_base(rbase) {}
    M_Opnd(I_32 disp, Reg_No rbase, Reg_No rindex, unsigned scale):
        RM_Opnd(Mem), m_disp(disp), m_scale(scale), m_index(rindex), m_base(rbase) {}
    M_Opnd(const M_Opnd & that) : RM_Opnd(Mem),
        m_disp((int)that.m_disp.get_value()), m_scale((int)that.m_scale.get_value()),
        m_index(that.m_index.reg_no()), m_base(that.m_base.reg_no())
        {}
    //
    inline const R_Opnd & base(void) const { return m_base; }
    inline const R_Opnd & index(void) const { return m_index; }
    inline const Imm_Opnd & scale(void) const { return m_scale; }
    inline const Imm_Opnd & disp(void) const { return m_disp; }
};

//
//  a memory operand with base register and displacement
//
class M_Base_Opnd: public M_Opnd {

public:
    M_Base_Opnd(Reg_No base, I_32 disp) : M_Opnd(disp, base, n_reg, 0) {}

private:
    // disallow copying - but it leads to ICC errors #734 in encoder.inl
    // M_Base_Opnd(const M_Base_Opnd &): M_Opnd(0) { assert(false); }
};

//
//  a memory operand with base register, scaled index register
//  and displacement.
//
class M_Index_Opnd : public M_Opnd {

public:
    M_Index_Opnd(Reg_No base, Reg_No index, I_32 disp, unsigned scale):
        M_Opnd(disp, base, index, scale) {}

private:
    // disallow copying - but it leads to ICC errors #734 in encoder.inl
    // M_Index_Opnd(const M_Index_Opnd &): M_Opnd(0) { assert(false); }
};

class XMM_Opnd : public Opnd {

protected:
    unsigned        m_idx;

public:
    XMM_Opnd(unsigned _idx): Opnd(XMM), m_idx(_idx) {};
    unsigned get_idx( void ) const { return m_idx; };

private:
    // disallow copying
    XMM_Opnd(const XMM_Opnd &): Opnd(XMM) { assert(false); }
};

//
// operand structures for ia32 registers
//
#ifdef _EM64T_

extern R_Opnd rax_opnd;
extern R_Opnd rcx_opnd;
extern R_Opnd rdx_opnd;
extern R_Opnd rbx_opnd;
extern R_Opnd rdi_opnd;
extern R_Opnd rsi_opnd;
extern R_Opnd rsp_opnd;
extern R_Opnd rbp_opnd;

extern R_Opnd r8_opnd;
extern R_Opnd r9_opnd;
extern R_Opnd r10_opnd;
extern R_Opnd r11_opnd;
extern R_Opnd r12_opnd;
extern R_Opnd r13_opnd;
extern R_Opnd r14_opnd;
extern R_Opnd r15_opnd;

extern XMM_Opnd xmm8_opnd;
extern XMM_Opnd xmm9_opnd;
extern XMM_Opnd xmm10_opnd;
extern XMM_Opnd xmm11_opnd;
extern XMM_Opnd xmm12_opnd;
extern XMM_Opnd xmm13_opnd;
extern XMM_Opnd xmm14_opnd;
extern XMM_Opnd xmm15_opnd;
#else

extern R_Opnd eax_opnd;
extern R_Opnd ecx_opnd;
extern R_Opnd edx_opnd;
extern R_Opnd ebx_opnd;
extern R_Opnd esp_opnd;
extern R_Opnd ebp_opnd;
extern R_Opnd esi_opnd;
extern R_Opnd edi_opnd;

#endif // _EM64T_

extern XMM_Opnd xmm0_opnd;
extern XMM_Opnd xmm1_opnd;
extern XMM_Opnd xmm2_opnd;
extern XMM_Opnd xmm3_opnd;
extern XMM_Opnd xmm4_opnd;
extern XMM_Opnd xmm5_opnd;
extern XMM_Opnd xmm6_opnd;
extern XMM_Opnd xmm7_opnd;

#ifdef NO_ENCODER_INLINE
    #define ENCODER_DECLARE_EXPORT
#else
    #define ENCODER_DECLARE_EXPORT inline
    #include "encoder.inl"
#endif

// prefix
ENCODER_DECLARE_EXPORT char * prefix(char * stream, InstrPrefix p);

// stack push and pop instructions
ENCODER_DECLARE_EXPORT char * push(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * push(char * stream, const Imm_Opnd & imm);
ENCODER_DECLARE_EXPORT char * pop(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);

// cmpxchg or xchg
ENCODER_DECLARE_EXPORT char * cmpxchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * xchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);

// inc(rement), dec(rement), not, neg(ate) instructions
ENCODER_DECLARE_EXPORT char * inc(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * dec(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * _not(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * neg(char * stream,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * nop(char * stream);
ENCODER_DECLARE_EXPORT char * int3(char * stream);

// alu instructions: add, or, adc, sbb, and, sub, xor, cmp
ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);

// test instruction
ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);

// shift instructions: shl, shr, sar, shld, shrd, ror
ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf);

// multiply instructions: mul, imul
ENCODER_DECLARE_EXPORT char * mul(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, const Imm_Opnd& imm, Opnd_Size sz = size_platf);

// divide instructions: div, idiv
ENCODER_DECLARE_EXPORT char * idiv(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * div(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);

// data movement: mov
ENCODER_DECLARE_EXPORT char * mov(char * stream, const M_Opnd & m,  const R_Opnd & r, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * mov(char * stream, const R_Opnd & r,  const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * mov(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf);

ENCODER_DECLARE_EXPORT char * movsx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * movzx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);

ENCODER_DECLARE_EXPORT char * movd(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm);
ENCODER_DECLARE_EXPORT char * movd(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm);
ENCODER_DECLARE_EXPORT char * movq(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm);
ENCODER_DECLARE_EXPORT char * movq(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm);

// sse mov
ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const M_Opnd & mem, const XMM_Opnd & xmm, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);

// sse add, sub, mul, div
ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);

ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);

ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);

ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);

// xor, compare
ENCODER_DECLARE_EXPORT char * sse_xor(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);

ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem, bool dbl);

// sse conversions
ENCODER_DECLARE_EXPORT char * sse_cvt_si(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const M_Opnd & mem, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const XMM_Opnd & xmm, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_cvt_fp2dq(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_cvt_dq2fp(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl);
ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem64);
ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);
ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem32);
ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1);

// condition operations
ENCODER_DECLARE_EXPORT char * cmov(char * stream, ConditionCode cc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * setcc(char * stream, ConditionCode cc, const RM_Opnd & rm8);

// load effective address: lea
ENCODER_DECLARE_EXPORT char * lea(char * stream, const R_Opnd & r, const M_Opnd & m, Opnd_Size sz = size_platf);
ENCODER_DECLARE_EXPORT char * cdq(char * stream);
ENCODER_DECLARE_EXPORT char * wait(char * stream);

// control-flow instructions
ENCODER_DECLARE_EXPORT char * loop(char * stream, const Imm_Opnd & imm);

// jump with 8-bit relative
ENCODER_DECLARE_EXPORT char * jump8(char * stream, const Imm_Opnd & imm);

// jump with 32-bit relative
ENCODER_DECLARE_EXPORT char * jump32(char * stream, const Imm_Opnd & imm);

// register indirect jump
ENCODER_DECLARE_EXPORT char * jump(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);

// jump to target address
ENCODER_DECLARE_EXPORT char *jump(char * stream, char *target);

// jump with displacement
//char * jump(char * stream, I_32 disp);

// conditional branch with 8-bit branch offset
ENCODER_DECLARE_EXPORT char * branch8(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix);

// conditional branch with 32-bit branch offset
ENCODER_DECLARE_EXPORT char * branch32(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix);

// conditional branch with target label address
//char * branch(char * stream, ConditionCode cc, const char * target, InstrPrefix prefix = no_prefix);

// conditional branch with displacement immediate
ENCODER_DECLARE_EXPORT char * branch(char * stream, ConditionCode cc, I_32 disp, InstrPrefix prefix = no_prefix);

// call with displacement
ENCODER_DECLARE_EXPORT char * call(char * stream, const Imm_Opnd & imm);

// indirect call through register or memory location
ENCODER_DECLARE_EXPORT char * call(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf);

// call target address
ENCODER_DECLARE_EXPORT char * call(char * stream, const char * target);

// return instruction
ENCODER_DECLARE_EXPORT char * ret(char * stream);
ENCODER_DECLARE_EXPORT char * ret(char * stream, unsigned short pop);
ENCODER_DECLARE_EXPORT char * ret(char * stream, const Imm_Opnd & imm);

// string operations
ENCODER_DECLARE_EXPORT char * set_d(char * stream, bool set);
ENCODER_DECLARE_EXPORT char * scas(char * stream, unsigned char prefix);
ENCODER_DECLARE_EXPORT char * stos(char * stream, unsigned char prefix);

// floating-point instructions

// st(0) = st(0) fp_op m{32,64}real
//!char * fp_op_mem(char * stream, FP_Opcode opc,const M_Opnd& mem,int is_double);

// st(0) = st(0) fp_op st(i)
//!char *fp_op(char * stream, FP_Opcode opc,unsigned i);

// st(i) = st(i) fp_op st(0)    ; optionally pop stack
//!char * fp_op(char * stream, FP_Opcode opc,unsigned i,unsigned pop_stk);

// compare st(0),st(1) and pop stack twice
//!char * fcompp(char * stream);
ENCODER_DECLARE_EXPORT char * fldcw(char * stream, const M_Opnd & mem);
ENCODER_DECLARE_EXPORT char * fnstcw(char * stream, const M_Opnd & mem);
ENCODER_DECLARE_EXPORT char * fnstsw(char * stream);
//!char * fchs(char * stream);
//!char * frem(char * stream);
//!char * fxch(char * stream,unsigned i);
//!char * fcomip(char * stream, unsigned i);

// load from memory (as fp) into fp register stack
ENCODER_DECLARE_EXPORT char * fld(char * stream, const M_Opnd & m, bool is_double);
//!char *fld80(char * stream,const M_Opnd& mem);

// load from memory (as int) into fp register stack
//!char * fild(char * stream,const M_Opnd& mem,int is_long);

// push st(i) onto fp register stack
//!char * fld(char * stream,unsigned i);

// push the constants 0.0 and 1.0 onto the fp register stack
//!char * fldz(char * stream);
//!char * fld1(char * stream);

// store stack to memory (as int), always popping the stack
ENCODER_DECLARE_EXPORT char * fist(char * stream, const M_Opnd & mem, bool is_long, bool pop_stk);
// store stack to to memory (as fp), optionally popping the stack
ENCODER_DECLARE_EXPORT char * fst(char * stream, const M_Opnd & m, bool is_double, bool pop_stk);
// store ST(0) to ST(i), optionally popping the stack. Takes 1 clock
ENCODER_DECLARE_EXPORT char * fst(char * stream, unsigned i, bool pop_stk);

//!char * pushad(char * stream);
//!char * pushfd(char * stream);
//!char * popad(char * stream);
//!char * popfd(char * stream);

// stack frame allocation instructions: enter & leave
//
//    enter frame_size
//
//    is equivalent to:
//
//    push    ebp
//    mov     ebp,esp
//    sub     esp,frame_size
//
//!char *enter(char * stream,const Imm_Opnd& imm);

// leave
// is equivalent to:
//
// mov        esp,ebp
// pop        ebp
//!char *leave(char * stream);

// sahf  loads SF, ZF, AF, PF, and CF flags from eax
//!char *sahf(char * stream);

// Intrinsic FP math functions

//!char *math_fsin(char * stream);
//!char *math_fcos(char * stream);
//!char *math_fabs(char * stream);
//!char *math_fpatan(char * stream);
ENCODER_DECLARE_EXPORT char * fprem(char * stream);
ENCODER_DECLARE_EXPORT char * fprem1(char * stream);
//!char *math_frndint(char * stream);
//!char *math_fptan(char * stream);

//
// Add 1-7 bytes padding, with as few instructions as possible,
// with no effect on the processor state (e.g., registers, flags)
//
//!char *padding(char * stream, unsigned num);

// prolog and epilog code generation
//- char *prolog(char * stream,unsigned frame_size,unsigned reg_save_mask);
//- char *epilog(char * stream,unsigned reg_save_mask);

//!extern R_Opnd reg_operand_array[];

// fsave and frstor
//!char *fsave(char * stream);
//!char *frstor(char * stream);

// lahf : Load Status Flags into AH Register
//!char *lahf(char * stream);

// mfence : Memory Fence
//!char *mfence(char * stream);

#endif // _VM_ENCODER_H_
