1288 lines
38 KiB
C
1288 lines
38 KiB
C
/*
|
|
* Print mips instructions.
|
|
*
|
|
* Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
|
|
* 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009
|
|
* Free Software Foundation, Inc.
|
|
* Contributed by Nobuyuki Hikichi (hikichi@sra.co.jp).
|
|
* Rewritten for VirtualMIPS by Serge Vakulenko.
|
|
*
|
|
* This file is part of the virtualmips distribution.
|
|
* See LICENSE file for terms of the license.
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "mips-opcode.h"
|
|
#include "mips-opc.c"
|
|
#include "mips16-opc.c"
|
|
|
|
/* FIXME: These are needed to figure out if the code is mips16 or
|
|
not. The low bit of the address is often a good indicator. No
|
|
symbol table is available when this code runs out in an embedded
|
|
system as when it is used for disassembler support in a monitor. */
|
|
|
|
/* Mips instructions are at maximum this many bytes long. */
|
|
#define INSNLEN 4
|
|
|
|
struct mips_cp0sel_name {
|
|
unsigned int cp0reg;
|
|
unsigned int sel;
|
|
const char *const name;
|
|
};
|
|
|
|
/* The mips16 registers. */
|
|
static const unsigned int mips16_to_32_reg_map[] = {
|
|
16, 17, 2, 3, 4, 5, 6, 7
|
|
};
|
|
|
|
#define mips16_reg_names(rn) mips_gpr_names[mips16_to_32_reg_map[rn]]
|
|
|
|
static const char *const mips_gpr_names[32] = {
|
|
"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
|
|
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
|
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
|
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
|
|
};
|
|
|
|
static const char *const mips_fpr_names[32] = {
|
|
"$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
|
|
"$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
|
|
"$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
|
|
"$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
|
|
};
|
|
|
|
static const char *const mips_cp0_names[32] = {
|
|
"c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
|
|
"c0_context", "c0_pagemask", "c0_wired", "c0_hwrena",
|
|
"c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
|
|
"c0_status", "c0_cause", "c0_epc", "c0_prid",
|
|
"c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
|
|
"c0_xcontext", "$21", "$22", "c0_debug",
|
|
"c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr",
|
|
"c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave",
|
|
};
|
|
|
|
static const struct mips_cp0sel_name mips_cp0sel_names[] = {
|
|
{4, 1, "c0_contextconfig"},
|
|
{0, 1, "c0_mvpcontrol"},
|
|
{0, 2, "c0_mvpconf0"},
|
|
{0, 3, "c0_mvpconf1"},
|
|
{1, 1, "c0_vpecontrol"},
|
|
{1, 2, "c0_vpeconf0"},
|
|
{1, 3, "c0_vpeconf1"},
|
|
{1, 4, "c0_yqmask"},
|
|
{1, 5, "c0_vpeschedule"},
|
|
{1, 6, "c0_vpeschefback"},
|
|
{2, 1, "c0_tcstatus"},
|
|
{2, 2, "c0_tcbind"},
|
|
{2, 3, "c0_tcrestart"},
|
|
{2, 4, "c0_tchalt"},
|
|
{2, 5, "c0_tccontext"},
|
|
{2, 6, "c0_tcschedule"},
|
|
{2, 7, "c0_tcschefback"},
|
|
{5, 1, "c0_pagegrain"},
|
|
{6, 1, "c0_srsconf0"},
|
|
{6, 2, "c0_srsconf1"},
|
|
{6, 3, "c0_srsconf2"},
|
|
{6, 4, "c0_srsconf3"},
|
|
{6, 5, "c0_srsconf4"},
|
|
{12, 1, "c0_intctl"},
|
|
{12, 2, "c0_srsctl"},
|
|
{12, 3, "c0_srsmap"},
|
|
{15, 1, "c0_ebase"},
|
|
{16, 1, "c0_config1"},
|
|
{16, 2, "c0_config2"},
|
|
{16, 3, "c0_config3"},
|
|
{18, 1, "c0_watchlo,1"},
|
|
{18, 2, "c0_watchlo,2"},
|
|
{18, 3, "c0_watchlo,3"},
|
|
{18, 4, "c0_watchlo,4"},
|
|
{18, 5, "c0_watchlo,5"},
|
|
{18, 6, "c0_watchlo,6"},
|
|
{18, 7, "c0_watchlo,7"},
|
|
{19, 1, "c0_watchhi,1"},
|
|
{19, 2, "c0_watchhi,2"},
|
|
{19, 3, "c0_watchhi,3"},
|
|
{19, 4, "c0_watchhi,4"},
|
|
{19, 5, "c0_watchhi,5"},
|
|
{19, 6, "c0_watchhi,6"},
|
|
{19, 7, "c0_watchhi,7"},
|
|
{23, 1, "c0_tracecontrol"},
|
|
{23, 2, "c0_tracecontrol2"},
|
|
{23, 3, "c0_usertracedata"},
|
|
{23, 4, "c0_tracebpc"},
|
|
{25, 1, "c0_perfcnt,1"},
|
|
{25, 2, "c0_perfcnt,2"},
|
|
{25, 3, "c0_perfcnt,3"},
|
|
{25, 4, "c0_perfcnt,4"},
|
|
{25, 5, "c0_perfcnt,5"},
|
|
{25, 6, "c0_perfcnt,6"},
|
|
{25, 7, "c0_perfcnt,7"},
|
|
{27, 1, "c0_cacheerr,1"},
|
|
{27, 2, "c0_cacheerr,2"},
|
|
{27, 3, "c0_cacheerr,3"},
|
|
{28, 1, "c0_datalo"},
|
|
{28, 2, "c0_taglo1"},
|
|
{28, 3, "c0_datalo1"},
|
|
{28, 4, "c0_taglo2"},
|
|
{28, 5, "c0_datalo2"},
|
|
{28, 6, "c0_taglo3"},
|
|
{28, 7, "c0_datalo3"},
|
|
{29, 1, "c0_datahi"},
|
|
{29, 2, "c0_taghi1"},
|
|
{29, 3, "c0_datahi1"},
|
|
{29, 4, "c0_taghi2"},
|
|
{29, 5, "c0_datahi2"},
|
|
{29, 6, "c0_taghi3"},
|
|
{29, 7, "c0_datahi3"},
|
|
};
|
|
static const int mips_cp0sel_names_len = sizeof (mips_cp0sel_names) / sizeof (*mips_cp0sel_names);
|
|
|
|
static const char *const mips_hwr_names[32] = {
|
|
"$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
|
|
"$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
|
|
"$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
|
|
"$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
|
|
};
|
|
|
|
/*
|
|
* If set disassemble as most general inst.
|
|
*/
|
|
static int no_aliases = 0;
|
|
|
|
static unsigned target;
|
|
|
|
static void
|
|
print_address (unsigned address, FILE *stream)
|
|
{
|
|
if (address < 16)
|
|
fprintf (stream, "%d", address);
|
|
else
|
|
fprintf (stream, "0x%x", address);
|
|
}
|
|
|
|
static const struct mips_cp0sel_name *
|
|
lookup_mips_cp0sel_name (const struct
|
|
mips_cp0sel_name *names, unsigned int len, unsigned int cp0reg,
|
|
unsigned int sel)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if (names[i].cp0reg == cp0reg && names[i].sel == sel)
|
|
return &names[i];
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* CP0 register including 'sel' code for mftc0, to be
|
|
* printed textually if known. If not known, print both
|
|
* CP0 register name and sel numerically since CP0 register
|
|
* with sel 0 may have a name unrelated to register being
|
|
* printed.
|
|
*/
|
|
const char *cp0reg_name (unsigned cp0reg, unsigned sel)
|
|
{
|
|
const struct mips_cp0sel_name *n;
|
|
static char name [32];
|
|
|
|
if (sel == 0)
|
|
return mips_cp0_names[cp0reg];
|
|
|
|
n = lookup_mips_cp0sel_name (mips_cp0sel_names,
|
|
mips_cp0sel_names_len, cp0reg, sel);
|
|
if (n != NULL)
|
|
return n->name;
|
|
|
|
sprintf (name, "CP0_R[%d,%d]", cp0reg, sel);
|
|
return name;
|
|
}
|
|
|
|
/* Print insn arguments for 32/64-bit code. */
|
|
|
|
static void
|
|
print_insn_args (const char *d,
|
|
register unsigned long int l,
|
|
unsigned pc, FILE *stream, const struct mips_opcode *opp)
|
|
{
|
|
int op, delta;
|
|
unsigned int lsb, msb, msbd;
|
|
|
|
lsb = 0;
|
|
|
|
for (; *d != '\0'; d++) {
|
|
switch (*d) {
|
|
case ',':
|
|
case '(':
|
|
case ')':
|
|
case '[':
|
|
case ']':
|
|
fprintf (stream, "%c", *d);
|
|
break;
|
|
|
|
case '+':
|
|
/* Extension character; switch for second char. */
|
|
d++;
|
|
switch (*d) {
|
|
case '\0':
|
|
/* xgettext:c-format */
|
|
fprintf (stream,
|
|
"# internal error, incomplete extension sequence (+)");
|
|
return;
|
|
|
|
case 'A':
|
|
lsb = (l >> OP_SH_SHAMT) & OP_MASK_SHAMT;
|
|
fprintf (stream, "0x%x", lsb);
|
|
break;
|
|
|
|
case 'B':
|
|
msb = (l >> OP_SH_INSMSB) & OP_MASK_INSMSB;
|
|
fprintf (stream, "0x%x", msb - lsb + 1);
|
|
break;
|
|
|
|
case '1':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_UDI1) & OP_MASK_UDI1);
|
|
break;
|
|
|
|
case '2':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_UDI2) & OP_MASK_UDI2);
|
|
break;
|
|
|
|
case '3':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_UDI3) & OP_MASK_UDI3);
|
|
break;
|
|
|
|
case '4':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_UDI4) & OP_MASK_UDI4);
|
|
break;
|
|
|
|
case 'C':
|
|
case 'H':
|
|
msbd = (l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD;
|
|
fprintf (stream, "0x%x", msbd + 1);
|
|
break;
|
|
|
|
case 'D':
|
|
{
|
|
const struct mips_cp0sel_name *n;
|
|
unsigned int cp0reg, sel;
|
|
|
|
cp0reg = (l >> OP_SH_RD) & OP_MASK_RD;
|
|
sel = (l >> OP_SH_SEL) & OP_MASK_SEL;
|
|
|
|
/* CP0 register including 'sel' code for mtcN (et al.), to be
|
|
* printed textually if known. If not known, print both
|
|
* CP0 register name and sel numerically since CP0 register
|
|
* with sel 0 may have a name unrelated to register being
|
|
* printed. */
|
|
n = lookup_mips_cp0sel_name (mips_cp0sel_names,
|
|
mips_cp0sel_names_len, cp0reg, sel);
|
|
if (n != NULL)
|
|
fprintf (stream, "%s", n->name);
|
|
else
|
|
fprintf (stream, "$%d,%d", cp0reg,
|
|
sel);
|
|
break;
|
|
}
|
|
|
|
case 'E':
|
|
lsb = ((l >> OP_SH_SHAMT) & OP_MASK_SHAMT) + 32;
|
|
fprintf (stream, "0x%x", lsb);
|
|
break;
|
|
|
|
case 'F':
|
|
msb = ((l >> OP_SH_INSMSB) & OP_MASK_INSMSB) + 32;
|
|
fprintf (stream, "0x%x", msb - lsb + 1);
|
|
break;
|
|
|
|
case 'G':
|
|
msbd = ((l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD) + 32;
|
|
fprintf (stream, "0x%x", msbd + 1);
|
|
break;
|
|
|
|
case 't': /* Coprocessor 0 reg name */
|
|
fprintf (stream, "%s",
|
|
mips_cp0_names[(l >> OP_SH_RT) & OP_MASK_RT]);
|
|
break;
|
|
|
|
case 'T': /* Coprocessor 0 reg name */
|
|
{
|
|
const struct mips_cp0sel_name *n;
|
|
unsigned int cp0reg, sel;
|
|
|
|
cp0reg = (l >> OP_SH_RT) & OP_MASK_RT;
|
|
sel = (l >> OP_SH_SEL) & OP_MASK_SEL;
|
|
|
|
/* CP0 register including 'sel' code for mftc0, to be
|
|
* printed textually if known. If not known, print both
|
|
* CP0 register name and sel numerically since CP0 register
|
|
* with sel 0 may have a name unrelated to register being
|
|
* printed. */
|
|
n = lookup_mips_cp0sel_name (mips_cp0sel_names,
|
|
mips_cp0sel_names_len, cp0reg, sel);
|
|
if (n != NULL)
|
|
fprintf (stream, "%s", n->name);
|
|
else
|
|
fprintf (stream, "$%d,%d", cp0reg,
|
|
sel);
|
|
break;
|
|
}
|
|
|
|
case 'x': /* bbit bit index */
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_BBITIND) & OP_MASK_BBITIND);
|
|
break;
|
|
|
|
case 'p': /* cins, cins32, exts and exts32 position */
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_CINSPOS) & OP_MASK_CINSPOS);
|
|
break;
|
|
|
|
case 's': /* cins and exts length-minus-one */
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_CINSLM1) & OP_MASK_CINSLM1);
|
|
break;
|
|
|
|
case 'S': /* cins32 and exts32 length-minus-one field */
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_CINSLM1) & OP_MASK_CINSLM1);
|
|
break;
|
|
|
|
case 'Q': /* seqi/snei immediate field */
|
|
op = (l >> OP_SH_SEQI) & OP_MASK_SEQI;
|
|
/* Sign-extend it. */
|
|
op = (op ^ 512) - 512;
|
|
fprintf (stream, "%d", op);
|
|
break;
|
|
|
|
default:
|
|
/* xgettext:c-format */
|
|
fprintf (stream, "# internal error, undefined extension sequence (+%c)", *d);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case '2':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_BP) & OP_MASK_BP);
|
|
break;
|
|
|
|
case '3':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_SA3) & OP_MASK_SA3);
|
|
break;
|
|
|
|
case '4':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_SA4) & OP_MASK_SA4);
|
|
break;
|
|
|
|
case '5':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_IMM8) & OP_MASK_IMM8);
|
|
break;
|
|
|
|
case '6':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_RS) & OP_MASK_RS);
|
|
break;
|
|
|
|
case '7':
|
|
fprintf (stream, "$ac%ld",
|
|
(l >> OP_SH_DSPACC) & OP_MASK_DSPACC);
|
|
break;
|
|
|
|
case '8':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_WRDSP) & OP_MASK_WRDSP);
|
|
break;
|
|
|
|
case '9':
|
|
fprintf (stream, "$ac%ld",
|
|
(l >> OP_SH_DSPACC_S) & OP_MASK_DSPACC_S);
|
|
break;
|
|
|
|
case '0': /* dsp 6-bit signed immediate in bit 20 */
|
|
delta = ((l >> OP_SH_DSPSFT) & OP_MASK_DSPSFT);
|
|
if (delta & 0x20) /* test sign bit */
|
|
delta |= ~OP_MASK_DSPSFT;
|
|
fprintf (stream, "%d", delta);
|
|
break;
|
|
|
|
case ':': /* dsp 7-bit signed immediate in bit 19 */
|
|
delta = ((l >> OP_SH_DSPSFT_7) & OP_MASK_DSPSFT_7);
|
|
if (delta & 0x40) /* test sign bit */
|
|
delta |= ~OP_MASK_DSPSFT_7;
|
|
fprintf (stream, "%d", delta);
|
|
break;
|
|
|
|
case '\'':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_RDDSP) & OP_MASK_RDDSP);
|
|
break;
|
|
|
|
case '@': /* dsp 10-bit signed immediate in bit 16 */
|
|
delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10);
|
|
if (delta & 0x200) /* test sign bit */
|
|
delta |= ~OP_MASK_IMM10;
|
|
fprintf (stream, "%d", delta);
|
|
break;
|
|
|
|
case '!':
|
|
fprintf (stream, "%ld",
|
|
(l >> OP_SH_MT_U) & OP_MASK_MT_U);
|
|
break;
|
|
|
|
case '$':
|
|
fprintf (stream, "%ld",
|
|
(l >> OP_SH_MT_H) & OP_MASK_MT_H);
|
|
break;
|
|
|
|
case '*':
|
|
fprintf (stream, "$ac%ld",
|
|
(l >> OP_SH_MTACC_T) & OP_MASK_MTACC_T);
|
|
break;
|
|
|
|
case '&':
|
|
fprintf (stream, "$ac%ld",
|
|
(l >> OP_SH_MTACC_D) & OP_MASK_MTACC_D);
|
|
break;
|
|
|
|
case 'g':
|
|
/* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2. */
|
|
fprintf (stream, "$%ld",
|
|
(l >> OP_SH_RD) & OP_MASK_RD);
|
|
break;
|
|
|
|
case 's':
|
|
case 'b':
|
|
case 'r':
|
|
case 'v':
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[(l >> OP_SH_RS) & OP_MASK_RS]);
|
|
break;
|
|
|
|
case 't':
|
|
case 'w':
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
|
|
break;
|
|
|
|
case 'i':
|
|
case 'u':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE);
|
|
break;
|
|
|
|
case 'j': /* Same as i, but sign-extended. */
|
|
case 'o':
|
|
delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
|
|
if (delta & 0x8000)
|
|
delta |= ~0xffff;
|
|
fprintf (stream, "%d", delta);
|
|
break;
|
|
|
|
case 'h':
|
|
fprintf (stream, "0x%x",
|
|
(unsigned int) ((l >> OP_SH_PREFX)
|
|
& OP_MASK_PREFX));
|
|
break;
|
|
|
|
case 'k':
|
|
fprintf (stream, "0x%x",
|
|
(unsigned int) ((l >> OP_SH_CACHE)
|
|
& OP_MASK_CACHE));
|
|
break;
|
|
|
|
case 'a':
|
|
target = (((pc + 4) & ~(unsigned) 0x0fffffff)
|
|
| (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2));
|
|
/* For gdb disassembler, force odd address on jalx. */
|
|
if (strcmp (opp->name, "jalx") == 0)
|
|
target |= 1;
|
|
print_address (target, stream);
|
|
break;
|
|
|
|
case 'p':
|
|
/* Sign extend the displacement. */
|
|
delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
|
|
if (delta & 0x8000)
|
|
delta |= ~0xffff;
|
|
target = (delta << 2) + pc + INSNLEN;
|
|
print_address (target, stream);
|
|
break;
|
|
|
|
case 'd':
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
|
|
break;
|
|
|
|
case 'U':
|
|
{
|
|
/* First check for both rd and rt being equal. */
|
|
unsigned int reg = (l >> OP_SH_RD) & OP_MASK_RD;
|
|
if (reg == ((l >> OP_SH_RT) & OP_MASK_RT))
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[reg]);
|
|
else {
|
|
/* If one is zero use the other. */
|
|
if (reg == 0)
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
|
|
else if (((l >> OP_SH_RT) & OP_MASK_RT) == 0)
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[reg]);
|
|
else /* Bogus, result depends on processor. */
|
|
fprintf (stream, "%s or %s",
|
|
mips_gpr_names[reg],
|
|
mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'z':
|
|
fprintf (stream, "%s", mips_gpr_names[0]);
|
|
break;
|
|
|
|
case '<':
|
|
case '1':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_SHAMT) & OP_MASK_SHAMT);
|
|
break;
|
|
|
|
case 'c':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_CODE) & OP_MASK_CODE);
|
|
break;
|
|
|
|
case 'q':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_CODE2) & OP_MASK_CODE2);
|
|
break;
|
|
|
|
case 'C':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_COPZ) & OP_MASK_COPZ);
|
|
break;
|
|
|
|
case 'B':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_CODE20) & OP_MASK_CODE20);
|
|
break;
|
|
|
|
case 'J':
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_CODE19) & OP_MASK_CODE19);
|
|
break;
|
|
|
|
case 'S':
|
|
case 'V':
|
|
fprintf (stream, "%s",
|
|
mips_fpr_names[(l >> OP_SH_FS) & OP_MASK_FS]);
|
|
break;
|
|
|
|
case 'T':
|
|
case 'W':
|
|
fprintf (stream, "%s",
|
|
mips_fpr_names[(l >> OP_SH_FT) & OP_MASK_FT]);
|
|
break;
|
|
|
|
case 'D':
|
|
fprintf (stream, "%s",
|
|
mips_fpr_names[(l >> OP_SH_FD) & OP_MASK_FD]);
|
|
break;
|
|
|
|
case 'R':
|
|
fprintf (stream, "%s",
|
|
mips_fpr_names[(l >> OP_SH_FR) & OP_MASK_FR]);
|
|
break;
|
|
|
|
case 'E':
|
|
/* Coprocessor register for lwcN instructions, et al.
|
|
*
|
|
* Note that there is no load/store cp0 instructions, and
|
|
* that FPU (cp1) instructions disassemble this field using
|
|
* 'T' format. Therefore, until we gain understanding of
|
|
* cp2 register names, we can simply print the register
|
|
* numbers. */
|
|
fprintf (stream, "$%ld",
|
|
(l >> OP_SH_RT) & OP_MASK_RT);
|
|
break;
|
|
|
|
case 'G':
|
|
/* Coprocessor register for mtcN instructions, et al. Note
|
|
* that FPU (cp1) instructions disassemble this field using
|
|
* 'S' format. Therefore, we only need to worry about cp0,
|
|
* cp2, and cp3. */
|
|
op = (l >> OP_SH_OP) & OP_MASK_OP;
|
|
if (op == OP_OP_COP0)
|
|
fprintf (stream, "%s",
|
|
mips_cp0_names[(l >> OP_SH_RD) & OP_MASK_RD]);
|
|
else
|
|
fprintf (stream, "$%ld",
|
|
(l >> OP_SH_RD) & OP_MASK_RD);
|
|
break;
|
|
|
|
case 'K':
|
|
fprintf (stream, "%s",
|
|
mips_hwr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
|
|
break;
|
|
|
|
case 'N':
|
|
fprintf (stream,
|
|
((opp->pinfo & (FP_D | FP_S)) != 0
|
|
? "$fcc%ld" : "$cc%ld"), (l >> OP_SH_BCC) & OP_MASK_BCC);
|
|
break;
|
|
|
|
case 'M':
|
|
fprintf (stream, "$fcc%ld",
|
|
(l >> OP_SH_CCC) & OP_MASK_CCC);
|
|
break;
|
|
|
|
case 'P':
|
|
fprintf (stream, "%ld",
|
|
(l >> OP_SH_PERFREG) & OP_MASK_PERFREG);
|
|
break;
|
|
|
|
case 'e':
|
|
fprintf (stream, "%ld",
|
|
(l >> OP_SH_VECBYTE) & OP_MASK_VECBYTE);
|
|
break;
|
|
|
|
case '%':
|
|
fprintf (stream, "%ld",
|
|
(l >> OP_SH_VECALIGN) & OP_MASK_VECALIGN);
|
|
break;
|
|
|
|
case 'H':
|
|
fprintf (stream, "%ld",
|
|
(l >> OP_SH_SEL) & OP_MASK_SEL);
|
|
break;
|
|
|
|
case 'O':
|
|
fprintf (stream, "%ld",
|
|
(l >> OP_SH_ALN) & OP_MASK_ALN);
|
|
break;
|
|
|
|
case 'Q':
|
|
{
|
|
unsigned int vsel = (l >> OP_SH_VSEL) & OP_MASK_VSEL;
|
|
|
|
if ((vsel & 0x10) == 0) {
|
|
int fmt;
|
|
|
|
vsel &= 0x0f;
|
|
for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
|
|
if ((vsel & 1) == 0)
|
|
break;
|
|
fprintf (stream, "$v%ld[%d]",
|
|
(l >> OP_SH_FT) & OP_MASK_FT, vsel >> 1);
|
|
} else if ((vsel & 0x08) == 0) {
|
|
fprintf (stream, "$v%ld",
|
|
(l >> OP_SH_FT) & OP_MASK_FT);
|
|
} else {
|
|
fprintf (stream, "0x%lx",
|
|
(l >> OP_SH_FT) & OP_MASK_FT);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'X':
|
|
fprintf (stream, "$v%ld",
|
|
(l >> OP_SH_FD) & OP_MASK_FD);
|
|
break;
|
|
|
|
case 'Y':
|
|
fprintf (stream, "$v%ld",
|
|
(l >> OP_SH_FS) & OP_MASK_FS);
|
|
break;
|
|
|
|
case 'Z':
|
|
fprintf (stream, "$v%ld",
|
|
(l >> OP_SH_FT) & OP_MASK_FT);
|
|
break;
|
|
|
|
default:
|
|
/* xgettext:c-format */
|
|
fprintf (stream, "# internal error, undefined modifier (%c)\n", *d);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print the mips instruction at address MEMADDR.
|
|
*/
|
|
static void
|
|
print_insn_mips (unsigned memaddr,
|
|
unsigned long int word, FILE *stream)
|
|
{
|
|
const struct mips_opcode *op;
|
|
static int init = 0;
|
|
static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
|
|
|
|
/* Build a hash table to shorten the search time. */
|
|
if (! init) {
|
|
unsigned int i;
|
|
|
|
for (i = 0; i <= OP_MASK_OP; i++) {
|
|
for (op = mips_opcodes; op < &mips_opcodes[mips_num_opcodes]; op++) {
|
|
if (op->pinfo == INSN_MACRO
|
|
|| (no_aliases && (op->pinfo2 & INSN2_ALIAS)))
|
|
continue;
|
|
if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)) {
|
|
mips_hash[i] = op;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
init = 1;
|
|
}
|
|
|
|
target = 0;
|
|
|
|
op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP];
|
|
if (op != NULL) {
|
|
for (; op < &mips_opcodes[mips_num_opcodes]; op++) {
|
|
if (op->pinfo != INSN_MACRO
|
|
&& !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
|
|
&& (word & op->mask) == op->match) {
|
|
const char *d;
|
|
|
|
fprintf (stream, "%s", op->name);
|
|
|
|
d = op->args;
|
|
if (d != NULL && *d != '\0') {
|
|
fprintf (stream, "\t");
|
|
print_insn_args (d, word, memaddr, stream, op);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Handle undefined instructions. */
|
|
fprintf (stream, "0x%lx", word);
|
|
}
|
|
|
|
/*
|
|
* Disassemble an operand for a mips16 instruction.
|
|
*/
|
|
static void
|
|
print_mips16_insn_arg (char type,
|
|
const struct mips_opcode *op,
|
|
int l,
|
|
int use_extend,
|
|
int extend, unsigned memaddr, FILE *stream)
|
|
{
|
|
switch (type) {
|
|
case ',':
|
|
case '(':
|
|
case ')':
|
|
fprintf (stream, "%c", type);
|
|
break;
|
|
|
|
case 'y':
|
|
case 'w':
|
|
fprintf (stream, "%s",
|
|
mips16_reg_names (((l >> MIPS16OP_SH_RY)
|
|
& MIPS16OP_MASK_RY)));
|
|
break;
|
|
|
|
case 'x':
|
|
case 'v':
|
|
fprintf (stream, "%s",
|
|
mips16_reg_names (((l >> MIPS16OP_SH_RX)
|
|
& MIPS16OP_MASK_RX)));
|
|
break;
|
|
|
|
case 'z':
|
|
fprintf (stream, "%s",
|
|
mips16_reg_names (((l >> MIPS16OP_SH_RZ)
|
|
& MIPS16OP_MASK_RZ)));
|
|
break;
|
|
|
|
case 'Z':
|
|
fprintf (stream, "%s",
|
|
mips16_reg_names (((l >> MIPS16OP_SH_MOVE32Z)
|
|
& MIPS16OP_MASK_MOVE32Z)));
|
|
break;
|
|
|
|
case '0':
|
|
fprintf (stream, "%s", mips_gpr_names[0]);
|
|
break;
|
|
|
|
case 'S':
|
|
fprintf (stream, "%s", mips_gpr_names[29]);
|
|
break;
|
|
|
|
case 'P':
|
|
fprintf (stream, "$pc");
|
|
break;
|
|
|
|
case 'R':
|
|
fprintf (stream, "%s", mips_gpr_names[31]);
|
|
break;
|
|
|
|
case 'X':
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[((l >> MIPS16OP_SH_REGR32)
|
|
& MIPS16OP_MASK_REGR32)]);
|
|
break;
|
|
|
|
case 'Y':
|
|
fprintf (stream, "%s",
|
|
mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]);
|
|
break;
|
|
|
|
case '<':
|
|
case '>':
|
|
case '[':
|
|
case ']':
|
|
case '4':
|
|
case '5':
|
|
case 'H':
|
|
case 'W':
|
|
case 'D':
|
|
case 'j':
|
|
case '6':
|
|
case '8':
|
|
case 'V':
|
|
case 'C':
|
|
case 'U':
|
|
case 'k':
|
|
case 'K':
|
|
case 'p':
|
|
case 'q':
|
|
case 'A':
|
|
case 'B':
|
|
case 'E':
|
|
{
|
|
int immed, nbits, shift, signedp, extbits, pcrel, extu, branch;
|
|
|
|
shift = 0;
|
|
signedp = 0;
|
|
extbits = 16;
|
|
pcrel = 0;
|
|
extu = 0;
|
|
branch = 0;
|
|
switch (type) {
|
|
case '<':
|
|
nbits = 3;
|
|
immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
|
|
extbits = 5;
|
|
extu = 1;
|
|
break;
|
|
case '>':
|
|
nbits = 3;
|
|
immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
|
|
extbits = 5;
|
|
extu = 1;
|
|
break;
|
|
case '[':
|
|
nbits = 3;
|
|
immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
|
|
extbits = 6;
|
|
extu = 1;
|
|
break;
|
|
case ']':
|
|
nbits = 3;
|
|
immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
|
|
extbits = 6;
|
|
extu = 1;
|
|
break;
|
|
case '4':
|
|
nbits = 4;
|
|
immed = (l >> MIPS16OP_SH_IMM4) & MIPS16OP_MASK_IMM4;
|
|
signedp = 1;
|
|
extbits = 15;
|
|
break;
|
|
case '5':
|
|
nbits = 5;
|
|
immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
|
|
break;
|
|
case 'H':
|
|
nbits = 5;
|
|
shift = 1;
|
|
immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
|
|
break;
|
|
case 'W':
|
|
nbits = 5;
|
|
shift = 2;
|
|
immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
|
|
break;
|
|
case 'D':
|
|
nbits = 5;
|
|
shift = 3;
|
|
immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
|
|
break;
|
|
case 'j':
|
|
nbits = 5;
|
|
immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
|
|
signedp = 1;
|
|
break;
|
|
case '6':
|
|
nbits = 6;
|
|
immed = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
|
|
break;
|
|
case '8':
|
|
nbits = 8;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
break;
|
|
case 'V':
|
|
nbits = 8;
|
|
shift = 2;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
/* FIXME: This might be lw, or it might be addiu to $sp or
|
|
* $pc. We assume it's load. */
|
|
break;
|
|
case 'C':
|
|
nbits = 8;
|
|
shift = 3;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
break;
|
|
case 'U':
|
|
nbits = 8;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
extu = 1;
|
|
break;
|
|
case 'k':
|
|
nbits = 8;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
signedp = 1;
|
|
break;
|
|
case 'K':
|
|
nbits = 8;
|
|
shift = 3;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
signedp = 1;
|
|
break;
|
|
case 'p':
|
|
nbits = 8;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
signedp = 1;
|
|
pcrel = 1;
|
|
branch = 1;
|
|
break;
|
|
case 'q':
|
|
nbits = 11;
|
|
immed = (l >> MIPS16OP_SH_IMM11) & MIPS16OP_MASK_IMM11;
|
|
signedp = 1;
|
|
pcrel = 1;
|
|
branch = 1;
|
|
break;
|
|
case 'A':
|
|
nbits = 8;
|
|
shift = 2;
|
|
immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
|
|
pcrel = 1;
|
|
/* FIXME: This can be lw or la. We assume it is lw. */
|
|
break;
|
|
case 'B':
|
|
nbits = 5;
|
|
shift = 3;
|
|
immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
|
|
pcrel = 1;
|
|
break;
|
|
case 'E':
|
|
nbits = 5;
|
|
shift = 2;
|
|
immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
|
|
pcrel = 1;
|
|
break;
|
|
default:
|
|
fprintf (stream, "# internal disassembler error, unrecognised mips16 type (%c)", type);
|
|
abort ();
|
|
}
|
|
|
|
if (!use_extend) {
|
|
if (signedp && immed >= (1 << (nbits - 1)))
|
|
immed -= 1 << nbits;
|
|
immed <<= shift;
|
|
if ((type == '<' || type == '>' || type == '[' || type == ']')
|
|
&& immed == 0)
|
|
immed = 8;
|
|
} else {
|
|
if (extbits == 16)
|
|
immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0);
|
|
else if (extbits == 15)
|
|
immed |= ((extend & 0xf) << 11) | (extend & 0x7f0);
|
|
else
|
|
immed = ((extend >> 6) & 0x1f) | (extend & 0x20);
|
|
immed &= (1 << extbits) - 1;
|
|
if (!extu && immed >= (1 << (extbits - 1)))
|
|
immed -= 1 << extbits;
|
|
}
|
|
|
|
if (!pcrel)
|
|
fprintf (stream, "%d", immed);
|
|
else {
|
|
unsigned baseaddr;
|
|
|
|
if (branch) {
|
|
immed *= 2;
|
|
baseaddr = memaddr + 2;
|
|
} else if (use_extend) {
|
|
baseaddr = memaddr - 2;
|
|
} else {
|
|
baseaddr = memaddr;
|
|
}
|
|
target = (baseaddr & ~((1 << shift) - 1)) + immed;
|
|
if (pcrel && branch)
|
|
/* For gdb disassembler, maintain odd address. */
|
|
target |= 1;
|
|
print_address (target, stream);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'a':
|
|
{
|
|
int jalx = l & 0x400;
|
|
|
|
if (!use_extend)
|
|
extend = 0;
|
|
l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2);
|
|
if (! jalx)
|
|
/* For gdb disassembler, maintain odd address. */
|
|
l |= 1;
|
|
}
|
|
target = ((memaddr + 4) & ~(unsigned) 0x0fffffff) | l;
|
|
print_address (target, stream);
|
|
break;
|
|
|
|
case 'l':
|
|
case 'L':
|
|
{
|
|
int need_comma, amask, smask;
|
|
|
|
need_comma = 0;
|
|
|
|
l = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
|
|
|
|
amask = (l >> 3) & 7;
|
|
|
|
if (amask > 0 && amask < 5) {
|
|
fprintf (stream, "%s", mips_gpr_names[4]);
|
|
if (amask > 1)
|
|
fprintf (stream, "-%s",
|
|
mips_gpr_names[amask + 3]);
|
|
need_comma = 1;
|
|
}
|
|
|
|
smask = (l >> 1) & 3;
|
|
if (smask == 3) {
|
|
fprintf (stream, "%s??",
|
|
need_comma ? "," : "");
|
|
need_comma = 1;
|
|
} else if (smask > 0) {
|
|
fprintf (stream, "%s%s",
|
|
need_comma ? "," : "", mips_gpr_names[16]);
|
|
if (smask > 1)
|
|
fprintf (stream, "-%s",
|
|
mips_gpr_names[smask + 15]);
|
|
need_comma = 1;
|
|
}
|
|
|
|
if (l & 1) {
|
|
fprintf (stream, "%s%s",
|
|
need_comma ? "," : "", mips_gpr_names[31]);
|
|
need_comma = 1;
|
|
}
|
|
|
|
if (amask == 5 || amask == 6) {
|
|
fprintf (stream, "%s$f0",
|
|
need_comma ? "," : "");
|
|
if (amask == 6)
|
|
fprintf (stream, "-$f1");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
case 'M':
|
|
/* MIPS16e save/restore. */
|
|
{
|
|
int need_comma = 0;
|
|
int amask, args, statics;
|
|
int nsreg, smask;
|
|
int framesz;
|
|
int i, j;
|
|
|
|
l = l & 0x7f;
|
|
if (use_extend)
|
|
l |= extend << 16;
|
|
|
|
amask = (l >> 16) & 0xf;
|
|
if (amask == MIPS16_ALL_ARGS) {
|
|
args = 4;
|
|
statics = 0;
|
|
} else if (amask == MIPS16_ALL_STATICS) {
|
|
args = 0;
|
|
statics = 4;
|
|
} else {
|
|
args = amask >> 2;
|
|
statics = amask & 3;
|
|
}
|
|
|
|
if (args > 0) {
|
|
fprintf (stream, "%s", mips_gpr_names[4]);
|
|
if (args > 1)
|
|
fprintf (stream, "-%s",
|
|
mips_gpr_names[4 + args - 1]);
|
|
need_comma = 1;
|
|
}
|
|
|
|
framesz = (((l >> 16) & 0xf0) | (l & 0x0f)) * 8;
|
|
if (framesz == 0 && !use_extend)
|
|
framesz = 128;
|
|
|
|
fprintf (stream, "%s%d",
|
|
need_comma ? "," : "", framesz);
|
|
|
|
if (l & 0x40) /* $ra */
|
|
fprintf (stream, ",%s",
|
|
mips_gpr_names[31]);
|
|
|
|
nsreg = (l >> 24) & 0x7;
|
|
smask = 0;
|
|
if (l & 0x20) /* $s0 */
|
|
smask |= 1 << 0;
|
|
if (l & 0x10) /* $s1 */
|
|
smask |= 1 << 1;
|
|
if (nsreg > 0) /* $s2-$s8 */
|
|
smask |= ((1 << nsreg) - 1) << 2;
|
|
|
|
/* Find first set static reg bit. */
|
|
for (i = 0; i < 9; i++) {
|
|
if (smask & (1 << i)) {
|
|
fprintf (stream, ",%s",
|
|
mips_gpr_names[i == 8 ? 30 : (16 + i)]);
|
|
/* Skip over string of set bits. */
|
|
for (j = i; smask & (2 << j); j++)
|
|
continue;
|
|
if (j > i)
|
|
fprintf (stream, "-%s",
|
|
mips_gpr_names[j == 8 ? 30 : (16 + j)]);
|
|
i = j + 1;
|
|
}
|
|
}
|
|
|
|
/* Statics $ax - $a3. */
|
|
if (statics == 1)
|
|
fprintf (stream, ",%s",
|
|
mips_gpr_names[7]);
|
|
else if (statics > 0)
|
|
fprintf (stream, ",%s-%s",
|
|
mips_gpr_names[7 - statics + 1], mips_gpr_names[7]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* xgettext:c-format */
|
|
fprintf (stream, "# internal disassembler error, unrecognised modifier (%c)\n", type);
|
|
abort ();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Disassemble mips16 instructions.
|
|
*/
|
|
static void print_insn_mips16 (unsigned memaddr,
|
|
unsigned int opcode, int nbytes, FILE *stream)
|
|
{
|
|
int insn, use_extend, extend;
|
|
const struct mips_opcode *op, *opend;
|
|
|
|
insn = opcode;
|
|
if (nbytes == 2) {
|
|
use_extend = 0;
|
|
extend = 0;
|
|
insn = opcode;
|
|
} else {
|
|
if ((opcode & 0xf8000000) == 0xf0000000) {
|
|
/* Handle the extend opcode specially. */
|
|
use_extend = 1;
|
|
insn = opcode & 0xffff;
|
|
extend = (opcode >> 16) & 0x7ff;
|
|
} else {
|
|
/* jal, jalx */
|
|
use_extend = 0;
|
|
insn = opcode >> 16;
|
|
extend = opcode & 0xffff;
|
|
}
|
|
}
|
|
|
|
/* FIXME: Should probably use a hash table on the major opcode here. */
|
|
|
|
opend = mips16_opcodes + mips16_num_opcodes;
|
|
for (op = mips16_opcodes; op < opend; op++) {
|
|
if (op->pinfo != INSN_MACRO
|
|
&& (insn & op->mask) == op->match) {
|
|
const char *s;
|
|
|
|
fprintf (stream, "%s", op->name);
|
|
if (op->args[0] != '\0')
|
|
fprintf (stream, "\t");
|
|
|
|
for (s = op->args; *s != '\0'; s++) {
|
|
if (*s == ','
|
|
&& s[1] == 'w'
|
|
&& (((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)
|
|
== ((insn >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY))) {
|
|
/* Skip the register and the comma. */
|
|
++s;
|
|
continue;
|
|
}
|
|
if (*s == ','
|
|
&& s[1] == 'v'
|
|
&& (((insn >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ)
|
|
== ((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX))) {
|
|
/* Skip the register and the comma. */
|
|
++s;
|
|
continue;
|
|
}
|
|
print_mips16_insn_arg (*s, op, insn,
|
|
use_extend, extend, memaddr, stream);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
fprintf (stream, "0x");
|
|
if (use_extend)
|
|
fprintf (stream, "%04x", extend | 0xf000);
|
|
fprintf (stream, "%04x", insn);
|
|
}
|
|
|
|
/*
|
|
* Disassemble and print the instruction mnemonics.
|
|
* Opcode size can be 4 or 2 bytes.
|
|
* Parameter `isa' defines an instruction set architecture:
|
|
* 0 - mips32 encoding
|
|
* 1 - mips16e encoding
|
|
* 2 - micromips encoding (someday)
|
|
*/
|
|
void print_mips (unsigned memaddr, unsigned int opcode, int nbytes,
|
|
int isa, FILE *stream)
|
|
{
|
|
switch (isa) {
|
|
default:
|
|
case 0:
|
|
print_insn_mips (memaddr, opcode, stream);
|
|
break;
|
|
case 1:
|
|
print_insn_mips16 (memaddr, opcode, nbytes, stream);
|
|
break;
|
|
case 2:
|
|
/* TODO: micromips encoding */
|
|
break;
|
|
}
|
|
}
|