2007-07-06 Mark Kettenis <kettenis@gnu.org>
[deliverable/binutils-gdb.git] / opcodes / m68k-dis.c
index 5e4150d05ed8e59d7cb1f05c67c5326a63bf04a9..b9d8a0471cb93a059fac47f154c00d10e7d02d47 100644 (file)
@@ -1,17 +1,19 @@
 /* Print Motorola 68k instructions.
    Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
-   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
 
-   This file is free software; you can redistribute it and/or modify
+   This file is part of the GNU opcodes library.
+
+   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 2 of the License, or
-   (at your option) any later version.
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
 
-   This program 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.
+   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; if not, write to the Free Software
@@ -280,6 +282,11 @@ fetch_arg (unsigned char *buffer,
       val = (buffer[1] >> 6);
       break;
 
+    case 'E':
+      FETCH_DATA (info, buffer + 3);
+      val = (buffer[2] >> 1);
+      break;
+
     case 'm':
       val = (buffer[1] & 0x40 ? 0x8 : 0)
        | ((buffer[0] >> 1) & 0x7)
@@ -310,29 +317,8 @@ fetch_arg (unsigned char *buffer,
       abort ();
     }
 
-  switch (bits)
-    {
-    case 1:
-      return val & 1;
-    case 2:
-      return val & 3;
-    case 3:
-      return val & 7;
-    case 4:
-      return val & 017;
-    case 5:
-      return val & 037;
-    case 6:
-      return val & 077;
-    case 7:
-      return val & 0177;
-    case 8:
-      return val & 0377;
-    case 12:
-      return val & 07777;
-    default:
-      abort ();
-    }
+  /* bits is never too big.  */
+  return val & ((1 << bits) - 1);
 }
 
 /* Check if an EA is valid for a particular code.  This is required
@@ -645,12 +631,17 @@ print_insn_arg (const char *d,
              {"%dtt0",0x006}, {"%dtt1",0x007}, {"%buscr",0x008},
             {"%usp", 0x800}, {"%vbr", 0x801}, {"%caar", 0x802},
             {"%msp", 0x803}, {"%isp", 0x804},
-            {"%flashbar", 0xc04}, {"%rambar", 0xc05}, /* mcf528x added these.  */
+            /* reg c04 is sometimes called flashbar or rambar.
+               rec c05 is also sometimes called rambar.  */
+            {"%rambar0", 0xc04}, {"%rambar1", 0xc05},
 
             /* Should we be calling this psr like we do in case 'Y'?  */
             {"%mmusr",0x805},
 
-             {"%urp", 0x806}, {"%srp", 0x807}, {"%pcr", 0x808}};
+             {"%urp", 0x806}, {"%srp", 0x807}, {"%pcr", 0x808},
+
+            /* Fido added these.  */
+             {"%cac", 0xffe}, {"%mbb", 0xfff}};
 
        val = fetch_arg (buffer, place, 12, info);
        for (regno = sizeof names / sizeof names[0] - 1; regno >= 0; regno--)
