MIPS16: Simplify extended operand handling
[deliverable/binutils-gdb.git] / opcodes / mips-dis.c
index c30bbd0207667e0a67c99f79bb3c4034ef045ebc..a1152cd721a11cc3b10e017d1c3f407adefa6196 100644 (file)
@@ -658,7 +658,7 @@ const struct mips_arch_choice mips_arch_choices[] =
 
   /* This entry, mips16, is here only for ISA/processor selection; do
      not print its name.  */
-  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3, 0,
+  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS64, 0,
     mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric,
     mips_hwr_names_numeric },
 };
@@ -839,15 +839,22 @@ set_default_mips_dis_options (struct disassemble_info *info)
       mips_cp1_names = chosen_arch->cp1_names;
       mips_hwr_names = chosen_arch->hwr_names;
     }
-#endif
 
   /* Update settings according to the ELF file header flags.  */
   if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
     {
       struct bfd *abfd = info->section->owner;
       Elf_Internal_Ehdr *header = elf_elfheader (abfd);
-      Elf_Internal_ABIFlags_v0 *abiflags = bfd_mips_elf_get_abiflags (abfd);
-
+      Elf_Internal_ABIFlags_v0 *abiflags = NULL;
+
+      /* We won't ever get here if !HAVE_BFD_MIPS_ELF_GET_ABIFLAGS,
+        because we won't then have a MIPS/ELF BFD, however we need
+        to guard against a link error in a `--enable-targets=...'
+        configuration with a 32-bit host where the MIPS target is
+        a secondary, or with MIPS/ECOFF configurations.  */
+#ifdef HAVE_BFD_MIPS_ELF_GET_ABIFLAGS
+      abiflags = bfd_mips_elf_get_abiflags (abfd);
+#endif
       /* If an ELF "newabi" binary, use the n32/(n)64 GPR names.  */
       if (is_newabi (header))
        mips_gpr_names = mips_gpr_names_newabi;
@@ -859,6 +866,7 @@ set_default_mips_dis_options (struct disassemble_info *info)
       else if (header->e_flags & EF_MIPS_ARCH_ASE_MDMX)
        mips_ase |= ASE_MDMX;
     }
+#endif
 }
 
 static void
@@ -1815,6 +1823,7 @@ print_mips16_insn_arg (struct disassemble_info *info,
   const fprintf_ftype infprintf = info->fprintf_func;
   void *is = info->stream;
   const struct mips_operand *operand, *ext_operand;
+  unsigned short ext_size;
   unsigned int uval;
   bfd_vma baseaddr;
 
@@ -1919,31 +1928,26 @@ print_mips16_insn_arg (struct disassemble_info *info,
          info->data_size = 1 << int_op->shift;
        }
 
-      if (operand->size == 26)
-       /* In this case INSN is the first two bytes of the instruction
-          and EXTEND is the second two bytes.  */
-       uval = ((insn & 0x1f) << 21) | ((insn & 0x3e0) << 11) | extend;
-      else
+      ext_size = 0;
+      if (use_extend)
        {
-         /* Calculate the full field value.  */
-         uval = mips_extract_operand (operand, insn);
-         if (use_extend)
+         ext_operand = decode_mips16_operand (type, TRUE);
+         if (ext_operand != operand)
            {
-             ext_operand = decode_mips16_operand (type, TRUE);
-             if (ext_operand != operand)
-               {
-                 operand = ext_operand;
-                 if (operand->size == 16)
-                   uval = (((extend & 0x1f) << 11) | (extend & 0x7e0)
-                           | (uval & 0x1f));
-                 else if (operand->size == 15)
-                   uval |= ((extend & 0xf) << 11) | (extend & 0x7f0);
-                 else
-                   uval = ((((extend >> 6) & 0x1f) | (extend & 0x20))
-                           & ((1U << operand->size) - 1));
-               }
+             ext_size = ext_operand->size;
+             operand = ext_operand;
            }
        }
+      if (operand->size == 26)
+       uval = ((extend & 0x1f) << 21) | ((extend & 0x3e0) << 11) | insn;
+      else if (ext_size == 16)
+       uval = ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+      else if (ext_size == 15)
+       uval = ((extend & 0xf) << 11) | (extend & 0x7f0) | (insn & 0xf);
+      else if (ext_size == 6)
+       uval = ((extend >> 6) & 0x1f) | (extend & 0x20);
+      else
+       uval = mips_extract_operand (operand, (extend << 16) | insn);
 
       baseaddr = memaddr + 2;
       if (operand->type == OP_PCREL)
@@ -2008,6 +2012,15 @@ is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr)
   return FALSE;
 }
 
+/* Whether none, a 32-bit or a 16-bit instruction match has been done.  */
+
+enum match_kind
+{
+  MATCH_NONE,
+  MATCH_FULL,
+  MATCH_SHORT
+};
+
 /* Disassemble mips16 instructions.  */
 
 static int
@@ -2016,13 +2029,13 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   const fprintf_ftype infprintf = info->fprintf_func;
   int status;
   bfd_byte buffer[4];
-  int length;
-  int insn;
-  bfd_boolean use_extend;
-  int extend = 0;
   const struct mips_opcode *op, *opend;
   struct mips_print_arg_state state;
   void *is = info->stream;
+  bfd_boolean have_second;
+  unsigned int second;
+  unsigned int first;
+  unsigned int full;
 
   info->bytes_per_chunk = 2;
   info->display_endian = info->endian;
@@ -2063,44 +2076,26 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
       return -1;
     }
 
-  length = 2;
-
   if (info->endian == BFD_ENDIAN_BIG)
