| /* Print i386 instructions for GDB, the GNU debugger. |
| Copyright 1988, 1989, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, |
| 2001 |
| Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| |
| /* |
| * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu) |
| * July 1988 |
| * modified by John Hassey (hassey@dg-rtp.dg.com) |
| * x86-64 support added by Jan Hubicka (jh@suse.cz) |
| */ |
| |
| /* |
| * The main tables describing the instructions is essentially a copy |
| * of the "Opcode Map" chapter (Appendix A) of the Intel 80386 |
| * Programmers Manual. Usually, there is a capital letter, followed |
| * by a small letter. The capital letter tell the addressing mode, |
| * and the small letter tells about the operand size. Refer to |
| * the Intel manual for details. |
| */ |
| |
| #include <stdlib.h> |
| #include "dis-asm.h" |
| #include "qemu-common.h" |
| |
| #define MAXLEN 20 |
| |
| #include <setjmp.h> |
| |
| #ifndef UNIXWARE_COMPAT |
| /* Set non-zero for broken, compatible instructions. Set to zero for |
| non-broken opcodes. */ |
| #define UNIXWARE_COMPAT 1 |
| #endif |
| |
| static int fetch_data PARAMS ((struct disassemble_info *, bfd_byte *)); |
| static void ckprefix PARAMS ((void)); |
| static const char *prefix_name PARAMS ((int, int)); |
| static int print_insn PARAMS ((bfd_vma, disassemble_info *)); |
| static void dofloat PARAMS ((int)); |
| static void OP_ST PARAMS ((int, int)); |
| static void OP_STi PARAMS ((int, int)); |
| static int putop PARAMS ((const char *, int)); |
| static void oappend PARAMS ((const char *)); |
| static void append_seg PARAMS ((void)); |
| static void OP_indirE PARAMS ((int, int)); |
| static void print_operand_value (char *buf, size_t bufsize, int hex, |
| bfd_vma disp); |
| static void OP_E PARAMS ((int, int)); |
| static void OP_G PARAMS ((int, int)); |
| static bfd_vma get64 PARAMS ((void)); |
| static bfd_signed_vma get32 PARAMS ((void)); |
| static bfd_signed_vma get32s PARAMS ((void)); |
| static int get16 PARAMS ((void)); |
| static void set_op PARAMS ((bfd_vma, int)); |
| static void OP_REG PARAMS ((int, int)); |
| static void OP_IMREG PARAMS ((int, int)); |
| static void OP_I PARAMS ((int, int)); |
| static void OP_I64 PARAMS ((int, int)); |
| static void OP_sI PARAMS ((int, int)); |
| static void OP_J PARAMS ((int, int)); |
| static void OP_SEG PARAMS ((int, int)); |
| static void OP_DIR PARAMS ((int, int)); |
| static void OP_OFF PARAMS ((int, int)); |
| static void OP_OFF64 PARAMS ((int, int)); |
| static void ptr_reg PARAMS ((int, int)); |
| static void OP_ESreg PARAMS ((int, int)); |
| static void OP_DSreg PARAMS ((int, int)); |
| static void OP_C PARAMS ((int, int)); |
| static void OP_D PARAMS ((int, int)); |
| static void OP_T PARAMS ((int, int)); |
| static void OP_Rd PARAMS ((int, int)); |
| static void OP_MMX PARAMS ((int, int)); |
| static void OP_XMM PARAMS ((int, int)); |
| static void OP_EM PARAMS ((int, int)); |
| static void OP_EX PARAMS ((int, int)); |
| static void OP_MS PARAMS ((int, int)); |
| static void OP_XS PARAMS ((int, int)); |
| static void OP_3DNowSuffix PARAMS ((int, int)); |
| static void OP_SIMD_Suffix PARAMS ((int, int)); |
| static void SIMD_Fixup PARAMS ((int, int)); |
| static void BadOp PARAMS ((void)); |
| |
| struct dis_private { |
| /* Points to first byte not fetched. */ |
| bfd_byte *max_fetched; |
| bfd_byte the_buffer[MAXLEN]; |
| bfd_vma insn_start; |
| int orig_sizeflag; |
| jmp_buf bailout; |
| }; |
| |
| /* The opcode for the fwait instruction, which we treat as a prefix |
| when we can. */ |
| #define FWAIT_OPCODE (0x9b) |
| |
| /* Set to 1 for 64bit mode disassembly. */ |
| static int mode_64bit; |
| |
| /* Flags for the prefixes for the current instruction. See below. */ |
| static int prefixes; |
| |
| /* REX prefix the current instruction. See below. */ |
| static int rex; |
| /* Bits of REX we've already used. */ |
| static int rex_used; |
| #define REX_MODE64 8 |
| #define REX_EXTX 4 |
| #define REX_EXTY 2 |
| #define REX_EXTZ 1 |
| /* Mark parts used in the REX prefix. When we are testing for |
| empty prefix (for 8bit register REX extension), just mask it |
| out. Otherwise test for REX bit is excuse for existence of REX |
| only in case value is nonzero. */ |
| #define USED_REX(value) \ |
| { \ |
| if (value) \ |
| rex_used |= (rex & value) ? (value) | 0x40 : 0; \ |
| else \ |
| rex_used |= 0x40; \ |
| } |
| |
| /* Flags for prefixes which we somehow handled when printing the |
| current instruction. */ |
| static int used_prefixes; |
| |
| /* Flags stored in PREFIXES. */ |
| #define PREFIX_REPZ 1 |
| #define PREFIX_REPNZ 2 |
| #define PREFIX_LOCK 4 |
| #define PREFIX_CS 8 |
| #define PREFIX_SS 0x10 |
| #define PREFIX_DS 0x20 |
| #define PREFIX_ES 0x40 |
| #define PREFIX_FS 0x80 |
| #define PREFIX_GS 0x100 |
| #define PREFIX_DATA 0x200 |
| #define PREFIX_ADDR 0x400 |
| #define PREFIX_FWAIT 0x800 |
| |
| /* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) |
| to ADDR (exclusive) are valid. Returns 1 for success, longjmps |
| on error. */ |
| #define FETCH_DATA(info, addr) \ |
| ((addr) <= ((struct dis_private *) (info->private_data))->max_fetched \ |
| ? 1 : fetch_data ((info), (addr))) |
| |
| static int |
| fetch_data (info, addr) |
| struct disassemble_info *info; |
| bfd_byte *addr; |
| { |
| int status; |
| struct dis_private *priv = (struct dis_private *) info->private_data; |
| bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); |
| |
| status = (*info->read_memory_func) (start, |
| priv->max_fetched, |
| addr - priv->max_fetched, |
| info); |
| if (status != 0) |
| { |
| /* If we did manage to read at least one byte, then |
| print_insn_i386 will do something sensible. Otherwise, print |
| an error. We do that here because this is where we know |
| STATUS. */ |
| if (priv->max_fetched == priv->the_buffer) |
| (*info->memory_error_func) (status, start, info); |
| longjmp (priv->bailout, 1); |
| } |
| else |
| priv->max_fetched = addr; |
| return 1; |
| } |
| |
| #define XX NULL, 0 |
| |
| #define Eb OP_E, b_mode |
| #define Ev OP_E, v_mode |
| #define Ed OP_E, d_mode |
| #define indirEb OP_indirE, b_mode |
| #define indirEv OP_indirE, v_mode |
| #define Ew OP_E, w_mode |
| #define Ma OP_E, v_mode |
| #define M OP_E, 0 /* lea, lgdt, etc. */ |
| #define Mp OP_E, 0 /* 32 or 48 bit memory operand for LDS, LES etc */ |
| #define Gb OP_G, b_mode |
| #define Gv OP_G, v_mode |
| #define Gd OP_G, d_mode |
| #define Gw OP_G, w_mode |
| #define Rd OP_Rd, d_mode |
| #define Rm OP_Rd, m_mode |
| #define Ib OP_I, b_mode |
| #define sIb OP_sI, b_mode /* sign extened byte */ |
| #define Iv OP_I, v_mode |
| #define Iq OP_I, q_mode |
| #define Iv64 OP_I64, v_mode |
| #define Iw OP_I, w_mode |
| #define Jb OP_J, b_mode |
| #define Jv OP_J, v_mode |
| #define Cm OP_C, m_mode |
| #define Dm OP_D, m_mode |
| #define Td OP_T, d_mode |
| |
| #define RMeAX OP_REG, eAX_reg |
| #define RMeBX OP_REG, eBX_reg |
| #define RMeCX OP_REG, eCX_reg |
| #define RMeDX OP_REG, eDX_reg |
| #define RMeSP OP_REG, eSP_reg |
| #define RMeBP OP_REG, eBP_reg |
| #define RMeSI OP_REG, eSI_reg |
| #define RMeDI OP_REG, eDI_reg |
| #define RMrAX OP_REG, rAX_reg |
| #define RMrBX OP_REG, rBX_reg |
| #define RMrCX OP_REG, rCX_reg |
| #define RMrDX OP_REG, rDX_reg |
| #define RMrSP OP_REG, rSP_reg |
| #define RMrBP OP_REG, rBP_reg |
| #define RMrSI OP_REG, rSI_reg |
| #define RMrDI OP_REG, rDI_reg |
| #define RMAL OP_REG, al_reg |
| #define RMAL OP_REG, al_reg |
| #define RMCL OP_REG, cl_reg |
| #define RMDL OP_REG, dl_reg |
| #define RMBL OP_REG, bl_reg |
| #define RMAH OP_REG, ah_reg |
| #define RMCH OP_REG, ch_reg |
| #define RMDH OP_REG, dh_reg |
| #define RMBH OP_REG, bh_reg |
| #define RMAX OP_REG, ax_reg |
| #define RMDX OP_REG, dx_reg |
| |
| #define eAX OP_IMREG, eAX_reg |
| #define eBX OP_IMREG, eBX_reg |
| #define eCX OP_IMREG, eCX_reg |
| #define eDX OP_IMREG, eDX_reg |
| #define eSP OP_IMREG, eSP_reg |
| #define eBP OP_IMREG, eBP_reg |
| #define eSI OP_IMREG, eSI_reg |
| #define eDI OP_IMREG, eDI_reg |
| #define AL OP_IMREG, al_reg |
| #define AL OP_IMREG, al_reg |
| #define CL OP_IMREG, cl_reg |
| #define DL OP_IMREG, dl_reg |
| #define BL OP_IMREG, bl_reg |
| #define AH OP_IMREG, ah_reg |
| #define CH OP_IMREG, ch_reg |
| #define DH OP_IMREG, dh_reg |
| #define BH OP_IMREG, bh_reg |
| #define AX OP_IMREG, ax_reg |
| #define DX OP_IMREG, dx_reg |
| #define indirDX OP_IMREG, indir_dx_reg |
| |
| #define Sw OP_SEG, w_mode |
| #define Ap OP_DIR, 0 |
| #define Ob OP_OFF, b_mode |
| #define Ob64 OP_OFF64, b_mode |
| #define Ov OP_OFF, v_mode |
| #define Ov64 OP_OFF64, v_mode |
| #define Xb OP_DSreg, eSI_reg |
| #define Xv OP_DSreg, eSI_reg |
| #define Yb OP_ESreg, eDI_reg |
| #define Yv OP_ESreg, eDI_reg |
| #define DSBX OP_DSreg, eBX_reg |
| |
| #define es OP_REG, es_reg |
| #define ss OP_REG, ss_reg |
| #define cs OP_REG, cs_reg |
| #define ds OP_REG, ds_reg |
| #define fs OP_REG, fs_reg |
| #define gs OP_REG, gs_reg |
| |
| #define MX OP_MMX, 0 |
| #define XM OP_XMM, 0 |
| #define EM OP_EM, v_mode |
| #define EX OP_EX, v_mode |
| #define MS OP_MS, v_mode |
| #define XS OP_XS, v_mode |
| #define None OP_E, 0 |
| #define OPSUF OP_3DNowSuffix, 0 |
| #define OPSIMD OP_SIMD_Suffix, 0 |
| |
| #define cond_jump_flag NULL, cond_jump_mode |
| #define loop_jcxz_flag NULL, loop_jcxz_mode |
| |
| /* bits in sizeflag */ |
| #define SUFFIX_ALWAYS 4 |
| #define AFLAG 2 |
| #define DFLAG 1 |
| |
| #define b_mode 1 /* byte operand */ |
| #define v_mode 2 /* operand size depends on prefixes */ |
| #define w_mode 3 /* word operand */ |
| #define d_mode 4 /* double word operand */ |
| #define q_mode 5 /* quad word operand */ |
| #define x_mode 6 |
| #define m_mode 7 /* d_mode in 32bit, q_mode in 64bit mode. */ |
| #define cond_jump_mode 8 |
| #define loop_jcxz_mode 9 |
| |
| #define es_reg 100 |
| #define cs_reg 101 |
| #define ss_reg 102 |
| #define ds_reg 103 |
| #define fs_reg 104 |
| #define gs_reg 105 |
| |
| #define eAX_reg 108 |
| #define eCX_reg 109 |
| #define eDX_reg 110 |
| #define eBX_reg 111 |
| #define eSP_reg 112 |
| #define eBP_reg 113 |
| #define eSI_reg 114 |
| #define eDI_reg 115 |
| |
| #define al_reg 116 |
| #define cl_reg 117 |
| #define dl_reg 118 |
| #define bl_reg 119 |
| #define ah_reg 120 |
| #define ch_reg 121 |
| #define dh_reg 122 |
| #define bh_reg 123 |
| |
| #define ax_reg 124 |
| #define cx_reg 125 |
| #define dx_reg 126 |
| #define bx_reg 127 |
| #define sp_reg 128 |
| #define bp_reg 129 |
| #define si_reg 130 |
| #define di_reg 131 |
| |
| #define rAX_reg 132 |
| #define rCX_reg 133 |
| #define rDX_reg 134 |
| #define rBX_reg 135 |
| #define rSP_reg 136 |
| #define rBP_reg 137 |
| #define rSI_reg 138 |
| #define rDI_reg 139 |
| |
| #define indir_dx_reg 150 |
| |
| #define FLOATCODE 1 |
| #define USE_GROUPS 2 |
| #define USE_PREFIX_USER_TABLE 3 |
| #define X86_64_SPECIAL 4 |
| |
| #define FLOAT NULL, NULL, FLOATCODE, NULL, 0, NULL, 0 |
| |
| #define GRP1b NULL, NULL, USE_GROUPS, NULL, 0, NULL, 0 |
| #define GRP1S NULL, NULL, USE_GROUPS, NULL, 1, NULL, 0 |
| #define GRP1Ss NULL, NULL, USE_GROUPS, NULL, 2, NULL, 0 |
| #define GRP2b NULL, NULL, USE_GROUPS, NULL, 3, NULL, 0 |
| #define GRP2S NULL, NULL, USE_GROUPS, NULL, 4, NULL, 0 |
| #define GRP2b_one NULL, NULL, USE_GROUPS, NULL, 5, NULL, 0 |
| #define GRP2S_one NULL, NULL, USE_GROUPS, NULL, 6, NULL, 0 |
| #define GRP2b_cl NULL, NULL, USE_GROUPS, NULL, 7, NULL, 0 |
| #define GRP2S_cl NULL, NULL, USE_GROUPS, NULL, 8, NULL, 0 |
| #define GRP3b NULL, NULL, USE_GROUPS, NULL, 9, NULL, 0 |
| #define GRP3S NULL, NULL, USE_GROUPS, NULL, 10, NULL, 0 |
| #define GRP4 NULL, NULL, USE_GROUPS, NULL, 11, NULL, 0 |
| #define GRP5 NULL, NULL, USE_GROUPS, NULL, 12, NULL, 0 |
| #define GRP6 NULL, NULL, USE_GROUPS, NULL, 13, NULL, 0 |
| #define GRP7 NULL, NULL, USE_GROUPS, NULL, 14, NULL, 0 |
| #define GRP8 NULL, NULL, USE_GROUPS, NULL, 15, NULL, 0 |
| #define GRP9 NULL, NULL, USE_GROUPS, NULL, 16, NULL, 0 |
| #define GRP10 NULL, NULL, USE_GROUPS, NULL, 17, NULL, 0 |
| #define GRP11 NULL, NULL, USE_GROUPS, NULL, 18, NULL, 0 |
| #define GRP12 NULL, NULL, USE_GROUPS, NULL, 19, NULL, 0 |
| #define GRP13 NULL, NULL, USE_GROUPS, NULL, 20, NULL, 0 |
| #define GRP14 NULL, NULL, USE_GROUPS, NULL, 21, NULL, 0 |
| #define GRPAMD NULL, NULL, USE_GROUPS, NULL, 22, NULL, 0 |
| |
| #define PREGRP0 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 0, NULL, 0 |
| #define PREGRP1 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 1, NULL, 0 |
| #define PREGRP2 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 2, NULL, 0 |
| #define PREGRP3 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 3, NULL, 0 |
| #define PREGRP4 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 4, NULL, 0 |
| #define PREGRP5 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 5, NULL, 0 |
| #define PREGRP6 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 6, NULL, 0 |
| #define PREGRP7 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 7, NULL, 0 |
| #define PREGRP8 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 8, NULL, 0 |
| #define PREGRP9 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 9, NULL, 0 |
| #define PREGRP10 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 10, NULL, 0 |
| #define PREGRP11 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 11, NULL, 0 |
| #define PREGRP12 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 12, NULL, 0 |
| #define PREGRP13 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 13, NULL, 0 |
| #define PREGRP14 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 14, NULL, 0 |
| #define PREGRP15 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 15, NULL, 0 |
| #define PREGRP16 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 16, NULL, 0 |
| #define PREGRP17 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 17, NULL, 0 |
| #define PREGRP18 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 18, NULL, 0 |
| #define PREGRP19 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 19, NULL, 0 |
| #define PREGRP20 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 20, NULL, 0 |
| #define PREGRP21 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 21, NULL, 0 |
| #define PREGRP22 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 22, NULL, 0 |
| #define PREGRP23 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 23, NULL, 0 |
| #define PREGRP24 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 24, NULL, 0 |
| #define PREGRP25 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 25, NULL, 0 |
| #define PREGRP26 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 26, NULL, 0 |
| |
| #define X86_64_0 NULL, NULL, X86_64_SPECIAL, NULL, 0, NULL, 0 |
| |
| typedef void (*op_rtn) PARAMS ((int bytemode, int sizeflag)); |
| |
| struct dis386 { |
| const char *name; |
| op_rtn op1; |
| int bytemode1; |
| op_rtn op2; |
| int bytemode2; |
| op_rtn op3; |
| int bytemode3; |
| }; |
| |
| /* Upper case letters in the instruction names here are macros. |
| 'A' => print 'b' if no register operands or suffix_always is true |
| 'B' => print 'b' if suffix_always is true |
| 'E' => print 'e' if 32-bit form of jcxz |
| 'F' => print 'w' or 'l' depending on address size prefix (loop insns) |
| 'H' => print ",pt" or ",pn" branch hint |
| 'L' => print 'l' if suffix_always is true |
| 'N' => print 'n' if instruction has no wait "prefix" |
| 'O' => print 'd', or 'o' |
| 'P' => print 'w', 'l' or 'q' if instruction has an operand size prefix, |
| . or suffix_always is true. print 'q' if rex prefix is present. |
| 'Q' => print 'w', 'l' or 'q' if no register operands or suffix_always |
| . is true |
| 'R' => print 'w', 'l' or 'q' ("wd" or "dq" in intel mode) |
| 'S' => print 'w', 'l' or 'q' if suffix_always is true |
| 'T' => print 'q' in 64bit mode and behave as 'P' otherwise |
| 'U' => print 'q' in 64bit mode and behave as 'Q' otherwise |
| 'X' => print 's', 'd' depending on data16 prefix (for XMM) |
| 'W' => print 'b' or 'w' ("w" or "de" in intel mode) |
| 'Y' => 'q' if instruction has an REX 64bit overwrite prefix |
| |
| Many of the above letters print nothing in Intel mode. See "putop" |
| for the details. |
| |
| Braces '{' and '}', and vertical bars '|', indicate alternative |
| mnemonic strings for AT&T, Intel, X86_64 AT&T, and X86_64 Intel |
| modes. In cases where there are only two alternatives, the X86_64 |
| instruction is reserved, and "(bad)" is printed. |
| */ |
| |
| static const struct dis386 dis386[] = { |
| /* 00 */ |
| { "addB", Eb, Gb, XX }, |
| { "addS", Ev, Gv, XX }, |
| { "addB", Gb, Eb, XX }, |
| { "addS", Gv, Ev, XX }, |
| { "addB", AL, Ib, XX }, |
| { "addS", eAX, Iv, XX }, |
| { "push{T|}", es, XX, XX }, |
| { "pop{T|}", es, XX, XX }, |
| /* 08 */ |
| { "orB", Eb, Gb, XX }, |
| { "orS", Ev, Gv, XX }, |
| { "orB", Gb, Eb, XX }, |
| { "orS", Gv, Ev, XX }, |
| { "orB", AL, Ib, XX }, |
| { "orS", eAX, Iv, XX }, |
| { "push{T|}", cs, XX, XX }, |
| { "(bad)", XX, XX, XX }, /* 0x0f extended opcode escape */ |
| /* 10 */ |
| { "adcB", Eb, Gb, XX }, |
| { "adcS", Ev, Gv, XX }, |
| { "adcB", Gb, Eb, XX }, |
| { "adcS", Gv, Ev, XX }, |
| { "adcB", AL, Ib, XX }, |
| { "adcS", eAX, Iv, XX }, |
| { "push{T|}", ss, XX, XX }, |
| { "popT|}", ss, XX, XX }, |
| /* 18 */ |
| { "sbbB", Eb, Gb, XX }, |
| { "sbbS", Ev, Gv, XX }, |
| { "sbbB", Gb, Eb, XX }, |
| { "sbbS", Gv, Ev, XX }, |
| { "sbbB", AL, Ib, XX }, |
| { "sbbS", eAX, Iv, XX }, |
| { "push{T|}", ds, XX, XX }, |
| { "pop{T|}", ds, XX, XX }, |
| /* 20 */ |
| { "andB", Eb, Gb, XX }, |
| { "andS", Ev, Gv, XX }, |
| { "andB", Gb, Eb, XX }, |
| { "andS", Gv, Ev, XX }, |
| { "andB", AL, Ib, XX }, |
| { "andS", eAX, Iv, XX }, |
| { "(bad)", XX, XX, XX }, /* SEG ES prefix */ |
| { "daa{|}", XX, XX, XX }, |
| /* 28 */ |
| { "subB", Eb, Gb, XX }, |
| { "subS", Ev, Gv, XX }, |
| { "subB", Gb, Eb, XX }, |
| { "subS", Gv, Ev, XX }, |
| { "subB", AL, Ib, XX }, |
| { "subS", eAX, Iv, XX }, |
| { "(bad)", XX, XX, XX }, /* SEG CS prefix */ |
| { "das{|}", XX, XX, XX }, |
| /* 30 */ |
| { "xorB", Eb, Gb, XX }, |
| { "xorS", Ev, Gv, XX }, |
| { "xorB", Gb, Eb, XX }, |
| { "xorS", Gv, Ev, XX }, |
| { "xorB", AL, Ib, XX }, |
| { "xorS", eAX, Iv, XX }, |
| { "(bad)", XX, XX, XX }, /* SEG SS prefix */ |
| { "aaa{|}", XX, XX, XX }, |
| /* 38 */ |
| { "cmpB", Eb, Gb, XX }, |
| { "cmpS", Ev, Gv, XX }, |
| { "cmpB", Gb, Eb, XX }, |
| { "cmpS", Gv, Ev, XX }, |
| { "cmpB", AL, Ib, XX }, |
| { "cmpS", eAX, Iv, XX }, |
| { "(bad)", XX, XX, XX }, /* SEG DS prefix */ |
| { "aas{|}", XX, XX, XX }, |
| /* 40 */ |
| { "inc{S|}", RMeAX, XX, XX }, |
| { "inc{S|}", RMeCX, XX, XX }, |
| { "inc{S|}", RMeDX, XX, XX }, |
| { "inc{S|}", RMeBX, XX, XX }, |
| { "inc{S|}", RMeSP, XX, XX }, |
| { "inc{S|}", RMeBP, XX, XX }, |
| { "inc{S|}", RMeSI, XX, XX }, |
| { "inc{S|}", RMeDI, XX, XX }, |
| /* 48 */ |
| { "dec{S|}", RMeAX, XX, XX }, |
| { "dec{S|}", RMeCX, XX, XX }, |
| { "dec{S|}", RMeDX, XX, XX }, |
| { "dec{S|}", RMeBX, XX, XX }, |
| { "dec{S|}", RMeSP, XX, XX }, |
| { "dec{S|}", RMeBP, XX, XX }, |
| { "dec{S|}", RMeSI, XX, XX }, |
| { "dec{S|}", RMeDI, XX, XX }, |
| /* 50 */ |
| { "pushS", RMrAX, XX, XX }, |
| { "pushS", RMrCX, XX, XX }, |
| { "pushS", RMrDX, XX, XX }, |
| { "pushS", RMrBX, XX, XX }, |
| { "pushS", RMrSP, XX, XX }, |
| { "pushS", RMrBP, XX, XX }, |
| { "pushS", RMrSI, XX, XX }, |
| { "pushS", RMrDI, XX, XX }, |
| /* 58 */ |
| { "popS", RMrAX, XX, XX }, |
| { "popS", RMrCX, XX, XX }, |
| { "popS", RMrDX, XX, XX }, |
| { "popS", RMrBX, XX, XX }, |
| { "popS", RMrSP, XX, XX }, |
| { "popS", RMrBP, XX, XX }, |
| { "popS", RMrSI, XX, XX }, |
| { "popS", RMrDI, XX, XX }, |
| /* 60 */ |
| { "pusha{P|}", XX, XX, XX }, |
| { "popa{P|}", XX, XX, XX }, |
| { "bound{S|}", Gv, Ma, XX }, |
| { X86_64_0 }, |
| { "(bad)", XX, XX, XX }, /* seg fs */ |
| { "(bad)", XX, XX, XX }, /* seg gs */ |
| { "(bad)", XX, XX, XX }, /* op size prefix */ |
| { "(bad)", XX, XX, XX }, /* adr size prefix */ |
| /* 68 */ |
| { "pushT", Iq, XX, XX }, |
| { "imulS", Gv, Ev, Iv }, |
| { "pushT", sIb, XX, XX }, |
| { "imulS", Gv, Ev, sIb }, |
| { "ins{b||b|}", Yb, indirDX, XX }, |
| { "ins{R||R|}", Yv, indirDX, XX }, |
| { "outs{b||b|}", indirDX, Xb, XX }, |
| { "outs{R||R|}", indirDX, Xv, XX }, |
| /* 70 */ |
| { "joH", Jb, XX, cond_jump_flag }, |
| { "jnoH", Jb, XX, cond_jump_flag }, |
| { "jbH", Jb, XX, cond_jump_flag }, |
| { "jaeH", Jb, XX, cond_jump_flag }, |
| { "jeH", Jb, XX, cond_jump_flag }, |
| { "jneH", Jb, XX, cond_jump_flag }, |
| { "jbeH", Jb, XX, cond_jump_flag }, |
| { "jaH", Jb, XX, cond_jump_flag }, |
| /* 78 */ |
| { "jsH", Jb, XX, cond_jump_flag }, |
| { "jnsH", Jb, XX, cond_jump_flag }, |
| { "jpH", Jb, XX, cond_jump_flag }, |
| { "jnpH", Jb, XX, cond_jump_flag }, |
| { "jlH", Jb, XX, cond_jump_flag }, |
| { "jgeH", Jb, XX, cond_jump_flag }, |
| { "jleH", Jb, XX, cond_jump_flag }, |
| { "jgH", Jb, XX, cond_jump_flag }, |
| /* 80 */ |
| { GRP1b }, |
| { GRP1S }, |
| { "(bad)", XX, XX, XX }, |
| { GRP1Ss }, |
| { "testB", Eb, Gb, XX }, |
| { "testS", Ev, Gv, XX }, |
| { "xchgB", Eb, Gb, XX }, |
| { "xchgS", Ev, Gv, XX }, |
| /* 88 */ |
| { "movB", Eb, Gb, XX }, |
| { "movS", Ev, Gv, XX }, |
| { "movB", Gb, Eb, XX }, |
| { "movS", Gv, Ev, XX }, |
| { "movQ", Ev, Sw, XX }, |
| { "leaS", Gv, M, XX }, |
| { "movQ", Sw, Ev, XX }, |
| { "popU", Ev, XX, XX }, |
| /* 90 */ |
| { "nop", XX, XX, XX }, |
| /* FIXME: NOP with REPz prefix is called PAUSE. */ |
| { "xchgS", RMeCX, eAX, XX }, |
| { "xchgS", RMeDX, eAX, XX }, |
| { "xchgS", RMeBX, eAX, XX }, |
| { "xchgS", RMeSP, eAX, XX }, |
| { "xchgS", RMeBP, eAX, XX }, |
| { "xchgS", RMeSI, eAX, XX }, |
| { "xchgS", RMeDI, eAX, XX }, |
| /* 98 */ |
| { "cW{tR||tR|}", XX, XX, XX }, |
| { "cR{tO||tO|}", XX, XX, XX }, |
| { "lcall{T|}", Ap, XX, XX }, |
| { "(bad)", XX, XX, XX }, /* fwait */ |
| { "pushfT", XX, XX, XX }, |
| { "popfT", XX, XX, XX }, |
| { "sahf{|}", XX, XX, XX }, |
| { "lahf{|}", XX, XX, XX }, |
| /* a0 */ |
| { "movB", AL, Ob64, XX }, |
| { "movS", eAX, Ov64, XX }, |
| { "movB", Ob64, AL, XX }, |
| { "movS", Ov64, eAX, XX }, |
| { "movs{b||b|}", Yb, Xb, XX }, |
| { "movs{R||R|}", Yv, Xv, XX }, |
| { "cmps{b||b|}", Xb, Yb, XX }, |
| { "cmps{R||R|}", Xv, Yv, XX }, |
| /* a8 */ |
| { "testB", AL, Ib, XX }, |
| { "testS", eAX, Iv, XX }, |
| { "stosB", Yb, AL, XX }, |
| { "stosS", Yv, eAX, XX }, |
| { "lodsB", AL, Xb, XX }, |
| { "lodsS", eAX, Xv, XX }, |
| { "scasB", AL, Yb, XX }, |
| { "scasS", eAX, Yv, XX }, |
| /* b0 */ |
| { "movB", RMAL, Ib, XX }, |
| { "movB", RMCL, Ib, XX }, |
| { "movB", RMDL, Ib, XX }, |
| { "movB", RMBL, Ib, XX }, |
| { "movB", RMAH, Ib, XX }, |
| { "movB", RMCH, Ib, XX }, |
| { "movB", RMDH, Ib, XX }, |
| { "movB", RMBH, Ib, XX }, |
| /* b8 */ |
| { "movS", RMeAX, Iv64, XX }, |
| { "movS", RMeCX, Iv64, XX }, |
| { "movS", RMeDX, Iv64, XX }, |
| { "movS", RMeBX, Iv64, XX }, |
| { "movS", RMeSP, Iv64, XX }, |
| { "movS", RMeBP, Iv64, XX }, |
| { "movS", RMeSI, Iv64, XX }, |
| { "movS", RMeDI, Iv64, XX }, |
| /* c0 */ |
| { GRP2b }, |
| { GRP2S }, |
| { "retT", Iw, XX, XX }, |
| { "retT", XX, XX, XX }, |
| { "les{S|}", Gv, Mp, XX }, |
| { "ldsS", Gv, Mp, XX }, |
| { "movA", Eb, Ib, XX }, |
| { "movQ", Ev, Iv, XX }, |
| /* c8 */ |
| { "enterT", Iw, Ib, XX }, |
| { "leaveT", XX, XX, XX }, |
| { "lretP", Iw, XX, XX }, |
| { "lretP", XX, XX, XX }, |
| { "int3", XX, XX, XX }, |
| { "int", Ib, XX, XX }, |
| { "into{|}", XX, XX, XX }, |
| { "iretP", XX, XX, XX }, |
| /* d0 */ |
| { GRP2b_one }, |
| { GRP2S_one }, |
| { GRP2b_cl }, |
| { GRP2S_cl }, |
| { "aam{|}", sIb, XX, XX }, |
| { "aad{|}", sIb, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "xlat", DSBX, XX, XX }, |
| /* d8 */ |
| { FLOAT }, |
| { FLOAT }, |
| { FLOAT }, |
| { FLOAT }, |
| { FLOAT }, |
| { FLOAT }, |
| { FLOAT }, |
| { FLOAT }, |
| /* e0 */ |
| { "loopneFH", Jb, XX, loop_jcxz_flag }, |
| { "loopeFH", Jb, XX, loop_jcxz_flag }, |
| { "loopFH", Jb, XX, loop_jcxz_flag }, |
| { "jEcxzH", Jb, XX, loop_jcxz_flag }, |
| { "inB", AL, Ib, XX }, |
| { "inS", eAX, Ib, XX }, |
| { "outB", Ib, AL, XX }, |
| { "outS", Ib, eAX, XX }, |
| /* e8 */ |
| { "callT", Jv, XX, XX }, |
| { "jmpT", Jv, XX, XX }, |
| { "ljmp{T|}", Ap, XX, XX }, |
| { "jmp", Jb, XX, XX }, |
| { "inB", AL, indirDX, XX }, |
| { "inS", eAX, indirDX, XX }, |
| { "outB", indirDX, AL, XX }, |
| { "outS", indirDX, eAX, XX }, |
| /* f0 */ |
| { "(bad)", XX, XX, XX }, /* lock prefix */ |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, /* repne */ |
| { "(bad)", XX, XX, XX }, /* repz */ |
| { "hlt", XX, XX, XX }, |
| { "cmc", XX, XX, XX }, |
| { GRP3b }, |
| { GRP3S }, |
| /* f8 */ |
| { "clc", XX, XX, XX }, |
| { "stc", XX, XX, XX }, |
| { "cli", XX, XX, XX }, |
| { "sti", XX, XX, XX }, |
| { "cld", XX, XX, XX }, |
| { "std", XX, XX, XX }, |
| { GRP4 }, |
| { GRP5 }, |
| }; |
| |
| static const struct dis386 dis386_twobyte[] = { |
| /* 00 */ |
| { GRP6 }, |
| { GRP7 }, |
| { "larS", Gv, Ew, XX }, |
| { "lslS", Gv, Ew, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "syscall", XX, XX, XX }, |
| { "clts", XX, XX, XX }, |
| { "sysretP", XX, XX, XX }, |
| /* 08 */ |
| { "invd", XX, XX, XX }, |
| { "wbinvd", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "ud2a", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { GRPAMD }, |
| { "femms", XX, XX, XX }, |
| { "", MX, EM, OPSUF }, /* See OP_3DNowSuffix. */ |
| /* 10 */ |
| { PREGRP8 }, |
| { PREGRP9 }, |
| { "movlpX", XM, EX, SIMD_Fixup, 'h' }, /* really only 2 operands */ |
| { "movlpX", EX, XM, SIMD_Fixup, 'h' }, |
| { "unpcklpX", XM, EX, XX }, |
| { "unpckhpX", XM, EX, XX }, |
| { "movhpX", XM, EX, SIMD_Fixup, 'l' }, |
| { "movhpX", EX, XM, SIMD_Fixup, 'l' }, |
| /* 18 */ |
| { GRP14 }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| /* 20 */ |
| { "movL", Rm, Cm, XX }, |
| { "movL", Rm, Dm, XX }, |
| { "movL", Cm, Rm, XX }, |
| { "movL", Dm, Rm, XX }, |
| { "movL", Rd, Td, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "movL", Td, Rd, XX }, |
| { "(bad)", XX, XX, XX }, |
| /* 28 */ |
| { "movapX", XM, EX, XX }, |
| { "movapX", EX, XM, XX }, |
| { PREGRP2 }, |
| { "movntpX", Ev, XM, XX }, |
| { PREGRP4 }, |
| { PREGRP3 }, |
| { "ucomisX", XM,EX, XX }, |
| { "comisX", XM,EX, XX }, |
| /* 30 */ |
| { "wrmsr", XX, XX, XX }, |
| { "rdtsc", XX, XX, XX }, |
| { "rdmsr", XX, XX, XX }, |
| { "rdpmc", XX, XX, XX }, |
| { "sysenter", XX, XX, XX }, |
| { "sysexit", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| /* 38 */ |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| /* 40 */ |
| { "cmovo", Gv, Ev, XX }, |
| { "cmovno", Gv, Ev, XX }, |
| { "cmovb", Gv, Ev, XX }, |
| { "cmovae", Gv, Ev, XX }, |
| { "cmove", Gv, Ev, XX }, |
| { "cmovne", Gv, Ev, XX }, |
| { "cmovbe", Gv, Ev, XX }, |
| { "cmova", Gv, Ev, XX }, |
| /* 48 */ |
| { "cmovs", Gv, Ev, XX }, |
| { "cmovns", Gv, Ev, XX }, |
| { "cmovp", Gv, Ev, XX }, |
| { "cmovnp", Gv, Ev, XX }, |
| { "cmovl", Gv, Ev, XX }, |
| { "cmovge", Gv, Ev, XX }, |
| { "cmovle", Gv, Ev, XX }, |
| { "cmovg", Gv, Ev, XX }, |
| /* 50 */ |
| { "movmskpX", Gd, XS, XX }, |
| { PREGRP13 }, |
| { PREGRP12 }, |
| { PREGRP11 }, |
| { "andpX", XM, EX, XX }, |
| { "andnpX", XM, EX, XX }, |
| { "orpX", XM, EX, XX }, |
| { "xorpX", XM, EX, XX }, |
| /* 58 */ |
| { PREGRP0 }, |
| { PREGRP10 }, |
| { PREGRP17 }, |
| { PREGRP16 }, |
| { PREGRP14 }, |
| { PREGRP7 }, |
| { PREGRP5 }, |
| { PREGRP6 }, |
| /* 60 */ |
| { "punpcklbw", MX, EM, XX }, |
| { "punpcklwd", MX, EM, XX }, |
| { "punpckldq", MX, EM, XX }, |
| { "packsswb", MX, EM, XX }, |
| { "pcmpgtb", MX, EM, XX }, |
| { "pcmpgtw", MX, EM, XX }, |
| { "pcmpgtd", MX, EM, XX }, |
| { "packuswb", MX, EM, XX }, |
| /* 68 */ |
| { "punpckhbw", MX, EM, XX }, |
| { "punpckhwd", MX, EM, XX }, |
| { "punpckhdq", MX, EM, XX }, |
| { "packssdw", MX, EM, XX }, |
| { PREGRP26 }, |
| { PREGRP24 }, |
| { "movd", MX, Ed, XX }, |
| { PREGRP19 }, |
| /* 70 */ |
| { PREGRP22 }, |
| { GRP10 }, |
| { GRP11 }, |
| { GRP12 }, |
| { "pcmpeqb", MX, EM, XX }, |
| { "pcmpeqw", MX, EM, XX }, |
| { "pcmpeqd", MX, EM, XX }, |
| { "emms", XX, XX, XX }, |
| /* 78 */ |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { PREGRP23 }, |
| { PREGRP20 }, |
| /* 80 */ |
| { "joH", Jv, XX, cond_jump_flag }, |
| { "jnoH", Jv, XX, cond_jump_flag }, |
| { "jbH", Jv, XX, cond_jump_flag }, |
| { "jaeH", Jv, XX, cond_jump_flag }, |
| { "jeH", Jv, XX, cond_jump_flag }, |
| { "jneH", Jv, XX, cond_jump_flag }, |
| { "jbeH", Jv, XX, cond_jump_flag }, |
| { "jaH", Jv, XX, cond_jump_flag }, |
| /* 88 */ |
| { "jsH", Jv, XX, cond_jump_flag }, |
| { "jnsH", Jv, XX, cond_jump_flag }, |
| { "jpH", Jv, XX, cond_jump_flag }, |
| { "jnpH", Jv, XX, cond_jump_flag }, |
| { "jlH", Jv, XX, cond_jump_flag }, |
| { "jgeH", Jv, XX, cond_jump_flag }, |
| { "jleH", Jv, XX, cond_jump_flag }, |
| { "jgH", Jv, XX, cond_jump_flag }, |
| /* 90 */ |
| { "seto", Eb, XX, XX }, |
| { "setno", Eb, XX, XX }, |
| { "setb", Eb, XX, XX }, |
| { "setae", Eb, XX, XX }, |
| { "sete", Eb, XX, XX }, |
| { "setne", Eb, XX, XX }, |
| { "setbe", Eb, XX, XX }, |
| { "seta", Eb, XX, XX }, |
| /* 98 */ |
| { "sets", Eb, XX, XX }, |
| { "setns", Eb, XX, XX }, |
| { "setp", Eb, XX, XX }, |
| { "setnp", Eb, XX, XX }, |
| { "setl", Eb, XX, XX }, |
| { "setge", Eb, XX, XX }, |
| { "setle", Eb, XX, XX }, |
| { "setg", Eb, XX, XX }, |
| /* a0 */ |
| { "pushT", fs, XX, XX }, |
| { "popT", fs, XX, XX }, |
| { "cpuid", XX, XX, XX }, |
| { "btS", Ev, Gv, XX }, |
| { "shldS", Ev, Gv, Ib }, |
| { "shldS", Ev, Gv, CL }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| /* a8 */ |
| { "pushT", gs, XX, XX }, |
| { "popT", gs, XX, XX }, |
| { "rsm", XX, XX, XX }, |
| { "btsS", Ev, Gv, XX }, |
| { "shrdS", Ev, Gv, Ib }, |
| { "shrdS", Ev, Gv, CL }, |
| { GRP13 }, |
| { "imulS", Gv, Ev, XX }, |
| /* b0 */ |
| { "cmpxchgB", Eb, Gb, XX }, |
| { "cmpxchgS", Ev, Gv, XX }, |
| { "lssS", Gv, Mp, XX }, |
| { "btrS", Ev, Gv, XX }, |
| { "lfsS", Gv, Mp, XX }, |
| { "lgsS", Gv, Mp, XX }, |
| { "movz{bR|x|bR|x}", Gv, Eb, XX }, |
| { "movz{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movzww ! */ |
| /* b8 */ |
| { "(bad)", XX, XX, XX }, |
| { "ud2b", XX, XX, XX }, |
| { GRP8 }, |
| { "btcS", Ev, Gv, XX }, |
| { "bsfS", Gv, Ev, XX }, |
| { "bsrS", Gv, Ev, XX }, |
| { "movs{bR|x|bR|x}", Gv, Eb, XX }, |
| { "movs{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movsww ! */ |
| /* c0 */ |
| { "xaddB", Eb, Gb, XX }, |
| { "xaddS", Ev, Gv, XX }, |
| { PREGRP1 }, |
| { "movntiS", Ev, Gv, XX }, |
| { "pinsrw", MX, Ed, Ib }, |
| { "pextrw", Gd, MS, Ib }, |
| { "shufpX", XM, EX, Ib }, |
| { GRP9 }, |
| /* c8 */ |
| { "bswap", RMeAX, XX, XX }, |
| { "bswap", RMeCX, XX, XX }, |
| { "bswap", RMeDX, XX, XX }, |
| { "bswap", RMeBX, XX, XX }, |
| { "bswap", RMeSP, XX, XX }, |
| { "bswap", RMeBP, XX, XX }, |
| { "bswap", RMeSI, XX, XX }, |
| { "bswap", RMeDI, XX, XX }, |
| /* d0 */ |
| { "(bad)", XX, XX, XX }, |
| { "psrlw", MX, EM, XX }, |
| { "psrld", MX, EM, XX }, |
| { "psrlq", MX, EM, XX }, |
| { "paddq", MX, EM, XX }, |
| { "pmullw", MX, EM, XX }, |
| { PREGRP21 }, |
| { "pmovmskb", Gd, MS, XX }, |
| /* d8 */ |
| { "psubusb", MX, EM, XX }, |
| { "psubusw", MX, EM, XX }, |
| { "pminub", MX, EM, XX }, |
| { "pand", MX, EM, XX }, |
| { "paddusb", MX, EM, XX }, |
| { "paddusw", MX, EM, XX }, |
| { "pmaxub", MX, EM, XX }, |
| { "pandn", MX, EM, XX }, |
| /* e0 */ |
| { "pavgb", MX, EM, XX }, |
| { "psraw", MX, EM, XX }, |
| { "psrad", MX, EM, XX }, |
| { "pavgw", MX, EM, XX }, |
| { "pmulhuw", MX, EM, XX }, |
| { "pmulhw", MX, EM, XX }, |
| { PREGRP15 }, |
| { PREGRP25 }, |
| /* e8 */ |
| { "psubsb", MX, EM, XX }, |
| { "psubsw", MX, EM, XX }, |
| { "pminsw", MX, EM, XX }, |
| { "por", MX, EM, XX }, |
| { "paddsb", MX, EM, XX }, |
| { "paddsw", MX, EM, XX }, |
| { "pmaxsw", MX, EM, XX }, |
| { "pxor", MX, EM, XX }, |
| /* f0 */ |
| { "(bad)", XX, XX, XX }, |
| { "psllw", MX, EM, XX }, |
| { "pslld", MX, EM, XX }, |
| { "psllq", MX, EM, XX }, |
| { "pmuludq", MX, EM, XX }, |
| { "pmaddwd", MX, EM, XX }, |
| { "psadbw", MX, EM, XX }, |
| { PREGRP18 }, |
| /* f8 */ |
| { "psubb", MX, EM, XX }, |
| { "psubw", MX, EM, XX }, |
| { "psubd", MX, EM, XX }, |
| { "psubq", MX, EM, XX }, |
| { "paddb", MX, EM, XX }, |
| { "paddw", MX, EM, XX }, |
| { "paddd", MX, EM, XX }, |
| { "(bad)", XX, XX, XX } |
| }; |
| |
| static const unsigned char onebyte_has_modrm[256] = { |
| /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ |
| /* ------------------------------- */ |
| /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */ |
| /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */ |
| /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */ |
| /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */ |
| /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */ |
| /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */ |
| /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */ |
| /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */ |
| /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */ |
| /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */ |
| /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */ |
| /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */ |
| /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */ |
| /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */ |
| /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */ |
| /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 /* f0 */ |
| /* ------------------------------- */ |
| /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ |
| }; |
| |
| static const unsigned char twobyte_has_modrm[256] = { |
| /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ |
| /* ------------------------------- */ |
| /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */ |
| /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, /* 1f */ |
| /* 20 */ 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1, /* 2f */ |
| /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ |
| /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */ |
| /* 50 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 5f */ |
| /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6f */ |
| /* 70 */ 1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1, /* 7f */ |
| /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ |
| /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */ |
| /* a0 */ 0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1, /* af */ |
| /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */ |
| /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */ |
| /* d0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* df */ |
| /* e0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* ef */ |
| /* f0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 /* ff */ |
| /* ------------------------------- */ |
| /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ |
| }; |
| |
| static const unsigned char twobyte_uses_SSE_prefix[256] = { |
| /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ |
| /* ------------------------------- */ |
| /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ |
| /* 10 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ |
| /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0, /* 2f */ |
| /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ |
| /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ |
| /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */ |
| /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, /* 6f */ |
| /* 70 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, /* 7f */ |
| /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ |
| /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ |
| /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ |
| /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ |
| /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ |
| /* d0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */ |
| /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */ |
| /* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0 /* ff */ |
| /* ------------------------------- */ |
| /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ |
| }; |
| |
| static char obuf[100]; |
| static char *obufp; |
| static char scratchbuf[100]; |
| static unsigned char *start_codep; |
| static unsigned char *insn_codep; |
| static unsigned char *codep; |
| static disassemble_info *the_info; |
| static int mod; |
| static int rm; |
| static int reg; |
| static unsigned char need_modrm; |
| |
| /* If we are accessing mod/rm/reg without need_modrm set, then the |
| values are stale. Hitting this abort likely indicates that you |
| need to update onebyte_has_modrm or twobyte_has_modrm. */ |
| #define MODRM_CHECK if (!need_modrm) abort () |
| |
| static const char **names64; |
| static const char **names32; |
| static const char **names16; |
| static const char **names8; |
| static const char **names8rex; |
| static const char **names_seg; |
| static const char **index16; |
| |
| static const char *intel_names64[] = { |
| "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", |
| "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" |
| }; |
| static const char *intel_names32[] = { |
| "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", |
| "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" |
| }; |
| static const char *intel_names16[] = { |
| "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", |
| "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" |
| }; |
| static const char *intel_names8[] = { |
| "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", |
| }; |
| static const char *intel_names8rex[] = { |
| "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", |
| "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" |
| }; |
| static const char *intel_names_seg[] = { |
| "es", "cs", "ss", "ds", "fs", "gs", "?", "?", |
| }; |
| static const char *intel_index16[] = { |
| "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx" |
| }; |
| |
| static const char *att_names64[] = { |
| "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", |
| "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" |
| }; |
| static const char *att_names32[] = { |
| "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", |
| "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" |
| }; |
| static const char *att_names16[] = { |
| "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", |
| "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" |
| }; |
| static const char *att_names8[] = { |
| "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh", |
| }; |
| static const char *att_names8rex[] = { |
| "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil", |
| "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" |
| }; |
| static const char *att_names_seg[] = { |
| "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?", |
| }; |
| static const char *att_index16[] = { |
| "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx" |
| }; |
| |
| static const struct dis386 grps[][8] = { |
| /* GRP1b */ |
| { |
| { "addA", Eb, Ib, XX }, |
| { "orA", Eb, Ib, XX }, |
| { "adcA", Eb, Ib, XX }, |
| { "sbbA", Eb, Ib, XX }, |
| { "andA", Eb, Ib, XX }, |
| { "subA", Eb, Ib, XX }, |
| { "xorA", Eb, Ib, XX }, |
| { "cmpA", Eb, Ib, XX } |
| }, |
| /* GRP1S */ |
| { |
| { "addQ", Ev, Iv, XX }, |
| { "orQ", Ev, Iv, XX }, |
| { "adcQ", Ev, Iv, XX }, |
| { "sbbQ", Ev, Iv, XX }, |
| { "andQ", Ev, Iv, XX }, |
| { "subQ", Ev, Iv, XX }, |
| { "xorQ", Ev, Iv, XX }, |
| { "cmpQ", Ev, Iv, XX } |
| }, |
| /* GRP1Ss */ |
| { |
| { "addQ", Ev, sIb, XX }, |
| { "orQ", Ev, sIb, XX }, |
| { "adcQ", Ev, sIb, XX }, |
| { "sbbQ", Ev, sIb, XX }, |
| { "andQ", Ev, sIb, XX }, |
| { "subQ", Ev, sIb, XX }, |
| { "xorQ", Ev, sIb, XX }, |
| { "cmpQ", Ev, sIb, XX } |
| }, |
| /* GRP2b */ |
| { |
| { "rolA", Eb, Ib, XX }, |
| { "rorA", Eb, Ib, XX }, |
| { "rclA", Eb, Ib, XX }, |
| { "rcrA", Eb, Ib, XX }, |
| { "shlA", Eb, Ib, XX }, |
| { "shrA", Eb, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "sarA", Eb, Ib, XX }, |
| }, |
| /* GRP2S */ |
| { |
| { "rolQ", Ev, Ib, XX }, |
| { "rorQ", Ev, Ib, XX }, |
| { "rclQ", Ev, Ib, XX }, |
| { "rcrQ", Ev, Ib, XX }, |
| { "shlQ", Ev, Ib, XX }, |
| { "shrQ", Ev, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "sarQ", Ev, Ib, XX }, |
| }, |
| /* GRP2b_one */ |
| { |
| { "rolA", Eb, XX, XX }, |
| { "rorA", Eb, XX, XX }, |
| { "rclA", Eb, XX, XX }, |
| { "rcrA", Eb, XX, XX }, |
| { "shlA", Eb, XX, XX }, |
| { "shrA", Eb, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "sarA", Eb, XX, XX }, |
| }, |
| /* GRP2S_one */ |
| { |
| { "rolQ", Ev, XX, XX }, |
| { "rorQ", Ev, XX, XX }, |
| { "rclQ", Ev, XX, XX }, |
| { "rcrQ", Ev, XX, XX }, |
| { "shlQ", Ev, XX, XX }, |
| { "shrQ", Ev, XX, XX }, |
| { "(bad)", XX, XX, XX}, |
| { "sarQ", Ev, XX, XX }, |
| }, |
| /* GRP2b_cl */ |
| { |
| { "rolA", Eb, CL, XX }, |
| { "rorA", Eb, CL, XX }, |
| { "rclA", Eb, CL, XX }, |
| { "rcrA", Eb, CL, XX }, |
| { "shlA", Eb, CL, XX }, |
| { "shrA", Eb, CL, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "sarA", Eb, CL, XX }, |
| }, |
| /* GRP2S_cl */ |
| { |
| { "rolQ", Ev, CL, XX }, |
| { "rorQ", Ev, CL, XX }, |
| { "rclQ", Ev, CL, XX }, |
| { "rcrQ", Ev, CL, XX }, |
| { "shlQ", Ev, CL, XX }, |
| { "shrQ", Ev, CL, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "sarQ", Ev, CL, XX } |
| }, |
| /* GRP3b */ |
| { |
| { "testA", Eb, Ib, XX }, |
| { "(bad)", Eb, XX, XX }, |
| { "notA", Eb, XX, XX }, |
| { "negA", Eb, XX, XX }, |
| { "mulA", Eb, XX, XX }, /* Don't print the implicit %al register, */ |
| { "imulA", Eb, XX, XX }, /* to distinguish these opcodes from other */ |
| { "divA", Eb, XX, XX }, /* mul/imul opcodes. Do the same for div */ |
| { "idivA", Eb, XX, XX } /* and idiv for consistency. */ |
| }, |
| /* GRP3S */ |
| { |
| { "testQ", Ev, Iv, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "notQ", Ev, XX, XX }, |
| { "negQ", Ev, XX, XX }, |
| { "mulQ", Ev, XX, XX }, /* Don't print the implicit register. */ |
| { "imulQ", Ev, XX, XX }, |
| { "divQ", Ev, XX, XX }, |
| { "idivQ", Ev, XX, XX }, |
| }, |
| /* GRP4 */ |
| { |
| { "incA", Eb, XX, XX }, |
| { "decA", Eb, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| }, |
| /* GRP5 */ |
| { |
| { "incQ", Ev, XX, XX }, |
| { "decQ", Ev, XX, XX }, |
| { "callT", indirEv, XX, XX }, |
| { "lcallT", indirEv, XX, XX }, |
| { "jmpT", indirEv, XX, XX }, |
| { "ljmpT", indirEv, XX, XX }, |
| { "pushU", Ev, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| }, |
| /* GRP6 */ |
| { |
| { "sldtQ", Ev, XX, XX }, |
| { "strQ", Ev, XX, XX }, |
| { "lldt", Ew, XX, XX }, |
| { "ltr", Ew, XX, XX }, |
| { "verr", Ew, XX, XX }, |
| { "verw", Ew, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX } |
| }, |
| /* GRP7 */ |
| { |
| { "sgdtQ", M, XX, XX }, |
| { "sidtQ", M, XX, XX }, |
| { "lgdtQ", M, XX, XX }, |
| { "lidtQ", M, XX, XX }, |
| { "smswQ", Ev, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "lmsw", Ew, XX, XX }, |
| { "invlpg", Ew, XX, XX }, |
| }, |
| /* GRP8 */ |
| { |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "btQ", Ev, Ib, XX }, |
| { "btsQ", Ev, Ib, XX }, |
| { "btrQ", Ev, Ib, XX }, |
| { "btcQ", Ev, Ib, XX }, |
| }, |
| /* GRP9 */ |
| { |
| { "(bad)", XX, XX, XX }, |
| { "cmpxchg8b", Ev, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| }, |
| /* GRP10 */ |
| { |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "psrlw", MS, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "psraw", MS, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "psllw", MS, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| }, |
| /* GRP11 */ |
| { |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "psrld", MS, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "psrad", MS, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "pslld", MS, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| }, |
| /* GRP12 */ |
| { |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "psrlq", MS, Ib, XX }, |
| { "psrldq", MS, Ib, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "psllq", MS, Ib, XX }, |
| { "pslldq", MS, Ib, XX }, |
| }, |
| /* GRP13 */ |
| { |
| { "fxsave", Ev, XX, XX }, |
| { "fxrstor", Ev, XX, XX }, |
| { "ldmxcsr", Ev, XX, XX }, |
| { "stmxcsr", Ev, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "lfence", None, XX, XX }, |
| { "mfence", None, XX, XX }, |
| { "sfence", None, XX, XX }, |
| /* FIXME: the sfence with memory operand is clflush! */ |
| }, |
| /* GRP14 */ |
| { |
| { "prefetchnta", Ev, XX, XX }, |
| { "prefetcht0", Ev, XX, XX }, |
| { "prefetcht1", Ev, XX, XX }, |
| { "prefetcht2", Ev, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| }, |
| /* GRPAMD */ |
| { |
| { "prefetch", Eb, XX, XX }, |
| { "prefetchw", Eb, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| { "(bad)", XX, XX, XX }, |
| } |
| }; |
| |
| static const struct dis386 prefix_user_table[][4] = { |
| /* PREGRP0 */ |
| { |
| { "addps", XM, EX, XX }, |
| { "addss", XM, EX, XX }, |
| { "addpd", XM, EX, XX }, |
| { "addsd", XM, EX, XX }, |
| }, |
| /* PREGRP1 */ |
| { |
| { "", XM, EX, OPSIMD }, /* See OP_SIMD_SUFFIX. */ |
| { "", XM, EX, OPSIMD }, |
| { "", XM, EX, OPSIMD }, |
| { "", XM, EX, OPSIMD }, |
| }, |
| /* PREGRP2 */ |
| { |
| { "cvtpi2ps", XM, EM, XX }, |
| { "cvtsi2ssY", XM, Ev, XX }, |
| { "cvtpi2pd", XM, EM, XX }, |
| { "cvtsi2sdY", XM, Ev, XX }, |
| }, |
| /* PREGRP3 */ |
| { |
| { "cvtps2pi", MX, EX, XX }, |
| { "cvtss2siY", Gv, EX, XX }, |
| { "cvtpd2pi", MX, EX, XX }, |
| { "cvtsd2siY", Gv, EX, XX }, |
| }, |
| /* PREGRP4 */ |
| { |
| { "cvttps2pi", MX, EX, XX }, |
| { "cvttss2siY", Gv, EX, XX }, |
| { "cvttpd2pi", MX, EX, XX }, |
| { "cvttsd2siY", Gv, EX, XX }, |
| }, |
| /* PREGRP5 */ |
| { |
| { "divps", XM, EX, XX }, |
| { "divss", XM, EX, XX }, |
| { "divpd", XM, EX, XX }, |
| { "divsd", XM, EX, XX }, |
| }, |
| /* PREGRP6 */ |
| { |
| { "maxps", XM, EX, XX }, |
| { "maxss", XM, EX, XX }, |
| { "maxpd", XM, EX, XX }, |
| { "maxsd", XM, EX, XX }, |
| }, |
| /* PREGRP7 */ |
| { |
| { "minps", XM, EX, XX }, |
| { "minss", XM, EX, XX }, |
| { "minpd", XM, EX, XX }, |
| { "minsd", XM, EX, XX }, |
| }, |
| /* PREGRP8 */ |
| { |
| { "movups", XM, EX, XX }, |
| { "movss", XM, EX, XX }, |
| { "movupd", XM, EX, XX }, |
| { "movsd", XM, EX, XX }, |
| }, |
| /* PREGRP9 */ |
| { |
| { "movups", EX, XM, XX }, |
| { "movss", EX, XM, XX }, |
| { "movupd", EX, XM, XX }, |
| { "movsd", EX, XM, XX }, |
| }, |
| /* PREGRP10 */ |
| { |
| { "mulps", XM, EX, XX }, |
| { "mulss", XM, EX, XX }, |
| { "mulpd", XM, EX, XX }, |
| { "mulsd", XM, EX, XX }, |
| }, |
| /* PREGRP11 */ |
| { |
| { "rcpps", XM, EX, XX }, |
| { "rcpss", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| }, |
| /* PREGRP12 */ |
| { |
| { "rsqrtps", XM, EX, XX }, |
| { "rsqrtss", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| }, |
| /* PREGRP13 */ |
| { |
| { "sqrtps", XM, EX, XX }, |
| { "sqrtss", XM, EX, XX }, |
| { "sqrtpd", XM, EX, XX }, |
| { "sqrtsd", XM, EX, XX }, |
| }, |
| /* PREGRP14 */ |
| { |
| { "subps", XM, EX, XX }, |
| { "subss", XM, EX, XX }, |
| { "subpd", XM, EX, XX }, |
| { "subsd", XM, EX, XX }, |
| }, |
| /* PREGRP15 */ |
| { |
| { "(bad)", XM, EX, XX }, |
| { "cvtdq2pd", XM, EX, XX }, |
| { "cvttpd2dq", XM, EX, XX }, |
| { "cvtpd2dq", XM, EX, XX }, |
| }, |
| /* PREGRP16 */ |
| { |
| { "cvtdq2ps", XM, EX, XX }, |
| { "cvttps2dq",XM, EX, XX }, |
| { "cvtps2dq",XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| }, |
| /* PREGRP17 */ |
| { |
| { "cvtps2pd", XM, EX, XX }, |
| { "cvtss2sd", XM, EX, XX }, |
| { "cvtpd2ps", XM, EX, XX }, |
| { "cvtsd2ss", XM, EX, XX }, |
| }, |
| /* PREGRP18 */ |
| { |
| { "maskmovq", MX, MS, XX }, |
| { "(bad)", XM, EX, XX }, |
| { "maskmovdqu", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| }, |
| /* PREGRP19 */ |
| { |
| { "movq", MX, EM, XX }, |
| { "movdqu", XM, EX, XX }, |
| { "movdqa", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| }, |
| /* PREGRP20 */ |
| { |
| { "movq", EM, MX, XX }, |
| { "movdqu", EX, XM, XX }, |
| { "movdqa", EX, XM, XX }, |
| { "(bad)", EX, XM, XX }, |
| }, |
| /* PREGRP21 */ |
| { |
| { "(bad)", EX, XM, XX }, |
| { "movq2dq", XM, MS, XX }, |
| { "movq", EX, XM, XX }, |
| { "movdq2q", MX, XS, XX }, |
| }, |
| /* PREGRP22 */ |
| { |
| { "pshufw", MX, EM, Ib }, |
| { "pshufhw", XM, EX, Ib }, |
| { "pshufd", XM, EX, Ib }, |
| { "pshuflw", XM, EX, Ib }, |
| }, |
| /* PREGRP23 */ |
| { |
| { "movd", Ed, MX, XX }, |
| { "movq", XM, EX, XX }, |
| { "movd", Ed, XM, XX }, |
| { "(bad)", Ed, XM, XX }, |
| }, |
| /* PREGRP24 */ |
| { |
| { "(bad)", MX, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| { "punpckhqdq", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| }, |
| /* PREGRP25 */ |
| { |
| { "movntq", Ev, MX, XX }, |
| { "(bad)", Ev, XM, XX }, |
| { "movntdq", Ev, XM, XX }, |
| { "(bad)", Ev, XM, XX }, |
| }, |
| /* PREGRP26 */ |
| { |
| { "(bad)", MX, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| { "punpcklqdq", XM, EX, XX }, |
| { "(bad)", XM, EX, XX }, |
| }, |
| }; |
| |
| static const struct dis386 x86_64_table[][2] = { |
| { |
| { "arpl", Ew, Gw, XX }, |
| { "movs{||lq|xd}", Gv, Ed, XX }, |
| }, |
| }; |
| |
| #define INTERNAL_DISASSEMBLER_ERROR _("<internal disassembler error>") |
| |
| static void |
| ckprefix () |
| { |
| int newrex; |
| rex = 0; |
| prefixes = 0; |
| used_prefixes = 0; |
| rex_used = 0; |
| while (1) |
| { |
| FETCH_DATA (the_info, codep + 1); |
| newrex = 0; |
| switch (*codep) |
| { |
| /* REX prefixes family. */ |
| case 0x40: |
| case 0x41: |
| case 0x42: |
| case 0x43: |
| case 0x44: |
| case 0x45: |
| case 0x46: |
| case 0x47: |
| case 0x48: |
| case 0x49: |
| case 0x4a: |
| case 0x4b: |
| case 0x4c: |
| case 0x4d: |
| case 0x4e: |
| case 0x4f: |
| if (mode_64bit) |
| newrex = *codep; |
| else |
| return; |
| break; |
| case 0xf3: |
| prefixes |= PREFIX_REPZ; |
| break; |
| case 0xf2: |
| prefixes |= PREFIX_REPNZ; |
| break; |
| case 0xf0: |
| prefixes |= PREFIX_LOCK; |
| break; |
| case 0x2e: |
| prefixes |= PREFIX_CS; |
| break; |
| case 0x36: |
| prefixes |= PREFIX_SS; |
| break; |
| case 0x3e: |
| prefixes |= PREFIX_DS; |
| break; |
| case 0x26: |
| prefixes |= PREFIX_ES; |
| break; |
| case 0x64: |
| prefixes |= PREFIX_FS; |
| break; |
| case 0x65: |
| prefixes |= PREFIX_GS; |
| break; |
| case 0x66: |
| prefixes |= PREFIX_DATA; |
| break; |
| case 0x67: |
| prefixes |= PREFIX_ADDR; |
| break; |
| case FWAIT_OPCODE: |
| /* fwait is really an instruction. If there are prefixes |
| before the fwait, they belong to the fwait, *not* to the |
| following instruction. */ |
| if (prefixes) |
| { |
| prefixes |= PREFIX_FWAIT; |
| codep++; |
| return; |
| } |
| prefixes = PREFIX_FWAIT; |
| break; |
| default: |
| return; |
| } |
| /* Rex is ignored when followed by another prefix. */ |
| if (rex) |
| { |
| oappend (prefix_name (rex, 0)); |
| oappend (" "); |
| } |
| rex = newrex; |
| codep++; |
| } |
| } |
| |
| /* Return the name of the prefix byte PREF, or NULL if PREF is not a |
| prefix byte. */ |
| |
| static const char * |
| prefix_name (pref, sizeflag) |
| int pref; |
| int sizeflag; |
| { |
| switch (pref) |
| { |
| /* REX prefixes family. */ |
| case 0x40: |
| return "rex"; |
| case 0x41: |
| return "rexZ"; |
| case 0x42: |
| return "rexY"; |
| case 0x43: |
| return "rexYZ"; |
| case 0x44: |
| return "rexX"; |
| case 0x45: |
| return "rexXZ"; |
| case 0x46: |
| return "rexXY"; |
| case 0x47: |
| return "rexXYZ"; |
| case 0x48: |
| return "rex64"; |
| case 0x49: |
| return "rex64Z"; |
| case 0x4a: |
| return "rex64Y"; |
| case 0x4b: |
| return "rex64YZ"; |
| case 0x4c: |
| return "rex64X"; |
| case 0x4d: |
| return "rex64XZ"; |
| case 0x4e: |
| return "rex64XY"; |
| case 0x4f: |
| return "rex64XYZ"; |
| case 0xf3: |
| return "repz"; |
| case 0xf2: |
| return "repnz"; |
| case 0xf0: |
| return "lock"; |
| case 0x2e: |
| return "cs"; |
| case 0x36: |
| return "ss"; |
| case 0x3e: |
| return "ds"; |
| case 0x26: |
| return "es"; |
| case 0x64: |
| return "fs"; |
| case 0x65: |
| return "gs"; |
| case 0x66: |
| return (sizeflag & DFLAG) ? "data16" : "data32"; |
| case 0x67: |
| if (mode_64bit) |
| return (sizeflag & AFLAG) ? "addr32" : "addr64"; |
| else |
| return ((sizeflag & AFLAG) && !mode_64bit) ? "addr16" : "addr32"; |
| case FWAIT_OPCODE: |
| return "fwait"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static char op1out[100], op2out[100], op3out[100]; |
| static int op_ad, op_index[3]; |
| static bfd_vma op_address[3]; |
| static bfd_vma op_riprel[3]; |
| static bfd_vma start_pc; |
| |
| /* |
| * On the 386's of 1988, the maximum length of an instruction is 15 bytes. |
| * (see topic "Redundant prefixes" in the "Differences from 8086" |
| * section of the "Virtual 8086 Mode" chapter.) |
| * 'pc' should be the address of this instruction, it will |
| * be used to print the target address if this is a relative jump or call |
| * The function returns the length of this instruction in bytes. |
| */ |
| |
| static int8_t intel_syntax; |
| static char open_char; |
| static char close_char; |
| static char separator_char; |
| static char scale_char; |
| |
| int |
| print_insn_i386 (pc, info) |
| bfd_vma pc; |
| disassemble_info *info; |
| { |
| intel_syntax = -1; |
| |
| return print_insn (pc, info); |
| } |
| |
| static int |
| print_insn (pc, info) |
| bfd_vma pc; |
| disassemble_info *info; |
| { |
| const struct dis386 *dp; |
| int i; |
| int two_source_ops; |
| char *first, *second, *third; |
| int needcomma; |
| unsigned char uses_SSE_prefix; |
| int sizeflag; |
| const char *p; |
| struct dis_private priv; |
| |
| mode_64bit = (info->mach == bfd_mach_x86_64_intel_syntax |
| || info->mach == bfd_mach_x86_64); |
| |
| if (intel_syntax == -1) |
| intel_syntax = (info->mach == bfd_mach_i386_i386_intel_syntax |
| || info->mach == bfd_mach_x86_64_intel_syntax); |
| |
| if (info->mach == bfd_mach_i386_i386 |
| || info->mach == bfd_mach_x86_64 |
| || info->mach == bfd_mach_i386_i386_intel_syntax |
| || info->mach == bfd_mach_x86_64_intel_syntax) |
| priv.orig_sizeflag = AFLAG | DFLAG; |
| else if (info->mach == bfd_mach_i386_i8086) |
| priv.orig_sizeflag = 0; |
| else |
| abort (); |
| |
| for (p = info->disassembler_options; p != NULL; ) |
| { |
| if (strncmp (p, "x86-64", 6) == 0) |
| { |
| mode_64bit = 1; |
| priv.orig_sizeflag = AFLAG | DFLAG; |
| } |
| else if (strncmp (p, "i386", 4) == 0) |
| { |
| mode_64bit = 0; |
| priv.orig_sizeflag = AFLAG | DFLAG; |
| } |
| else if (strncmp (p, "i8086", 5) == 0) |
| { |
| mode_64bit = 0; |
| priv.orig_sizeflag = 0; |
| } |
| else if (strncmp (p, "intel", 5) == 0) |
| { |
| intel_syntax = 1; |
| } |
| else if (strncmp (p, "att", 3) == 0) |
| { |
| intel_syntax = 0; |
| } |
| else if (strncmp (p, "addr", 4) == 0) |
| { |
| if (p[4] == '1' && p[5] == '6') |
| priv.orig_sizeflag &= ~AFLAG; |
| else if (p[4] == '3' && p[5] == '2') |
| priv.orig_sizeflag |= AFLAG; |
| } |
| else if (strncmp (p, "data", 4) == 0) |
| { |
| if (p[4] == '1' && p[5] == '6') |
| priv.orig_sizeflag &= ~DFLAG; |
| else if (p[4] == '3' && p[5] == '2') |
| priv.orig_sizeflag |= DFLAG; |
| } |
| else if (strncmp (p, "suffix", 6) == 0) |
| priv.orig_sizeflag |= SUFFIX_ALWAYS; |
| |
| p = strchr (p, ','); |
| if (p != NULL) |
| p++; |
| } |
| |
| if (intel_syntax) |
| { |
| names64 = intel_names64; |
| names32 = intel_names32; |
| names16 = intel_names16; |
| names8 = intel_names8; |
| names8rex = intel_names8rex; |
| names_seg = intel_names_seg; |
| index16 = intel_index16; |
| open_char = '['; |
| close_char = ']'; |
| separator_char = '+'; |
| scale_char = '*'; |
| } |
| else |
| { |
| names64 = att_names64; |
| names32 = att_names32; |
| names16 = att_names16; |
| names8 = att_names8; |
| names8rex = att_names8rex; |
| names_seg = att_names_seg; |
| index16 = att_index16; |
| open_char = '('; |
| close_char = ')'; |
| separator_char = ','; |
| scale_char = ','; |
| } |
| |
| /* The output looks better if we put 7 bytes on a line, since that |
| puts most long word instructions on a single line. */ |
| info->bytes_per_line = 7; |
| |
| info->private_data = (PTR) &priv; |
| priv.max_fetched = priv.the_buffer; |
| priv.insn_start = pc; |
| |
| obuf[0] = 0; |
| op1out[0] = 0; |
| op2out[0] = 0; |
| op3out[0] = 0; |
| |
| op_index[0] = op_index[1] = op_index[2] = -1; |
| |
| the_info = info; |
| start_pc = pc; |
| start_codep = priv.the_buffer; |
| codep = priv.the_buffer; |
| |
| if (setjmp (priv.bailout) != 0) |
| { |
| const char *name; |
| |
| /* Getting here means we tried for data but didn't get it. That |
| means we have an incomplete instruction of some sort. Just |
| print the first byte as a prefix or a .byte pseudo-op. */ |
| if (codep > priv.the_buffer) |
| { |
| name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); |
| if (name != NULL) |
| (*info->fprintf_func) (info->stream, "%s", name); |
| else |
| { |
| /* Just print the first byte as a .byte instruction. */ |
| (*info->fprintf_func) (info->stream, ".byte 0x%x", |
| (unsigned int) priv.the_buffer[0]); |
| } |
| |
| return 1; |
| } |
| |
| return -1; |
| } |
| |
| obufp = obuf; |
| ckprefix (); |
| |
| insn_codep = codep; |
| sizeflag = priv.orig_sizeflag; |
| |
| FETCH_DATA (info, codep + 1); |
| two_source_ops = (*codep == 0x62) || (*codep == 0xc8); |
| |
| if ((prefixes & PREFIX_FWAIT) |
| && ((*codep < 0xd8) || (*codep > 0xdf))) |
| { |
| const char *name; |
| |
| /* fwait not followed by floating point instruction. Print the |
| first prefix, which is probably fwait itself. */ |
| name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); |
| if (name == NULL) |
| name = INTERNAL_DISASSEMBLER_ERROR; |
| (*info->fprintf_func) (info->stream, "%s", name); |
| return 1; |
| } |
| |
| if (*codep == 0x0f) |
| { |
| FETCH_DATA (info, codep + 2); |
| dp = &dis386_twobyte[*++codep]; |
| need_modrm = twobyte_has_modrm[*codep]; |
| uses_SSE_prefix = twobyte_uses_SSE_prefix[*codep]; |
| } |
| else |
| { |
| dp = &dis386[*codep]; |
| need_modrm = onebyte_has_modrm[*codep]; |
| uses_SSE_prefix = 0; |
| } |
| codep++; |
| |
| if (!uses_SSE_prefix && (prefixes & PREFIX_REPZ)) |
| { |
| oappend ("repz "); |
| used_prefixes |= PREFIX_REPZ; |
| } |
| if (!uses_SSE_prefix && (prefixes & PREFIX_REPNZ)) |
| { |
| oappend ("repnz "); |
| used_prefixes |= PREFIX_REPNZ; |
| } |
| if (prefixes & PREFIX_LOCK) |
| { |
| oappend ("lock "); |
| used_prefixes |= PREFIX_LOCK; |
| } |
| |
| if (prefixes & PREFIX_ADDR) |
| { |
| sizeflag ^= AFLAG; |
| if (dp->bytemode3 != loop_jcxz_mode || intel_syntax) |
| { |
| if ((sizeflag & AFLAG) || mode_64bit) |
| oappend ("addr32 "); |
| else |
| oappend ("addr16 "); |
| used_prefixes |= PREFIX_ADDR; |
| } |
| } |
| |
| if (!uses_SSE_prefix && (prefixes & PREFIX_DATA)) |
| { |
| sizeflag ^= DFLAG; |
| if (dp->bytemode3 == cond_jump_mode |
| && dp->bytemode1 == v_mode |
| && !intel_syntax) |
| { |
| if (sizeflag & DFLAG) |
| oappend ("data32 "); |
| else |
| oappend ("data16 "); |
| used_prefixes |= PREFIX_DATA; |
| } |
| } |
| |
| if (need_modrm) |
| { |
| FETCH_DATA (info, codep + 1); |
| mod = (*codep >> 6) & 3; |
| reg = (*codep >> 3) & 7; |
| rm = *codep & 7; |
| } |
| |
| if (dp->name == NULL && dp->bytemode1 == FLOATCODE) |
| { |
| dofloat (sizeflag); |
| } |
| else |
| { |
| int index; |
| if (dp->name == NULL) |
| { |
| switch (dp->bytemode1) |
| { |
| case USE_GROUPS: |
| dp = &grps[dp->bytemode2][reg]; |
| break; |
| |
| case USE_PREFIX_USER_TABLE: |
| index = 0; |
| used_prefixes |= (prefixes & PREFIX_REPZ); |
| if (prefixes & PREFIX_REPZ) |
| index = 1; |
| else |
| { |
| used_prefixes |= (prefixes & PREFIX_DATA); |
| if (prefixes & PREFIX_DATA) |
| index = 2; |
| else |
| { |
| used_prefixes |= (prefixes & PREFIX_REPNZ); |
| if (prefixes & PREFIX_REPNZ) |
| index = 3; |
| } |
| } |
| dp = &prefix_user_table[dp->bytemode2][index]; |
| break; |
| |
| case X86_64_SPECIAL: |
| dp = &x86_64_table[dp->bytemode2][mode_64bit]; |
| break; |
| |
| default: |
| oappend (INTERNAL_DISASSEMBLER_ERROR); |
| break; |
| } |
| } |
| |
| if (putop (dp->name, sizeflag) == 0) |
| { |
| obufp = op1out; |
| op_ad = 2; |
| if (dp->op1) |
| (*dp->op1) (dp->bytemode1, sizeflag); |
| |
| obufp = op2out; |
| op_ad = 1; |
| if (dp->op2) |
| (*dp->op2) (dp->bytemode2, sizeflag); |
| |
| obufp = op3out; |
| op_ad = 0; |
| if (dp->op3) |
| (*dp->op3) (dp->bytemode3, sizeflag); |
| } |
| } |
| |
| /* See if any prefixes were not used. If so, print the first one |
| separately. If we don't do this, we'll wind up printing an |
| instruction stream which does not precisely correspond to the |
| bytes we are disassembling. */ |
| if ((prefixes & ~used_prefixes) != 0) |
| { |
| const char *name; |
| |
| name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); |
| if (name == NULL) |
| name = INTERNAL_DISASSEMBLER_ERROR; |
| (*info->fprintf_func) (info->stream, "%s", name); |
| return 1; |
| } |
| if (rex & ~rex_used) |
| { |
| const char *name; |
| name = prefix_name (rex | 0x40, priv.orig_sizeflag); |
| if (name == NULL) |
| name = INTERNAL_DISASSEMBLER_ERROR; |
| (*info->fprintf_func) (info->stream, "%s ", name); |
| } |
| |
| obufp = obuf + strlen (obuf); |
| for (i = strlen (obuf); i < 6; i++) |
| oappend (" "); |
| oappend (" "); |
| (*info->fprintf_func) (info->stream, "%s", obuf); |
| |
| /* The enter and bound instructions are printed with operands in the same |
| order as the intel book; everything else is printed in reverse order. */ |
| if (intel_syntax || two_source_ops) |
| { |
| first = op1out; |
| second = op2out; |
| third = op3out; |
| op_ad = op_index[0]; |
| op_index[0] = op_index[2]; |
| op_index[2] = op_ad; |
| } |
| else |
| { |
| first = op3out; |
| second = op2out; |
| third = op1out; |
| } |
| needcomma = 0; |
| if (*first) |
| { |
| if (op_index[0] != -1 && !op_riprel[0]) |
| (*info->print_address_func) ((bfd_vma) op_address[op_index[0]], info); |
| else |
| (*info->fprintf_func) (info->stream, "%s", first); |
| needcomma = 1; |
| } |
| if (*second) |
| { |
| if (needcomma) |
| (*info->fprintf_func) (info->stream, ","); |
| if (op_index[1] != -1 && !op_riprel[1]) |
| (*info->print_address_func) ((bfd_vma) op_address[op_index[1]], info); |
| else |
| (*info->fprintf_func) (info->stream, "%s", second); |
| needcomma = 1; |
| } |
| if (*third) |
| { |
| if (needcomma) |
| (*info->fprintf_func) (info->stream, ","); |
| if (op_index[2] != -1 && !op_riprel[2]) |
| (*info->print_address_func) ((bfd_vma) op_address[op_index[2]], info); |
| else |
| (*info->fprintf_func) (info->stream, "%s", third); |
| } |
| for (i = 0; i < 3; i++) |
| if (op_index[i] != -1 && op_riprel[i]) |
| { |
| (*info->fprintf_func) (info->stream, " # "); |
| (*info->print_address_func) ((bfd_vma) (start_pc + codep - start_codep |
| + op_address[op_index[i]]), info); |
| } |
| return codep - priv.the_buffer; |
| } |
| |
| static const char *float_mem[] = { |
| /* d8 */ |
| "fadd{s||s|}", |
| "fmul{s||s|}", |
| "fcom{s||s|}", |
| "fcomp{s||s|}", |
| "fsub{s||s|}", |
| "fsubr{s||s|}", |
| "fdiv{s||s|}", |
| "fdivr{s||s|}", |
| /* d9 */ |
| "fld{s||s|}", |
| "(bad)", |
| "fst{s||s|}", |
| "fstp{s||s|}", |
| "fldenv", |
| "fldcw", |
| "fNstenv", |
| "fNstcw", |
| /* da */ |
| "fiadd{l||l|}", |
| "fimul{l||l|}", |
| "ficom{l||l|}", |
| "ficomp{l||l|}", |
| "fisub{l||l|}", |
| "fisubr{l||l|}", |
| "fidiv{l||l|}", |
| "fidivr{l||l|}", |
| /* db */ |
| "fild{l||l|}", |
| "(bad)", |
| "fist{l||l|}", |
| "fistp{l||l|}", |
| "(bad)", |
| "fld{t||t|}", |
| "(bad)", |
| "fstp{t||t|}", |
| /* dc */ |
| "fadd{l||l|}", |
| "fmul{l||l|}", |
| "fcom{l||l|}", |
| "fcomp{l||l|}", |
| "fsub{l||l|}", |
| "fsubr{l||l|}", |
| "fdiv{l||l|}", |
| "fdivr{l||l|}", |
| /* dd */ |
| "fld{l||l|}", |
| "(bad)", |
| "fst{l||l|}", |
| "fstp{l||l|}", |
| "frstor", |
| "(bad)", |
| "fNsave", |
| "fNstsw", |
| /* de */ |
| "fiadd", |
| "fimul", |
| "ficom", |
| "ficomp", |
| "fisub", |
| "fisubr", |
|