/* cpu.h x86 cpu-description header-file */
/* (c) in 2005-2006,2017,2024 by Frank Wille */

#define LITTLEENDIAN 1
#define BIGENDIAN 0
#define BITSPERBYTE 8
#define VASM_CPU_X86 1
#define MNEMOHTABSIZE 0x10000

/* maximum number of operands in one mnemonic */
#define MAX_OPERANDS 3

/* maximum number of mnemonic-qualifiers per mnemonic */
#define MAX_QUALIFIERS 1
/* but no qualifiers for macros */
#define NO_MACRO_QUALIFIERS

/* data type to represent a target-address */
typedef int64_t taddr;
typedef uint64_t utaddr;

/* assembler mode: 16, 32 or 64 bit */
enum codetype {
  CODE_32BIT,
  CODE_16BIT,
  CODE_64BIT
};

/* we use OPTS atoms for cpu-specific options */
#define HAVE_CPU_OPTS 1
typedef struct {
  enum codetype mode;
} cpuopts;

/* we support floating point constants */
#define FLOAT_PARSER 1

/* minimum instruction alignment */
#define INST_ALIGN 1

/* default alignment for n-bit data */
#define DATA_ALIGN(n) 1

/* default alignment mode for .align directive */
#define CPU_DEF_ALIGN 1  /* alignment in bytes */

/* operand class for n-bit data definitions */
int x86_data_operand(int);
#define DATA_OPERAND(n) x86_data_operand(n)

/* make sure operand is cleared upon first entry into parse_operand() */
#define CLEAR_OPERANDS_ON_START 1

/* may use '#' to introduce comments in std-syntax */
#define SYNTAX_STD_COMMENTCHAR_HASH


/* register symbols */
#define HAVE_REGSYMS

/* reg_flags: */
#define RegRex	    0x1         /* Extended register.  */
#define RegRex64    0x2         /* Extended 8 bit register.  */


/* type to store each operand */
typedef struct {
  int type;                     /* real type of operand */
  int parsed_type;              /* type recognized by parser */
  unsigned int flags;
  expr *value;                  /* displacement or immediate value */
  regsym *basereg;              /* base-reg. for SIB or single register */
  regsym *indexreg;
  expr *scalefactor;
  regsym *segoverride;
  int log2_scale;               /* 0-3 for scale factor 1,2,4,8 */
} operand;

/* register operand types */
#define Reg8               0x1
#define Reg16              0x2
#define Reg32              0x4
#define Reg64              0x8

/* immediate operand types*/
#define Imm8              0x10
#define Imm8S             0x20
#define Imm16             0x40
#define Imm32             0x80
#define Imm32S           0x100
#define Imm64            0x200
#define Imm1             0x400  /* for 1-bit shift-instructions */

/* memory operand types */
#define Disp8            0x800
#define Disp16          0x1000
#define Disp32          0x2000
#define Disp32S         0x4000
#define Disp64          0x8000
#define BaseIndex      0x10000  /* indirect base-register has optional index */

/* special operand types */
#define ShiftCntReg    0x20000  /* Shift-count register (%cl) */
#define IOPortReg      0x40000  /* I/O port register (%dx) */
#define CtrlReg        0x80000
#define DebugReg      0x100000
#define TestReg       0x200000
#define Acc           0x400000  /* Accumulator (%al or %ax or %eax) */
#define SegReg2       0x800000  /* Segment register with 2 bits */
#define SegReg3      0x1000000  /* Segment register with 3 bits */
#define MMXReg       0x2000000
#define XMMReg       0x4000000
#define FloatReg     0x8000000  /* Float register %st(n) */
#define FloatAcc    0x10000000  /* Float stack top %st(0), float-accumulator */
#define EsSeg       0x20000000  /* String instr. oper. with fixed ES seg. */
#define JmpAbs      0x40000000  /* Absolute jump/call instruction */
#define InvMem      0x80000000  /* modrm-byte doesn't support memory form */

