aboutsummaryrefslogtreecommitdiff
path: root/opcodes
diff options
context:
space:
mode:
authorRichard Sandiford <rdsandiford@googlemail.com>2013-07-14 13:28:56 +0000
committerRichard Sandiford <rdsandiford@googlemail.com>2013-07-14 13:28:56 +0000
commitab90248154ba05dc800c480712c3cb03eb33b135 (patch)
tree111a36595f3d3ad10b07a5fc1b7c6a443ac757a0 /opcodes
parent2f8b73cc2b2dd77e0f4cc772262c9ae0092d4e14 (diff)
downloadgdb-ab90248154ba05dc800c480712c3cb03eb33b135.zip
gdb-ab90248154ba05dc800c480712c3cb03eb33b135.tar.gz
gdb-ab90248154ba05dc800c480712c3cb03eb33b135.tar.bz2
include/opcode/
* 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. opcodes/ * 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. gas/ * 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.
Diffstat (limited to 'opcodes')
-rw-r--r--opcodes/ChangeLog26
-rw-r--r--opcodes/micromips-opc.c149
-rw-r--r--opcodes/mips-dis.c1401
-rw-r--r--opcodes/mips-formats.h113
-rw-r--r--opcodes/mips-opc.c117
5 files changed, 720 insertions, 1086 deletions
diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog
index aa4d0f4..17cc1c6 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 de8053c..8630769 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 7e3d123..cd7c633 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 0000000..7deba5e
--- /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 e6833f7..702016b 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. */