/* $NetBSD: db_disasm.c,v 1.16 2014/03/20 20:51:40 christos 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 /* RCS ID & Copyright macro defns */ __KERNEL_RCSID(0, "$NetBSD: db_disasm.c,v 1.16 2014/03/20 20:51:40 christos Exp $"); #include #include #include #include #include #include #include #include #include #include /* * This would belong in a header file, except noone else needs it * * XXX THESE SHOULD BE CONVERTED TO ra, rb, rc FORMAT. */ 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 const char * const 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(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 * const mXpr_name[8] = { "", "/i", "/a", "/ai", "/p", "/pi", "/pa", "/pai" }; static const char * const 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 * const arit_c0[8] = { "addl", 0, "addq", 0, "addl/v", 0, "addq/v", }; static const char * const arit_c2[8] = { "s4addl", "s8addl", "s4addq", "s8addq", }; static const char * const arit_c9[8] = { "subl", 0, "subq", 0, "subl/v", 0, "subq/v", }; static const char * const arit_cB[8] = { "s4subl", "s8subl", "s4subq", "s8subq", }; static const char * const arit_cD[8] = { 0, "cmpult", "cmpeq", "cmpule", "cmplt", 0, "cmple", }; static const char * const arit_cF[1] = { "cmpbge" }; static const char * const * const arit_opname[8] = { arit_c0, arit_c2, 0, 0, arit_c9, arit_cB, arit_cD, arit_cF }; static const char * arit_name(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 * const logical_c0[4] = { "and", "or", "xor", 0 }; static const char * const logical_c4[4] = { "cmovlbs", "cmoveq", "cmovlt", "cmovle" }; static const char * const logical_c6[4] = { "cmovlbc", "cmovne", "cmovge", "cmovgt" }; static const char * const logical_c8[4] = { "andnot", "ornot", "xornot", 0 }; static const char * logical_name(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 * const bitop_c3[8] = { "zapnot", "mskql", "srl", "extql", "sll", "insql", "sra", 0 }; static const char * const bitop_c2[8] = { "mskbl", "mskwl", "mskll", 0/*mskql*/, 0, "mskwh", "msklh", "mskqh" }; static const char * const 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 const char * bitop_name(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 4 entries in this one */ static const char * const mul_opname[4] = { "mull", "mulq", "mull/v", "mulq/v" }; static const char * mul_name(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 usually enough to dispatch. * We single out the `f' case to halve the table size, as * well as the cases in which the high nibble isn't enough. */ static const char * const special_opname[8] = { "trapb", 0, "mb", 0, "fetch", "fetch_m", "rpcc", "rc" }; static const char * special_name(int op) { static char unk[32]; const char *name; switch (op) { case op_excb: name = "excb"; break; case op_wmb: name = "wmb"; break; case op_ecb: name = "ecb"; break; case op_rs: name = "rs"; break; case op_wh64: name = "wh64"; break; default: name = 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 * const 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 * const intmisc_opname_3x[16] = { "ctpop", "perr", "ctlz", "cttz", "unpkbw", "unpkbl", "pkwb", "pklb", "minsb8", "minsw4", "minub8", "minuw4", "maxub8", "maxuw4", "maxsb8", "maxsw4", }; static const char * intmisc_name(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(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 * const 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(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. */ static int alpha_print_instruction(db_addr_t iadr, alpha_instruction i, bool showregs) { const char *opcode; int ireg; long signed_immediate; bool 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.ra)); 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.rb)); db_printf(",%s", register_name(i.operate_lit_format.rc)); 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.fa, i.float_format.fb, i.float_format.fc); break; case op_special: /* * Miscellaneous. */ { register unsigned int code; code = (i.mem_format.displacement)&0xffff; opcode = special_name(code); switch (code) { case op_ecb: db_printf("%s\t(%s)", opcode, register_name(i.mem_format.rb)); break; case op_fetch: case op_fetch_m: db_printf("%s\t0(%s)", opcode, register_name(i.mem_format.rb)); break; case op_rpcc: case op_rc: case op_rs: db_printf("%s\t%s", opcode, register_name(i.mem_format.ra)); break; 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.ra), register_name(i.jump_format.rb)); 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.ra), register_name(i.jump_format.rb)); 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.ra); else db_printf("%s\t%s,", opcode, register_name(i.mem_format.ra)); signed_immediate = (long)i.mem_format.displacement; loadstore_address: { char tbuf[24]; db_format_hex(tbuf, 24, signed_immediate, false); db_printf("%s(%s)", tbuf, register_name(i.mem_format.rb)); } /* * 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.rb)); } 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.ra)); branch_displacement: db_printsym(iadr + sizeof(alpha_instruction) + (signed_immediate << 2), DB_STGY_PROC, db_printf); 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)); } db_addr_t db_disasm(db_addr_t loc, bool altfmt) { alpha_instruction inst; inst.bits = db_get_value(loc, 4, 0); loc += alpha_print_instruction(loc, inst, altfmt); return (loc); }