@@ -680,6 +671,16 @@ print_insn_arg (const char *d,
       (*info->fprintf_func) (info->stream, "#%d", val);
       break;
 
+    case 'j':
+      val = fetch_arg (buffer, place, 3, info);
+      (*info->fprintf_func) (info->stream, "#%d", val+1);
+      break;
+
+    case 'K':
+      val = fetch_arg (buffer, place, 9, info);
+      (*info->fprintf_func) (info->stream, "#%d", val);
+      break;
+
     case 'M':
       if (place == 'h')
        {
@@ -1204,18 +1205,22 @@ print_insn_arg (const char *d,
 static int
 match_insn_m68k (bfd_vma memaddr,
                 disassemble_info * info,
-                const struct m68k_opcode * best,
-                struct private * priv)
+                const struct m68k_opcode * best)
 {
   unsigned char *save_p;
   unsigned char *p;
   const char *d;
+  const char *args = best->args;
 
+  struct private *priv = (struct private *) info->private_data;
   bfd_byte *buffer = priv->the_buffer;
   fprintf_ftype save_printer = info->fprintf_func;
   void (* save_print_address) (bfd_vma, struct disassemble_info *)
     = info->print_address_func;
 
+  if (*args == '.')
+    args++;
+  
   /* Point at first word of argument data,
      and at descriptor for first argument.  */
   p = buffer + 2;
@@ -1224,7 +1229,7 @@ match_insn_m68k (bfd_vma memaddr,
      The only place this is stored in the opcode table is
      in the arguments--look for arguments which specify fields in the 2nd
      or 3rd words of the instruction.  */
-  for (d = best->args; *d; d += 2)
+  for (d = args; *d; d += 2)
     {
       /* I don't think it is necessary to be checking d[0] here;
         I suspect all this could be moved to the case statement below.  */
@@ -1271,8 +1276,8 @@ match_insn_m68k (bfd_vma memaddr,
      three words long.  */
   if (p - buffer < 6
       && (best->match & 0xffff) == 0xffff
-      && best->args[0] == '#'
-      && best->args[1] == 'w')
+      && args[0] == '#'
+      && args[1] == 'w')
     {
       /* Copy the one word argument into the usual location for a one
         word argument, to simplify printing it.  We can get away with
@@ -1286,15 +1291,13 @@ match_insn_m68k (bfd_vma memaddr,
 
   FETCH_DATA (info, p);
 
-  d = best->args;
-
   save_p = p;
   info->print_address_func = dummy_print_address;
   info->fprintf_func = (fprintf_ftype) dummy_printer;
 
   /* We scan the operands twice.  The first time we don't print anything,
      but look for errors.  */
-  for (; *d; d += 2)
+  for (d = args; *d; d += 2)
     {
       int eaten = print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
 
@@ -1308,12 +1311,14 @@ match_insn_m68k (bfd_vma memaddr,
        }
       else
        {
+         /* We must restore the print functions before trying to print the
+            error message.  */
+         info->fprintf_func = save_printer;
+         info->print_address_func = save_print_address;
          info->fprintf_func (info->stream,
                              /* xgettext:c-format */
                              _("<internal error in opcode table: %s %s>\n"),
                              best->name,  best->args);
-         info->fprintf_func = save_printer;
-         info->print_address_func = save_print_address;
          return 2;
        }
     }
@@ -1322,7 +1327,7 @@ match_insn_m68k (bfd_vma memaddr,
   info->fprintf_func = save_printer;
   info->print_address_func = save_print_address;
 
-  d = best->args;
+  d = args;
 
   info->fprintf_func (info->stream, "%s", best->name);
 
@@ -1341,21 +1346,24 @@ match_insn_m68k (bfd_vma memaddr,
   return p - buffer;
 }
 
-/* Print the m68k instruction at address MEMADDR in debugged memory,
-   on INFO->STREAM.  Returns length of the instruction, in bytes.  */
+/* Try to interpret the instruction at address MEMADDR as one that
+   can execute on a processor with the features given by ARCH_MASK.
+   If successful, print the instruction to INFO->STREAM and return
+   its length in bytes.  Return 0 otherwise.  */
 
-int
-print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
+static int
+m68k_scan_mask (bfd_vma memaddr, disassemble_info *info,
+               unsigned int arch_mask)
 {
   int i;
   const char *d;
-  unsigned int arch_mask;
-  struct private priv;
-  bfd_byte *buffer = priv.the_buffer;
-  int major_opcode;
-  static int numopcodes[16];
   static const struct m68k_opcode **opcodes[16];
+  static int numopcodes[16];
   int val;
+  int major_opcode;
+
+  struct private *priv = (struct private *) info->private_data;
+  bfd_byte *buffer = priv->the_buffer;
 
   if (!opcodes[0])
     {
@@ -1383,72 +1391,6 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
        *opc_pointer[(m68k_opcodes[i].opcode >> 28) & 15]++ = &m68k_opcodes[i];
     }
 
-  info->private_data = (PTR) &priv;
-  /* Tell objdump to use two bytes per chunk
-     and six bytes per line for displaying raw data.  */
-  info->bytes_per_chunk = 2;
-  info->bytes_per_line = 6;
-  info->display_endian = BFD_ENDIAN_BIG;
-  priv.max_fetched = priv.the_buffer;
-  priv.insn_start = memaddr;
-
-  if (setjmp (priv.bailout) != 0)
-    /* Error return.  */
-    return -1;
-
-  switch (info->mach)
-    {
-    default:
-    case 0:
-      arch_mask = (unsigned int) -1;
-      break;
-    case bfd_mach_m68000:
-      arch_mask = m68000|m68881|m68851;
-      break;
-    case bfd_mach_m68008:
-      arch_mask = m68008|m68881|m68851;
-      break;
-    case bfd_mach_m68010:
-      arch_mask = m68010|m68881|m68851;
-      break;
-    case bfd_mach_m68020:
-      arch_mask = m68020|m68881|m68851;
-      break;
-    case bfd_mach_m68030:
-      arch_mask = m68030|m68881|m68851;
-      break;
-    case bfd_mach_m68040:
-      arch_mask = m68040|m68881|m68851;
-      break;
-    case bfd_mach_m68060:
-      arch_mask = m68060|m68881|m68851;
-      break;
-    case bfd_mach_mcf5200:
-      arch_mask = mcfisa_a;
-      break;
-    case bfd_mach_mcf521x:
-    case bfd_mach_mcf528x:
-      arch_mask = mcfisa_a|mcfhwdiv|mcfisa_aa|mcfusp|mcfemac;
-      break;
-    case bfd_mach_mcf5206e:
-      arch_mask = mcfisa_a|mcfhwdiv|mcfmac;
-      break;
-    case bfd_mach_mcf5249:
-      arch_mask = mcfisa_a|mcfhwdiv|mcfemac;
-      break;
-    case bfd_mach_mcf5307:
-      arch_mask = mcfisa_a|mcfhwdiv|mcfmac;
-      break;
-    case bfd_mach_mcf5407:
-      arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfmac;
-      break;
-    case bfd_mach_mcf547x:
-    case bfd_mach_mcf548x:
-    case bfd_mach_mcfv4e:
-      arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfusp|cfloat|mcfemac;
-      break;
-    }
-
   FETCH_DATA (info, buffer + 2);
   major_opcode = (buffer[0] >> 4) & 15;
 
@@ -1457,6 +1399,10 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
       const struct m68k_opcode *opc = opcodes[major_opcode][i];
       unsigned long opcode = opc->opcode;
       unsigned long match = opc->match;
+      const char *args = opc->args;
+
+      if (*args == '.')
+       args++;
 
       if (((0xff & buffer[0] & (match >> 24)) == (0xff & (opcode >> 24)))
          && ((0xff & buffer[1] & (match >> 16)) == (0xff & (opcode >> 16)))
@@ -1472,7 +1418,7 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
          /* Don't use for printout the variants of divul and divsl
             that have the same register number in two places.
             The more general variants will match instead.  */
-         for (d = opc->args; *d; d += 2)
+         for (d = args; *d; d += 2)
            if (d[1] == 'D')
              break;
 
@@ -1480,7 +1426,7 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
             point coprocessor instructions which use the same
             register number in two places, as above.  */
          if (*d == '\0')
-           for (d = opc->args; *d; d += 2)
+           for (d = args; *d; d += 2)
              if (d[1] == 't')
                break;
 
@@ -1488,7 +1434,7 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
             wait for fmoveml.  */
          if (*d == '\0')
            {
-             for (d = opc->args; *d; d += 2)
+             for (d = args; *d; d += 2)
                {
                  if (d[0] == 's' && d[1] == '8')
                    {
@@ -1502,7 +1448,7 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
          /* Don't match FPU insns with non-default coprocessor ID.  */
          if (*d == '\0')
            {
-             for (d = opc->args; *d; d += 2)
+             for (d = args; *d; d += 2)
                {
                  if (d[0] == 'I')
                    {
@@ -1514,10 +1460,81 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
            }
 
          if (*d == '\0')
-           if ((val = match_insn_m68k (memaddr, info, opc, & priv)))
+           if ((val = match_insn_m68k (memaddr, info, opc)))
              return val;
        }
     }
+  return 0;
+}              
+
+/* Print the m68k instruction at address MEMADDR in debugged memory,
+   on INFO->STREAM.  Returns length of the instruction, in bytes.  */
+
+int
+print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
+{
+  unsigned int arch_mask;
+  struct private priv;
+  int val;
+
+  bfd_byte *buffer = priv.the_buffer;
+
+  /* Save these printing functions in case we need to restore them
+     later.  */
+  fprintf_ftype save_printer = info->fprintf_func;
+  void (* save_print_address) (bfd_vma, struct disassemble_info *)
+    = info->print_address_func;
+
+  info->private_data = (PTR) &priv;
+  /* Tell objdump to use two bytes per chunk
+     and six bytes per line for displaying raw data.  */
+  info->bytes_per_chunk = 2;
+  info->bytes_per_line = 6;
+  info->display_endian = BFD_ENDIAN_BIG;
+  priv.max_fetched = priv.the_buffer;
+  priv.insn_start = memaddr;
+
+  if (setjmp (priv.bailout) != 0)
+    {
+      /* longjmp may be called while these printing functions are
+        temporarily replaced with dummy functions.  Restore them
+        before we leave.
+
+        Admittedly, this save-and-restore operation is somewhat ugly
+        in that we are exposing the fact that match_insn_m68k
+        temporarily replaces insn->fprintf_func and
+        insn->print_address_func.  Perhaps, a real fix is to report a
+        FETCH_DATA failure with a return value of some sort, without
+        using setjmp/longjmp.  A better fix may be to teach the m68k
+        disassembler do its job without temporarily replacing
+        insn->fprintf_func and insn->print_address_func, but that's a
+        task for another day.  */
+      info->fprintf_func = save_printer;
+      info->print_address_func = save_print_address;
+
+      /* Error return.  */
+      return -1;
+    }
+
+  arch_mask = bfd_m68k_mach_to_features (info->mach);
+  if (!arch_mask)
+    {
+      /* First try printing an m680x0 instruction.  Try printing a Coldfire
+        one if that fails.  */
+      val = m68k_scan_mask (memaddr, info, m68k_mask);
+      if (val)
+       return val;
+
+      val = m68k_scan_mask (memaddr, info, mcf_mask);
+      if (val)
+       return val;
+    }
+  else
+    {
+      val = m68k_scan_mask (memaddr, info, arch_mask);
+      if (val)
+       return val;
+    }
 
   /* Handle undefined instructions.  */
   info->fprintf_func (info->stream, "0%o", (buffer[0] << 8) + buffer[1]);
This page took 0.030073 seconds and 4 git commands to generate.