/* Print i386 instructions for GDB, the GNU debugger.
Copyright (C) 1988, 1989 Free Software Foundation, Inc.
This file is part of GDB.
GDB 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 1, or (at your option)
GDB 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 GDB; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
* 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu)
* 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.
#define indirEb OP_indirE, b_mode
#define indirEv OP_indirE, v_mode
#define Mp OP_E, 0 /* ? */
#define sIb OP_sI, b_mode /* sign extened byte */
#define eAX OP_REG, eAX_reg
#define eBX OP_REG, eBX_reg
#define eCX OP_REG, eCX_reg
#define eDX OP_REG, eDX_reg
#define eSP OP_REG, eSP_reg
#define eBP OP_REG, eBP_reg
#define eSI OP_REG, eSI_reg
#define eDI OP_REG, eDI_reg
#define AL OP_REG, al_reg
#define CL OP_REG, cl_reg
#define DL OP_REG, dl_reg
#define BL OP_REG, bl_reg
#define AH OP_REG, ah_reg
#define CH OP_REG, ch_reg
#define DH OP_REG, dh_reg
#define BH OP_REG, bh_reg
#define AX OP_REG, ax_reg
#define DX OP_REG, dx_reg
#define indirDX OP_REG, indir_dx_reg
#define Sw OP_SEG, w_mode
#define Av OP_DIR, v_mode
#define Ob OP_OFF, b_mode
#define Ov OP_OFF, v_mode
#define Xb OP_DSSI, b_mode
#define Xv OP_DSSI, v_mode
#define Yb OP_ESDI, b_mode
#define Yv OP_ESDI, v_mode
#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
int OP_E(), OP_indirE(), OP_G(), OP_I(), OP_sI(), OP_REG();
int OP_DIR(), OP_OFF(), OP_DSSI(), OP_ESDI(), OP_ONE(), OP_C();
int OP_D(), OP_T(), OP_rm();
#define GRP1b NULL, NULL, 0
#define GRP1S NULL, NULL, 1
#define GRP1Ss NULL, NULL, 2
#define GRP2b NULL, NULL, 3
#define GRP2S NULL, NULL, 4
#define GRP2b_one NULL, NULL, 5
#define GRP2S_one NULL, NULL, 6
#define GRP2b_cl NULL, NULL, 7
#define GRP2S_cl NULL, NULL, 8
#define GRP3b NULL, NULL, 9
#define GRP3S NULL, NULL, 10
#define GRP4 NULL, NULL, 11
#define GRP5 NULL, NULL, 12
#define GRP6 NULL, NULL, 13
#define GRP7 NULL, NULL, 14
#define GRP8 NULL, NULL, 15
#define FLOAT NULL, NULL, FLOATCODE
struct dis386 dis386
[] = {
{ "(bad)" }, /* 0x0f extended opcode escape */
{ "(bad)" }, /* SEG ES prefix */
{ "(bad)" }, /* SEG CS prefix */
{ "(bad)" }, /* SEG SS prefix */
{ "(bad)" }, /* SEG DS prefix */
{ "(bad)" }, /* seg fs */
{ "(bad)" }, /* seg gs */
{ "(bad)" }, /* op size prefix */
{ "(bad)" }, /* adr size prefix */
{ "pushS", Iv
}, /* 386 book wrong */
{ "pushl", sIb
}, /* push of byte really pushes 4 bytes */
{ "outsb", indirDX
, Xb
},
{ "outsS", indirDX
, Xv
},
{ "outS", indirDX
, eAX
},
{ "(bad)" }, /* lock prefix */
struct dis386 dis386_twobyte
[] = {
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
/* these are all backward in appendix A of the intel book */
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "lssS", Gv
, Mp
}, /* 386 lists only Mp */
{ "lfsS", Gv
, Mp
}, /* 386 lists only Mp */
{ "lgsS", Gv
, Mp
}, /* 386 lists only Mp */
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
{ "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" },
static char scratchbuf
[100];
static unsigned char *start_codep
;
static unsigned char *codep
;
"%eax","%ecx","%edx","%ebx", "%esp","%ebp","%esi","%edi",
static char *names16
[] = {
"%ax","%cx","%dx","%bx","%sp","%bp","%si","%di",
static char *names8
[] = {
"%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh",
static char *names_seg
[] = {
"%es","%cs","%ss","%ds","%fs","%gs","%?","%?",
struct dis386 grps
[][8] = {
#define PREFIX_DATA 0x200
#define PREFIX_FWAIT 0x800
prefixes
|= PREFIX_REPNZ
;
prefixes
|= PREFIX_FWAIT
;
static char op1out
[100], op2out
[100], op3out
[100];
* disassemble the first instruction in 'inbuf'. You have to make
* sure all of the bytes of the instruction are filled in.
* 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
* 'outbuf' gets filled in with the disassembled instruction. it should
* be long enough to hold the longest disassembled instruction.
* 100 bytes is certainly enough, unless symbol printing is added later
* The function returns the length of this instruction in bytes.
i386dis (pc
, inbuf
, outbuf
)
char *first
, *second
, *third
;
if (prefixes
& PREFIX_REPZ
)
if (prefixes
& PREFIX_REPNZ
)
if (prefixes
& PREFIX_LOCK
)
if ((prefixes
& PREFIX_FWAIT
)
&& ((*codep
< 0xd8) || (*codep
> 0xdf)))
/* fwait not followed by floating point instruction */
/* these would be initialized to 0 if disassembling for 8086 or 286 */
if (prefixes
& PREFIX_DATA
)
if (prefixes
& PREFIX_ADR
)
dp
= &dis386_twobyte
[*++codep
];
if (dp
->name
== NULL
&& dp
->bytemode1
== FLOATCODE
)
dp
= &grps
[dp
->bytemode1
][reg
];
(*dp
->op1
)(dp
->bytemode1
);
(*dp
->op2
)(dp
->bytemode2
);
(*dp
->op3
)(dp
->bytemode3
);
obufp
= obuf
+ strlen (obuf
);
for (i
= strlen (obuf
); i
< 6; i
++)
/* enter instruction is printed with operands in the
* same order as the intel book; everything else
* is printed in reverse order
#define FGRPd9_2 NULL, NULL, 0
#define FGRPd9_4 NULL, NULL, 1
#define FGRPd9_5 NULL, NULL, 2
#define FGRPd9_6 NULL, NULL, 3
#define FGRPd9_7 NULL, NULL, 4
#define FGRPda_5 NULL, NULL, 5
#define FGRPdb_4 NULL, NULL, 6
#define FGRPde_3 NULL, NULL, 7
#define FGRPdf_4 NULL, NULL, 8
struct dis386 float_reg
[][8] = {
"fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
"fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)",
"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)",
"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp",
"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos",
"(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
"feni(287 only)","fdisi(287 only)","fNclex","fNinit",
"fNsetpm(287 only)","(bad)","(bad)","(bad)",
"(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
"fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
putop (float_mem
[(floatop
- 0xd8) * 8 + reg
]);
dp
= &float_reg
[floatop
- 0xd8][reg
];
putop (fgrps
[dp
->bytemode1
][rm
]);
/* instruction fnstsw is only one with strange arg */
if (floatop
== 0xdf && *codep
== 0xe0)
(*dp
->op1
)(dp
->bytemode1
);
(*dp
->op2
)(dp
->bytemode2
);
sprintf (scratchbuf
, "%%st(%d)", rm
);
/* capital letters in template are macros */
for (p
= template; *p
; p
++)
case 'C': /* For jcxz/jecxz */
if ((prefixes
& PREFIX_FWAIT
) == 0)
if (prefixes
& PREFIX_CS
)
if (prefixes
& PREFIX_DS
)
if (prefixes
& PREFIX_SS
)
if (prefixes
& PREFIX_ES
)
if (prefixes
& PREFIX_FS
)
if (prefixes
& PREFIX_GS
)
oappend ("<bad dis table>");
scale
= (*codep
>> 6) & 3;
index
= (*codep
>> 3) & 7;
/* implies havesib and havebase */
if (mod
!= 0 || rm
== 5 || (havesib
&& base
== 5))
sprintf (scratchbuf
, "%d", disp
);
sprintf (scratchbuf
, ",%s", names32
[index
]);
sprintf (scratchbuf
, ",%d", 1 << scale
);
oappend ("<internal disassembler error>");
x
|= (*codep
++ & 0xff) << 8;
x
|= (*codep
++ & 0xff) << 16;
x
|= (*codep
++ & 0xff) << 24;
x
|= (*codep
++ & 0xff) << 8;
case indir_dx_reg
: s
= "(%dx)"; break;
case ax_reg
: case cx_reg
: case dx_reg
: case bx_reg
:
case sp_reg
: case bp_reg
: case si_reg
: case di_reg
:
s
= names16
[code
- ax_reg
];
case es_reg
: case ss_reg
: case cs_reg
:
case ds_reg
: case fs_reg
: case gs_reg
:
s
= names_seg
[code
- es_reg
];
case al_reg
: case ah_reg
: case cl_reg
: case ch_reg
:
case dl_reg
: case dh_reg
: case bl_reg
: case bh_reg
:
s
= names8
[code
- al_reg
];
case eAX_reg
: case eCX_reg
: case eDX_reg
: case eBX_reg
:
case eSP_reg
: case eBP_reg
: case eSI_reg
: case eDI_reg
:
s
= names32
[code
- eAX_reg
];
s
= names16
[code
- eAX_reg
];
s
= "<internal disassembler error>";
oappend ("<internal disassembler error>");
sprintf (scratchbuf
, "$0x%x", op
);
oappend ("<internal disassembler error>");
sprintf (scratchbuf
, "$0x%x", op
);
/* for some reason, a data16 prefix on a jump instruction
means that the pc is masked to 16 bits after the
displacement is added! */
oappend ("<internal disassembelr error>");
sprintf (scratchbuf
, "0x%x",
(start_pc
+ codep
- start_codep
+ disp
) & mask
);
"%es","%cs","%ss","%ds","%fs","%gs","%?","%?",
sprintf (scratchbuf
, "0x%x,0x%x", seg
, offset
);
offset
= (short)get16 ();
sprintf (scratchbuf
, "0x%x",
start_pc
+ codep
- start_codep
+ offset
);
oappend ("<internal disassembler error>");
sprintf (scratchbuf
, "0x%x", off
);
oappend (aflag
? "%edi" : "%di");
oappend (aflag
? "%esi" : "%si");
codep
++; /* skip mod/rm */
sprintf (scratchbuf
, "%%cr%d", reg
);
codep
++; /* skip mod/rm */
sprintf (scratchbuf
, "%%db%d", reg
);
codep
++; /* skip mod/rm */
sprintf (scratchbuf
, "%%tr%d", reg
);
print_insn (memaddr
, stream
)
unsigned char buffer
[MAXLEN
];
/* should be expanded if disassembler prints symbol names */
read_memory (memaddr
, buffer
, MAXLEN
);
n
= i386dis ((int)memaddr
, buffer
, outbuf
);