43e585ce84
kvprintf() doesn't print out a sign for hex anyways.
1097 lines
25 KiB
C
1097 lines
25 KiB
C
/* $FreeBSD$ */
|
|
/* $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));
|
|
}
|