diff options
-rw-r--r-- | gas/ChangeLog | 9 | ||||
-rw-r--r-- | gas/config/tc-mips.c | 455 | ||||
-rw-r--r-- | include/opcode/ChangeLog | 10 | ||||
-rw-r--r-- | include/opcode/mips.h | 288 | ||||
-rw-r--r-- | opcodes/ChangeLog | 26 | ||||
-rw-r--r-- | opcodes/micromips-opc.c | 149 | ||||
-rw-r--r-- | opcodes/mips-dis.c | 1401 | ||||
-rw-r--r-- | opcodes/mips-formats.h | 113 | ||||
-rw-r--r-- | opcodes/mips-opc.c | 117 |
9 files changed, 1134 insertions, 1434 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index dc609b9ba5..0162174715 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,5 +1,14 @@ 2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * config/tc-mips.c (validate_mips_insn): Move further up file. + Add insn_bits and decode_operand arguments. Use the mips_operand + fields to work out which bits an operand occupies. Detect double + definitions. + (validate_micromips_insn): Move further up file. Call into + validate_mips_insn. + +2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * config/tc-mips.c (mips16_macro_build): Remove 'Y' case. 2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index 4e878c89d3..6bb44c571b 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -1326,8 +1326,6 @@ static void s_mips_file (int); static void s_mips_loc (int); static bfd_boolean pic_need_relax (symbolS *, asection *); static int relaxed_branch_length (fragS *, asection *, int); -static int validate_mips_insn (const struct mips_opcode *); -static int validate_micromips_insn (const struct mips_opcode *); static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int); static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int); @@ -2707,6 +2705,111 @@ is_delay_slot_valid (const struct mips_opcode *mo) return TRUE; } +/* For consistency checking, verify that all bits of OPCODE are + specified either by the match/mask part of the instruction + definition, or by the operand list. INSN_BITS says which + bits of the instruction are significant and DECODE_OPERAND + provides the mips_operand description of each operand. */ + +static int +validate_mips_insn (const struct mips_opcode *opcode, + unsigned long insn_bits, + const struct mips_operand *(*decode_operand) (const char *)) +{ + const char *s; + unsigned long used_bits, doubled, undefined; + const struct mips_operand *operand; + + if ((opcode->mask & opcode->match) != opcode->match) + { + as_bad (_("internal: bad mips opcode (mask error): %s %s"), + opcode->name, opcode->args); + return 0; + } + used_bits = 0; + for (s = opcode->args; *s; ++s) + switch (*s) + { + case ',': + case '(': + case ')': + break; + + default: + operand = decode_operand (s); + if (!operand) + { + as_bad (_("internal: unknown operand type: %s %s"), + opcode->name, opcode->args); + return 0; + } + used_bits |= ((1 << operand->size) - 1) << operand->lsb; + if (operand->type == OP_MDMX_IMM_REG) + /* Bit 5 is the format selector (OB vs QH). The opcode table + has separate entries for each format. */ + used_bits &= ~(1 << (operand->lsb + 5)); + /* Skip prefix characters. */ + if (*s == '+' || *s == 'm') + ++s; + break; + } + doubled = used_bits & opcode->mask & insn_bits; + if (doubled) + { + as_bad (_("internal: bad mips opcode (bits 0x%08lx doubly defined):" + " %s %s"), doubled, opcode->name, opcode->args); + return 0; + } + used_bits |= opcode->mask; + undefined = ~used_bits & insn_bits; + if (undefined) + { + as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"), + undefined, opcode->name, opcode->args); + return 0; + } + used_bits &= ~insn_bits; + if (used_bits) + { + as_bad (_("internal: bad mips opcode (bits 0x%08lx defined): %s %s"), + used_bits, opcode->name, opcode->args); + return 0; + } + return 1; +} + +/* The microMIPS version of validate_mips_insn. */ + +static int +validate_micromips_insn (const struct mips_opcode *opc) +{ + unsigned long insn_bits; + unsigned long major; + unsigned int length; + + length = micromips_insn_length (opc); + if (length != 2 && length != 4) + { + as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): " + "%s %s"), length, opc->name, opc->args); + return 0; + } + major = opc->match >> (10 + 8 * (length - 2)); + if ((length == 2 && (major & 7) != 1 && (major & 6) != 2) + || (length == 4 && (major & 7) != 0 && (major & 4) != 4)) + { + as_bad (_("Internal error: bad microMIPS opcode " + "(opcode/length mismatch): %s %s"), opc->name, opc->args); + return 0; + } + + /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */ + insn_bits = 1 << 4 * length; + insn_bits <<= 4 * length; + insn_bits -= 1; + return validate_mips_insn (opc, insn_bits, decode_micromips_operand); +} + /* This function is called once, at assembler startup time. It should set up all the tables, etc. that the MD part of the assembler will need. */ @@ -2745,7 +2848,8 @@ md_begin (void) { if (mips_opcodes[i].pinfo != INSN_MACRO) { - if (!validate_mips_insn (&mips_opcodes[i])) + if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff, + decode_mips_operand)) broken = 1; if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0) { @@ -10748,351 +10852,6 @@ mips16_macro (struct mips_cl_insn *ip) } } -/* For consistency checking, verify that all bits are specified either - by the match/mask part of the instruction definition, or by the - operand list. */ -static int -validate_mips_insn (const struct mips_opcode *opc) -{ - const char *p = opc->args; - char c; - unsigned long used_bits = opc->mask; - - if ((used_bits & opc->match) != opc->match) - { - as_bad (_("internal: bad mips opcode (mask error): %s %s"), - opc->name, opc->args); - return 0; - } -#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift))) - while (*p) - switch (c = *p++) - { - case ',': break; - case '(': break; - case ')': break; - case '+': - switch (c = *p++) - { - case '1': USE_BITS (OP_MASK_UDI1, OP_SH_UDI1); break; - case '2': USE_BITS (OP_MASK_UDI2, OP_SH_UDI2); break; - case '3': USE_BITS (OP_MASK_UDI3, OP_SH_UDI3); break; - case '4': USE_BITS (OP_MASK_UDI4, OP_SH_UDI4); break; - case 'A': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; - case 'B': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break; - case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break; - case 'E': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; - case 'F': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break; - case 'G': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break; - case 'H': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break; - case 'I': break; - case 'J': USE_BITS (OP_MASK_CODE10, OP_SH_CODE10); break; - case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break; - case 'x': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break; - case 'X': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break; - case 'p': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break; - case 'P': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break; - case 'Q': USE_BITS (OP_MASK_SEQI, OP_SH_SEQI); break; - case 's': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break; - case 'S': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break; - case 'z': USE_BITS (OP_MASK_RZ, OP_SH_RZ); break; - case 'Z': USE_BITS (OP_MASK_FZ, OP_SH_FZ); break; - case 'a': USE_BITS (OP_MASK_OFFSET_A, OP_SH_OFFSET_A); break; - case 'b': USE_BITS (OP_MASK_OFFSET_B, OP_SH_OFFSET_B); break; - case 'c': USE_BITS (OP_MASK_OFFSET_C, OP_SH_OFFSET_C); break; - case 'i': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break; - case 'j': USE_BITS (OP_MASK_EVAOFFSET, OP_SH_EVAOFFSET); break; - - default: - as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"), - c, opc->name, opc->args); - return 0; - } - break; - case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; - case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break; - case 'A': break; - case 'B': USE_BITS (OP_MASK_CODE20, OP_SH_CODE20); break; - case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break; - case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break; - case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break; - case 'F': break; - case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break; - case 'H': USE_BITS (OP_MASK_SEL, OP_SH_SEL); break; - case 'I': break; - case 'J': USE_BITS (OP_MASK_CODE19, OP_SH_CODE19); break; - case 'K': USE_BITS (OP_MASK_RD, OP_SH_RD); break; - case 'L': break; - case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break; - case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break; - case 'O': USE_BITS (OP_MASK_ALN, OP_SH_ALN); break; - case 'Q': USE_BITS (OP_MASK_VSEL, OP_SH_VSEL); - USE_BITS (OP_MASK_FT, OP_SH_FT); break; - case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break; - case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break; - case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break; - case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break; - case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break; - case 'X': USE_BITS (OP_MASK_FD, OP_SH_FD); break; - case 'Y': USE_BITS (OP_MASK_FS, OP_SH_FS); break; - case 'Z': USE_BITS (OP_MASK_FT, OP_SH_FT); break; - case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break; - case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break; - case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break; - case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break; - case 'f': break; - case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break; - case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break; - case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; - case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break; - case 'l': break; - case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; - case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break; - case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break; - case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break; - case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break; - case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break; - case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break; - case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break; - case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break; - case 'x': break; - case 'z': break; - case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break; - case 'U': USE_BITS (OP_MASK_RD, OP_SH_RD); - USE_BITS (OP_MASK_RT, OP_SH_RT); break; - case 'e': USE_BITS (OP_MASK_VECBYTE, OP_SH_VECBYTE); break; - case '%': USE_BITS (OP_MASK_VECALIGN, OP_SH_VECALIGN); break; - case '1': USE_BITS (OP_MASK_STYPE, OP_SH_STYPE); break; - case '2': USE_BITS (OP_MASK_BP, OP_SH_BP); break; - case '3': USE_BITS (OP_MASK_SA3, OP_SH_SA3); break; - case '4': USE_BITS (OP_MASK_SA4, OP_SH_SA4); break; - case '5': USE_BITS (OP_MASK_IMM8, OP_SH_IMM8); break; - case '6': USE_BITS (OP_MASK_RS, OP_SH_RS); break; - case '7': USE_BITS (OP_MASK_DSPACC, OP_SH_DSPACC); break; - case '8': USE_BITS (OP_MASK_WRDSP, OP_SH_WRDSP); break; - case '9': USE_BITS (OP_MASK_DSPACC_S, OP_SH_DSPACC_S);break; - case '0': USE_BITS (OP_MASK_DSPSFT, OP_SH_DSPSFT); break; - case '\'': USE_BITS (OP_MASK_RDDSP, OP_SH_RDDSP); break; - case ':': USE_BITS (OP_MASK_DSPSFT_7, OP_SH_DSPSFT_7);break; - case '@': USE_BITS (OP_MASK_IMM10, OP_SH_IMM10); break; - case '!': USE_BITS (OP_MASK_MT_U, OP_SH_MT_U); break; - case '$': USE_BITS (OP_MASK_MT_H, OP_SH_MT_H); break; - case '*': USE_BITS (OP_MASK_MTACC_T, OP_SH_MTACC_T); break; - case '&': USE_BITS (OP_MASK_MTACC_D, OP_SH_MTACC_D); break; - case '\\': USE_BITS (OP_MASK_3BITPOS, OP_SH_3BITPOS); break; - case '~': USE_BITS (OP_MASK_OFFSET12, OP_SH_OFFSET12); break; - case 'g': USE_BITS (OP_MASK_RD, OP_SH_RD); break; - default: - as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"), - c, opc->name, opc->args); - return 0; - } -#undef USE_BITS - if (used_bits != 0xffffffff) - { - as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"), - ~used_bits & 0xffffffff, opc->name, opc->args); - return 0; - } - return 1; -} - -/* For consistency checking, verify that the length implied matches the - major opcode and that all bits are specified either by the match/mask - part of the instruction definition, or by the operand list. */ - -static int -validate_micromips_insn (const struct mips_opcode *opc) -{ - unsigned long match = opc->match; - unsigned long mask = opc->mask; - const char *p = opc->args; - unsigned long insn_bits; - unsigned long used_bits; - unsigned long major; - unsigned int length; - char e; - char c; - - if ((mask & match) != match) - { - as_bad (_("Internal error: bad microMIPS opcode (mask error): %s %s"), - opc->name, opc->args); - return 0; - } - length = micromips_insn_length (opc); - if (length != 2 && length != 4) - { - as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): " - "%s %s"), length, opc->name, opc->args); - return 0; - } - major = match >> (10 + 8 * (length - 2)); - if ((length == 2 && (major & 7) != 1 && (major & 6) != 2) - || (length == 4 && (major & 7) != 0 && (major & 4) != 4)) - { - as_bad (_("Internal error: bad microMIPS opcode " - "(opcode/length mismatch): %s %s"), opc->name, opc->args); - return 0; - } - - /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */ - insn_bits = 1 << 4 * length; - insn_bits <<= 4 * length; - insn_bits -= 1; - used_bits = mask; -#define USE_BITS(field) \ - (used_bits |= MICROMIPSOP_MASK_##field << MICROMIPSOP_SH_##field) - while (*p) - switch (c = *p++) - { - case ',': break; - case '(': break; - case ')': break; - case '+': - e = c; - switch (c = *p++) - { - case 'A': USE_BITS (EXTLSB); break; - case 'B': USE_BITS (INSMSB); break; - case 'C': USE_BITS (EXTMSBD); break; - case 'E': USE_BITS (EXTLSB); break; - case 'F': USE_BITS (INSMSB); break; - case 'G': USE_BITS (EXTMSBD); break; - case 'H': USE_BITS (EXTMSBD); break; - case 'i': USE_BITS (TARGET); break; - case 'j': USE_BITS (EVAOFFSET); break; - default: - as_bad (_("Internal error: bad mips opcode " - "(unknown extension operand type `%c%c'): %s %s"), - e, c, opc->name, opc->args); - return 0; - } - break; - case 'm': - e = c; - switch (c = *p++) - { - case 'A': USE_BITS (IMMA); break; - case 'B': USE_BITS (IMMB); break; - case 'C': USE_BITS (IMMC); break; - case 'D': USE_BITS (IMMD); break; - case 'E': USE_BITS (IMME); break; - case 'F': USE_BITS (IMMF); break; - case 'G': USE_BITS (IMMG); break; - case 'H': USE_BITS (IMMH); break; - case 'I': USE_BITS (IMMI); break; - case 'J': USE_BITS (IMMJ); break; - case 'L': USE_BITS (IMML); break; - case 'M': USE_BITS (IMMM); break; - case 'N': USE_BITS (IMMN); break; - case 'O': USE_BITS (IMMO); break; - case 'P': USE_BITS (IMMP); break; - case 'Q': USE_BITS (IMMQ); break; - case 'U': USE_BITS (IMMU); break; - case 'W': USE_BITS (IMMW); break; - case 'X': USE_BITS (IMMX); break; - case 'Y': USE_BITS (IMMY); break; - case 'Z': break; - case 'a': break; - case 'b': USE_BITS (MB); break; - case 'c': USE_BITS (MC); break; - case 'd': USE_BITS (MD); break; - case 'e': USE_BITS (ME); break; - case 'f': USE_BITS (MF); break; - case 'g': USE_BITS (MG); break; - case 'h': USE_BITS (MH); break; - case 'j': USE_BITS (MJ); break; - case 'l': USE_BITS (ML); break; - case 'm': USE_BITS (MM); break; - case 'n': USE_BITS (MN); break; - case 'p': USE_BITS (MP); break; - case 'q': USE_BITS (MQ); break; - case 'r': break; - case 's': break; - case 't': break; - case 'x': break; - case 'y': break; - case 'z': break; - default: - as_bad (_("Internal error: bad mips opcode " - "(unknown extension operand type `%c%c'): %s %s"), - e, c, opc->name, opc->args); - return 0; - } - break; - case '.': USE_BITS (OFFSET10); break; - case '1': USE_BITS (STYPE); break; - case '2': USE_BITS (BP); break; - case '3': USE_BITS (SA3); break; - case '4': USE_BITS (SA4); break; - case '5': USE_BITS (IMM8); break; - case '6': USE_BITS (RS); break; - case '7': USE_BITS (DSPACC); break; - case '8': USE_BITS (WRDSP); break; - case '0': USE_BITS (DSPSFT); break; - case '<': USE_BITS (SHAMT); break; - case '>': USE_BITS (SHAMT); break; - case '@': USE_BITS (IMM10); break; - case 'B': USE_BITS (CODE10); break; - case 'C': USE_BITS (COPZ); break; - case 'D': USE_BITS (FD); break; - case 'E': USE_BITS (RT); break; - case 'G': USE_BITS (RS); break; - case 'H': USE_BITS (SEL); break; - case 'K': USE_BITS (RS); break; - case 'M': USE_BITS (CCC); break; - case 'N': USE_BITS (BCC); break; - case 'R': USE_BITS (FR); break; - case 'S': USE_BITS (FS); break; - case 'T': USE_BITS (FT); break; - case 'V': USE_BITS (FS); break; - case '\\': USE_BITS (3BITPOS); break; - case '^': USE_BITS (RD); break; - case 'a': USE_BITS (TARGET); break; - case 'b': USE_BITS (RS); break; - case 'c': USE_BITS (CODE); break; - case 'd': USE_BITS (RD); break; - case 'h': USE_BITS (PREFX); break; - case 'i': USE_BITS (IMMEDIATE); break; - case 'j': USE_BITS (DELTA); break; - case 'k': USE_BITS (CACHE); break; - case 'n': USE_BITS (RT); break; - case 'o': USE_BITS (DELTA); break; - case 'p': USE_BITS (DELTA); break; - case 'q': USE_BITS (CODE2); break; - case 'r': USE_BITS (RS); break; - case 's': USE_BITS (RS); break; - case 't': USE_BITS (RT); break; - case 'u': USE_BITS (IMMEDIATE); break; - case 'v': USE_BITS (RS); break; - case 'w': USE_BITS (RT); break; - case 'y': USE_BITS (RS3); break; - case 'z': break; - case '|': USE_BITS (TRAP); break; - case '~': USE_BITS (OFFSET12); break; - default: - as_bad (_("Internal error: bad microMIPS opcode " - "(unknown operand type `%c'): %s %s"), - c, opc->name, opc->args); - return 0; - } -#undef USE_BITS - if (used_bits != insn_bits) - { - if (~used_bits & insn_bits) - as_bad (_("Internal error: bad microMIPS opcode " - "(bits 0x%lx undefined): %s %s"), - ~used_bits & insn_bits, opc->name, opc->args); - if (used_bits & ~insn_bits) - as_bad (_("Internal error: bad microMIPS opcode " - "(bits 0x%lx defined): %s %s"), - used_bits & ~insn_bits, opc->name, opc->args); - return 0; - } - return 1; -} - /* UDI immediates. */ struct mips_immed { char type; diff --git a/include/opcode/ChangeLog b/include/opcode/ChangeLog index a96a4ab1ba..fc991a01e4 100644 --- a/include/opcode/ChangeLog +++ b/include/opcode/ChangeLog @@ -1,5 +1,15 @@ 2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * mips.h (mips_operand_type, mips_reg_operand_type): New enums. + (mips_operand, mips_int_operand, mips_mapped_int_operand) + (mips_msb_operand, mips_reg_operand, mips_reg_pair_operand) + (mips_pcrel_operand): New structures. + (mips_insert_operand, mips_extract_operand, mips_signed_operand) + (mips_decode_int_operand, mips_decode_pcrel_operand): New functions. + (decode_mips_operand, decode_micromips_operand): Declare. + +2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * mips.h: Document MIPS16 "I" opcode. 2013-07-07 Richard Sandiford <rdsandiford@googlemail.com> diff --git a/include/opcode/mips.h b/include/opcode/mips.h index 21128892a9..2d77b95940 100644 --- a/include/opcode/mips.h +++ b/include/opcode/mips.h @@ -332,6 +332,292 @@ #define OP_SH_EVAOFFSET 7 #define OP_MASK_EVAOFFSET 0x1ff +/* Enumerates the various types of MIPS operand. */ +enum mips_operand_type { + /* Described by mips_int_operand. */ + OP_INT, + + /* Described by mips_mapped_int_operand. */ + OP_MAPPED_INT, + + /* Described by mips_msb_operand. */ + OP_MSB, + + /* Described by mips_reg_operand. */ + OP_REG, + + /* Described by mips_reg_pair_operand. */ + OP_REG_PAIR, + + /* Described by mips_pcrel_operand. */ + OP_PCREL, + + /* A performance register. The field is 5 bits in size, but the supported + values are much more restricted. */ + OP_PERF_REG, + + /* The final operand in a microMIPS ADDIUSP instruction. It mostly acts + as a normal 9-bit signed offset that is multiplied by four, but there + are four special cases: + + -2 * 4 => -258 * 4 + -1 * 4 => -257 * 4 + 0 * 4 => 256 * 4 + 1 * 4 => 257 * 4. */ + OP_ADDIUSP_INT, + + /* The target of a (D)CLO or (D)CLZ instruction. The operand spans two + 5-bit register fields, both of which must be set to the destination + register. */ + OP_CLO_CLZ_DEST, + + /* A register list for a microMIPS LWM or SWM instruction. The operand + size determines whether the 16-bit or 32-bit encoding is required. */ + OP_LWM_SWM_LIST, + + /* A 10-bit field VVVVVNNNNN used for octobyte and quadhalf instructions: + + V Meaning + ----- ------- + 0EEE0 8 copies of $vN[E], OB format + 0EE01 4 copies of $vN[E], QH format + 10110 all 8 elements of $vN, OB format + 10101 all 4 elements of $vN, QH format + 11110 8 copies of immediate N, OB format + 11101 4 copies of immediate N, QH format. */ + OP_MDMX_IMM_REG, + + /* A register operand that must match the destination register. */ + OP_REPEAT_DEST_REG, + + /* A register operand that must match the previous register. */ + OP_REPEAT_PREV_REG, + + /* $pc, which has no encoding in the architectural instruction. */ + OP_PC +}; + +/* Enumerates the types of MIPS register. */ +enum mips_reg_operand_type { + /* General registers $0-$31. Software names like $at can also be used. */ + OP_REG_GP, + + /* Floating-point registers $f0-$f31. */ + OP_REG_FP, + + /* Coprocessor condition code registers $cc0-$cc7. FPU condition codes + can also be written $fcc0-$fcc7. */ + OP_REG_CCC, + + /* FPRs used in a vector capacity. They can be written $f0-$f31 + or $v0-$v31, although the latter form is not used for the VR5400 + vector instructions. */ + OP_REG_VEC, + + /* DSP accumulator registers $ac0-$ac3. */ + OP_REG_ACC, + + /* Coprocessor registers $0-$31. Mnemonic names like c0_cause can + also be used in some contexts. */ + OP_REG_COPRO, + + /* Hardware registers $0-$31. Mnemonic names like hwr_cpunum can + also be used in some contexts. */ + OP_REG_HW +}; + +/* Base class for all operands. */ +struct mips_operand +{ + /* The type of the operand. */ + enum mips_operand_type type; + + /* The operand occupies SIZE bits of the instruction, starting at LSB. */ + unsigned short size; + unsigned short lsb; +}; + +/* Describes an integer operand with a regular encoding pattern. */ +struct mips_int_operand +{ + struct mips_operand root; + + /* The low ROOT.SIZE bits of MAX_VAL encodes (MAX_VAL + BIAS) << SHIFT. + The cyclically previous field value encodes 1 << SHIFT less than that, + and so on. E.g. + + - for { { T, 4, L }, 14, 0, 0 }, field values 0...14 encode themselves, + but 15 encodes -1. + + - { { T, 8, L }, 127, 0, 2 } is a normal signed 8-bit operand that is + shifted left two places. + + - { { T, 3, L }, 8, 0, 0 } is a normal unsigned 3-bit operand except + that 0 encodes 8. + + - { { ... }, 0, 1, 3 } means that N encodes (N + 1) << 3. */ + unsigned int max_val; + int bias; + unsigned int shift; + + /* True if the operand should be printed as hex rather than decimal. */ + bfd_boolean print_hex; +}; + +/* Uses a lookup table to describe a small integer operand. */ +struct mips_mapped_int_operand +{ + struct mips_operand root; + + /* Maps each encoding value to the integer that it represents. */ + const int *int_map; + + /* True if the operand should be printed as hex rather than decimal. */ + bfd_boolean print_hex; +}; + +/* An operand that encodes the most significant bit position of a bitfield. + Given a bitfield that spans bits [MSB, LSB], some operands of this type + encode MSB directly while others encode MSB - LSB. Each operand of this + type is preceded by an integer operand that specifies LSB. + + The assembly form varies between instructions. For some instructions, + such as EXT, the operand is written as the bitfield size. For others, + such as EXTS, it is written in raw MSB - LSB form. */ +struct mips_msb_operand +{ + struct mips_operand root; + + /* The assembly-level operand encoded by a field value of 0. */ + int bias; + + /* True if the operand encodes MSB directly, false if it encodes + MSB - LSB. */ + bfd_boolean add_lsb; + + /* The maximum value of MSB + 1. */ + unsigned int opsize; +}; + +/* Describes a single register operand. */ +struct mips_reg_operand +{ + struct mips_operand root; + + /* The type of register. */ + enum mips_reg_operand_type reg_type; + + /* If nonnull, REG_MAP[N] gives the register associated with encoding N, + otherwise the encoding is the same as the register number. */ + const unsigned char *reg_map; +}; + +/* Describes an operand that encodes a pair of registers. */ +struct mips_reg_pair_operand +{ + struct mips_operand root; + + /* The type of register. */ + enum mips_reg_operand_type reg_type; + + /* Encoding N represents REG1_MAP[N], REG2_MAP[N]. */ + unsigned char *reg1_map; + unsigned char *reg2_map; +}; + +/* Describes an operand that is calculated relative to a base PC. + The base PC is usually the address of the following instruction, + but the rules for MIPS16 instructions like ADDIUPC are more complicated. */ +struct mips_pcrel_operand +{ + struct mips_operand root; + + /* The low ALIGN_LOG2 bits of the base PC are cleared to give PC'. */ + unsigned int align_log2 : 8; + + /* The operand is shifted left SHIFT places and added to PC'. + The operand is signed if IS_SIGNED. */ + unsigned int shift : 8; + unsigned int is_signed : 1; + + /* If INCLUDE_ISA_BIT, the ISA bit of the original base PC is then + reinstated. This is true for jumps and branches and false for + PC-relative data instructions. */ + unsigned int include_isa_bit : 1; + + /* If FLIP_ISA_BIT, the ISA bit of the result is inverted. + This is true for JALX and false otherwise. */ + unsigned int flip_isa_bit : 1; +}; + +/* Return a version of INSN in which the field specified by OPERAND + has value UVAL. */ + +static inline unsigned int +mips_insert_operand (const struct mips_operand *operand, unsigned int insn, + unsigned int uval) +{ + unsigned int mask; + + mask = (1 << operand->size) - 1; + insn &= ~(mask << operand->lsb); + insn |= (uval & mask) << operand->lsb; + return insn; +} + +/* Extract OPERAND from instruction INSN. */ + +static inline unsigned int +mips_extract_operand (const struct mips_operand *operand, unsigned int insn) +{ + return (insn >> operand->lsb) & ((1 << operand->size) - 1); +} + +/* UVAL is the value encoded by OPERAND. Return it in signed form. */ + +static inline int +mips_signed_operand (const struct mips_operand *operand, unsigned int uval) +{ + unsigned int sign_bit, mask; + + mask = (1 << operand->size) - 1; + sign_bit = 1 << (operand->size - 1); + return ((uval + sign_bit) & mask) - sign_bit; +} + +/* Return the integer that OPERAND encodes as UVAL. */ + +static inline int +mips_decode_int_operand (const struct mips_int_operand *operand, + unsigned int uval) +{ + uval |= (operand->max_val - uval) & -(1 << operand->root.size); + uval += operand->bias; + uval <<= operand->shift; + return uval; +} + +/* PC-relative operand OPERAND has value UVAL and is relative to BASE_PC. + Return the address that it encodes. */ + +static inline bfd_vma +mips_decode_pcrel_operand (const struct mips_pcrel_operand *operand, + bfd_vma base_pc, unsigned int uval) +{ + bfd_vma addr; + + addr = base_pc & -(1 << operand->align_log2); + if (operand->is_signed) + addr += mips_signed_operand (&operand->root, uval) * (1 << operand->shift); + else + addr += uval << operand->shift; + if (operand->include_isa_bit) + addr |= base_pc & 1; + if (operand->flip_isa_bit) + addr ^= 1; + return addr; +} + /* This structure holds information for a particular instruction. */ struct mips_opcode @@ -1215,6 +1501,7 @@ enum Many instructions are short hand for other instructions (i.e., The jal <register> instruction is short for jalr <register>). */ +extern const struct mips_operand *decode_mips_operand (const char *); extern const struct mips_opcode mips_builtin_opcodes[]; extern const int bfd_mips_num_builtin_opcodes; extern struct mips_opcode *mips_opcodes; @@ -1780,6 +2067,7 @@ extern const int bfd_mips16_num_opcodes; " bcdefghij lmn pq st xyz" */ +extern const struct mips_operand *decode_micromips_operand (const char *); extern const struct mips_opcode micromips_opcodes[]; extern const int bfd_micromips_num_opcodes; diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index aa4d0f4606..17cc1c6d49 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,5 +1,31 @@ 2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * mips-formats.h: New file. + * mips-opc.c: Include mips-formats.h. + (reg_0_map): New static array. + (decode_mips_operand): New function. + * micromips-opc.c: Remove <stdio.h> include. Include mips-formats.h. + (reg_0_map, reg_28_map, reg_29_map, reg_31_map, reg_m16_map) + (reg_mn_map, reg_q_map, reg_h_map1, reg_h_map2, int_b_map) + (int_c_map): New static arrays. + (decode_micromips_operand): New function. + * mips-dis.c (micromips_to_32_reg_b_map, micromips_to_32_reg_c_map) + (micromips_to_32_reg_d_map, micromips_to_32_reg_e_map) + (micromips_to_32_reg_f_map, micromips_to_32_reg_g_map) + (micromips_to_32_reg_h_map1, micromips_to_32_reg_h_map2) + (micromips_to_32_reg_l_map, micromips_to_32_reg_m_map) + (micromips_to_32_reg_n_map, micromips_to_32_reg_q_map) + (micromips_imm_b_map, micromips_imm_c_map): Delete. + (print_reg): New function. + (mips_print_arg_state): New structure. + (init_print_arg_state, print_insn_arg): New functions. + (print_insn_args): Change interface and use mips_operand structures. + Delete GET_OP_S. Move GET_OP definition to... + (print_insn_mips): ...here. Update the call to print_insn_args. + (print_insn_micromips): Use print_insn_args. + +2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * mips16-opc.c (mips16_opcodes): Use "I" for immediate operands in macros. diff --git a/opcodes/micromips-opc.c b/opcodes/micromips-opc.c index de8053c5ed..8630769d3e 100644 --- a/opcodes/micromips-opc.c +++ b/opcodes/micromips-opc.c @@ -20,8 +20,155 @@ MA 02110-1301, USA. */ #include "sysdep.h" -#include <stdio.h> #include "opcode/mips.h" +#include "mips-formats.h" + +static unsigned char reg_0_map[] = { 0 }; +static unsigned char reg_28_map[] = { 28 }; +static unsigned char reg_29_map[] = { 29 }; +static unsigned char reg_31_map[] = { 31 }; +static unsigned char reg_m16_map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; +static unsigned char reg_mn_map[] = { 0, 17, 2, 3, 16, 18, 19, 20 }; +static unsigned char reg_q_map[] = { 0, 17, 2, 3, 4, 5, 6, 7 }; + +static unsigned char reg_h_map1[] = { 5, 5, 6, 4, 4, 4, 4, 4 }; +static unsigned char reg_h_map2[] = { 6, 7, 7, 21, 22, 5, 6, 7 }; + +static int int_b_map[] = { + 1, 4, 8, 12, 16, 20, 24, -1 +}; +static int int_c_map[] = { + 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535 +}; + +/* Return the mips_operand structure for the operand at the beginning of P. */ + +const struct mips_operand * +decode_micromips_operand (const char *p) +{ + switch (p[0]) + { + case 'm': + switch (p[1]) + { + case 'a': MAPPED_REG (0, 0, GP, reg_28_map); + case 'b': MAPPED_REG (3, 23, GP, reg_m16_map); + case 'c': MAPPED_REG (3, 4, GP, reg_m16_map); + case 'd': MAPPED_REG (3, 7, GP, reg_m16_map); + case 'e': MAPPED_REG (3, 1, GP, reg_m16_map); + case 'f': MAPPED_REG (3, 3, GP, reg_m16_map); + case 'g': MAPPED_REG (3, 0, GP, reg_m16_map); + case 'h': REG_PAIR (3, 7, GP, reg_h_map); + case 'j': REG (5, 0, GP); + case 'l': MAPPED_REG (3, 4, GP, reg_m16_map); + case 'm': MAPPED_REG (3, 1, GP, reg_mn_map); + case 'n': MAPPED_REG (3, 4, GP, reg_mn_map); + case 'p': REG (5, 5, GP); + case 'q': MAPPED_REG (3, 7, GP, reg_q_map); + case 'r': SPECIAL (0, 0, PC); + case 's': MAPPED_REG (0, 0, GP, reg_29_map); + case 't': SPECIAL (0, 0, REPEAT_PREV_REG); + case 'x': SPECIAL (0, 0, REPEAT_DEST_REG); + case 'y': MAPPED_REG (0, 0, GP, reg_31_map); + case 'z': MAPPED_REG (0, 0, GP, reg_0_map); + + case 'A': INT_ADJ (7, 0, 63, 2, FALSE); /* (-64 .. 63) << 2 */ + case 'B': MAPPED_INT (3, 1, int_b_map, FALSE); + case 'C': MAPPED_INT (4, 0, int_c_map, TRUE); + case 'D': BRANCH (10, 0, 1); + case 'E': BRANCH (7, 0, 1); + case 'F': HINT (4, 0); + case 'G': INT_ADJ (4, 0, 14, 0, FALSE); /* (-1 .. 14) */ + case 'H': INT_ADJ (4, 0, 15, 1, FALSE); /* (0 .. 15) << 1 */ + case 'I': INT_ADJ (7, 0, 126, 0, FALSE); /* (-1 .. 126) */ + case 'J': INT_ADJ (4, 0, 15, 2, FALSE); /* (0 .. 15) << 2 */ + case 'L': INT_ADJ (4, 0, 15, 0, FALSE); /* (0 .. 15) */ + case 'M': INT_ADJ (3, 1, 8, 0, FALSE); /* (1 .. 8) */ + case 'N': SPECIAL (2, 4, LWM_SWM_LIST); + case 'O': HINT (4, 0); + case 'P': INT_ADJ (5, 0, 31, 2, FALSE); /* (0 .. 31) << 2 */ + case 'Q': INT_ADJ (23, 0, 4194303, 2, FALSE); + /* (-4194304 .. 4194303) */ + case 'U': INT_ADJ (5, 0, 31, 2, FALSE); /* (0 .. 31) << 2 */ + case 'W': INT_ADJ (6, 1, 63, 2, FALSE); /* (0 .. 63) << 2 */ + case 'X': SINT (4, 1); + case 'Y': SPECIAL (9, 1, ADDIUSP_INT); + case 'Z': UINT (0, 0); /* 0 only */ + } + break; + + case '+': + switch (p[1]) + { + case 'A': BIT (5, 6, 0); /* (0 .. 31) */ + case 'B': MSB (5, 11, 1, TRUE, 32); /* (1 .. 32), 32-bit op */ + case 'C': MSB (5, 11, 1, FALSE, 32); /* (1 .. 32), 32-bit op */ + case 'E': BIT (5, 6, 32); /* (32 .. 63) */ + case 'F': MSB (5, 11, 33, TRUE, 64); /* (33 .. 64), 64-bit op */ + case 'G': MSB (5, 11, 33, FALSE, 64); /* (33 .. 64), 64-bit op */ + case 'H': MSB (5, 11, 1, FALSE, 64); /* (1 .. 32), 64-bit op */ + + case 'i': JALX (26, 0, 2); + case 'j': SINT (9, 0); + } + break; + + case '.': SINT (10, 6); + case '<': BIT (5, 11, 0); /* (0 .. 31) */ + case '>': BIT (5, 11, 32); /* (32 .. 63) */ + case '\\': BIT (3, 21, 0); /* (0 .. 7) */ + case '|': HINT (4, 12); + case '~': SINT (12, 0); + case '@': SINT (10, 16); + case '^': HINT (5, 11); + + case '0': SINT (6, 16); + case '1': HINT (5, 16); + case '2': HINT (2, 14); + case '3': HINT (3, 13); + case '4': HINT (4, 12); + case '5': HINT (8, 13); + case '6': HINT (5, 16); + case '7': REG (2, 14, ACC); + case '8': HINT (6, 14); + + case 'B': HINT (10, 16); + case 'C': HINT (23, 3); + case 'D': REG (5, 11, FP); + case 'E': REG (5, 21, COPRO); + case 'G': REG (5, 16, COPRO); + case 'K': REG (5, 16, HW); + case 'H': UINT (3, 11); + case 'M': REG (3, 13, CCC); + case 'N': REG (3, 18, CCC); + case 'R': REG (5, 6, FP); + case 'S': REG (5, 16, FP); + case 'T': REG (5, 21, FP); + case 'V': REG (5, 16, FP); + + case 'a': JUMP (26, 0, 1); + case 'b': REG (5, 16, GP); + case 'c': HINT (10, 16); + case 'd': REG (5, 11, GP); + case 'h': HINT (5, 11); + case 'i': HINT (16, 0); + case 'j': SINT (16, 0); + case 'k': HINT (5, 21); + case 'n': SPECIAL (5, 21, LWM_SWM_LIST); + case 'o': SINT (16, 0); + case 'p': BRANCH (16, 0, 1); + case 'q': HINT (10, 6); + case 'r': REG (5, 16, GP); + case 's': REG (5, 16, GP); + case 't': REG (5, 21, GP); + case 'u': HINT (16, 0); + case 'v': REG (5, 16, GP); + case 'w': REG (5, 21, GP); + case 'y': REG (5, 6, GP); + case 'z': MAPPED_REG (0, 0, GP, reg_0_map); + } + return 0; +} #define UBD INSN_UNCOND_BRANCH_DELAY #define CBD INSN_COND_BRANCH_DELAY diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c index 7e3d123232..cd7c63387d 100644 --- a/opcodes/mips-dis.c +++ b/opcodes/mips-dis.c @@ -57,89 +57,6 @@ static const unsigned int mips16_to_32_reg_map[] = 16, 17, 2, 3, 4, 5, 6, 7 }; -/* The microMIPS registers with type b. */ -#define micromips_to_32_reg_b_map mips16_to_32_reg_map - -/* The microMIPS registers with type c. */ -#define micromips_to_32_reg_c_map mips16_to_32_reg_map - -/* The microMIPS registers with type d. */ -#define micromips_to_32_reg_d_map mips16_to_32_reg_map - -/* The microMIPS registers with type e. */ -#define micromips_to_32_reg_e_map mips16_to_32_reg_map - -/* The microMIPS registers with type f. */ -#define micromips_to_32_reg_f_map mips16_to_32_reg_map - -/* The microMIPS registers with type g. */ -#define micromips_to_32_reg_g_map mips16_to_32_reg_map - -/* The microMIPS registers with type h. */ -static const unsigned int micromips_to_32_reg_h_map1[] = -{ - 5, 5, 6, 4, 4, 4, 4, 4 -}; -static const unsigned int micromips_to_32_reg_h_map2[] = -{ - 6, 7, 7, 21, 22, 5, 6, 7 -}; - -/* The microMIPS registers with type j: 32 registers. */ - -/* The microMIPS registers with type l. */ -#define micromips_to_32_reg_l_map mips16_to_32_reg_map - -/* The microMIPS registers with type m. */ -static const unsigned int micromips_to_32_reg_m_map[] = -{ - 0, 17, 2, 3, 16, 18, 19, 20 -}; - -/* The microMIPS registers with type n. */ -#define micromips_to_32_reg_n_map micromips_to_32_reg_m_map - -/* The microMIPS registers with type p: 32 registers. */ - -/* The microMIPS registers with type q. */ -static const unsigned int micromips_to_32_reg_q_map[] = -{ - 0, 17, 2, 3, 4, 5, 6, 7 -}; - -/* reg type s is $29. */ - -/* reg type t is the same as the last register. */ - -/* reg type y is $31. */ - -/* reg type z is $0. */ - -/* micromips imm B type. */ -static const int micromips_imm_b_map[8] = -{ - 1, 4, 8, 12, 16, 20, 24, -1 -}; - -/* micromips imm C type. */ -static const int micromips_imm_c_map[16] = -{ - 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535 -}; - -/* micromips imm D type: (-512..511)<<1. */ -/* micromips imm E type: (-64..63)<<1. */ -/* micromips imm F type: (0..63). */ -/* micromips imm G type: (-1..14). */ -/* micromips imm H type: (0..15)<<1. */ -/* micromips imm I type: (-1..126). */ -/* micromips imm J type: (0..15)<<2. */ -/* micromips imm L type: (0..15). */ -/* micromips imm M type: (1..8). */ -/* micromips imm W type: (0..63)<<2. */ -/* micromips imm X type: (-8..7). */ -/* micromips imm Y type: (-258..-3, 2..257)<<2. */ - #define mips16_reg_names(rn) mips_gpr_names[mips16_to_32_reg_map[rn]] @@ -964,476 +881,361 @@ lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names, return &names[i]; return NULL; } - -/* Print insn arguments for 32/64-bit code. */ + +/* Print register REGNO, of type TYPE, for instruction OPCODE. */ static void -print_insn_args (const char *d, - int l, - bfd_vma pc, - struct disassemble_info *info, - const struct mips_opcode *opp) +print_reg (struct disassemble_info *info, const struct mips_opcode *opcode, + enum mips_reg_operand_type type, int regno) { - const fprintf_ftype infprintf = info->fprintf_func; - unsigned int lsb, msb, msbd, cpreg; - void *is = info->stream; + switch (type) + { + case OP_REG_GP: + info->fprintf_func (info->stream, "%s", mips_gpr_names[regno]); + break; - lsb = 0; + case OP_REG_FP: + info->fprintf_func (info->stream, "%s", mips_fpr_names[regno]); + break; -#define GET_OP(insn, field) \ - (((insn) >> OP_SH_##field) & OP_MASK_##field) -#define GET_OP_S(insn, field) \ - ((GET_OP (insn, field) ^ ((OP_MASK_##field >> 1) + 1)) \ - - ((OP_MASK_##field >> 1) + 1)) - for (; *d != '\0'; d++) - { - switch (*d) - { - case ',': - case '(': - case ')': - infprintf (is, "%c", *d); - break; + case OP_REG_CCC: + if (opcode->pinfo & (FP_D | FP_S)) + info->fprintf_func (info->stream, "$fcc%d", regno); + else + info->fprintf_func (info->stream, "$cc%d", regno); + break; - case '+': - /* Extension character; switch for second char. */ - d++; - switch (*d) - { - case '\0': - /* xgettext:c-format */ - infprintf (is, - _("# internal error, " - "incomplete extension sequence (+)")); - return; + case OP_REG_VEC: + if (opcode->membership & INSN_5400) + info->fprintf_func (info->stream, "$f%d", regno); + else + info->fprintf_func (info->stream, "$v%d", regno); + break; - case 'A': - lsb = GET_OP (l, SHAMT); - infprintf (is, "0x%x", lsb); - break; - - case 'B': - msb = GET_OP (l, INSMSB); - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case '1': - infprintf (is, "0x%x", GET_OP (l, UDI1)); - break; - - case '2': - infprintf (is, "0x%x", GET_OP (l, UDI2)); - break; - - case '3': - infprintf (is, "0x%x", GET_OP (l, UDI3)); - break; - - case '4': - infprintf (is, "0x%x", GET_OP (l, UDI4)); - break; - - case 'C': - case 'H': - msbd = GET_OP (l, EXTMSBD); - infprintf (is, "0x%x", msbd + 1); - break; - - case 'E': - lsb = GET_OP (l, SHAMT) + 32; - infprintf (is, "0x%x", lsb); - break; - - case 'F': - msb = GET_OP (l, INSMSB) + 32; - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case 'G': - msbd = GET_OP (l, EXTMSBD) + 32; - infprintf (is, "0x%x", msbd + 1); - break; - - case 'J': /* hypcall operand */ - infprintf (is, "0x%x", GET_OP (l, CODE10)); - break; - - case 't': /* Coprocessor 0 reg name */ - infprintf (is, "%s", mips_cp0_names[GET_OP (l, RT)]); - break; - - case 'x': /* bbit bit index */ - infprintf (is, "0x%x", GET_OP (l, BBITIND)); - break; - - case 'p': /* cins, cins32, exts and exts32 position */ - infprintf (is, "0x%x", GET_OP (l, CINSPOS)); - break; - - case 's': /* cins32 and exts32 length-minus-one */ - case 'S': /* cins and exts length-minus-one field */ - infprintf (is, "0x%x", GET_OP (l, CINSLM1)); - break; - - case 'Q': /* seqi/snei immediate field */ - infprintf (is, "%d", GET_OP_S (l, SEQI)); - break; - - case 'a': /* 8-bit signed offset in bit 6 */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_A)); - break; - - case 'b': /* 8-bit signed offset in bit 3 */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_B)); - break; - - case 'c': /* 9-bit signed offset in bit 6 */ - /* Left shift 4 bits to print the real offset. */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_C) << 4); - break; - - case 'z': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RZ)]); - break; - - case 'Z': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FZ)]); - break; - - case 'i': /* JALX destination */ - info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff) - | (GET_OP (l, TARGET) << 2)); - /* For gdb disassembler, force odd address on jalx. */ - if (info->flavour == bfd_target_unknown_flavour) - info->target |= 1; - (*info->print_address_func) (info->target, info); - break; - - case 'j': /* 9-bit signed offset in bit 7. */ - infprintf (is, "%d", GET_OP_S (l, EVAOFFSET)); - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal error, " - "undefined extension sequence (+%c)"), - *d); - return; - } - break; + case OP_REG_ACC: + info->fprintf_func (info->stream, "$ac%d", regno); + break; - case '2': - infprintf (is, "0x%x", GET_OP (l, BP)); - break; + case OP_REG_COPRO: + if (opcode->name[strlen (opcode->name) - 1] == '0') + info->fprintf_func (info->stream, "%s", mips_cp0_names[regno]); + else + info->fprintf_func (info->stream, "$%d", regno); + break; - case '3': - infprintf (is, "0x%x", GET_OP (l, SA3)); - break; + case OP_REG_HW: + info->fprintf_func (info->stream, "%s", mips_hwr_names[regno]); + break; + } +} + +/* Used to track the state carried over from previous operands in + an instruction. */ +struct mips_print_arg_state { + /* The value of the last OP_INT seen. We only use this for OP_MSB, + where the value is known to be unsigned and small. */ + unsigned int last_int; + + /* The type and number of the last OP_REG seen. We only use this for + OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG. */ + enum mips_reg_operand_type last_reg_type; + unsigned int last_regno; +}; - case '4': - infprintf (is, "0x%x", GET_OP (l, SA4)); - break; +/* Initialize STATE for the start of an instruction. */ - case '5': - infprintf (is, "0x%x", GET_OP (l, IMM8)); - break; +static inline void +init_print_arg_state (struct mips_print_arg_state *state) +{ + memset (state, 0, sizeof (*state)); +} - case '6': - infprintf (is, "0x%x", GET_OP (l, RS)); - break; +/* Print operand OPERAND of OPCODE, using STATE to track inter-operand state. + UVAL is the encoding of the operand (shifted into bit 0) and BASE_PC is + the base address for OP_PCREL operands. */ - case '7': - infprintf (is, "$ac%d", GET_OP (l, DSPACC)); - break; +static void +print_insn_arg (struct disassemble_info *info, + struct mips_print_arg_state *state, + const struct mips_opcode *opcode, + const struct mips_operand *operand, + bfd_vma base_pc, + unsigned int uval) +{ + const fprintf_ftype infprintf = info->fprintf_func; + void *is = info->stream; - case '8': - infprintf (is, "0x%x", GET_OP (l, WRDSP)); - break; + switch (operand->type) + { + case OP_INT: + { + const struct mips_int_operand *int_op; - case '9': - infprintf (is, "$ac%d", GET_OP (l, DSPACC_S)); - break; + int_op = (const struct mips_int_operand *) operand; + uval = mips_decode_int_operand (int_op, uval); + state->last_int = uval; + if (int_op->print_hex) + infprintf (is, "0x%x", uval); + else + infprintf (is, "%d", uval); + } + break; - case '0': /* dsp 6-bit signed immediate in bit 20 */ - infprintf (is, "%d", GET_OP_S (l, DSPSFT)); - break; + case OP_MAPPED_INT: + { + const struct mips_mapped_int_operand *mint_op; - case ':': /* dsp 7-bit signed immediate in bit 19 */ - infprintf (is, "%d", GET_OP_S (l, DSPSFT_7)); - break; + mint_op = (const struct mips_mapped_int_operand *) operand; + uval = mint_op->int_map[uval]; + state->last_int = uval; + if (mint_op->print_hex) + infprintf (is, "0x%x", uval); + else + infprintf (is, "%d", uval); + } + break; - case '~': - infprintf (is, "%d", GET_OP_S (l, OFFSET12)); - break; + case OP_MSB: + { + const struct mips_msb_operand *msb_op; - case '\\': - infprintf (is, "0x%x", GET_OP (l, 3BITPOS)); - break; + msb_op = (const struct mips_msb_operand *) operand; + uval += msb_op->bias; + if (msb_op->add_lsb) + uval -= state->last_int; + infprintf (is, "0x%x", uval); + } + break; - case '\'': - infprintf (is, "0x%x", GET_OP (l, RDDSP)); - break; + case OP_REG: + { + const struct mips_reg_operand *reg_op; - case '@': /* dsp 10-bit signed immediate in bit 16 */ - infprintf (is, "%d", GET_OP_S (l, IMM10)); - break; + reg_op = (const struct mips_reg_operand *) operand; + if (reg_op->reg_map) + uval = reg_op->reg_map[uval]; + print_reg (info, opcode, reg_op->reg_type, uval); - case '!': - infprintf (is, "%d", GET_OP (l, MT_U)); - break; + state->last_reg_type = reg_op->reg_type; + state->last_regno = uval; + } + break; - case '$': - infprintf (is, "%d", GET_OP (l, MT_H)); - break; + case OP_REG_PAIR: + { + const struct mips_reg_pair_operand *pair_op; + + pair_op = (const struct mips_reg_pair_operand *) operand; + print_reg (info, opcode, pair_op->reg_type, + pair_op->reg1_map[uval]); + infprintf (is, ","); + print_reg (info, opcode, pair_op->reg_type, + pair_op->reg2_map[uval]); + } + break; - case '*': - infprintf (is, "$ac%d", GET_OP (l, MTACC_T)); - break; + case OP_PCREL: + { + const struct mips_pcrel_operand *pcrel_op; - case '&': - infprintf (is, "$ac%d", GET_OP (l, MTACC_D)); - break; + pcrel_op = (const struct mips_pcrel_operand *) operand; + info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval); - case 'g': - /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2. */ - infprintf (is, "$%d", GET_OP (l, RD)); - break; + /* Preserve the ISA bit for the GDB disassembler, + otherwise clear it. */ + if (info->flavour != bfd_target_unknown_flavour) + info->target &= -2; - case 's': - case 'b': - case 'r': - case 'v': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RS)]); - break; + (*info->print_address_func) (info->target, info); + } + break; - case 't': - case 'w': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]); - break; + case OP_PERF_REG: + infprintf (is, "%d", uval); + break; - case 'i': - case 'u': - infprintf (is, "0x%x", GET_OP (l, IMMEDIATE)); - break; + case OP_ADDIUSP_INT: + { + int sval; - case 'j': /* Same as i, but sign-extended. */ - case 'o': - infprintf (is, "%d", GET_OP_S (l, DELTA)); - break; + sval = mips_signed_operand (operand, uval) * 4; + if (sval >= -8 && sval < 8) + sval ^= 0x400; + infprintf (is, "%d", sval); + break; + } - case 'h': - infprintf (is, "0x%x", GET_OP (l, PREFX)); - break; + case OP_CLO_CLZ_DEST: + { + unsigned int reg1, reg2; + + reg1 = uval & 31; + reg2 = uval >> 5; + /* If one is zero use the other. */ + if (reg1 == reg2 || reg2 == 0) + infprintf (is, "%s", mips_gpr_names[reg1]); + else if (reg1 == 0) + infprintf (is, "%s", mips_gpr_names[reg2]); + else + /* Bogus, result depends on processor. */ + infprintf (is, "%s or %s", mips_gpr_names[reg1], + mips_gpr_names[reg2]); + } + break; - case 'k': - infprintf (is, "0x%x", GET_OP (l, CACHE)); - break; + case OP_LWM_SWM_LIST: + if (operand->size == 2) + { + if (uval == 0) + infprintf (is, "%s,%s", + mips_gpr_names[16], + mips_gpr_names[31]); + else + infprintf (is, "%s-%s,%s", + mips_gpr_names[16], + mips_gpr_names[16 + uval], + mips_gpr_names[31]); + } + else + { + int s_reg_encode; - case 'a': - info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff) - | (GET_OP (l, TARGET) << 2)); - (*info->print_address_func) (info->target, info); - break; + s_reg_encode = uval & 0xf; + if (s_reg_encode != 0) + { + if (s_reg_encode == 1) + infprintf (is, "%s", mips_gpr_names[16]); + else if (s_reg_encode < 9) + infprintf (is, "%s-%s", + mips_gpr_names[16], + mips_gpr_names[15 + s_reg_encode]); + else if (s_reg_encode == 9) + infprintf (is, "%s-%s,%s", + mips_gpr_names[16], + mips_gpr_names[23], + mips_gpr_names[30]); + else + infprintf (is, "UNKNOWN"); + } - case 'p': - /* Sign extend the displacement. */ - info->target = (GET_OP_S (l, DELTA) << 2) + pc + INSNLEN; - (*info->print_address_func) (info->target, info); - break; + if (uval & 0x10) /* For ra. */ + { + if (s_reg_encode == 0) + infprintf (is, "%s", mips_gpr_names[31]); + else + infprintf (is, ",%s", mips_gpr_names[31]); + } + } + break; - case 'd': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RD)]); - break; + case OP_MDMX_IMM_REG: + { + unsigned int vsel; - case 'U': + vsel = uval >> 5; + uval &= 31; + if ((vsel & 0x10) == 0) { - /* First check for both rd and rt being equal. */ - unsigned int reg; - - reg = GET_OP (l, RD); - if (reg == GET_OP (l, RT)) - infprintf (is, "%s", mips_gpr_names[reg]); - else - { - /* If one is zero use the other. */ - if (reg == 0) - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]); - else if (GET_OP (l, RT) == 0) - infprintf (is, "%s", mips_gpr_names[reg]); - else /* Bogus, result depends on processor. */ - infprintf (is, "%s or %s", - mips_gpr_names[reg], - mips_gpr_names[GET_OP (l, RT)]); - } + int fmt; + + vsel &= 0x0f; + for (fmt = 0; fmt < 3; fmt++, vsel >>= 1) + if ((vsel & 1) == 0) + break; + print_reg (info, opcode, OP_REG_VEC, uval); + infprintf (is, "[%d]", vsel >> 1); } - break; - - case 'z': - infprintf (is, "%s", mips_gpr_names[0]); - break; - - case '<': - case '1': - infprintf (is, "0x%x", GET_OP (l, SHAMT)); - break; - - case 'c': - infprintf (is, "0x%x", GET_OP (l, CODE)); - break; - - case 'q': - infprintf (is, "0x%x", GET_OP (l, CODE2)); - break; - - case 'C': - infprintf (is, "0x%x", GET_OP (l, COPZ)); - break; + else if ((vsel & 0x08) == 0) + print_reg (info, opcode, OP_REG_VEC, uval); + else + infprintf (is, "0x%x", uval); + } + break; - case 'B': - infprintf (is, "0x%x", GET_OP (l, CODE20)); - break; + case OP_REPEAT_PREV_REG: + print_reg (info, opcode, state->last_reg_type, state->last_regno); + break; - case 'J': - infprintf (is, "0x%x", GET_OP (l, CODE19)); - break; + case OP_REPEAT_DEST_REG: + /* Should always match OP_REPEAT_PREV_REG first. */ + abort (); - case 'S': - case 'V': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FS)]); - break; + case OP_PC: + infprintf (is, "$pc"); + break; + } +} - case 'T': - case 'W': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FT)]); - break; +/* Print the arguments for INSN, which is described by OPCODE. + Use DECODE_OPERAND to get the encoding of each operand. Use BASE_PC + as the base of OP_PCREL operands. */ - case 'D': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FD)]); - break; +static void +print_insn_args (struct disassemble_info *info, + const struct mips_opcode *opcode, + const struct mips_operand *(*decode_operand) (const char *), + unsigned int insn, bfd_vma base_pc) +{ + const fprintf_ftype infprintf = info->fprintf_func; + void *is = info->stream; + struct mips_print_arg_state state; + const struct mips_operand *operand; + const char *s; - case 'R': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FR)]); + init_print_arg_state (&state); + for (s = opcode->args; *s; ++s) + { + switch (*s) + { + case ',': + case '(': + case ')': + infprintf (is, "%c", *s); break; - case 'E': - cpreg = GET_OP (l, RT); - goto copro; - - case 'G': - cpreg = GET_OP (l, RD); - copro: - /* 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. */ - if (opp->name[strlen (opp->name) - 1] == '0') + default: + operand = decode_operand (s); + if (!operand) { - if (d[1] == ',' && d[2] == 'H') - { - const struct mips_cp0sel_name *n; - unsigned int sel; - - sel = GET_OP (l, 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, - cpreg, sel); - if (n != NULL) - infprintf (is, "%s", n->name); - else - infprintf (is, "$%d,%d", cpreg, sel); - d += 2; - } + /* xgettext:c-format */ + infprintf (is, + _("# internal error, undefined operand in `%s %s'"), + opcode->name, opcode->args); + return; + } + if (operand->type == OP_REG + && s[1] == ',' + && s[2] == 'H' + && opcode->name[strlen (opcode->name) - 1] == '0') + { + /* Coprocessor register 0 with sel field (MT ASE). */ + const struct mips_cp0sel_name *n; + unsigned int reg, sel; + + reg = mips_extract_operand (operand, insn); + s += 2; + operand = decode_operand (s); + sel = mips_extract_operand (operand, insn); + + /* 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, + reg, sel); + if (n != NULL) + infprintf (is, "%s", n->name); else - infprintf (is, "%s", mips_cp0_names[cpreg]); + infprintf (is, "$%d,%d", reg, sel); } else - infprintf (is, "$%d", cpreg); - break; - - case 'K': - infprintf (is, "%s", mips_hwr_names[GET_OP (l, RD)]); - break; - - case 'N': - infprintf (is, - (opp->pinfo & (FP_D | FP_S)) != 0 ? "$fcc%d" : "$cc%d", - GET_OP (l, BCC)); - break; - - case 'M': - infprintf (is, "$fcc%d", GET_OP (l, CCC)); - break; - - case 'P': - infprintf (is, "%d", GET_OP (l, PERFREG)); - break; - - case 'e': - infprintf (is, "%d", GET_OP (l, VECBYTE)); + print_insn_arg (info, &state, opcode, operand, base_pc, + mips_extract_operand (operand, insn)); + if (*s == 'm' || *s == '+') + ++s; break; - - case '%': - infprintf (is, "%d", GET_OP (l, VECALIGN)); - break; - - case 'H': - infprintf (is, "%d", GET_OP (l, SEL)); - break; - - case 'O': - infprintf (is, "%d", GET_OP (l, ALN)); - break; - - case 'Q': - { - unsigned int vsel = GET_OP (l, VSEL); - char prefix; - - prefix = opp->membership & INSN_5400 ? 'f' : 'v'; - if ((vsel & 0x10) == 0) - { - int fmt; - - vsel &= 0x0f; - for (fmt = 0; fmt < 3; fmt++, vsel >>= 1) - if ((vsel & 1) == 0) - break; - infprintf (is, "$%c%d[%d]", prefix, GET_OP (l, FT), vsel >> 1); - } - else if ((vsel & 0x08) == 0) - { - infprintf (is, "$%c%d", prefix, GET_OP (l, FT)); - } - else - { - infprintf (is, "0x%x", GET_OP (l, FT)); - } - } - break; - - case 'X': - infprintf (is, "$v%d", GET_OP (l, FD)); - break; - - case 'Y': - infprintf (is, "$v%d", GET_OP (l, FS)); - break; - - case 'Z': - infprintf (is, "$v%d", GET_OP (l, FT)); - break; - - default: - /* xgettext:c-format */ - infprintf (is, _("# internal error, undefined modifier (%c)"), *d); - return; } } } @@ -1448,6 +1250,8 @@ print_insn_mips (bfd_vma memaddr, int word, struct disassemble_info *info) { +#define GET_OP(insn, field) \ + (((insn) >> OP_SH_##field) & OP_MASK_##field) static const struct mips_opcode *mips_hash[OP_MASK_OP + 1]; const fprintf_ftype infprintf = info->fprintf_func; const struct mips_opcode *op; @@ -1495,8 +1299,6 @@ print_insn_mips (bfd_vma memaddr, && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) && (word & op->mask) == op->match) { - const char *d; - /* We always allow to disassemble the jalx instruction. */ if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor) && strcmp (op->name, "jalx")) @@ -1527,18 +1329,17 @@ print_insn_mips (bfd_vma memaddr, infprintf (is, "%s", op->name); - d = op->args; - if (d != NULL && *d != '\0') + if (op->args[0]) { infprintf (is, "\t"); - print_insn_args (d, word, memaddr, info, op); + print_insn_args (info, op, decode_mips_operand, word, + memaddr + 4); } return INSNLEN; } } } -#undef GET_OP_S #undef GET_OP /* Handle undefined instructions. */ @@ -2235,19 +2036,12 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) { const fprintf_ftype infprintf = info->fprintf_func; const struct mips_opcode *op, *opend; - unsigned int lsb, msbd, msb; void *is = info->stream; - unsigned int regno; bfd_byte buffer[2]; - int lastregno = 0; - int higher; - int length; + unsigned int higher; + unsigned int length; int status; - int delta; - int immed; - int insn; - - lsb = 0; + unsigned int insn; info->bytes_per_chunk = 2; info->display_endian = info->endian; @@ -2331,11 +2125,6 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) /* FIXME: Should probably use a hash table on the major opcode here. */ -#define GET_OP(insn, field) \ - (((insn) >> MICROMIPSOP_SH_##field) & MICROMIPSOP_MASK_##field) -#define GET_OP_S(insn, field) \ - ((GET_OP (insn, field) ^ ((MICROMIPSOP_MASK_##field >> 1) + 1)) \ - - ((MICROMIPSOP_MASK_##field >> 1) + 1)) opend = micromips_opcodes + bfd_micromips_num_opcodes; for (op = micromips_opcodes; op < opend; op++) { @@ -2345,569 +2134,13 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) && ((length == 2 && (op->mask & 0xffff0000) == 0) || (length == 4 && (op->mask & 0xffff0000) != 0))) { - const char *s; - infprintf (is, "%s", op->name); - if (op->args[0] != '\0') - infprintf (is, "\t"); - for (s = op->args; *s != '\0'; s++) + if (op->args[0]) { - switch (*s) - { - case ',': - case '(': - case ')': - infprintf (is, "%c", *s); - break; - - case '.': - infprintf (is, "%d", GET_OP_S (insn, OFFSET10)); - break; - - case '1': - infprintf (is, "0x%x", GET_OP (insn, STYPE)); - break; - - case '2': - infprintf (is, "0x%x", GET_OP (insn, BP)); - break; - - case '3': - infprintf (is, "0x%x", GET_OP (insn, SA3)); - break; - - case '4': - infprintf (is, "0x%x", GET_OP (insn, SA4)); - break; - - case '5': - infprintf (is, "0x%x", GET_OP (insn, IMM8)); - break; - - case '6': - infprintf (is, "0x%x", GET_OP (insn, RS)); - break; - - case '7': - infprintf (is, "$ac%d", GET_OP (insn, DSPACC)); - break; - - case '8': - infprintf (is, "0x%x", GET_OP (insn, WRDSP)); - break; - - case '0': /* DSP 6-bit signed immediate in bit 16. */ - delta = (GET_OP (insn, DSPSFT) ^ 0x20) - 0x20; - infprintf (is, "%d", delta); - break; - - case '<': - infprintf (is, "0x%x", GET_OP (insn, SHAMT)); - break; - - case '\\': - infprintf (is, "0x%x", GET_OP (insn, 3BITPOS)); - break; - - case '^': - infprintf (is, "0x%x", GET_OP (insn, RD)); - break; - - case '|': - infprintf (is, "0x%x", GET_OP (insn, TRAP)); - break; - - case '~': - infprintf (is, "%d", GET_OP_S (insn, OFFSET12)); - break; - - case 'a': - info->target = (((memaddr + 4) & ~(bfd_vma) 0x07ffffff) - | (GET_OP (insn, TARGET) << 1)); - /* For gdb disassembler, maintain odd address. */ - if (info->flavour == bfd_target_unknown_flavour) - info->target |= 1; - (*info->print_address_func) (info->target, info); - break; - - case 'b': - case 'r': - case 's': - case 'v': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS)]); - break; - - case 'c': - infprintf (is, "0x%x", GET_OP (insn, CODE)); - break; - - case 'd': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RD)]); - break; - - case 'h': - infprintf (is, "0x%x", GET_OP (insn, PREFX)); - break; - - case 'i': - case 'u': - infprintf (is, "0x%x", GET_OP (insn, IMMEDIATE)); - break; - - case 'j': /* Same as i, but sign-extended. */ - case 'o': - infprintf (is, "%d", GET_OP_S (insn, DELTA)); - break; - - case 'k': - infprintf (is, "0x%x", GET_OP (insn, CACHE)); - break; - - case 'n': - { - int s_reg_encode; - - immed = GET_OP (insn, RT); - s_reg_encode = immed & 0xf; - if (s_reg_encode != 0) - { - if (s_reg_encode == 1) - infprintf (is, "%s", mips_gpr_names[16]); - else if (s_reg_encode < 9) - infprintf (is, "%s-%s", - mips_gpr_names[16], - mips_gpr_names[15 + s_reg_encode]); - else if (s_reg_encode == 9) - infprintf (is, "%s-%s,%s", - mips_gpr_names[16], - mips_gpr_names[23], - mips_gpr_names[30]); - else - infprintf (is, "UNKNOWN"); - } - - if (immed & 0x10) /* For ra. */ - { - if (s_reg_encode == 0) - infprintf (is, "%s", mips_gpr_names[31]); - else - infprintf (is, ",%s", mips_gpr_names[31]); - } - break; - } - - case 'p': - /* Sign-extend the displacement. */ - delta = GET_OP_S (insn, DELTA); - info->target = (delta << 1) + memaddr + length; - (*info->print_address_func) (info->target, info); - break; - - case 'q': - infprintf (is, "0x%x", GET_OP (insn, CODE2)); - break; - - case 't': - case 'w': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RT)]); - break; - - case 'y': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS3)]); - break; - - case 'z': - infprintf (is, "%s", mips_gpr_names[0]); - break; - - case '@': /* DSP 10-bit signed immediate in bit 16. */ - delta = (GET_OP (insn, IMM10) ^ 0x200) - 0x200; - infprintf (is, "%d", delta); - break; - - case 'B': - infprintf (is, "0x%x", GET_OP (insn, CODE10)); - break; - - case 'C': - infprintf (is, "0x%x", GET_OP (insn, COPZ)); - break; - - case 'D': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FD)]); - 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. */ - infprintf (is, "$%d", GET_OP (insn, 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. */ - if (op->name[strlen (op->name) - 1] == '0') - { - if (s[1] == ',' && s[2] == 'H') - { - const struct mips_cp0sel_name *n; - unsigned int cp0reg, sel; - - cp0reg = GET_OP (insn, RS); - sel = GET_OP (insn, 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) - infprintf (is, "%s", n->name); - else - infprintf (is, "$%d,%d", cp0reg, sel); - s += 2; - } - else - infprintf (is, "%s", mips_cp0_names[GET_OP (insn, RS)]); - } - else - infprintf (is, "$%d", GET_OP (insn, RS)); - break; - - case 'H': - infprintf (is, "%d", GET_OP (insn, SEL)); - break; - - case 'K': - infprintf (is, "%s", mips_hwr_names[GET_OP (insn, RS)]); - break; - - case 'M': - infprintf (is, "$fcc%d", GET_OP (insn, CCC)); - break; - - case 'N': - infprintf (is, - (op->pinfo & (FP_D | FP_S)) != 0 - ? "$fcc%d" : "$cc%d", - GET_OP (insn, BCC)); - break; - - case 'R': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FR)]); - break; - - case 'S': - case 'V': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FS)]); - break; - - case 'T': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FT)]); - break; - - case '+': - /* Extension character; switch for second char. */ - s++; - switch (*s) - { - case 'A': - lsb = GET_OP (insn, EXTLSB); - infprintf (is, "0x%x", lsb); - break; - - case 'B': - msb = GET_OP (insn, INSMSB); - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case 'C': - case 'H': - msbd = GET_OP (insn, EXTMSBD); - infprintf (is, "0x%x", msbd + 1); - break; - - case 'E': - lsb = GET_OP (insn, EXTLSB) + 32; - infprintf (is, "0x%x", lsb); - break; - - case 'F': - msb = GET_OP (insn, INSMSB) + 32; - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case 'G': - msbd = GET_OP (insn, EXTMSBD) + 32; - infprintf (is, "0x%x", msbd + 1); - break; - - case 'i': - info->target = (((memaddr + 4) & ~(bfd_vma) 0x0fffffff) - | (GET_OP (insn, TARGET) << 2)); - (*info->print_address_func) (info->target, info); - break; - - case 'j': /* 9-bit signed offset in bit 0. */ - delta = GET_OP_S (insn, EVAOFFSET); - infprintf (is, "%d", delta); - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (+%c)"), - *s); - abort (); - } - break; - - case 'm': - /* Extension character; switch for second char. */ - s++; - switch (*s) - { - case 'a': /* global pointer. */ - infprintf (is, "%s", mips_gpr_names[28]); - break; - - case 'b': - regno = micromips_to_32_reg_b_map[GET_OP (insn, MB)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'c': - regno = micromips_to_32_reg_c_map[GET_OP (insn, MC)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'd': - regno = micromips_to_32_reg_d_map[GET_OP (insn, MD)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'e': - regno = micromips_to_32_reg_e_map[GET_OP (insn, ME)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'f': - /* Save lastregno for "mt" to print out later. */ - lastregno = micromips_to_32_reg_f_map[GET_OP (insn, MF)]; - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; - - case 'g': - regno = micromips_to_32_reg_g_map[GET_OP (insn, MG)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'h': - regno = micromips_to_32_reg_h_map1[GET_OP (insn, MH)]; - infprintf (is, "%s", mips_gpr_names[regno]); - regno = micromips_to_32_reg_h_map2[GET_OP (insn, MH)]; - infprintf (is, ",%s", mips_gpr_names[regno]); - break; - - case 'j': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, MJ)]); - break; - - case 'l': - regno = micromips_to_32_reg_l_map[GET_OP (insn, ML)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'm': - regno = micromips_to_32_reg_m_map[GET_OP (insn, MM)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'n': - regno = micromips_to_32_reg_n_map[GET_OP (insn, MN)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'p': - /* Save lastregno for "mt" to print out later. */ - lastregno = GET_OP (insn, MP); - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; - - case 'q': - regno = micromips_to_32_reg_q_map[GET_OP (insn, MQ)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'r': /* program counter. */ - infprintf (is, "$pc"); - break; - - case 's': /* stack pointer. */ - lastregno = 29; - infprintf (is, "%s", mips_gpr_names[29]); - break; - - case 't': - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; - - case 'z': /* $0. */ - infprintf (is, "%s", mips_gpr_names[0]); - break; - - case 'A': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMA) << 2; - infprintf (is, "%d", immed); - break; - - case 'B': - immed = micromips_imm_b_map[GET_OP (insn, IMMB)]; - infprintf (is, "%d", immed); - break; - - case 'C': - immed = micromips_imm_c_map[GET_OP (insn, IMMC)]; - infprintf (is, "0x%x", immed); - break; - - case 'D': - /* Sign-extend the displacement. */ - delta = GET_OP_S (insn, IMMD); - info->target = (delta << 1) + memaddr + length; - (*info->print_address_func) (info->target, info); - break; - - case 'E': - /* Sign-extend the displacement. */ - delta = GET_OP_S (insn, IMME); - info->target = (delta << 1) + memaddr + length; - (*info->print_address_func) (info->target, info); - break; - - case 'F': - immed = GET_OP (insn, IMMF); - infprintf (is, "0x%x", immed); - break; - - case 'G': - immed = (insn >> MICROMIPSOP_SH_IMMG) + 1; - immed = (immed & MICROMIPSOP_MASK_IMMG) - 1; - infprintf (is, "%d", immed); - break; - - case 'H': - immed = GET_OP (insn, IMMH) << 1; - infprintf (is, "%d", immed); - break; - - case 'I': - immed = (insn >> MICROMIPSOP_SH_IMMI) + 1; - immed = (immed & MICROMIPSOP_MASK_IMMI) - 1; - infprintf (is, "%d", immed); - break; - - case 'J': - immed = GET_OP (insn, IMMJ) << 2; - infprintf (is, "%d", immed); - break; - - case 'L': - immed = GET_OP (insn, IMML); - infprintf (is, "%d", immed); - break; - - case 'M': - immed = (insn >> MICROMIPSOP_SH_IMMM) - 1; - immed = (immed & MICROMIPSOP_MASK_IMMM) + 1; - infprintf (is, "%d", immed); - break; - - case 'N': - immed = GET_OP (insn, IMMN); - if (immed == 0) - infprintf (is, "%s,%s", - mips_gpr_names[16], - mips_gpr_names[31]); - else - infprintf (is, "%s-%s,%s", - mips_gpr_names[16], - mips_gpr_names[16 + immed], - mips_gpr_names[31]); - break; - - case 'O': - immed = GET_OP (insn, IMMO); - infprintf (is, "0x%x", immed); - break; - - case 'P': - immed = GET_OP (insn, IMMP) << 2; - infprintf (is, "%d", immed); - break; - - case 'Q': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMQ) << 2; - infprintf (is, "%d", immed); - break; - - case 'U': - immed = GET_OP (insn, IMMU) << 2; - infprintf (is, "%d", immed); - break; - - case 'W': - immed = GET_OP (insn, IMMW) << 2; - infprintf (is, "%d", immed); - break; - - case 'X': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMX); - infprintf (is, "%d", immed); - break; - - case 'Y': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMY) << 2; - if ((unsigned int) (immed + 8) < 16) - immed ^= 0x400; - infprintf (is, "%d", immed); - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (m%c)"), - *s); - abort (); - } - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (%c)"), - *s); - abort (); - } + infprintf (is, "\t"); + print_insn_args (info, op, decode_micromips_operand, insn, + memaddr + length + 1); } /* Figure out instruction type and branch delay information. */ @@ -2937,8 +2170,6 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) return length; } } -#undef GET_OP_S -#undef GET_OP infprintf (is, "0x%x", insn); info->insn_type = dis_noninsn; diff --git a/opcodes/mips-formats.h b/opcodes/mips-formats.h new file mode 100644 index 0000000000..7deba5e0eb --- /dev/null +++ b/opcodes/mips-formats.h @@ -0,0 +1,113 @@ +/* mips-formats.h + Copyright 2013 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING3. If not, + see <http://www.gnu.org/licenses/>. */ + +/* For ARRAY_SIZE. */ +#include "libiberty.h" + +#define INT_ADJ(SIZE, LSB, MAX_VAL, SHIFT, PRINT_HEX) \ + { \ + static const struct mips_int_operand op = { \ + { OP_INT, SIZE, LSB }, MAX_VAL, 0, SHIFT, PRINT_HEX \ + }; \ + return &op.root; \ + } + +#define UINT(SIZE, LSB) \ + INT_ADJ(SIZE, LSB, (1 << (SIZE)) - 1, 0, FALSE) + +#define SINT(SIZE, LSB) \ + INT_ADJ(SIZE, LSB, (1 << ((SIZE) - 1)) - 1, 0, FALSE) + +#define HINT(SIZE, LSB) \ + INT_ADJ(SIZE, LSB, (1 << (SIZE)) - 1, 0, TRUE) + +#define BIT(SIZE, LSB, BIAS) \ + { \ + static const struct mips_int_operand op = { \ + { OP_INT, SIZE, LSB }, (1 << (SIZE)) - 1, BIAS, 0, TRUE \ + }; \ + return &op.root; \ + } + +#define MAPPED_INT(SIZE, LSB, MAP, PRINT_HEX) \ + { \ + typedef char static_assert[(1 << (SIZE)) == ARRAY_SIZE (MAP)]; \ + static const struct mips_mapped_int_operand op = { \ + { OP_MAPPED_INT, SIZE, LSB }, MAP, PRINT_HEX \ + }; \ + return &op.root; \ + } + +#define MSB(SIZE, LSB, BIAS, ADD_LSB, OPSIZE) \ + { \ + static const struct mips_msb_operand op = { \ + { OP_MSB, SIZE, LSB }, BIAS, ADD_LSB, OPSIZE \ + }; \ + return &op.root; \ + } + +#define REG(SIZE, LSB, BANK) \ + { \ + static const struct mips_reg_operand op = { \ + { OP_REG, SIZE, LSB }, OP_REG_##BANK, 0 \ + }; \ + return &op.root; \ + } + +#define MAPPED_REG(SIZE, LSB, BANK, MAP) \ + { \ + typedef char static_assert[(1 << (SIZE)) == ARRAY_SIZE (MAP)]; \ + static const struct mips_reg_operand op = { \ + { OP_REG, SIZE, LSB }, OP_REG_##BANK, MAP \ + }; \ + return &op.root; \ + } + +#define REG_PAIR(SIZE, LSB, BANK, MAP) \ + { \ + typedef char static_assert1[(1 << (SIZE)) == ARRAY_SIZE (MAP##1)]; \ + typedef char static_assert2[(1 << (SIZE)) == ARRAY_SIZE (MAP##2)]; \ + static const struct mips_reg_pair_operand op = { \ + { OP_REG_PAIR, SIZE, LSB }, OP_REG_##BANK, MAP##1, MAP##2 \ + }; \ + return &op.root; \ + } + +#define PCREL(SIZE, LSB, ALIGN_LOG2, SHIFT, IS_SIGNED, INCLUDE_ISA_BIT, \ + FLIP_ISA_BIT) \ + { \ + static const struct mips_pcrel_operand op = { \ + { OP_PCREL, SIZE, LSB }, ALIGN_LOG2, SHIFT, IS_SIGNED, \ + INCLUDE_ISA_BIT, FLIP_ISA_BIT \ + }; \ + return &op.root; \ + } + +#define JUMP(SIZE, LSB, SHIFT) \ + PCREL (SIZE, LSB, SIZE + SHIFT, SHIFT, FALSE, TRUE, FALSE) + +#define JALX(SIZE, LSB, SHIFT) \ + PCREL (SIZE, LSB, SIZE + SHIFT, SHIFT, FALSE, TRUE, TRUE) + +#define BRANCH(SIZE, LSB, SHIFT) \ + PCREL (SIZE, LSB, 0, SHIFT, TRUE, TRUE, FALSE) + +#define SPECIAL(SIZE, LSB, TYPE) \ + { \ + static const struct mips_operand op = { OP_##TYPE, SIZE, LSB }; \ + return &op; \ + } diff --git a/opcodes/mips-opc.c b/opcodes/mips-opc.c index e6833f7438..702016b2c3 100644 --- a/opcodes/mips-opc.c +++ b/opcodes/mips-opc.c @@ -28,6 +28,123 @@ #include "sysdep.h" #include <stdio.h> #include "opcode/mips.h" +#include "mips-formats.h" + +static unsigned char reg_0_map[] = { 0 }; + +/* Return the mips_operand structure for the operand at the beginning of P. */ + +const struct mips_operand * +decode_mips_operand (const char *p) +{ + switch (p[0]) + { + case '+': + switch (p[1]) + { + case '1': HINT (5, 6); + case '2': HINT (10, 6); + case '3': HINT (15, 6); + case '4': HINT (20, 6); + + case 'A': BIT (5, 6, 0); /* (0 .. 31) */ + case 'B': MSB (5, 11, 1, TRUE, 32); /* (1 .. 32), 32-bit op */ + case 'C': MSB (5, 11, 1, FALSE, 32); /* (1 .. 32), 32-bit op */ + case 'E': BIT (5, 6, 32); /* (32 .. 63) */ + case 'F': MSB (5, 11, 33, TRUE, 64); /* (33 .. 64), 64-bit op */ + case 'G': MSB (5, 11, 33, FALSE, 64); /* (33 .. 64), 64-bit op */ + case 'H': MSB (5, 11, 1, FALSE, 64); /* (1 .. 32), 64-bit op */ + case 'J': HINT (10, 11); + case 'P': BIT (5, 6, 32); /* (32 .. 63) */ + case 'Q': SINT (10, 6); + case 'S': MSB (5, 11, 0, FALSE, 63); /* (0 .. 31), 64-bit op */ + case 'X': BIT (5, 16, 32); /* (32 .. 63) */ + case 'Z': REG (5, 0, FP); + + case 'a': SINT (8, 6); + case 'b': SINT (8, 3); + case 'c': INT_ADJ (9, 6, 255, 4, FALSE); /* (-256 .. 255) << 4 */ + case 'i': JALX (26, 0, 2); + case 'j': SINT (9, 7); + case 'p': BIT (5, 6, 0); /* (0 .. 31), 32-bit op */ + case 's': MSB (5, 11, 0, FALSE, 31); /* (0 .. 31) */ + case 't': REG (5, 16, COPRO); + case 'x': BIT (5, 16, 0); /* (0 .. 31) */ + case 'z': REG (5, 0, GP); + } + break; + + case '<': BIT (5, 6, 0); /* (0 .. 31) */ + case '>': BIT (5, 6, 32); /* (32 .. 63) */ + case '%': UINT (3, 21); + case ':': SINT (7, 19); + case '\'': HINT (6, 16); + case '@': SINT (10, 16); + case '!': UINT (1, 5); + case '$': UINT (1, 4); + case '*': REG (2, 18, ACC); + case '&': REG (2, 13, ACC); + case '~': SINT (12, 0); + case '\\': BIT (3, 12, 0); /* (0 .. 7) */ + + case '0': SINT (6, 20); + case '1': HINT (5, 6); + case '2': HINT (2, 11); + case '3': HINT (3, 21); + case '4': HINT (4, 21); + case '5': HINT (8, 16); + case '6': HINT (5, 21); + case '7': REG (2, 11, ACC); + case '8': HINT (6, 11); + case '9': REG (2, 21, ACC); + + case 'B': HINT (20, 6); + case 'C': HINT (25, 0); + case 'D': REG (5, 6, FP); + case 'E': REG (5, 16, COPRO); + case 'G': REG (5, 11, COPRO); + case 'H': UINT (3, 0); + case 'J': HINT (19, 6); + case 'K': REG (5, 11, HW); + case 'M': REG (3, 8, CCC); + case 'N': REG (3, 18, CCC); + case 'O': UINT (3, 21); + case 'P': SPECIAL (5, 1, PERF_REG); + case 'Q': SPECIAL (10, 16, MDMX_IMM_REG); + case 'R': REG (5, 21, FP); + case 'S': REG (5, 11, FP); + case 'T': REG (5, 16, FP); + case 'U': SPECIAL (10, 11, CLO_CLZ_DEST); + case 'V': REG (5, 11, FP); + case 'W': REG (5, 16, FP); + case 'X': REG (5, 6, VEC); + case 'Y': REG (5, 11, VEC); + case 'Z': REG (5, 16, VEC); + + case 'a': JUMP (26, 0, 2); + case 'b': REG (5, 21, GP); + case 'c': HINT (10, 16); + case 'd': REG (5, 11, GP); + case 'e': UINT (3, 22) + case 'g': REG (5, 11, COPRO); + case 'h': HINT (5, 11); + case 'i': HINT (16, 0); + case 'j': SINT (16, 0); + case 'k': HINT (5, 16); + case 'o': SINT (16, 0); + case 'p': BRANCH (16, 0, 2); + case 'q': HINT (10, 6); + case 'r': REG (5, 21, GP); + case 's': REG (5, 21, GP); + case 't': REG (5, 16, GP); + case 'u': HINT (16, 0); + case 'v': REG (5, 21, GP); + case 'w': REG (5, 16, GP); + case 'x': REG (0, 0, GP); + case 'z': MAPPED_REG (0, 0, GP, reg_0_map); + } + return 0; +} /* Short hand so the lines aren't too long. */ |