include/opcode/
authorRichard Sandiford <rdsandiford@googlemail.com>
Sun, 14 Jul 2013 13:28:56 +0000 (13:28 +0000)
committerRichard Sandiford <rdsandiford@googlemail.com>
Sun, 14 Jul 2013 13:28:56 +0000 (13:28 +0000)
* 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.

gas/ChangeLog
gas/config/tc-mips.c
include/opcode/ChangeLog
include/opcode/mips.h
opcodes/ChangeLog
opcodes/micromips-opc.c
opcodes/mips-dis.c
opcodes/mips-formats.h [new file with mode: 0644]
opcodes/mips-opc.c

index dc609b9..0162174 100644 (file)
@@ -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>
index 4e878c8..6bb44c5 100644 (file)
@@ -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;
index a96a4ab..fc991a0 100644 (file)
@@ -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>
index 2112889..2d77b95 100644 (file)
 #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;
 
index aa4d0f4..17cc1c6 100644 (file)
@@ -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.
 
index de8053c..8630769 100644 (file)
    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
index 7e3d123..cd7c633 100644 (file)
@@ -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;
 }
-\f
-/* 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;
+    }
+}
+\f
+/* 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 (file)
index 0000000..7deba5e
--- /dev/null
@@ -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; \
+  }
index e6833f7..702016b 100644 (file)
 #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.  */