aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--libtcc.c6
-rw-r--r--tcc.h20
-rw-r--r--tccasm.c6
-rw-r--r--tcctok.h221
-rw-r--r--x86_64-asm.c1263
-rw-r--r--x86_64-asm.h446
-rw-r--r--x86_64-tok.h224
8 files changed, 1973 insertions, 221 deletions
diff --git a/Makefile b/Makefile
index 01c3e27..e7e4100 100644
--- a/Makefile
+++ b/Makefile
@@ -79,10 +79,10 @@ C67_CROSS = c67-tcc$(EXESUF)
CORE_FILES = tcc.c libtcc.c tccpp.c tccgen.c tccelf.c tccasm.c \
tcc.h config.h libtcc.h tcctok.h
-I386_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h
-WIN32_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h tccpe.c
-WIN64_FILES = $(CORE_FILES) x86_64-gen.c tccpe.c
-X86_64_FILES = $(CORE_FILES) x86_64-gen.c
+I386_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h i386-tok.h
+WIN32_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h i386-tok.h tccpe.c
+WIN64_FILES = $(CORE_FILES) x86_64-gen.c x86_64-asm.c x86_64-asm.h x86_64-tok.h tccpe.c
+X86_64_FILES = $(CORE_FILES) x86_64-gen.c x86_64-asm.c x86_64-asm.h x86_64-tok.h
ARM_FILES = $(CORE_FILES) arm-gen.c
C67_FILES = $(CORE_FILES) c67-gen.c tcccoff.c
diff --git a/libtcc.c b/libtcc.c
index 513de90..e2027da 100644
--- a/libtcc.c
+++ b/libtcc.c
@@ -324,9 +324,15 @@ static inline int toup(int c)
#include "tccgen.c"
#ifdef CONFIG_TCC_ASM
+
#ifdef TCC_TARGET_I386
#include "i386-asm.c"
#endif
+
+#ifdef TCC_TARGET_X86_64
+#include "x86_64-asm.c"
+#endif
+
#include "tccasm.c"
#else
static void asm_instr(void)
diff --git a/tcc.h b/tcc.h
index 9407f63..8d52491 100644
--- a/tcc.h
+++ b/tcc.h
@@ -118,8 +118,7 @@
#endif
/* define it to include assembler support */
-#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67) && \
- !defined(TCC_TARGET_X86_64)
+#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67)
#define CONFIG_TCC_ASM
#endif
@@ -675,6 +674,23 @@ struct TCCState {
DEF(TOK_ASM_ ## x ## l, #x "l") \
DEF(TOK_ASM_ ## x, #x)
+#ifdef TCC_TARGET_X86_64
+
+#define DEF_BWLQ(x) \
+ DEF(TOK_ASM_ ## x ## b, #x "b") \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x ## q, #x "q") \
+ DEF(TOK_ASM_ ## x, #x)
+
+#define DEF_WLQ(x) \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x ## q, #x "q") \
+ DEF(TOK_ASM_ ## x, #x)
+
+#endif
+
#define DEF_FP1(x) \
DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
diff --git a/tccasm.c b/tccasm.c
index 3d8a46e..6862ef5 100644
--- a/tccasm.c
+++ b/tccasm.c
@@ -585,6 +585,12 @@ static void asm_parse_directive(TCCState *s1)
}
break;
#endif
+#ifdef TCC_TARGET_X86_64
+ /* added for compatibility with GAS */
+ case TOK_ASM_code64:
+ next();
+ break;
+#endif
default:
error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
break;
diff --git a/tcctok.h b/tcctok.h
index 8e1c6d6..2b65c02 100644
--- a/tcctok.h
+++ b/tcctok.h
@@ -254,222 +254,13 @@
#if defined(TCC_TARGET_I386)
DEF_ASM(code16)
DEF_ASM(code32)
+#elif defined(TCC_TARGET_X86_64)
+ DEF_ASM(code64)
#endif
#ifdef TCC_TARGET_I386
-
-/* WARNING: relative order of tokens is important. */
- DEF_ASM(al)
- DEF_ASM(cl)
- DEF_ASM(dl)
- DEF_ASM(bl)
- DEF_ASM(ah)
- DEF_ASM(ch)
- DEF_ASM(dh)
- DEF_ASM(bh)
- DEF_ASM(ax)
- DEF_ASM(cx)
- DEF_ASM(dx)
- DEF_ASM(bx)
- DEF_ASM(sp)
- DEF_ASM(bp)
- DEF_ASM(si)
- DEF_ASM(di)
- DEF_ASM(eax)
- DEF_ASM(ecx)
- DEF_ASM(edx)
- DEF_ASM(ebx)
- DEF_ASM(esp)
- DEF_ASM(ebp)
- DEF_ASM(esi)
- DEF_ASM(edi)
- DEF_ASM(mm0)
- DEF_ASM(mm1)
- DEF_ASM(mm2)
- DEF_ASM(mm3)
- DEF_ASM(mm4)
- DEF_ASM(mm5)
- DEF_ASM(mm6)
- DEF_ASM(mm7)
- DEF_ASM(xmm0)
- DEF_ASM(xmm1)
- DEF_ASM(xmm2)
- DEF_ASM(xmm3)
- DEF_ASM(xmm4)
- DEF_ASM(xmm5)
- DEF_ASM(xmm6)
- DEF_ASM(xmm7)
- DEF_ASM(cr0)
- DEF_ASM(cr1)
- DEF_ASM(cr2)
- DEF_ASM(cr3)
- DEF_ASM(cr4)
- DEF_ASM(cr5)
- DEF_ASM(cr6)
- DEF_ASM(cr7)
- DEF_ASM(tr0)
- DEF_ASM(tr1)
- DEF_ASM(tr2)
- DEF_ASM(tr3)
- DEF_ASM(tr4)
- DEF_ASM(tr5)
- DEF_ASM(tr6)
- DEF_ASM(tr7)
- DEF_ASM(db0)
- DEF_ASM(db1)
- DEF_ASM(db2)
- DEF_ASM(db3)
- DEF_ASM(db4)
- DEF_ASM(db5)
- DEF_ASM(db6)
- DEF_ASM(db7)
- DEF_ASM(dr0)
- DEF_ASM(dr1)
- DEF_ASM(dr2)
- DEF_ASM(dr3)
- DEF_ASM(dr4)
- DEF_ASM(dr5)
- DEF_ASM(dr6)
- DEF_ASM(dr7)
- DEF_ASM(es)
- DEF_ASM(cs)
- DEF_ASM(ss)
- DEF_ASM(ds)
- DEF_ASM(fs)
- DEF_ASM(gs)
- DEF_ASM(st)
-
- DEF_BWL(mov)
-
- /* generic two operands */
- DEF_BWL(add)
- DEF_BWL(or)
- DEF_BWL(adc)
- DEF_BWL(sbb)
- DEF_BWL(and)
- DEF_BWL(sub)
- DEF_BWL(xor)
- DEF_BWL(cmp)
-
- /* unary ops */
- DEF_BWL(inc)
- DEF_BWL(dec)
- DEF_BWL(not)
- DEF_BWL(neg)
- DEF_BWL(mul)
- DEF_BWL(imul)
- DEF_BWL(div)
- DEF_BWL(idiv)
-
- DEF_BWL(xchg)
- DEF_BWL(test)
-
- /* shifts */
- DEF_BWL(rol)
- DEF_BWL(ror)
- DEF_BWL(rcl)
- DEF_BWL(rcr)
- DEF_BWL(shl)
- DEF_BWL(shr)
- DEF_BWL(sar)
-
- DEF_ASM(shldw)
- DEF_ASM(shldl)
- DEF_ASM(shld)
- DEF_ASM(shrdw)
- DEF_ASM(shrdl)
- DEF_ASM(shrd)
-
- DEF_ASM(pushw)
- DEF_ASM(pushl)
- DEF_ASM(push)
- DEF_ASM(popw)
- DEF_ASM(popl)
- DEF_ASM(pop)
- DEF_BWL(in)
- DEF_BWL(out)
-
- DEF_WL(movzb)
-
- DEF_ASM(movzwl)
- DEF_ASM(movsbw)
- DEF_ASM(movsbl)
- DEF_ASM(movswl)
-
- DEF_WL(lea)
-
- DEF_ASM(les)
- DEF_ASM(lds)
- DEF_ASM(lss)
- DEF_ASM(lfs)
- DEF_ASM(lgs)
-
- DEF_ASM(call)
- DEF_ASM(jmp)
- DEF_ASM(lcall)
- DEF_ASM(ljmp)
-
- DEF_ASMTEST(j)
-
- DEF_ASMTEST(set)
- DEF_ASMTEST(cmov)
-
- DEF_WL(bsf)
- DEF_WL(bsr)
- DEF_WL(bt)
- DEF_WL(bts)
- DEF_WL(btr)
- DEF_WL(btc)
-
- DEF_WL(lsl)
-
- /* generic FP ops */
- DEF_FP(add)
- DEF_FP(mul)
-
- DEF_ASM(fcom)
- DEF_ASM(fcom_1) /* non existant op, just to have a regular table */
- DEF_FP1(com)
-
- DEF_FP(comp)
- DEF_FP(sub)
- DEF_FP(subr)
- DEF_FP(div)
- DEF_FP(divr)
-
- DEF_BWL(xadd)
- DEF_BWL(cmpxchg)
-
- /* string ops */
- DEF_BWL(cmps)
- DEF_BWL(scmp)
- DEF_BWL(ins)
- DEF_BWL(outs)
- DEF_BWL(lods)
- DEF_BWL(slod)
- DEF_BWL(movs)
- DEF_BWL(smov)
- DEF_BWL(scas)
- DEF_BWL(ssca)
- DEF_BWL(stos)
- DEF_BWL(ssto)
-
- /* generic asm ops */
-
-#define ALT(x)
-#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
-#define DEF_ASM_OP0L(name, opcode, group, instr_type)
-#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
-#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
-#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
-#include "i386-asm.h"
-
-#define ALT(x)
-#define DEF_ASM_OP0(name, opcode)
-#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
-#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
-#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
-#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
-#include "i386-asm.h"
-
+#include "i386-tok.h"
+#elif TCC_TARGET_X86_64
+#include "x86_64-tok.h"
#endif
+
diff --git a/x86_64-asm.c b/x86_64-asm.c
new file mode 100644
index 0000000..228ae3a
--- /dev/null
+++ b/x86_64-asm.c
@@ -0,0 +1,1263 @@
+/*
+ * x86_64 specific functions for TCC assembler
+ *
+ * Copyright (c) 2009 Frédéric Feret
+ *
+ * Based on i386-asm.c by Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MAX_OPERANDS 3
+
+typedef struct ASMInstr {
+ uint16_t sym;
+ uint16_t opcode;
+ uint16_t instr_type;
+#define OPC_JMP 0x01 /* jmp operand */
+#define OPC_B 0x02 /* only used zith OPC_WL */
+#define OPC_WL 0x04 /* accepts w, l or no suffix */
+#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
+#define OPC_REG 0x08 /* register is added to opcode */
+#define OPC_MODRM 0x10 /* modrm encoding */
+#define OPC_FWAIT 0x20 /* add fwait opcode */
+#define OPC_TEST 0x40 /* test opcodes */
+#define OPC_SHIFT 0x80 /* shift opcodes */
+#define OPC_D16 0x0100 /* generate data16 prefix */
+#define OPC_ARITH 0x0200 /* arithmetic opcodes */
+#define OPC_SHORTJMP 0x0400 /* short jmp operand */
+#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */
+#define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */
+#define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
+#define OPC_GROUP_SHIFT 13
+
+/* in order to compress the operand type, we use specific operands and
+ we or only with EA */
+#define OPT_REG8 0 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_REG64 3
+#define OPT_MMX 4 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_SSE 5 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_CR 6 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_TR 7 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_DB 8 /* warning: value is hardcoded from TOK_ASM_xxx */
+#define OPT_SEG 9
+#define OPT_ST 10
+#define OPT_IM8 11
+#define OPT_IM8S 12
+#define OPT_IM16 13
+#define OPT_IM32 14
+#define OPT_IM64 15
+#define OPT_EAX 16 /* %al, %ax, %eax or %rax register */
+#define OPT_ST0 17 /* %st(0) register */
+#define OPT_CL 18 /* %cl register */
+#define OPT_DX 19 /* %dx register */
+#define OPT_ADDR 20 /* OP_EA with only offset */
+#define OPT_INDIR 21 /* *(expr) */
+
+/* composite types */
+#define OPT_COMPOSITE_FIRST 22
+#define OPT_IM 23 /* IM8 | IM16 | IM32 | IM64 */
+#define OPT_REG 24 /* REG8 | REG16 | REG32 | REG64 */
+#define OPT_REGW 25 /* REG16 | REG32 | REG64 */
+#define OPT_IMW 26 /* IM16 | IM32 | IM64 */
+#define OPT_IMNO64 27 /* IM16 | IM32 */
+
+/* can be ored with any OPT_xxx */
+#define OPT_EA 0x80
+
+ uint8_t nb_ops;
+ uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
+} ASMInstr;
+
+typedef struct Operand {
+ uint32_t type;
+#define OP_REG8 (1 << OPT_REG8)
+#define OP_REG16 (1 << OPT_REG16)
+#define OP_REG32 (1 << OPT_REG32)
+#define OP_MMX (1 << OPT_MMX)
+#define OP_SSE (1 << OPT_SSE)
+#define OP_CR (1 << OPT_CR)
+#define OP_TR (1 << OPT_TR)
+#define OP_DB (1 << OPT_DB)
+#define OP_SEG (1 << OPT_SEG)
+#define OP_ST (1 << OPT_ST)
+#define OP_IM8 (1 << OPT_IM8)
+#define OP_IM8S (1 << OPT_IM8S)
+#define OP_IM16 (1 << OPT_IM16)
+#define OP_IM32 (1 << OPT_IM32)
+#define OP_EAX (1 << OPT_EAX)
+#define OP_ST0 (1 << OPT_ST0)
+#define OP_CL (1 << OPT_CL)
+#define OP_DX (1 << OPT_DX)
+#define OP_ADDR (1 << OPT_ADDR)
+#define OP_INDIR (1 << OPT_INDIR)
+#define OP_REG64 (1 << OPT_REG64)
+#define OP_IM64 (1 << OPT_IM64)
+
+#define OP_EA 0x40000000
+#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64)
+#define OP_IM OP_IM64
+ int8_t reg; /* register, -1 if none */
+ int8_t reg2; /* second register, -1 if none */
+ uint8_t shift;
+ ExprValue e;
+} Operand;
+
+static const uint8_t reg_to_size[7] = {
+/*
+ [OP_REG8] = 0,
+ [OP_REG16] = 1,
+ [OP_REG32] = 2,
+ [OP_REG64] = 3,
+*/
+ 0, 0, 1, 0, 2, 0, 3
+};
+
+#define NB_TEST_OPCODES 30
+
+static const uint8_t test_bits[NB_TEST_OPCODES] = {
+ 0x00, /* o */
+ 0x01, /* no */
+ 0x02, /* b */
+ 0x02, /* c */
+ 0x02, /* nae */
+ 0x03, /* nb */
+ 0x03, /* nc */
+ 0x03, /* ae */
+ 0x04, /* e */
+ 0x04, /* z */
+ 0x05, /* ne */
+ 0x05, /* nz */
+ 0x06, /* be */
+ 0x06, /* na */
+ 0x07, /* nbe */
+ 0x07, /* a */
+ 0x08, /* s */
+ 0x09, /* ns */
+ 0x0a, /* p */
+ 0x0a, /* pe */
+ 0x0b, /* np */
+ 0x0b, /* po */
+ 0x0c, /* l */
+ 0x0c, /* nge */
+ 0x0d, /* nl */
+ 0x0d, /* ge */
+ 0x0e, /* le */
+ 0x0e, /* ng */
+ 0x0f, /* nle */
+ 0x0f, /* g */
+};
+
+static const uint8_t segment_prefixes[] = {
+ 0x26, /* es */
+ 0x2e, /* cs */
+ 0x36, /* ss */
+ 0x3e, /* ds */
+ 0x64, /* fs */
+ 0x65 /* gs */
+};
+
+static const ASMInstr asm_instrs[] = {
+#define ALT(x) x
+#define DEF_ASM_OP0(name, opcode)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 },
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }},
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }},
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }},
+#include "x86_64-asm.h"
+
+ /* last operation */
+ { 0, },
+};
+
+static const uint16_t op0_codes[] = {
+#define ALT(x)
+#define DEF_ASM_OP0(x, opcode) opcode,
+#define DEF_ASM_OP0L(name, opcode, group, instr_type)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
+#include "x86_64-asm.h"
+};
+
+static inline int get_reg_shift(TCCState *s1)
+{
+ int shift, v;
+
+ v = asm_int_expr(s1);
+ switch(v) {
+ case 1:
+ shift = 0;
+ break;
+ case 2:
+ shift = 1;
+ break;
+ case 4:
+ shift = 2;
+ break;
+ case 8:
+ shift = 3;
+ break;
+ default:
+ expect("1, 2, 4 or 8 constant");
+ shift = 0;
+ break;
+ }
+ return shift;
+}
+
+static int asm_parse_reg(void)
+{
+ int reg;
+ if (tok != '%')
+ goto error_32;
+ next();
+ if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
+ reg = tok - TOK_ASM_rax;
+ next();
+ return reg;
+ } else if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
+ reg = tok - TOK_ASM_eax;
+ next();
+ return reg;
+ } else {
+ error_32:
+ expect("64 bit register");
+ return 0;
+ }
+}
+
+static void parse_operand(TCCState *s1, Operand *op)
+{
+ ExprValue e;
+ int reg, indir;
+ const char *p;
+
+ indir = 0;
+ if (tok == '*') {
+ next();
+ indir = OP_INDIR;
+ }
+
+ if (tok == '%') {
+ next();
+ if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
+ reg = tok - TOK_ASM_al;
+ op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
+ op->reg = reg & 7;
+ if ((op->type & OP_REG) && op->reg == TREG_RAX)
+ op->type |= OP_EAX;
+ else if (op->type == OP_REG8 && op->reg == TREG_RCX)
+ op->type |= OP_CL;
+ else if (op->type == OP_REG16 && op->reg == TREG_RDX)
+ op->type |= OP_DX;
+ } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
+ op->type = OP_DB;
+ op->reg = tok - TOK_ASM_dr0;
+ } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
+ op->type = OP_SEG;
+ op->reg = tok - TOK_ASM_es;
+ } else if (tok == TOK_ASM_st) {
+ op->type = OP_ST;
+ op->reg = 0;
+ next();
+ if (tok == '(') {
+ next();
+ if (tok != TOK_PPNUM)
+ goto reg_error;
+ p = tokc.cstr->data;
+ reg = p[0] - '0';
+ if ((unsigned)reg >= 8 || p[1] != '\0')
+ goto reg_error;
+ op->reg = reg;
+ next();
+ skip(')');
+ }
+ if (op->reg == 0)
+ op->type |= OP_ST0;
+ goto no_skip;
+ } else {
+ reg_error:
+ error("unknown register");
+ }
+ next();
+ no_skip: ;
+ } else if (tok == '$') {
+ /* constant value */
+ next();
+ asm_expr(s1, &e);
+ op->type = OP_IM64;
+ op->e.v = e.v;
+ op->e.sym = e.sym;
+ if (!op->e.sym) {
+ if (op->e.v == (uint8_t)op->e.v)
+ op->type |= OP_IM8;
+ if (op->e.v == (int8_t)op->e.v)
+ op->type |= OP_IM8S;
+ if (op->e.v == (uint16_t)op->e.v)
+ op->type |= OP_IM16;
+ if (op->e.v == (uint32_t)op->e.v)
+ op->type |= OP_IM32;
+ }
+ } else {
+ /* address(reg,reg2,shift) with all variants */
+ op->type = OP_EA;
+ op->reg = -1;
+ op->reg2 = -1;
+ op->shift = 0;
+ if (tok != '(') {
+ asm_expr(s1, &e);
+ op->e.v = e.v;
+ op->e.sym = e.sym;
+ } else {
+ op->e.v = 0;
+ op->e.sym = NULL;
+ }
+ if (tok == '(') {
+ next();
+ if (tok != ',') {
+ op->reg = asm_parse_reg();
+ }
+ if (tok == ',') {
+ next();
+ if (tok != ',') {
+ op->reg2 = asm_parse_reg();
+ }
+ if (tok == ',') {
+ next();
+ op->shift = get_reg_shift(s1);
+ }
+ }
+ skip(')');
+ }
+ if (op->reg == -1 && op->reg2 == -1)
+ op->type |= OP_ADDR;
+ }
+ op->type |= indir;
+}
+
+/* XXX: unify with C code output ? */
+static void gen_expr32(ExprValue *pe)
+{
+ if (pe->sym)
+ greloc(cur_text_section, pe->sym, ind, R_X86_64_32);
+ gen_le32(pe->v);
+}
+
+static void gen_expr64(ExprValue *pe)
+{
+ if (pe->sym)
+ greloc(cur_text_section, pe->sym, ind, R_X86_64_64);
+ gen_le64(pe->v);
+}
+
+/* XXX: unify with C code output ? */
+static void gen_disp32(ExprValue *pe)
+{
+ Sym *sym;
+ sym = pe->sym;
+ if (sym) {
+ if (sym->r == cur_text_section->sh_num) {
+ /* same section: we can output an absolute value. Note
+ that the TCC compiler behaves differently here because
+ it always outputs a relocation to ease (future) code
+ elimination in the linker */
+ gen_le32(pe->v + (long)sym->next - ind - 4);
+ } else {
+ greloc(cur_text_section, sym, ind, R_X86_64_PC32);
+ gen_le32(pe->v - 4);
+ }
+ } else {
+ /* put an empty PC32 relocation */
+ put_elf_reloc(symtab_section, cur_text_section,
+ ind, R_X86_64_PC32, 0);
+ gen_le32(pe->v - 4);
+ }
+}
+
+
+static void gen_le16(int v)
+{
+ g(v);
+ g(v >> 8);
+}
+
+/* generate the modrm operand */
+static inline void asm_modrm(int reg, Operand *op)
+{
+ int mod, reg1, reg2, sib_reg1;
+
+ if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
+ g(0xc0 + (reg << 3) + op->reg);
+ } else if (op->reg == -1 && op->reg2 == -1) {
+ /* displacement only */
+ g(0x05 + (reg << 3));
+ gen_expr32(&op->e);
+ } else {
+ sib_reg1 = op->reg;
+ /* fist compute displacement encoding */
+ if (sib_reg1 == -1) {
+ sib_reg1 = 5;
+ mod = 0x00;
+ } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
+ mod = 0x00;
+ } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
+ mod = 0x40;
+ } else {
+ mod = 0x80;
+ }
+ /* compute if sib byte needed */
+ reg1 = op->reg;
+ if (op->reg2 != -1)
+ reg1 = 4;
+ g(mod + (reg << 3) + reg1);
+ if (reg1 == 4) {
+ /* add sib byte */
+ reg2 = op->reg2;
+ if (reg2 == -1)
+ reg2 = 4; /* indicate no index */
+ g((op->shift << 6) + (reg2 << 3) + sib_reg1);
+ }
+
+ /* add offset */
+ if (mod == 0x40) {
+ g(op->e.v);
+ } else if (mod == 0x80 || op->reg == -1) {
+ gen_expr32(&op->e);
+ }
+ }
+}
+
+static void asm_opcode(TCCState *s1, int opcode)
+{
+ const ASMInstr *pa;
+ int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix;
+ int nb_ops, s, ss;
+ Operand ops[MAX_OPERANDS], *pop;
+ int op_type[3]; /* decoded op type */
+ char rex;
+
+ /* get operands */
+ pop = ops;
+ nb_ops = 0;
+ seg_prefix = 0;
+ rex = 0x48;
+ for(;;) {
+ if (tok == ';' || tok == TOK_LINEFEED)
+ break;
+ if (nb_ops >= MAX_OPERANDS) {
+ error("incorrect number of operands");
+ }
+ parse_operand(s1, pop);
+ if (tok == ':') {
+ if (pop->type != OP_SEG || seg_prefix) {
+ error("incorrect prefix");
+ }
+ seg_prefix = segment_prefixes[pop->reg];
+ next();
+ parse_operand(s1, pop);
+ if (!(pop->type & OP_EA)) {
+ error("segment prefix must be followed by memory reference");
+ }
+ }
+ pop++;
+ nb_ops++;
+ if (tok != ',')
+ break;
+ next();
+ }
+
+ is_short_jmp = 0;
+ s = 0; /* avoid warning */
+
+ /* optimize matching by using a lookup table (no hashing is needed
+ !) */
+ for(pa = asm_instrs; pa->sym != 0; pa++) {
+ s = 0;
+ if (pa->instr_type & OPC_FARITH) {
+ v = opcode - pa->sym;
+ if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
+ continue;
+ } else if (pa->instr_type & OPC_ARITH) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4))
+ continue;
+ goto compute_size;
+ } else if (pa->instr_type & OPC_SHIFT) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4))
+ continue;
+ goto compute_size;
+ } else if (pa->instr_type & OPC_TEST) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
+ continue;
+ } else if (pa->instr_type & OPC_B) {
+ if (!(opcode >= pa->sym && opcode <= pa->sym + 3))
+ continue;
+ compute_size:
+ s = (opcode - pa->sym) & 3;
+ } else if (pa->instr_type & OPC_WLQ) {
+ if (!(opcode >= pa->sym && opcode <= pa->sym + 2))
+ continue;
+ s = opcode - pa->sym + 1;
+ } else {
+ if (pa->sym != opcode)
+ continue;
+ }
+ if (pa->nb_ops != nb_ops)
+ continue;
+ /* now decode and check each operand */
+ for(i = 0; i < nb_ops; i++) {
+ int op1, op2;
+ op1 = pa->op_type[i];
+ op2 = op1 & 0x1f;
+ switch(op2) {
+ case OPT_IM:
+ v = OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64;
+ break;
+ case OPT_REG:
+ v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64;
+ break;
+ case OPT_REGW:
+ v = OP_REG16 | OP_REG32 | OP_REG64;
+ break;
+ case OPT_IMW:
+ v = OP_IM16 | OP_IM32 | OP_IM64;
+ break;
+ case OPT_IMNO64:
+ v = OP_IM16 | OP_IM32;
+ break;
+ default:
+ v = 1 << op2;
+ break;
+ }
+ if (op1 & OPT_EA)
+ v |= OP_EA;
+ op_type[i] = v;
+ if ((ops[i].type & v) == 0)
+ goto next;
+ }
+ /* all is matching ! */
+ break;
+ next: ;
+ }
+ if (pa->sym == 0) {
+ if (opcode >= TOK_ASM_clc && opcode <= TOK_ASM_emms) {
+ int b;
+ b = op0_codes[opcode - TOK_ASM_clc];
+ if (b & 0xff00)
+ g(b >> 8);
+ g(b);
+ return;
+ } else {
+ error("unknown opcode '%s'",
+ get_tok_str(opcode, NULL));
+ }
+ }
+ /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
+ if (s == 4) {
+ for(i = 0; s == 4 && i < nb_ops; i++) {
+ if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
+ s = reg_to_size[ops[i].type & OP_REG];
+ }
+ if (s == 4) {
+ if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
+ (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32 | OP_IM64)))
+ s = 2;
+ else
+ error("cannot infer opcode suffix");
+ }
+ }
+
+ /* generate data16 prefix if needed */
+ ss = s;
+ if (s == 1 || (pa->instr_type & OPC_D16))
+ g(0x66);
+ else if (s == 2)
+ s = 1;
+ else if (s == 3) {
+ /* generate REX prefix */
+ g(rex);
+ s = 1;
+ }
+ /* now generates the operation */
+ if (pa->instr_type & OPC_FWAIT)
+ g(0x9b);
+ if (seg_prefix)
+ g(seg_prefix);
+
+ v = pa->opcode;
+ if (v == 0x69 || v == 0x69) {
+ /* kludge for imul $im, %reg */
+ nb_ops = 3;
+ ops[2] = ops[1];
+ } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
+ v--; /* int $3 case */
+ nb_ops = 0;
+ } else if ((v == 0x06 || v == 0x07)) {
+ if (ops[0].reg >= 4) {
+ /* push/pop %fs or %gs */
+ v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
+ } else {
+ v += ops[0].reg << 3;
+ }
+ nb_ops = 0;
+ } else if (v <= 0x05) {
+ /* arith case */
+ v += ((opcode - TOK_ASM_addb) >> 2) << 3;
+ } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) {
+ /* fpu arith case */
+ v += ((opcode - pa->sym) / 6) << 3;
+ }
+ if (pa->instr_type & OPC_REG) {
+ for(i = 0; i < nb_ops; i++) {
+ if (op_type[i] & (OP_REG | OP_ST)) {
+ v += ops[i].reg;
+ break;
+ }
+ }
+ /* mov $im, %reg case */
+ if (pa->opcode == 0xb0 && s >= 1)
+ v += 7;
+ }
+ if (pa->instr_type & OPC_B)
+ v += s;
+ if (pa->instr_type & OPC_TEST)
+ v += test_bits[opcode - pa->sym];
+ if (pa->instr_type & OPC_SHORTJMP) {
+ Sym *sym;
+ int jmp_disp;
+
+ /* see if we can really generate the jump with a byte offset */
+ sym = ops[0].e.sym;
+ if (!sym)
+ goto no_short_jump;
+ if (sym->r != cur_text_section->sh_num)
+ goto no_short_jump;
+ jmp_disp = ops[0].e.v + (long)sym->next - ind - 2;
+ if (jmp_disp == (int8_t)jmp_disp) {
+ /* OK to generate jump */
+ is_short_jmp = 1;
+ ops[0].e.v = jmp_disp;
+ } else {
+ no_short_jump:
+ if (pa->instr_type & OPC_JMP) {
+ /* long jump will be allowed. need to modify the
+ opcode slightly */
+ if (v == 0xeb)
+ v = 0xe9;
+ else
+ v += 0x0f10;
+ } else {
+ error("invalid displacement");
+ }
+ }
+ }
+ op1 = v >> 8;
+ if (op1)
+ g(op1);
+ g(v);
+
+ /* search which operand will used for modrm */
+ modrm_index = 0;
+ if (pa->instr_type & OPC_SHIFT) {
+ reg = (opcode - pa->sym) >> 2;
+ if (reg == 6)
+ reg = 7;
+ } else if (pa->instr_type & OPC_ARITH) {
+ reg = (opcode - pa->sym) >> 2;
+ } else if (pa->instr_type & OPC_FARITH) {
+ reg = (opcode - pa->sym) / 6;
+ } else {
+ reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
+ }
+ if (pa->instr_type & OPC_MODRM) {
+ /* first look for an ea operand */
+ for(i = 0;i < nb_ops; i++) {
+ if (op_type[i] & OP_EA)
+ goto modrm_found;
+ }
+ /* then if not found, a register or indirection (shift instructions) */
+ for(i = 0;i < nb_ops; i++) {
+ if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
+ goto modrm_found;
+ }
+#ifdef ASM_DEBUG
+ error("bad op table");
+#endif
+ modrm_found:
+ modrm_index = i;
+ /* if a register is used in another operand then it is
+ used instead of group */
+ for(i = 0;i < nb_ops; i++) {
+ v = op_type[i];
+ if (i != modrm_index &&
+ (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
+ reg = ops[i].reg;
+ break;
+ }
+ }
+
+ asm_modrm(reg, &ops[modrm_index]);
+ }
+
+ /* emit constants */
+ {
+ for(i = 0;i < nb_ops; i++) {
+ v = op_type[i];
+ if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) {
+ /* if multiple sizes are given it means we must look
+ at the op size */
+ if (v == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64) ||
+ v == (OP_IM16 | OP_IM32 | OP_IM64)) {
+ if (ss == 0)
+ v = OP_IM8;
+ else if (ss == 1)
+ v = OP_IM16;
+ else if (ss == 2)
+ v = OP_IM32;
+ else
+ v = OP_IM64;
+ } else if (v == (OP_IM8 | OP_IM16 | OP_IM32) ||
+ v == (OP_IM16 | OP_IM32)) {
+ if (ss == 0)
+ v = OP_IM8;
+ else if (ss == 1)
+ v = OP_IM16;
+ else
+ v = OP_IM32;
+ }
+ if (v & (OP_IM8 | OP_IM8S)) {
+ if (ops[i].e.sym)
+ goto error_relocate;
+ g(ops[i].e.v);
+ } else if (v & OP_IM16) {
+ if (ops[i].e.sym) {
+ error_relocate:
+ error("cannot relocate");
+ }
+ gen_le16(ops[i].e.v);
+ } else {
+ if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
+ if (is_short_jmp)
+ g(ops[i].e.v);
+ else
+ gen_disp32(&ops[i].e);
+ } else {
+ if (v & OP_IM64)
+ gen_expr64(&ops[i].e);
+ else
+ gen_expr32(&ops[i].e);
+ }
+ }
+ } else if (v & (OP_REG32 | OP_REG64)) {
+ if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
+ /* jmp $r */
+ g(0xE0 + ops[i].reg);
+ }
+ }
+ }
+ }
+}
+
+#define NB_SAVED_REGS 3
+#define NB_ASM_REGS 8
+
+/* return the constraint priority (we allocate first the lowest
+ numbered constraints) */
+static inline int constraint_priority(const char *str)
+{
+ int priority, c, pr;
+
+ /* we take the lowest priority */
+ priority = 0;
+ for(;;) {
+ c = *str;
+ if (c == '\0')
+ break;
+ str++;
+ switch(c) {
+ case 'A':
+ pr = 0;
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'S':
+ case 'D':
+ pr = 1;
+ break;
+ case 'q':
+ pr = 2;
+ break;
+ case 'r':
+ pr = 3;
+ break;
+ case 'N':
+ case 'M':
+ case 'I':
+ case 'i':
+ case 'm':
+ case 'g':
+ pr = 4;
+ break;
+ default:
+ error("unknown constraint '%c'", c);
+ pr = 0;
+ }
+ if (pr > priority)
+ priority = pr;
+ }
+ return priority;
+}
+
+static const char *skip_constraint_modifiers(const char *p)
+{
+ while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
+ p++;
+ return p;
+}
+
+#define REG_OUT_MASK 0x01
+#define REG_IN_MASK 0x02
+
+#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
+
+static void asm_compute_constraints(ASMOperand *operands,
+ int nb_operands, int nb_outputs,
+ const uint8_t *clobber_regs,
+ int *pout_reg)
+{
+ ASMOperand *op;
+ int sorted_op[MAX_ASM_OPERANDS];
+ int i, j, k, p1, p2, tmp, reg, c, reg_mask;
+ const char *str;
+ uint8_t regs_allocated[NB_ASM_REGS];
+
+ /* init fields */
+ for(i=0;i<nb_operands;i++) {
+ op = &operands[i];
+ op->input_index = -1;
+ op->ref_index = -1;
+ op->reg = -1;
+ op->is_memory = 0;
+ op->is_rw = 0;
+ }
+ /* compute constraint priority and evaluate references to output
+ constraints if input constraints */
+ for(i=0;i<nb_operands;i++) {
+ op = &operands[i];
+ str = op->constraint;
+ str = skip_constraint_modifiers(str);
+ if (isnum(*str) || *str == '[') {
+ /* this is a reference to another constraint */
+ k = find_constraint(operands, nb_operands, str, NULL);
+ if ((unsigned)k >= i || i < nb_outputs)
+ error("invalid reference in constraint %d ('%s')",
+ i, str);
+ op->ref_index = k;
+ if (operands[k].input_index >= 0)
+ error("cannot reference twice the same operand");
+ operands[k].input_index = i;
+ op->priority = 5;
+ } else {
+ op->priority = constraint_priority(str);
+ }
+ }
+
+ /* sort operands according to their priority */
+ for(i=0;i<nb_operands;i++)
+ sorted_op[i] = i;
+ for(i=0;i<nb_operands - 1;i++) {
+ for(j=i+1;j<nb_operands;j++) {
+ p1 = operands[sorted_op[i]].priority;
+ p2 = operands[sorted_op[j]].priority;
+ if (p2 < p1) {
+ tmp = sorted_op[i];
+ sorted_op[i] = sorted_op[j];
+ sorted_op[j] = tmp;
+ }
+ }
+ }
+
+ for(i = 0;i < NB_ASM_REGS; i++) {
+ if (clobber_regs[i])
+ regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
+ else
+ regs_allocated[i] = 0;
+ }
+ /* esp cannot be used */
+ regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
+ /* ebp cannot be used yet */
+ regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;
+
+ /* allocate registers and generate corresponding asm moves */
+ for(i=0;i<nb_operands;i++) {
+ j = sorted_op[i];
+ op = &operands[j];
+ str = op->constraint;
+ /* no need to allocate references */
+ if (op->ref_index >= 0)
+ continue;
+ /* select if register is used for output, input or both */
+ if (op->input_index >= 0) {
+ reg_mask = REG_IN_MASK | REG_OUT_MASK;
+ } else if (j < nb_outputs) {
+ reg_mask = REG_OUT_MASK;
+ } else {
+ reg_mask = REG_IN_MASK;
+ }
+ try_next:
+ c = *str++;
+ switch(c) {
+ case '=':
+ goto try_next;
+ case '+':
+ op->is_rw = 1;
+ /* FALL THRU */
+ case '&':
+ if (j >= nb_outputs)
+ error("'%c' modifier can only be applied to outputs", c);
+ reg_mask = REG_IN_MASK | REG_OUT_MASK;
+ goto try_next;
+ case 'A':
+ /* allocate both eax and edx */
+ if (is_reg_allocated(TREG_RAX) ||
+ is_reg_allocated(TREG_RDX))
+ goto try_next;
+ op->is_llong = 1;
+ op->reg = TREG_RAX;
+ regs_allocated[TREG_RAX] |= reg_mask;
+ regs_allocated[TREG_RDX] |= reg_mask;
+ break;
+ case 'a':
+ reg = TREG_RAX;
+ goto alloc_reg;
+ case 'b':
+ reg = 3;
+ goto alloc_reg;
+ case 'c':
+ reg = TREG_RCX;
+ goto alloc_reg;
+ case 'd':
+ reg = TREG_RDX;
+ goto alloc_reg;
+ case 'S':
+ reg = 6;
+ goto alloc_reg;
+ case 'D':
+ reg = 7;
+ alloc_reg:
+ if (is_reg_allocated(reg))
+ goto try_next;
+ goto reg_found;
+ case 'q':
+ /* eax, ebx, ecx or edx */
+ for(reg = 0; reg < 4; reg++) {
+ if (!is_reg_allocated(reg))
+ goto reg_found;
+ }
+ goto try_next;
+ case 'r':
+ /* any general register */
+ for(reg = 0; reg < 8; reg++) {
+ if (!is_reg_allocated(reg))
+ goto reg_found;
+ }
+ goto try_next;
+ reg_found:
+ /* now we can reload in the register */
+ op->is_llong = 0;
+ op->reg = reg;
+ regs_allocated[reg] |= reg_mask;
+ break;
+ case 'i':
+ if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
+ goto try_next;
+ break;
+ case 'I':
+ case 'N':
+ case 'M':
+ if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
+ goto try_next;
+ break;
+ case 'm':
+ case 'g':
+ /* nothing special to do because the operand is already in
+ memory, except if the pointer itself is stored in a
+ memory variable (VT_LLOCAL case) */
+ /* XXX: fix constant case */
+ /* if it is a reference to a memory zone, it must lie
+ in a register, so we reserve the register in the
+ input registers and a load will be generated
+ later */
+ if (j < nb_outputs || c == 'm') {
+ if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
+ /* any general register */
+ for(reg = 0; reg < 8; reg++) {
+ if (!(regs_allocated[reg] & REG_IN_MASK))
+ goto reg_found1;
+ }
+ goto try_next;
+ reg_found1:
+ /* now we can reload in the register */
+ regs_allocated[reg] |= REG_IN_MASK;
+ op->reg = reg;
+ op->is_memory = 1;
+ }
+ }
+ break;
+ default:
+ error("asm constraint %d ('%s') could not be satisfied",
+ j, op->constraint);
+ break;
+ }
+ /* if a reference is present for that operand, we assign it too */
+ if (op->input_index >= 0) {
+ operands[op->input_index].reg = op->reg;
+ operands[op->input_index].is_llong = op->is_llong;
+ }
+ }
+
+ /* compute out_reg. It is used to store outputs registers to memory
+ locations references by pointers (VT_LLOCAL case) */
+ *pout_reg = -1;
+ for(i=0;i<nb_operands;i++) {
+ op = &operands[i];
+ if (op->reg >= 0 &&
+ (op->vt->r & VT_VALMASK) == VT_LLOCAL &&
+ !op->is_memory) {
+ for(reg = 0; reg < 8; reg++) {
+ if (!(regs_allocated[reg] & REG_OUT_MASK))
+ goto reg_found2;
+ }
+ error("could not find free output register for reloading");
+ reg_found2:
+ *pout_reg = reg;
+ break;
+ }
+ }
+
+ /* print sorted constraints */
+#ifdef ASM_DEBUG
+ for(i=0;i<nb_operands;i++) {
+ j = sorted_op[i];
+ op = &operands[j];
+ printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
+ j,
+ op->id ? get_tok_str(op->id, NULL) : "",
+ op->constraint,
+ op->vt->r,
+ op->reg);
+ }
+ if (*pout_reg >= 0)
+ printf("out_reg=%d\n", *pout_reg);
+#endif
+}
+
+static void subst_asm_operand(CString *add_str,
+ SValue *sv, int modifier)
+{
+ int r, reg, size, val;
+ char buf[64];
+
+ r = sv->r;
+ if ((r & VT_VALMASK) == VT_CONST) {
+ if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n')
+ cstr_ccat(add_str, '$');
+ if (r & VT_SYM) {
+ cstr_cat(add_str, get_tok_str(sv->sym->v, NULL));
+ if (sv->c.i != 0) {
+ cstr_ccat(add_str, '+');
+ } else {
+ return;
+ }
+ }
+ val = sv->c.i;
+ if (modifier == 'n')
+ val = -val;
+ snprintf(buf, sizeof(buf), "%d", sv->c.i);
+ cstr_cat(add_str, buf);
+ } else if ((r & VT_VALMASK) == VT_LOCAL) {
+ snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i);
+ cstr_cat(add_str, buf);
+ } else if (r & VT_LVAL) {
+ reg = r & VT_VALMASK;
+ if (reg >= VT_CONST)
+ error("internal compiler error");
+ snprintf(buf, sizeof(buf), "(%%%s)",
+ get_tok_str(TOK_ASM_eax + reg, NULL));
+ cstr_cat(add_str, buf);
+ } else {
+ /* register case */
+ reg = r & VT_VALMASK;
+ if (reg >= VT_CONST)
+ error("internal compiler error");
+
+ /* choose register operand size */
+ if ((sv->type.t & VT_BTYPE) == VT_BYTE)
+ size = 1;
+ else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
+ size = 2;
+ else if ((sv->type.t & VT_BTYPE) == VT_LLONG)
+ size = 8;
+ else
+ size = 4;
+ if (size == 1 && reg >= 4)
+ size = 4;
+
+ if (modifier == 'b') {
+ if (reg >= 4)
+ error("cannot use byte register");
+ size = 1;
+ } else if (modifier == 'h') {
+ if (reg >= 4)
+ error("cannot use byte register");
+ size = -1;
+ } else if (modifier == 'w') {
+ size = 2;
+ } else if (modifier == 'q') {
+ size = 8;
+ }
+
+ switch(size) {
+ case -1:
+ reg = TOK_ASM_ah + reg;
+ break;
+ case 1:
+ reg = TOK_ASM_al + reg;
+ break;
+ case 2:
+ reg = TOK_ASM_ax + reg;
+ break;
+ case 4:
+ reg = TOK_ASM_eax + reg;
+ break;
+ default:
+ reg = TOK_ASM_rax + reg;
+ break;
+ }
+ snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
+ cstr_cat(add_str, buf);
+ }
+}
+
+/* generate prolog and epilog code for asm statment */
+static void asm_gen_code(ASMOperand *operands, int nb_operands,
+ int nb_outputs, int is_output,
+ uint8_t *clobber_regs,
+ int out_reg)
+{
+ uint8_t regs_allocated[NB_ASM_REGS];
+ ASMOperand *op;
+ int i, reg;
+ static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 };
+
+ /* mark all used registers */
+ memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
+ for(i = 0; i < nb_operands;i++) {
+ op = &operands[i];
+ if (op->reg >= 0)
+ regs_allocated[op->reg] = 1;
+ }
+ if (!is_output) {
+ /* generate reg save code */
+ for(i = 0; i < NB_SAVED_REGS; i++) {
+ reg = reg_saved[i];
+ if (regs_allocated[reg])
+ g(0x50 + reg);
+ }
+
+ /* generate load code */
+ for(i = 0; i < nb_operands; i++) {
+ op = &operands[i];
+ if (op->reg >= 0) {
+ if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
+ op->is_memory) {
+ /* memory reference case (for both input and
+ output cases) */
+ SValue sv;
+ sv = *op->vt;
+ sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
+ load(op->reg, &sv);
+ } else if (i >= nb_outputs || op->is_rw) {
+ /* load value in register */
+ load(op->reg, op->vt);
+ if (op->is_llong) {
+ SValue sv;
+ sv = *op->vt;
+ sv.c.ul += 4;
+ load(TREG_RDX, &sv);
+ }
+ }
+ }
+ }
+ } else {
+ /* generate save code */
+ for(i = 0 ; i < nb_outputs; i++) {
+ op = &operands[i];
+ if (op->reg >= 0) {
+ if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
+ if (!op->is_memory) {
+ SValue sv;
+ sv = *op->vt;
+ sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
+ load(out_reg, &sv);
+
+ sv.r = (sv.r & ~VT_VALMASK) | out_reg;
+ store(op->reg, &sv);
+ }
+ } else {
+ store(op->reg, op->vt);
+ if (op->is_llong) {
+ SValue sv;
+ sv = *op->vt;
+ sv.c.ul += 4;
+ store(TREG_RDX, &sv);
+ }
+ }
+ }
+ }
+ /* generate reg restore code */
+ for(i = NB_SAVED_REGS - 1; i >= 0; i--) {
+ reg = reg_saved[i];
+ if (regs_allocated[reg])
+ g(0x58 + reg);
+ }
+ }
+}
+
+static void asm_clobber(uint8_t *clobber_regs, const char *str)
+{
+ int reg;
+ TokenSym *ts;
+
+ if (!strcmp(str, "memory") ||
+ !strcmp(str, "cc"))
+ return;
+ ts = tok_alloc(str, strlen(str));
+ reg = ts->tok;
+ if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) {
+ reg -= TOK_ASM_rax;
+ } else if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
+ reg -= TOK_ASM_eax;
+ } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
+ reg -= TOK_ASM_ax;
+ } else {
+ error("invalid clobber register '%s'", str);
+ }
+ clobber_regs[reg] = 1;
+}
diff --git a/x86_64-asm.h b/x86_64-asm.h
new file mode 100644
index 0000000..2e52cbe
--- /dev/null
+++ b/x86_64-asm.h
@@ -0,0 +1,446 @@
+ DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */
+ DEF_ASM_OP0(cld, 0xfc)
+ DEF_ASM_OP0(cli, 0xfa)
+ DEF_ASM_OP0(clts, 0x0f06)
+ DEF_ASM_OP0(cmc, 0xf5)
+ DEF_ASM_OP0(lahf, 0x9f)
+ DEF_ASM_OP0(sahf, 0x9e)
+ DEF_ASM_OP0(pushfl, 0x9c)
+ DEF_ASM_OP0(popfl, 0x9d)
+ DEF_ASM_OP0(pushf, 0x9c)
+ DEF_ASM_OP0(popf, 0x9d)
+ DEF_ASM_OP0(stc, 0xf9)
+ DEF_ASM_OP0(std, 0xfd)
+ DEF_ASM_OP0(sti, 0xfb)
+ DEF_ASM_OP0(aaa, 0x37)
+ DEF_ASM_OP0(aas, 0x3f)
+ DEF_ASM_OP0(daa, 0x27)
+ DEF_ASM_OP0(das, 0x2f)
+ DEF_ASM_OP0(aad, 0xd50a)
+ DEF_ASM_OP0(aam, 0xd40a)
+ DEF_ASM_OP0(cbw, 0x6698)
+ DEF_ASM_OP0(cwd, 0x6699)
+ DEF_ASM_OP0(cwde, 0x98)
+ DEF_ASM_OP0(cdq, 0x99)
+ DEF_ASM_OP0(cbtw, 0x6698)
+ DEF_ASM_OP0(cwtl, 0x98)
+ DEF_ASM_OP0(cwtd, 0x6699)
+ DEF_ASM_OP0(cltd, 0x99)
+ DEF_ASM_OP0(int3, 0xcc)
+ DEF_ASM_OP0(into, 0xce)
+ DEF_ASM_OP0(iret, 0xcf)
+ DEF_ASM_OP0(rsm, 0x0faa)
+ DEF_ASM_OP0(hlt, 0xf4)
+ DEF_ASM_OP0(wait, 0x9b)
+ DEF_ASM_OP0(nop, 0x90)
+ DEF_ASM_OP0(xlat, 0xd7)
+
+ /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWLQ))
+
+ /* bits */
+
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ /* prefixes */
+ DEF_ASM_OP0(lock, 0xf0)
+ DEF_ASM_OP0(rep, 0xf3)
+ DEF_ASM_OP0(repe, 0xf3)
+ DEF_ASM_OP0(repz, 0xf3)
+ DEF_ASM_OP0(repne, 0xf2)
+ DEF_ASM_OP0(repnz, 0xf2)
+
+ DEF_ASM_OP0(invd, 0x0f08)
+ DEF_ASM_OP0(wbinvd, 0x0f09)
+ DEF_ASM_OP0(cpuid, 0x0fa2)
+ DEF_ASM_OP0(wrmsr, 0x0f30)
+ DEF_ASM_OP0(rdtsc, 0x0f31)
+ DEF_ASM_OP0(rdmsr, 0x0f32)
+ DEF_ASM_OP0(rdpmc, 0x0f33)
+ DEF_ASM_OP0(ud2, 0x0f0b)
+
+ /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWLQ, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWLQ, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWLQ, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWLQ, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WLQ, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WLQ, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WLQ, OPT_CR, OPT_REG64))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WLQ, OPT_DB, OPT_REG64))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WLQ, OPT_TR, OPT_REG64))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WLQ, OPT_REG64))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WLQ, OPT_REG64 | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WLQ, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WLQ, OPT_SEG))
+ DEF_ASM_OP1(pushb, 0x6a, 0, OPC_B, OPT_IM8S)
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WLQ, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WLQ, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WLQ, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+ /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWLQ, OPT_IMNO64, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWLQ, OPT_IMNO64, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WLQ, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WLQ, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WLQ, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX))
+
+ /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 0, OPC_JMP | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+ DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+ DEF_ASM_OP0(leave, 0xc9)
+ DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+ DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+ DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+
+ /* float */
+ /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+ DEF_ASM_OP0(fucompp, 0xdae9)
+ DEF_ASM_OP0(ftst, 0xd9e4)
+ DEF_ASM_OP0(fxam, 0xd9e5)
+ DEF_ASM_OP0(fld1, 0xd9e8)
+ DEF_ASM_OP0(fldl2t, 0xd9e9)
+ DEF_ASM_OP0(fldl2e, 0xd9ea)
+ DEF_ASM_OP0(fldpi, 0xd9eb)
+ DEF_ASM_OP0(fldlg2, 0xd9ec)
+ DEF_ASM_OP0(fldln2, 0xd9ed)
+ DEF_ASM_OP0(fldz, 0xd9ee)
+
+ DEF_ASM_OP0(f2xm1, 0xd9f0)
+ DEF_ASM_OP0(fyl2x, 0xd9f1)
+ DEF_ASM_OP0(fptan, 0xd9f2)
+ DEF_ASM_OP0(fpatan, 0xd9f3)
+ DEF_ASM_OP0(fxtract, 0xd9f4)
+ DEF_ASM_OP0(fprem1, 0xd9f5)
+ DEF_ASM_OP0(fdecstp, 0xd9f6)
+ DEF_ASM_OP0(fincstp, 0xd9f7)
+ DEF_ASM_OP0(fprem, 0xd9f8)
+ DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+ DEF_ASM_OP0(fsqrt, 0xd9fa)
+ DEF_ASM_OP0(fsincos, 0xd9fb)
+ DEF_ASM_OP0(frndint, 0xd9fc)
+ DEF_ASM_OP0(fscale, 0xd9fd)
+ DEF_ASM_OP0(fsin, 0xd9fe)
+ DEF_ASM_OP0(fcos, 0xd9ff)
+ DEF_ASM_OP0(fchs, 0xd9e0)
+ DEF_ASM_OP0(fabs, 0xd9e1)
+ DEF_ASM_OP0(fninit, 0xdbe3)
+ DEF_ASM_OP0(fnclex, 0xdbe2)
+ DEF_ASM_OP0(fnop, 0xd9d0)
+ DEF_ASM_OP0(fwait, 0x9b)
+
+ /* fp load */
+ DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+ DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+ DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+
+ /* fp store */
+ DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+ DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+ DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+ /* exchange */
+ DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+ /* misc FPU */
+ DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+ DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+ DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+ DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+ DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+ DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+ /* segments */
+ DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+ DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+ DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+ DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+ DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+ DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+ DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+ /* 486 */
+ DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+ DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+ DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+ /* pentium */
+ DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+
+ /* pentium pro */
+ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovno, 0x0f41, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovc, 0x0f42, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovnc, 0x0f43, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovz, 0x0f44, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovnz, 0x0f45, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovna, 0x0f46, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmova, 0x0f47, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+ DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+ DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+ /* mmx */
+ DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+ DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+ALT(DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ))
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+ DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
diff --git a/x86_64-tok.h b/x86_64-tok.h
new file mode 100644
index 0000000..85cbb44
--- /dev/null
+++ b/x86_64-tok.h
@@ -0,0 +1,224 @@
+
+/* WARNING: relative order of tokens is important. */
+ DEF_ASM(al)
+ DEF_ASM(cl)
+ DEF_ASM(dl)
+ DEF_ASM(bl)
+ DEF_ASM(ah)
+ DEF_ASM(ch)
+ DEF_ASM(dh)
+ DEF_ASM(bh)
+ DEF_ASM(ax)
+ DEF_ASM(cx)
+ DEF_ASM(dx)
+ DEF_ASM(bx)
+ DEF_ASM(sp)
+ DEF_ASM(bp)
+ DEF_ASM(si)
+ DEF_ASM(di)
+ DEF_ASM(eax)
+ DEF_ASM(ecx)
+ DEF_ASM(edx)
+ DEF_ASM(ebx)
+ DEF_ASM(esp)
+ DEF_ASM(ebp)
+ DEF_ASM(esi)
+ DEF_ASM(edi)
+ DEF_ASM(rax)
+ DEF_ASM(rcx)
+ DEF_ASM(rdx)
+ DEF_ASM(rbx)
+ DEF_ASM(rsp)
+ DEF_ASM(rbp)
+ DEF_ASM(rsi)
+ DEF_ASM(rdi)
+ DEF_ASM(mm0)
+ DEF_ASM(mm1)
+ DEF_ASM(mm2)
+ DEF_ASM(mm3)
+ DEF_ASM(mm4)
+ DEF_ASM(mm5)
+ DEF_ASM(mm6)
+ DEF_ASM(mm7)
+ DEF_ASM(xmm0)
+ DEF_ASM(xmm1)
+ DEF_ASM(xmm2)
+ DEF_ASM(xmm3)
+ DEF_ASM(xmm4)
+ DEF_ASM(xmm5)
+ DEF_ASM(xmm6)
+ DEF_ASM(xmm7)
+ DEF_ASM(cr0)
+ DEF_ASM(cr1)
+ DEF_ASM(cr2)
+ DEF_ASM(cr3)
+ DEF_ASM(cr4)
+ DEF_ASM(cr5)
+ DEF_ASM(cr6)
+ DEF_ASM(cr7)
+ DEF_ASM(tr0)
+ DEF_ASM(tr1)
+ DEF_ASM(tr2)
+ DEF_ASM(tr3)
+ DEF_ASM(tr4)
+ DEF_ASM(tr5)
+ DEF_ASM(tr6)
+ DEF_ASM(tr7)
+ DEF_ASM(db0)
+ DEF_ASM(db1)
+ DEF_ASM(db2)
+ DEF_ASM(db3)
+ DEF_ASM(db4)
+ DEF_ASM(db5)
+ DEF_ASM(db6)
+ DEF_ASM(db7)
+ DEF_ASM(dr0)
+ DEF_ASM(dr1)
+ DEF_ASM(dr2)
+ DEF_ASM(dr3)
+ DEF_ASM(dr4)
+ DEF_ASM(dr5)
+ DEF_ASM(dr6)
+ DEF_ASM(dr7)
+ DEF_ASM(es)
+ DEF_ASM(cs)
+ DEF_ASM(ss)
+ DEF_ASM(ds)
+ DEF_ASM(fs)
+ DEF_ASM(gs)
+ DEF_ASM(st)
+
+ DEF_BWLQ(mov)
+
+ /* generic two operands */
+ DEF_BWLQ(add)
+ DEF_BWLQ(or)
+ DEF_BWLQ(adc)
+ DEF_BWLQ(sbb)
+ DEF_BWLQ(and)
+ DEF_BWLQ(sub)
+ DEF_BWLQ(xor)
+ DEF_BWLQ(cmp)
+
+ /* unary ops */
+ DEF_BWLQ(inc)
+ DEF_BWLQ(dec)
+ DEF_BWLQ(not)
+ DEF_BWLQ(neg)
+ DEF_BWLQ(mul)
+ DEF_BWLQ(imul)
+ DEF_BWLQ(div)
+ DEF_BWLQ(idiv)
+
+ DEF_BWLQ(xchg)
+ DEF_BWLQ(test)
+
+ /* shifts */
+ DEF_BWLQ(rol)
+ DEF_BWLQ(ror)
+ DEF_BWLQ(rcl)
+ DEF_BWLQ(rcr)
+ DEF_BWLQ(shl)
+ DEF_BWLQ(shr)
+ DEF_BWLQ(sar)
+
+ DEF_ASM(shldw)
+ DEF_ASM(shldl)
+ DEF_ASM(shld)
+ DEF_ASM(shrdw)
+ DEF_ASM(shrdl)
+ DEF_ASM(shrd)
+
+ DEF_ASM(pushw)
+ DEF_ASM(pushl)
+ DEF_ASM(pushq)
+ DEF_ASM(push)
+ DEF_ASM(popw)
+ DEF_ASM(popl)
+ DEF_ASM(popq)
+ DEF_ASM(pop)
+ DEF_BWL(in)
+ DEF_BWL(out)
+
+ DEF_WL(movzb)
+
+ DEF_ASM(movzwl)
+ DEF_ASM(movsbw)
+ DEF_ASM(movsbl)
+ DEF_ASM(movswl)
+
+ DEF_WLQ(lea)
+
+ DEF_ASM(les)
+ DEF_ASM(lds)
+ DEF_ASM(lss)
+ DEF_ASM(lfs)
+ DEF_ASM(lgs)
+
+ DEF_ASM(call)
+ DEF_ASM(jmp)
+ DEF_ASM(lcall)
+ DEF_ASM(ljmp)
+
+ DEF_ASMTEST(j)
+
+ DEF_ASMTEST(set)
+ DEF_ASMTEST(cmov)
+
+ DEF_WLQ(bsf)
+ DEF_WLQ(bsr)
+ DEF_WLQ(bt)
+ DEF_WLQ(bts)
+ DEF_WLQ(btr)
+ DEF_WLQ(btc)
+
+ DEF_WLQ(lsl)
+
+ /* generic FP ops */
+ DEF_FP(add)
+ DEF_FP(mul)
+
+ DEF_ASM(fcom)
+ DEF_ASM(fcom_1) /* non existant op, just to have a regular table */
+ DEF_FP1(com)
+
+ DEF_FP(comp)
+ DEF_FP(sub)
+ DEF_FP(subr)
+ DEF_FP(div)
+ DEF_FP(divr)
+
+ DEF_BWLQ(xadd)
+ DEF_BWLQ(cmpxchg)
+
+ /* string ops */
+ DEF_BWLQ(cmps)
+ DEF_BWLQ(scmp)
+ DEF_BWL(ins)
+ DEF_BWL(outs)
+ DEF_BWLQ(lods)
+ DEF_BWLQ(slod)
+ DEF_BWLQ(movs)
+ DEF_BWLQ(smov)
+ DEF_BWLQ(scas)
+ DEF_BWLQ(ssca)
+ DEF_BWLQ(stos)
+ DEF_BWLQ(ssto)
+
+ /* generic asm ops */
+
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
+#include "x86_64-asm.h"
+
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
+#include "x86_64-asm.h"