-    insn = bfd_getb16 (buffer);
+    first = bfd_getb16 (buffer);
   else
-    insn = bfd_getl16 (buffer);
+    first = bfd_getl16 (buffer);
 
-  /* Handle the extend opcode specially.  */
-  use_extend = FALSE;
-  if ((insn & 0xf800) == 0xf000)
+  status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
+  if (status == 0)
     {
-      use_extend = TRUE;
-      extend = insn & 0x7ff;
-
-      memaddr += 2;
-
-      status = (*info->read_memory_func) (memaddr, buffer, 2, info);
-      if (status != 0)
-       {
-         infprintf (is, "extend\t0x%x", (unsigned int) extend);
-         (*info->memory_error_func) (status, memaddr, info);
-         return -1;
-       }
-
+      have_second = TRUE;
       if (info->endian == BFD_ENDIAN_BIG)
-       insn = bfd_getb16 (buffer);
+       second = bfd_getb16 (buffer);
       else
-       insn = bfd_getl16 (buffer);
-
-      /* Check for an extend opcode followed by an extend opcode.  */
-      if ((insn & 0xf800) == 0xf000)
-       {
-         infprintf (is, "extend\t0x%x", (unsigned int) extend);
-         info->insn_type = dis_noninsn;
-         return length;
-       }
-
-      length += 2;
+       second = bfd_getl16 (buffer);
+      full = (first << 16) | second;
+    }
+  else
+    {
+      have_second = FALSE;
+      second = 0;
+      full = first;
     }
 
   /* FIXME: Should probably use a hash table on the major opcode here.  */
@@ -2108,37 +2103,39 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   opend = mips16_opcodes + bfd_mips16_num_opcodes;
   for (op = mips16_opcodes; op < opend; op++)
     {
-      if (op->pinfo != INSN_MACRO
-         && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
-         && (insn & op->mask) == op->match)
-       {
-         const char *s;
-
-         if (op->args[0] == 'a' || op->args[0] == 'i')
-           {
-             if (use_extend)
-               {
-                 infprintf (is, "extend\t0x%x", (unsigned int) extend);
-                 info->insn_type = dis_noninsn;
-                 return length - 2;
-               }
+      enum match_kind match;
 
-             use_extend = FALSE;
+      if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor))
+       continue;
 
-             memaddr += 2;
+      if (op->pinfo == INSN_MACRO
+         || (no_aliases && (op->pinfo2 & INSN2_ALIAS)))
+       match = MATCH_NONE;
+      else if (mips_opcode_32bit_p (op))
+       {
+         if (have_second
+             && (full & op->mask) == op->match)
+           match = MATCH_FULL;
+         else
+           match = MATCH_NONE;
+       }
+      else if ((first & op->mask) == op->match)
+       {
+         match = MATCH_SHORT;
+         second = 0;
+         full = first;
+       }
+      else if ((first & 0xf800) == 0xf000
+              && have_second
+              && !(op->pinfo2 & INSN2_SHORT_ONLY)
+              && (second & op->mask) == op->match)
+       match = MATCH_FULL;
+      else
+       match = MATCH_NONE;
 
-             status = (*info->read_memory_func) (memaddr, buffer, 2,
-                                                 info);
-             if (status == 0)
-               {
-                 use_extend = TRUE;
-                 if (info->endian == BFD_ENDIAN_BIG)
-                   extend = bfd_getb16 (buffer);
-                 else
-                   extend = bfd_getl16 (buffer);
-                 length += 2;
-               }
-           }
+      if (match != MATCH_NONE)
+       {
+         const char *s;
 
          infprintf (is, "%s", op->name);
          if (op->args[0] != '\0')
@@ -2149,7 +2146,7 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
            {
              if (*s == ','
                  && s[1] == 'w'
-                 && GET_OP (insn, RX) == GET_OP (insn, RY))
+                 && GET_OP (full, RX) == GET_OP (full, RY))
                {
                  /* Skip the register and the comma.  */
                  ++s;
@@ -2157,14 +2154,25 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
                }
              if (*s == ','
                  && s[1] == 'v'
-                 && GET_OP (insn, RZ) == GET_OP (insn, RX))
+                 && GET_OP (full, RZ) == GET_OP (full, RX))
                {
                  /* Skip the register and the comma.  */
                  ++s;
                  continue;
                }
-             print_mips16_insn_arg (info, &state, op, *s, memaddr, insn,
-                                    use_extend, extend, s[1] == '(');
+             switch (match)
+               {
+                 case MATCH_FULL:
+                   print_mips16_insn_arg (info, &state, op, *s, memaddr + 2,
+                                          second, TRUE, first, s[1] == '(');
+                   break;
+                 case MATCH_SHORT:
+                   print_mips16_insn_arg (info, &state, op, *s, memaddr,
+                                          first, FALSE, 0, s[1] == '(');
+                   break;
+                 case MATCH_NONE:      /* Stop the compiler complaining.  */
+                   break;
+               }
            }
 
          /* Figure out branch instruction type and delay slot information.  */
@@ -2181,17 +2189,15 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
          else if ((op->pinfo2 & INSN2_COND_BRANCH) != 0)
            info->insn_type = dis_condbranch;
 
-         return length;
+         return match == MATCH_FULL ? 4 : 2;
        }
     }
 #undef GET_OP
 
-  if (use_extend)
-    infprintf (is, "0x%x ", extend | 0xf000);
-  infprintf (is, "0x%x", insn);
+  infprintf (is, "0x%x", first);
   info->insn_type = dis_noninsn;
 
-  return length;
+  return 2;
 }
 
 /* Disassemble microMIPS instructions.  */
This page took 0.037871 seconds and 4 git commands to generate.