/* data operands (@@@ define their own flags?) */
#define FloatData   (FloatAcc)
#define Data8       (Disp8)
#define Data16      (Disp16)
#define Data32      (Disp32)
#define Data64      (Disp64)
#define Float32     (Disp32|FloatData)
#define Float64     (Disp64|FloatData)

/* operand type groups */
#define Reg         (Reg8|Reg16|Reg32|Reg64)
#define WordReg     (Reg16|Reg32|Reg64)
#define AnyReg      (Reg|MMXReg|XMMReg|SegReg2|SegReg3|CtrlReg|DebugReg|TestReg)
#define ImpliedReg  (IOPortReg|ShiftCntReg|Acc|FloatAcc)
#define Imm         (Imm8|Imm8S|Imm16|Imm32S|Imm32|Imm64)
#define EncImm      (Imm8|Imm16|Imm32|Imm32S)
#define Disp        (Disp8|Disp16|Disp32|Disp32S|Disp64)
#define AnyMem      (Disp8|Disp16|Disp32|Disp32S|BaseIndex|InvMem)

/* currently we do not differentiate between those memory types, */
/* but the opcode table is prepared for it */
#define LLongMem    AnyMem
#define LongMem     AnyMem
#define ShortMem    AnyMem
#define WordMem     AnyMem
#define ByteMem     AnyMem

/* flags */
#define OPER_REG        1       /* operand is a direct register */
#define OPER_PCREL      2       /* PC-relative operand */


/* x86 opcode prefixes */
#define WAIT_PREFIX     0
#define LOCKREP_PREFIX  1
#define ADDR_PREFIX     2
#define DATA_PREFIX     3
#define SEG_PREFIX      4
#define REX_PREFIX      5       /* must be the last one */
#define MAX_PREFIXES    6       /* maximum number of prefixes */


/* x86 segment register numbers */
#define ESEG_REGNUM     0
#define CSEG_REGNUM     1
#define SSEG_REGNUM     2
#define DSEG_REGNUM     3
#define FSEG_REGNUM     4
#define GSEG_REGNUM     5


/* Mod-R/M byte, which follows the opcode */
typedef struct {
  unsigned char mode;           /* how to interpret regmem & reg */
  unsigned char reg;            /* register operand or extended opcode */
  unsigned char regmem;         /* register or memory operand (addr.mode) */
} modrm_byte;

/* SIB (scale, index, base) byte, which follows modrm_byte in some
   i386 addressing modes */
typedef struct {
  unsigned char scale;          /* scale-factor (1, 2, 4 or 8) */
  unsigned char index;          /* index register */
  unsigned char base;           /* base register */
} sib_byte;

#define EBP_REGNUM 5
#define ESP_REGNUM 4

/* modrm_byte.regmem for 2-byte opcode escape */
#define TWO_BYTE_ESCAPE     (ESP_REGNUM)
/* sib_byte.index for no index register */
#define NO_INDEXREG         (ESP_REGNUM)
/* sib_byte.base for no base register */
#define NO_BASEREG          (EBP_REGNUM)
#define NO_BASEREG16        6


/* instruction extension */
#define HAVE_INSTRUCTION_EXTENSION 1

typedef struct {
  uint32_t base_opcode;
  unsigned char flags;
  unsigned char num_prefixes;
  unsigned char prefix[MAX_PREFIXES];
  modrm_byte rm;
  sib_byte sib;
  short last_size;
} instruction_ext;

/* flags: */
#define HAS_REG_OPER      0x1   /* inst. has direct-register operands */
#define SUFFIX_CHECKED    0x2   /* suffix assigned and checked */
#define MODRM_BYTE        0x4   /* needs mod/rm byte */
#define SIB_BYTE          0x8   /* needs sib byte */
#define NEGOPT            0x40  /* negatively optimized, bytes gained */
#define POSOPT            0x80  /* positively optimized, bytes gained */
#define OPTFAILED         (POSOPT|NEGOPT)  /* no longer try to optimize this */

