2127f26023
for possible buffer overflow problems. Replaced most sprintf()'s with snprintf(); for others cases, added terminating NUL bytes where appropriate, replaced constants like "16" with sizeof(), etc. These changes include several bug fixes, but most changes are for maintainability's sake. Any instance where it wasn't "immediately obvious" that a buffer overflow could not occur was made safer. Reviewed by: Bruce Evans <bde@zeta.org.au> Reviewed by: Matthew Dillon <dillon@apollo.backplane.com> Reviewed by: Mike Spengler <mks@networkcs.com>
1096 lines
25 KiB
C
1096 lines
25 KiB
C
/* $NetBSD: db_disasm.c,v 1.4 1997/09/16 22:52:40 thorpej Exp $ */
|
|
|
|
/*
|
|
* Mach Operating System
|
|
* Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
|
|
* All Rights Reserved.
|
|
*
|
|
* Permission to use, copy, modify and distribute this software and its
|
|
* documentation is hereby granted, provided that both the copyright
|
|
* notice and this permission notice appear in all copies of the
|
|
* software, derivative works or modified versions, and any portions
|
|
* thereof, and that both notices appear in supporting documentation.
|
|
*
|
|
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
|
|
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
|
|
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
|
*
|
|
* Carnegie Mellon requests users of this software to return to
|
|
*
|
|
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
|
|
* School of Computer Science
|
|
* Carnegie Mellon University
|
|
* Pittsburgh PA 15213-3890
|
|
*
|
|
* any improvements or extensions that they make and grant Carnegie Mellon
|
|
* the rights to redistribute these changes.
|
|
*/
|
|
|
|
/*
|
|
* File: db_disasm.c
|
|
* Author: Alessandro Forin, Carnegie Mellon University
|
|
* Date: 11/91
|
|
*
|
|
* Disassembler for Alpha
|
|
*
|
|
* Modified for NetBSD/alpha by:
|
|
*
|
|
* Christopher G. Demetriou, Carnegie Mellon University
|
|
*
|
|
* Jason R. Thorpe, Numerical Aerospace Simulation Facility,
|
|
* NASA Ames Research Center
|
|
*
|
|
* This code was derived exclusively from information available in
|
|
* "Alpha Architecture Reference Manual", Richard L. Sites ed.
|
|
* Digital Press, Burlington, MA 01803
|
|
* ISBN 1-55558-098-X, Order no. EY-L520E-DP
|
|
*/
|
|
|
|
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
|
|
|
|
/* __KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.4 1997/09/16 22:52:40 thorpej Exp $"); */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <machine/db_machdep.h>
|
|
#include <alpha/alpha/db_instruction.h>
|
|
|
|
#include <machine/pal.h>
|
|
|
|
#include <ddb/ddb.h>
|
|
#include <ddb/db_access.h>
|
|
#include <ddb/db_sym.h>
|
|
#include <ddb/db_output.h>
|
|
|
|
/*
|
|
* This would belong in a header file, except noone else needs it
|
|
*/
|
|
typedef union {
|
|
/*
|
|
* All instructions are 32 bits wide, PAL included
|
|
*/
|
|
unsigned int bits;
|
|
|
|
/*
|
|
* Internal processor register access instrs
|
|
* specify the IPR index, doubly specify the
|
|
* (same) GP register as src/dest, and qualifiers
|
|
* for the IPR set involved (abox/ibox/tmp)
|
|
*/
|
|
struct {
|
|
unsigned index : 5,
|
|
regset : 3, /* a,i,p */
|
|
xxx : 8,
|
|
rs : 5,
|
|
rd : 5,
|
|
opcode : 6;
|
|
} mXpr_format;
|
|
|
|
/*
|
|
* Load/store instructions have a 12 bit displacement,
|
|
* and two register specifiers just as normal ld/st.
|
|
* Four bits have special meanings:
|
|
* phy: bypass the MMU (physical access)
|
|
* alt: use mode in ALT register for checks,
|
|
* or if PHY is also on locked/linked access
|
|
* rwc: read-with-write-check (probew)
|
|
* qw: quadword access
|
|
*/
|
|
struct {
|
|
signed int displacement : 12;
|
|
unsigned qw : 1,
|
|
qualif : 3,
|
|
rs : 5,
|
|
rd : 5,
|
|
opcode : 6;
|
|
} mem_format;
|
|
|
|
/*
|
|
* Return from exception or interrupt has
|
|
* a branch-like encoding, but only one
|
|
* instantiation is actually usable.
|
|
*/
|
|
struct {
|
|
unsigned xxx : 14,
|
|
zero : 1, /* branch prediction! */
|
|
one : 1,
|
|
rb : 5, /* r31 or stall */
|
|
ra : 5, /* r31 or stall */
|
|
opcode : 6;
|
|
} rei_format;
|
|
|
|
} pal_instruction;
|
|
|
|
|
|
/*
|
|
* Major opcodes
|
|
*/
|
|
static char *op_name[64] = {
|
|
/* 0 */ "call_pal", "op1", "op2", "op3", "op4", "op5", "op6", "op7",
|
|
/* 8 */ "lda", "ldah", "ldbu", "ldq_u","ldwu", "stw", "stb", "stq_u",
|
|
/*16 */ "arit", "logical","bit","mul", "op20", "vaxf", "ieeef","anyf",
|
|
/*24 */ "spec", "hw_mfpr","jump","hw_ld","intmisc","hw_mtpr","hw_rei","hw_st",
|
|
/*32 */ "ldf", "ldg", "lds", "ldt", "stf", "stg", "sts", "stt",
|
|
/*40 */ "ldl", "ldq", "ldl_l","ldq_l","stl", "stq", "stl_c","stq_c",
|
|
/*48 */ "br", "fbeq", "fblt", "fble", "bsr", "fbne", "fbge", "fbgt",
|
|
/*56 */ "blbc", "beq", "blt", "ble", "blbs", "bne", "bge", "bgt"
|
|
};
|
|
|
|
/*
|
|
* The function field is too big (7 or 11 bits), so the sub-tables
|
|
* are addressed in a somewhat complicated manner to save
|
|
* space. After all, alu operations is what RISCs are good at.
|
|
*/
|
|
|
|
struct tbl {
|
|
const char *name;
|
|
int code;
|
|
};
|
|
|
|
static const struct tbl pal_op_tbl[] = {
|
|
/* Common PAL function codes. */
|
|
{ "halt", PAL_halt },
|
|
{ "cflush", PAL_cflush },
|
|
{ "draina", PAL_draina },
|
|
{ "cserve", PAL_cserve, },
|
|
{ "swppal", PAL_swppal },
|
|
{ "ipir", PAL_ipir },
|
|
{ "bpt", PAL_bpt },
|
|
{ "bugchk", PAL_bugchk },
|
|
{ "imb", PAL_imb },
|
|
{ "rdunique", PAL_rdunique },
|
|
{ "wrunique", PAL_wrunique },
|
|
{ "gentrap", PAL_gentrap },
|
|
|
|
/* OSF/1 PAL function codes. */
|
|
{ "osf1_rdmces", PAL_OSF1_rdmces },
|
|
{ "osf1_wrmces", PAL_OSF1_wrmces },
|
|
{ "osf1_wrfen", PAL_OSF1_wrfen },
|
|
{ "osf1_wrvptptr", PAL_OSF1_wrvptptr },
|
|
{ "osf1_swpctx", PAL_OSF1_swpctx },
|
|
{ "osf1_wrval", PAL_OSF1_wrval },
|
|
{ "osf1_rdval", PAL_OSF1_rdval },
|
|
{ "osf1_tbi", PAL_OSF1_tbi },
|
|
{ "osf1_wrent", PAL_OSF1_wrent },
|
|
{ "osf1_swpipl", PAL_OSF1_swpipl },
|
|
{ "osf1_rdps", PAL_OSF1_rdps },
|
|
{ "osf1_wrkgp", PAL_OSF1_wrkgp },
|
|
{ "osf1_wrusp", PAL_OSF1_wrusp },
|
|
{ "osf1_wrperfmon", PAL_OSF1_wrperfmon },
|
|
{ "osf1_rdusp", PAL_OSF1_rdusp },
|
|
{ "osf1_whami", PAL_OSF1_whami },
|
|
{ "osf1_retsys", PAL_OSF1_retsys },
|
|
{ "osf1_rti", PAL_OSF1_rti },
|
|
{ "osf1_callsys", PAL_OSF1_callsys },
|
|
|
|
{ NULL, -1 },
|
|
};
|
|
|
|
static const char *pal_opname __P((int));
|
|
|
|
static const char *
|
|
pal_opname(op)
|
|
int op;
|
|
{
|
|
static char unk[8];
|
|
int i;
|
|
|
|
for (i = 0; pal_op_tbl[i].name != NULL; i++) {
|
|
if (pal_op_tbl[i].code == op)
|
|
return (pal_op_tbl[i].name);
|
|
}
|
|
|
|
snprintf(unk, sizeof(unk), "0x%x", op);
|
|
return (unk);
|
|
}
|
|
|
|
/* HW (PAL) instruction qualifiers, stright tables */
|
|
static const char *mXpr_name[8] = {
|
|
"", "/i", "/a", "/ai", "/p", "/pi", "/pa", "/pai"
|
|
};
|
|
static const char *hwlds_name[8] = {
|
|
"", "/r", "/a", "/ar", "/p", "/p?r", "_l-c", "_l-c/?r"
|
|
};
|
|
|
|
/*
|
|
* For this one we take the low nibble (valid values 0/2/9/b/d)
|
|
* and shift it down one to get the row index. Within a row
|
|
* we can just take the high nibble deprived of the high bit
|
|
* (valid values 0/1/2/3/4/6). We could have used a flat 64
|
|
* entry array, but in this way we use just 48 pointers.
|
|
* BUGFIX: the 'cmpbge 0x0f' opcode fits in here too
|
|
*/
|
|
static const char *arit_c0[8] = {
|
|
"addl", 0, "addq", 0, "addl/v", 0, "addq/v",
|
|
};
|
|
static const char *arit_c2[8] = {
|
|
"s4addl", "s8addl", "s4addq", "s8addq",
|
|
};
|
|
static const char *arit_c9[8] = {
|
|
"subl", 0, "subq", 0, "subl/v", 0, "subq/v",
|
|
};
|
|
static const char *arit_cB[8] = {
|
|
"s4subl", "s8subl", "s4subq", "s8subq",
|
|
};
|
|
static const char *arit_cD[8] = {
|
|
0, "cmpult", "cmpeq", "cmpule", "cmplt", 0, "cmple",
|
|
};
|
|
static const char *arit_cF[1] = {
|
|
"cmpbge"
|
|
};
|
|
static const char **arit_opname[8] = {
|
|
arit_c0, arit_c2, 0, 0, arit_c9, arit_cB, arit_cD, arit_cF
|
|
};
|
|
|
|
static __inline const char *arit_name __P((int));
|
|
static __inline const char *
|
|
arit_name(op)
|
|
int op;
|
|
{
|
|
static char unk[32];
|
|
const char *name = NULL;
|
|
|
|
if (arit_opname[((op)&0xe)>>1])
|
|
name = arit_opname[((op)&0xe)>>1][((op)&0x70)>>4];
|
|
|
|
if (name != NULL)
|
|
return (name);
|
|
|
|
snprintf(unk, sizeof(unk), "?arit 0x%x?", op);
|
|
return (unk);
|
|
}
|
|
|
|
/*
|
|
* Something similar for this one, except there are only
|
|
* 16 entries so the row indexing is done by enumeration
|
|
* of the low nibble (valid values 0/4/6/8). Then we can
|
|
* just shift the high nibble to index inside the row
|
|
* (valid values are 0/2/4 or 1/2/4/6)
|
|
*
|
|
* There are two functions that don't play by these simple rules,
|
|
* so we special-case them.
|
|
*/
|
|
static const char *logical_c0[4] = {
|
|
"and", "or", "xor", 0
|
|
};
|
|
static const char *logical_c4[4] = {
|
|
"cmovlbs", "cmoveq", "cmovlt", "cmovle"
|
|
};
|
|
static const char *logical_c6[4] = {
|
|
"cmovlbc", "cmovne", "cmovge", "cmovgt"
|
|
};
|
|
static const char *logical_c8[4] = {
|
|
"andnot", "ornot", "xornot", 0
|
|
};
|
|
|
|
static __inline const char *logical_name __P((int));
|
|
static __inline const char *
|
|
logical_name(op)
|
|
int op;
|
|
{
|
|
static char unk[32];
|
|
const char *name = NULL;
|
|
|
|
if (op == op_amask)
|
|
return ("amask");
|
|
else if (op == op_implver)
|
|
return ("implver");
|
|
|
|
switch (op & 0xf) {
|
|
case 0: name = logical_c0[((op)>>5)&3]; break;
|
|
case 4: name = logical_c4[((op)>>5)&3]; break;
|
|
case 6: name = logical_c6[((op)>>5)&3]; break;
|
|
case 8: name = logical_c8[((op)>>5)&3]; break;
|
|
}
|
|
|
|
if (name != NULL)
|
|
return (name);
|
|
|
|
snprintf(unk, sizeof(unk), "?logical 0x%x?", op);
|
|
return (unk);
|
|
}
|
|
|
|
/*
|
|
* This is the messy one. First, we single out the dense
|
|
* case of a 3 in the high nibble (valid values 0/1/2/4/6/9/b/c).
|
|
* Then the case of a 2 in the low nibble (valid values 0/1/2/5/6/7).
|
|
* For the remaining codes (6/7/a/b) we do as above: high
|
|
* nibble has valid values 0/1/2 or 5/6/7. The low nibble
|
|
* can be used as row index picking bits 0 and 2, for the
|
|
* high one just the lower two bits.
|
|
*/
|
|
static const char *bitop_c3[8] = {
|
|
"zapnot", "mskql", "srl", "extql", "sll", "insql", "sra", 0
|
|
};
|
|
static const char *bitop_c2[8] = {
|
|
"mskbl", "mskwl", "mskll", 0/*mskql*/, 0, "mskwh", "msklh", "mskqh"
|
|
};
|
|
static const char *bitop_c67ab[4][4] = {
|
|
/* a */ { 0, "extwh", "extlh", "extqh"},
|
|
/* b */ { "insbl", "inswl", "insll", 0 },
|
|
/* 6 */ { "extbl", "extwl", "extll", 0 },
|
|
/* 7 */ { 0, "inswh", "inslh", "insqh" },
|
|
};
|
|
|
|
static __inline const char *bitop_name __P((int));
|
|
static __inline const char *
|
|
bitop_name(op)
|
|
int op;
|
|
{
|
|
static char unk[32];
|
|
const char *name = NULL;
|
|
|
|
if ((op & 0x70) == 0x30)
|
|
name = (op == op_zap) ? "zap" : bitop_c3[((op)&0xe)>>1];
|
|
else if ((op & 0xf) == 0x02)
|
|
name = bitop_c2[(op)>>4];
|
|
else
|
|
name =
|
|
bitop_c67ab[(((op)&1)|(((op)&0x4)>>1))][(((op)&0x30)>>4)];
|
|
|
|
if (name != NULL)
|
|
return (name);
|
|
|
|
snprintf(unk, sizeof(unk), "?bit 0x%x?", op);
|
|
return (unk);
|
|
}
|
|
|
|
/*
|
|
* Only 5 entries in this one
|
|
*/
|
|
static const char *mul_opname[4] = {
|
|
"mull", "mulq", "mull/v", "mulq/v"
|
|
};
|
|
|
|
static __inline const char *mul_name __P((int));
|
|
static __inline const char *
|
|
mul_name(op)
|
|
int op;
|
|
{
|
|
static char unk[32];
|
|
const char *name = NULL;
|
|
|
|
name = (op == op_umulh) ? "umulh" : mul_opname[((op)>>5)&3];
|
|
|
|
if (name != NULL)
|
|
return (name);
|
|
|
|
snprintf(unk, sizeof(unk), "?mul 0x%x?", op);
|
|
return (unk);
|
|
}
|
|
|
|
/*
|
|
* These are few, the high nibble is enough to dispatch.
|
|
* We single out the "f" case to halve the table size.
|
|
*/
|
|
static const char *special_opname[8] = {
|
|
"drain_t", 0, "mb", 0, "fetch", "fetch_m", "rpcc", "rc"
|
|
};
|
|
|
|
static __inline const char *special_name __P((int));
|
|
static __inline const char *
|
|
special_name(op)
|
|
int op;
|
|
{
|
|
static char unk[32];
|
|
const char *name;
|
|
|
|
name = (op == op_rs) ? "rs" : special_opname[(op)>>13];
|
|
|
|
if (name != NULL)
|
|
return (name);
|
|
|
|
snprintf(unk, sizeof(unk), "?special 0x%x?", op);
|
|
return (unk);
|
|
}
|
|
|
|
/*
|
|
* This is trivial
|
|
*/
|
|
static const char *jump_opname[4] = {
|
|
"jmp", "jsr", "ret", "jcr"
|
|
};
|
|
#define jump_name(ix) jump_opname[ix]
|
|
|
|
/*
|
|
* For all but 4 of these, we can dispatch on the lower nibble of
|
|
* the "function".
|
|
*/
|
|
static const char *intmisc_opname_3x[16] = {
|
|
"ctpop", "perr", "ctlz", "cttz", "unpkbw", "unpkbl", "pkwb",
|
|
"pklb", "minsb8", "minsw4", "minub8", "minuw4", "maxub8",
|
|
"maxuw4", "maxsb8", "maxsw4",
|
|
};
|
|
|
|
static __inline const char *intmisc_name __P((int));
|
|
static __inline const char *
|
|
intmisc_name(op)
|
|
int op;
|
|
{
|
|
static char unk[32];
|
|
|
|
if ((op & 0xf0) == 0x30)
|
|
return (intmisc_opname_3x[op & 0x0f]);
|
|
|
|
switch (op) {
|
|
case op_sextb: return ("sextb");
|
|
case op_sextw: return ("sextw");
|
|
case op_ftoit: return ("ftoit");
|
|
case op_ftois: return ("ftois");
|
|
}
|
|
|
|
snprintf(unk, sizeof(unk), "?intmisc 0x%x?", op);
|
|
return (unk);
|
|
}
|
|
|
|
static const char *float_name __P((const struct tbl[], int, const char *type));
|
|
|
|
static const char *
|
|
float_name(tbl, op, type)
|
|
const struct tbl tbl[];
|
|
int op;
|
|
const char *type;
|
|
{
|
|
static char unk[32];
|
|
int i;
|
|
|
|
for (i = 0; tbl[i].name != NULL; i++) {
|
|
if (tbl[i].code == op)
|
|
return (tbl[i].name);
|
|
}
|
|
|
|
snprintf(unk, sizeof(unk), "?%s 0x%x?", type, op);
|
|
return (unk);
|
|
}
|
|
|
|
#define vaxf_name(op) float_name(vaxf_tbl, op, "vaxfl")
|
|
#define ieeef_name(op) float_name(ieeef_tbl, op, "ieeefl")
|
|
#define anyf_name(op) float_name(anyf_tbl, op, "anyfl")
|
|
|
|
static const struct tbl anyf_tbl[] = {
|
|
{ "cvtlq", 0x010},
|
|
{ "cpys", 0x020},
|
|
{ "cpysn", 0x021},
|
|
{ "cpyse", 0x022},
|
|
{ "mt_fpcr", 0x024},
|
|
{ "mf_fpcr", 0x025},
|
|
{ "fcmoveq", 0x02a},
|
|
{ "fcmovne", 0x02b},
|
|
{ "fcmovlt", 0x02c},
|
|
{ "fcmovge", 0x02d},
|
|
{ "fcmovle", 0x02e},
|
|
{ "fcmovgt", 0x02f},
|
|
{ "cvtql", 0x030},
|
|
{ "cvtql/v", 0x130},
|
|
{ "cvtql/sv", 0x330},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static const struct tbl ieeef_tbl[] = {
|
|
{ "adds/c", 0x000},
|
|
{ "subs/c", 0x001},
|
|
{ "muls/c", 0x002},
|
|
{ "divs/c", 0x003},
|
|
{ "addt/c", 0x020},
|
|
{ "subt/c", 0x021},
|
|
{ "mult/c", 0x022},
|
|
{ "divt/c", 0x023},
|
|
{ "cvtts/c", 0x02c},
|
|
{ "cvttq/c", 0x02f},
|
|
{ "cvtqs/c", 0x03c},
|
|
{ "cvtqt/c", 0x03e},
|
|
{ "adds/m", 0x040},
|
|
{ "subs/m", 0x041},
|
|
{ "muls/m", 0x042},
|
|
{ "divs/m", 0x043},
|
|
{ "addt/m", 0x060},
|
|
{ "subt/m", 0x061},
|
|
{ "mult/m", 0x062},
|
|
{ "divt/m", 0x063},
|
|
{ "cvtts/m", 0x06c},
|
|
{ "cvtqs/m", 0x07c},
|
|
{ "cvtqt/m", 0x07e},
|
|
{ "adds", 0x080},
|
|
{ "subs", 0x081},
|
|
{ "muls", 0x082},
|
|
{ "divs", 0x083},
|
|
{ "addt", 0x0a0},
|
|
{ "subt", 0x0a1},
|
|
{ "mult", 0x0a2},
|
|
{ "divt", 0x0a3},
|
|
{ "cmptun", 0x0a4},
|
|
{ "cmpteq", 0x0a5},
|
|
{ "cmptlt", 0x0a6},
|
|
{ "cmptle", 0x0a7},
|
|
{ "cvtts", 0x0ac},
|
|
{ "cvttq", 0x0af},
|
|
{ "cvtqs", 0x0bc},
|
|
{ "cvtqt", 0x0be},
|
|
{ "adds/d", 0x0c0},
|
|
{ "subs/d", 0x0c1},
|
|
{ "muls/d", 0x0c2},
|
|
{ "divs/d", 0x0c3},
|
|
{ "addt/d", 0x0e0},
|
|
{ "subt/d", 0x0e1},
|
|
{ "mult/d", 0x0e2},
|
|
{ "divt/d", 0x0e3},
|
|
{ "cvtts/d", 0x0ec},
|
|
{ "cvtqs/d", 0x0fc},
|
|
{ "cvtqt/d", 0x0fe},
|
|
{ "adds/uc", 0x100},
|
|
{ "subs/uc", 0x101},
|
|
{ "muls/uc", 0x102},
|
|
{ "divs/uc", 0x103},
|
|
{ "addt/uc", 0x120},
|
|
{ "subt/uc", 0x121},
|
|
{ "mult/uc", 0x122},
|
|
{ "divt/uc", 0x123},
|
|
{ "cvtts/uc", 0x12c},
|
|
{ "cvttq/vc", 0x12f},
|
|
{ "adds/um", 0x140},
|
|
{ "subs/um", 0x141},
|
|
{ "muls/um", 0x142},
|
|
{ "divs/um", 0x143},
|
|
{ "addt/um", 0x160},
|
|
{ "subt/um", 0x161},
|
|
{ "mult/um", 0x162},
|
|
{ "divt/um", 0x163},
|
|
{ "cvtts/um", 0x16c},
|
|
{ "adds/u", 0x180},
|
|
{ "subs/u", 0x181},
|
|
{ "muls/u", 0x182},
|
|
{ "divs/u", 0x183},
|
|
{ "addt/u", 0x1a0},
|
|
{ "subt/u", 0x1a1},
|
|
{ "mult/u", 0x1a2},
|
|
{ "divt/u", 0x1a3},
|
|
{ "cvtts/u", 0x1ac},
|
|
{ "cvttq/v", 0x1af},
|
|
{ "adds/ud", 0x1c0},
|
|
{ "subs/ud", 0x1c1},
|
|
{ "muls/ud", 0x1c2},
|
|
{ "divs/ud", 0x1c3},
|
|
{ "addt/ud", 0x1e0},
|
|
{ "subt/ud", 0x1e1},
|
|
{ "mult/ud", 0x1e2},
|
|
{ "divt/ud", 0x1e3},
|
|
{ "cvtts/ud", 0x1ec},
|
|
{ "adds/suc", 0x500},
|
|
{ "subs/suc", 0x501},
|
|
{ "muls/suc", 0x502},
|
|
{ "divs/suc", 0x503},
|
|
{ "addt/suc", 0x520},
|
|
{ "subt/suc", 0x521},
|
|
{ "mult/suc", 0x522},
|
|
{ "divt/suc", 0x523},
|
|
{ "cvtts/suc", 0x52c},
|
|
{ "cvttq/svc", 0x52f},
|
|
{ "adds/sum", 0x540},
|
|
{ "subs/sum", 0x541},
|
|
{ "muls/sum", 0x542},
|
|
{ "divs/sum", 0x543},
|
|
{ "addt/sum", 0x560},
|
|
{ "subt/sum", 0x561},
|
|
{ "mult/sum", 0x562},
|
|
{ "divt/sum", 0x563},
|
|
{ "cvtts/sum", 0x56c},
|
|
{ "adds/su", 0x580},
|
|
{ "subs/su", 0x581},
|
|
{ "muls/su", 0x582},
|
|
{ "divs/su", 0x583},
|
|
{ "addt/su", 0x5a0},
|
|
{ "subt/su", 0x5a1},
|
|
{ "mult/su", 0x5a2},
|
|
{ "divt/su", 0x5a3},
|
|
{ "cmptun/su", 0x5a4},
|
|
{ "cmpteq/su", 0x5a5},
|
|
{ "cmptlt/su", 0x5a6},
|
|
{ "cmptle/su", 0x5a7},
|
|
{ "cvtts/su", 0x5ac},
|
|
{ "cvttq/sv", 0x5af},
|
|
{ "adds/sud", 0x5c0},
|
|
{ "subs/sud", 0x5c1},
|
|
{ "muls/sud", 0x5c2},
|
|
{ "divs/sud", 0x5c3},
|
|
{ "addt/sud", 0x5e0},
|
|
{ "subt/sud", 0x5e1},
|
|
{ "mult/sud", 0x5e2},
|
|
{ "divt/sud", 0x5e3},
|
|
{ "cvtts/sud", 0x5ec},
|
|
{ "adds/suic", 0x700},
|
|
{ "subs/suic", 0x701},
|
|
{ "muls/suic", 0x702},
|
|
{ "divs/suic", 0x703},
|
|
{ "addt/suic", 0x720},
|
|
{ "subt/suic", 0x721},
|
|
{ "mult/suic", 0x722},
|
|
{ "divt/suic", 0x723},
|
|
{ "cvtts/suic", 0x72c},
|
|
{ "cvttq/svic", 0x72f},
|
|
{ "cvtqs/suic", 0x73c},
|
|
{ "cvtqt/suic", 0x73e},
|
|
{ "adds/suim", 0x740},
|
|
{ "subs/suim", 0x741},
|
|
{ "muls/suim", 0x742},
|
|
{ "divs/suim", 0x743},
|
|
{ "addt/suim", 0x760},
|
|
{ "subt/suim", 0x761},
|
|
{ "mult/suim", 0x762},
|
|
{ "divt/suim", 0x763},
|
|
{ "cvtts/suim", 0x76c},
|
|
{ "cvtqs/suim", 0x77c},
|
|
{ "cvtqt/suim", 0x77e},
|
|
{ "adds/sui", 0x780},
|
|
{ "subs/sui", 0x781},
|
|
{ "muls/sui", 0x782},
|
|
{ "divs/sui", 0x783},
|
|
{ "addt/sui", 0x7a0},
|
|
{ "subt/sui", 0x7a1},
|
|
{ "mult/sui", 0x7a2},
|
|
{ "divt/sui", 0x7a3},
|
|
{ "cvtts/sui", 0x7ac},
|
|
{ "cvttq/svi", 0x7af},
|
|
{ "cvtqs/sui", 0x7bc},
|
|
{ "cvtqt/sui", 0x7be},
|
|
{ "adds/suid", 0x7c0},
|
|
{ "subs/suid", 0x7c1},
|
|
{ "muls/suid", 0x7c2},
|
|
{ "divs/suid", 0x7c3},
|
|
{ "addt/suid", 0x7e0},
|
|
{ "subt/suid", 0x7e1},
|
|
{ "mult/suid", 0x7e2},
|
|
{ "divt/suid", 0x7e3},
|
|
{ "cvtts/suid", 0x7ec},
|
|
{ "cvtqs/suid", 0x7fc},
|
|
{ "cvtqt/suid", 0x7fe},
|
|
{ 0, 0}
|
|
};
|
|
|
|
static const struct tbl vaxf_tbl[] = {
|
|
{ "addf/c", 0x000},
|
|
{ "subf/c", 0x001},
|
|
{ "mulf/c", 0x002},
|
|
{ "divf/c", 0x003},
|
|
{ "cvtdg/c", 0x01e},
|
|
{ "addg/c", 0x020},
|
|
{ "subg/c", 0x021},
|
|
{ "mulg/c", 0x022},
|
|
{ "divg/c", 0x023},
|
|
{ "cvtgf/c", 0x02c},
|
|
{ "cvtgd/c", 0x02d},
|
|
{ "cvtgq/c", 0x02f},
|
|
{ "cvtqf/c", 0x03c},
|
|
{ "cvtqg/c", 0x03e},
|
|
{ "addf", 0x080},
|
|
{ "subf", 0x081},
|
|
{ "mulf", 0x082},
|
|
{ "divf", 0x083},
|
|
{ "cvtdg", 0x09e},
|
|
{ "addg", 0x0a0},
|
|
{ "subg", 0x0a1},
|
|
{ "mulg", 0x0a2},
|
|
{ "divg", 0x0a3},
|
|
{ "cmpgeq", 0x0a5},
|
|
{ "cmpglt", 0x0a6},
|
|
{ "cmpgle", 0x0a7},
|
|
{ "cvtgf", 0x0ac},
|
|
{ "cvtgd", 0x0ad},
|
|
{ "cvtgq", 0x0af},
|
|
{ "cvtqf", 0x0bc},
|
|
{ "cvtqg", 0x0be},
|
|
{ "addf/uc", 0x100},
|
|
{ "subf/uc", 0x101},
|
|
{ "mulf/uc", 0x102},
|
|
{ "divf/uc", 0x103},
|
|
{ "cvtdg/uc", 0x11e},
|
|
{ "addg/uc", 0x120},
|
|
{ "subg/uc", 0x121},
|
|
{ "mulg/uc", 0x122},
|
|
{ "divg/uc", 0x123},
|
|
{ "cvtgf/uc", 0x12c},
|
|
{ "cvtgd/uc", 0x12d},
|
|
{ "cvtgq/vc", 0x12f},
|
|
{ "addf/u", 0x180},
|
|
{ "subf/u", 0x181},
|
|
{ "mulf/u", 0x182},
|
|
{ "divf/u", 0x183},
|
|
{ "cvtdg/u", 0x19e},
|
|
{ "addg/u", 0x1a0},
|
|
{ "subg/u", 0x1a1},
|
|
{ "mulg/u", 0x1a2},
|
|
{ "divg/u", 0x1a3},
|
|
{ "cvtgf/u", 0x1ac},
|
|
{ "cvtgd/u", 0x1ad},
|
|
{ "cvtgq/v", 0x1af},
|
|
{ "addf/sc", 0x400},
|
|
{ "subf/sc", 0x401},
|
|
{ "mulf/sc", 0x402},
|
|
{ "divf/sc", 0x403},
|
|
{ "cvtdg/sc", 0x41e},
|
|
{ "addg/sc", 0x420},
|
|
{ "subg/sc", 0x421},
|
|
{ "mulg/sc", 0x422},
|
|
{ "divg/sc", 0x423},
|
|
{ "cvtgf/sc", 0x42c},
|
|
{ "cvtgd/sc", 0x42d},
|
|
{ "cvtgq/sc", 0x42f},
|
|
{ "cvtqf/sc", 0x43c},
|
|
{ "cvtqg/sc", 0x43e},
|
|
{ "addf/s", 0x480},
|
|
{ "subf/s", 0x481},
|
|
{ "mulf/s", 0x482},
|
|
{ "divf/s", 0x483},
|
|
{ "cvtdg/s", 0x49e},
|
|
{ "addg/s", 0x4a0},
|
|
{ "subg/s", 0x4a1},
|
|
{ "mulg/s", 0x4a2},
|
|
{ "divg/s", 0x4a3},
|
|
{ "cmpgeq/s", 0x4a5},
|
|
{ "cmpglt/s", 0x4a6},
|
|
{ "cmpgle/s", 0x4a7},
|
|
{ "cvtgf/s", 0x4ac},
|
|
{ "cvtgd/s", 0x4ad},
|
|
{ "cvtgq/s", 0x4af},
|
|
{ "cvtqf/s", 0x4bc},
|
|
{ "cvtqg/s", 0x4be},
|
|
{ "addf/suc", 0x500},
|
|
{ "subf/suc", 0x501},
|
|
{ "mulf/suc", 0x502},
|
|
{ "divf/suc", 0x503},
|
|
{ "cvtdg/suc", 0x51e},
|
|
{ "addg/suc", 0x520},
|
|
{ "subg/suc", 0x521},
|
|
{ "mulg/suc", 0x522},
|
|
{ "divg/suc", 0x523},
|
|
{ "cvtgf/suc", 0x52c},
|
|
{ "cvtgd/suc", 0x52d},
|
|
{ "cvtgq/svc", 0x52f},
|
|
{ "addf/su", 0x580},
|
|
{ "subf/su", 0x581},
|
|
{ "mulf/su", 0x582},
|
|
{ "divf/su", 0x583},
|
|
{ "cvtdg/su", 0x59e},
|
|
{ "addg/su", 0x5a0},
|
|
{ "subg/su", 0x5a1},
|
|
{ "mulg/su", 0x5a2},
|
|
{ "divg/su", 0x5a3},
|
|
{ "cvtgf/su", 0x5ac},
|
|
{ "cvtgd/su", 0x5ad},
|
|
{ "cvtgq/sv", 0x5af},
|
|
{ 0, 0}
|
|
};
|
|
|
|
/*
|
|
* General purpose registers
|
|
*/
|
|
static const char *name_of_register[32] = {
|
|
"v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
|
|
"t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6",
|
|
"a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9",
|
|
"t10", "t11", "ra", "pv", "at", "gp", "sp", "zero"
|
|
};
|
|
|
|
static int regcount; /* how many regs used in this inst */
|
|
static int regnum[3]; /* which regs used in this inst */
|
|
|
|
static const char *register_name __P((int));
|
|
|
|
static const char *
|
|
register_name (ireg)
|
|
int ireg;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < regcount; i++)
|
|
if (regnum[i] == ireg)
|
|
break;
|
|
if (i >= regcount)
|
|
regnum[regcount++] = ireg;
|
|
return (name_of_register[ireg]);
|
|
}
|
|
|
|
/*
|
|
* Disassemble instruction at 'loc'. 'altfmt' specifies an
|
|
* (optional) alternate format. Return address of start of
|
|
* next instruction.
|
|
*/
|
|
int alpha_print_instruction __P((db_addr_t, alpha_instruction, boolean_t));
|
|
|
|
db_addr_t
|
|
db_disasm(loc, altfmt)
|
|
db_addr_t loc;
|
|
boolean_t altfmt;
|
|
{
|
|
alpha_instruction inst;
|
|
|
|
inst.bits = db_get_value(loc, 4, 0);
|
|
|
|
loc += alpha_print_instruction(loc, inst, altfmt);
|
|
return (loc);
|
|
}
|
|
|
|
int
|
|
alpha_print_instruction(iadr, i, showregs)
|
|
db_addr_t iadr;
|
|
alpha_instruction i;
|
|
boolean_t showregs;
|
|
{
|
|
const char *opcode;
|
|
int ireg;
|
|
long signed_immediate;
|
|
boolean_t fstore;
|
|
pal_instruction p;
|
|
|
|
regcount = 0;
|
|
fstore = FALSE;
|
|
opcode = op_name[i.mem_format.opcode];
|
|
|
|
/*
|
|
* Dispatch directly on the opcode, save code
|
|
* duplication sometimes via "harmless gotos".
|
|
*/
|
|
switch (i.mem_format.opcode) {
|
|
case op_pal:
|
|
/* "call_pal" is a long string; just use a space. */
|
|
db_printf("%s %s", opcode, pal_opname(i.pal_format.function));
|
|
break;
|
|
case op_lda:
|
|
case op_ldah:
|
|
case op_ldbu:
|
|
case op_ldq_u:
|
|
case op_ldwu:
|
|
case op_stw:
|
|
case op_stb:
|
|
case op_stq_u:
|
|
/*
|
|
* These loadstores are here to make compiling the
|
|
* switch a bit easier. Could embellish the output
|
|
* someday, too.
|
|
*/
|
|
goto loadstore;
|
|
break;
|
|
case op_arit:
|
|
/*
|
|
* For this and the following three groups we
|
|
* just need different opcode strings
|
|
*/
|
|
opcode = arit_name(i.operate_lit_format.function);
|
|
goto operate;
|
|
break;
|
|
case op_logical:
|
|
opcode = logical_name(i.operate_lit_format.function);
|
|
goto operate;
|
|
break;
|
|
case op_bit:
|
|
opcode = bitop_name(i.operate_lit_format.function);
|
|
goto operate;
|
|
break;
|
|
case op_mul:
|
|
opcode = mul_name(i.operate_lit_format.function);
|
|
operate:
|
|
/*
|
|
* Nice and uniform, just check for literals
|
|
*/
|
|
db_printf("%s\t%s,", opcode,
|
|
register_name(i.operate_lit_format.rs));
|
|
if (i.operate_lit_format.one)
|
|
db_printf("#0x%x", i.operate_lit_format.literal);
|
|
else
|
|
db_printf("%s", register_name(i.operate_reg_format.rt));
|
|
db_printf(",%s", register_name(i.operate_lit_format.rd));
|
|
break;
|
|
case op_vax_float:
|
|
/*
|
|
* The three floating point groups are even simpler
|
|
*/
|
|
opcode = vaxf_name(i.float_format.function);
|
|
goto foperate;
|
|
break;
|
|
case op_ieee_float:
|
|
opcode = ieeef_name(i.float_format.function);
|
|
goto foperate;
|
|
break;
|
|
case op_any_float:
|
|
opcode = anyf_name(i.float_format.function);
|
|
foperate:
|
|
db_printf("%s\tf%d,f%d,f%d", opcode,
|
|
i.float_format.fs,
|
|
i.float_format.ft,
|
|
i.float_format.fd);
|
|
break;
|
|
case op_special:
|
|
/*
|
|
* Miscellaneous.
|
|
*/
|
|
{
|
|
register unsigned int code;
|
|
|
|
code = (i.mem_format.displacement)&0xffff;
|
|
opcode = special_name(code);
|
|
|
|
switch (code) {
|
|
case op_fetch:
|
|
case op_fetch_m:
|
|
db_printf("%s\t0(%s)", opcode,
|
|
register_name(i.mem_format.rs));
|
|
break;
|
|
case op_rpcc:
|
|
case op_rc:
|
|
case op_rs:
|
|
db_printf("%s\t%s", opcode,
|
|
register_name(i.mem_format.rd));
|
|
break;
|
|
case op_draint:
|
|
case op_mb:
|
|
default:
|
|
db_printf("%s", opcode);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case op_j:
|
|
/*
|
|
* Jump instructions really are of two sorts,
|
|
* depending on the use of the hint info.
|
|
*/
|
|
opcode = jump_name(i.jump_format.action);
|
|
switch (i.jump_format.action) {
|
|
case op_jmp:
|
|
case op_jsr:
|
|
db_printf("%s\t%s,(%s),", opcode,
|
|
register_name(i.jump_format.rd),
|
|
register_name(i.jump_format.rs));
|
|
signed_immediate = i.jump_format.hint;
|
|
goto branch_displacement;
|
|
break;
|
|
case op_ret:
|
|
case op_jcr:
|
|
db_printf("%s\t%s,(%s)", opcode,
|
|
register_name(i.jump_format.rd),
|
|
register_name(i.jump_format.rs));
|
|
break;
|
|
}
|
|
break;
|
|
case op_intmisc:
|
|
/*
|
|
* These are just in "operate" format.
|
|
*/
|
|
opcode = intmisc_name(i.operate_lit_format.function);
|
|
goto operate;
|
|
break;
|
|
/* HW instructions, possibly chip-specific XXXX */
|
|
case op_pal19: /* "hw_mfpr" */
|
|
case op_pal1d: /* "hw_mtpr" */
|
|
p.bits = i.bits;
|
|
db_printf("\t%s%s\t%s, %d", opcode,
|
|
mXpr_name[p.mXpr_format.regset],
|
|
register_name(p.mXpr_format.rd),
|
|
p.mXpr_format.index);
|
|
break;
|
|
case op_pal1b: /* "hw_ld" */
|
|
case op_pal1f: /* "hw_st" */
|
|
p.bits = i.bits;
|
|
db_printf("\t%s%c%s\t%s,", opcode,
|
|
(p.mem_format.qw) ? 'q' : 'l',
|
|
hwlds_name[p.mem_format.qualif],
|
|
register_name(p.mem_format.rd));
|
|
signed_immediate = (long)p.mem_format.displacement;
|
|
goto loadstore_address;
|
|
|
|
case op_pal1e: /* "hw_rei" */
|
|
db_printf("\t%s", opcode);
|
|
break;
|
|
|
|
case op_ldf:
|
|
case op_ldg:
|
|
case op_lds:
|
|
case op_ldt:
|
|
case op_stf:
|
|
case op_stg:
|
|
case op_sts:
|
|
case op_stt:
|
|
fstore = TRUE;
|
|
/* fall through */
|
|
case op_ldl:
|
|
case op_ldq:
|
|
case op_ldl_l:
|
|
case op_ldq_l:
|
|
case op_stl:
|
|
case op_stq:
|
|
case op_stl_c:
|
|
case op_stq_c:
|
|
/*
|
|
* Memory operations, including floats
|
|
*/
|
|
loadstore:
|
|
if (fstore)
|
|
db_printf("%s\tf%d,", opcode, i.mem_format.rd);
|
|
else
|
|
db_printf("%s\t%s,", opcode,
|
|
register_name(i.mem_format.rd));
|
|
signed_immediate = (long)i.mem_format.displacement;
|
|
loadstore_address:
|
|
db_printf("%+#lx(%s)", signed_immediate,
|
|
register_name(i.mem_format.rs));
|
|
/*
|
|
* For convenience, do the address computation
|
|
*/
|
|
if (showregs) {
|
|
if (i.mem_format.opcode == op_ldah)
|
|
signed_immediate <<= 16;
|
|
db_printf(" <0x%lx>", signed_immediate +
|
|
db_register_value(DDB_REGS, i.mem_format.rs));
|
|
}
|
|
break;
|
|
case op_br:
|
|
case op_fbeq:
|
|
case op_fblt:
|
|
case op_fble:
|
|
case op_bsr:
|
|
case op_fbne:
|
|
case op_fbge:
|
|
case op_fbgt:
|
|
case op_blbc:
|
|
case op_beq:
|
|
case op_blt:
|
|
case op_ble:
|
|
case op_blbs:
|
|
case op_bne:
|
|
case op_bge:
|
|
case op_bgt:
|
|
/*
|
|
* We want to know where we are branching to
|
|
*/
|
|
signed_immediate = (long)i.branch_format.displacement;
|
|
db_printf("%s\t%s,", opcode,
|
|
register_name(i.branch_format.rd));
|
|
branch_displacement:
|
|
db_printsym(iadr + sizeof(alpha_instruction) +
|
|
(signed_immediate << 2), DB_STGY_PROC);
|
|
break;
|
|
default:
|
|
/*
|
|
* Shouldn't happen
|
|
*/
|
|
db_printf("? 0x%x ?", i.bits);
|
|
}
|
|
|
|
/*
|
|
* Print out the registers used in this instruction
|
|
*/
|
|
if (showregs && regcount > 0) {
|
|
db_printf("\t<");
|
|
for (ireg = 0; ireg < regcount; ireg++) {
|
|
if (ireg != 0)
|
|
db_printf(",");
|
|
db_printf("%s=0x%lx",
|
|
name_of_register[regnum[ireg]],
|
|
db_register_value(DDB_REGS, regnum[ireg]));
|
|
}
|
|
db_printf(">");
|
|
}
|
|
db_printf("\n");
|
|
return (sizeof(alpha_instruction));
|
|
}
|