/* max_opt_tries:
   An instruction optimized more frequent than that is been considered
   'oscillating' (e.g. by effects of alignment-directives) and should
   no longer be optimized! */
#define MAX_OPT_TRIES     10


/* additional mnemonic data */
typedef struct {
  uint32_t base_opcode;
  uint32_t extension_opcode;
  uint32_t opcode_modifier;
  uint32_t available;
} mnemonic_extension;

/* extension_opcode */
#define NO_EXTOPCODE      0xffff

/* cpu available flags: */
#define CPU086            0x1
#define CPU186            0x2
#define CPU286            0x4
#define CPU386            0x8
#define CPU486           0x10
#define CPU586           0x20
#define CPU686           0x40
#define CPUP4            0x80
#define CPUK6           0x100
#define CPUAthlon       0x200
#define CPUSledgehammer 0x400
#define CPUMMX          0x800
#define CPUSSE         0x1000
#define CPUSSE2        0x2000
#define CPU3dnow       0x4000
#define CPUPNI         0x8000
#define CPUPadLock    0x10000
#define CPU64       0x4000000
#define CPUNo64     0x8000000
#define CPUAny (CPU086|CPU186|CPU286|CPU386|CPU486|CPU586|CPU686|CPUP4|CPUSledgehammer|CPUMMX|CPUSSE|CPUSSE2|CPUPNI|CPU3dnow|CPUK6|CPUAthlon|CPUPadLock)

/* opcode_modifier: */
#define W                  0x1  /* operands can be words or dwords */
#define M                  0x4  /* has Modrm byte */
#define FloatR             0x8  /* swapped src/dest for floats: MUST BE 0x8 */
#define ShortForm         0x10  /* reg. enc. in 3 low-order bits of opcode */
#define FloatMF           0x20  /* FP insn memory format bit, sized by 0x4 */
#define Jmp               0x40  /* relative conditional and uncond. branches */
#define JmpDword          0x80  /* relative 16/32 bit calls */
#define JmpByte          0x100  /* loop and jecxz */
#define JmpInterSeg      0x200  /* inter segment calls and branches */
#define FloatD           0x400  /* direction for FP insns:  MUST BE 0x400 */
#define Seg2ShortForm    0x800  /* 2-bit segment reg. encoded in opcode */
#define Seg3ShortForm   0x1000  /* 3-bit segment reg. encoded in opcode  */
#define Size16          0x2000  /* needs a size prefix in 32-bit mode */
#define Size32          0x4000  /* needs a size prefix in 16-bit mode */
#define Size64          0x8000  /* needs a size prefix in 16-bit mode */
#define IgnoreSize     0x10000  /* operand size prefix is ignored */
#define DefaultSize    0x20000  /* default instruction size depends on mode */
#define b_Illegal      0x40000  /* b suffix is illegal */
#define w_Illegal      0x80000  /* w suffix is illegal */
#define l_Illegal     0x100000  /* l suffix is illegal */
#define s_Illegal     0x200000  /* s suffix is illegal */
#define q_Illegal     0x400000  /* q suffix is illegal */
#define x_Illegal     0x800000  /* x suffix is illegal */
#define FWait        0x1000000  /* needs an FWAIT prefix */
#define StrInst      0x2000000  /* string instructions */
#define FakeLastReg  0x4000000  /* duplicate last reg. for clr and imul */
#define IsPrefix     0x8000000  /* opcode is a prefix */
#define ImmExt      0x10000000  /* has extension in 8 bit immediate */
#define NoRex64     0x20000000  /* doesn't require REX64 prefix */
#define Rex64       0x40000000  /* requires REX64 prefix */
#define Deprecated  0x80000000  /* deprecated FPU instruction */

/* instruction doesn't take any suffix (or is already part of mnemonic) */
#define NOSUF (b_Illegal|w_Illegal|l_Illegal|s_Illegal|x_Illegal|q_Illegal)
