ppc-dis.c revision 78828
160484Sobrien/* ppc-dis.c -- Disassemble PowerPC instructions
278828Sobrien   Copyright 1994, 1995, 2000 Free Software Foundation, Inc.
360484Sobrien   Written by Ian Lance Taylor, Cygnus Support
460484Sobrien
560484SobrienThis file is part of GDB, GAS, and the GNU binutils.
660484Sobrien
760484SobrienGDB, GAS, and the GNU binutils are free software; you can redistribute
860484Sobrienthem and/or modify them under the terms of the GNU General Public
960484SobrienLicense as published by the Free Software Foundation; either version
1060484Sobrien2, or (at your option) any later version.
1160484Sobrien
1260484SobrienGDB, GAS, and the GNU binutils are distributed in the hope that they
1360484Sobrienwill be useful, but WITHOUT ANY WARRANTY; without even the implied
1460484Sobrienwarranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
1560484Sobrienthe GNU General Public License for more details.
1660484Sobrien
1760484SobrienYou should have received a copy of the GNU General Public License
1860484Sobrienalong with this file; see the file COPYING.  If not, write to the Free
1960484SobrienSoftware Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2060484Sobrien
2160484Sobrien#include <stdio.h>
2260484Sobrien#include "sysdep.h"
2360484Sobrien#include "dis-asm.h"
2460484Sobrien#include "opcode/ppc.h"
2560484Sobrien
2660484Sobrien/* This file provides several disassembler functions, all of which use
2760484Sobrien   the disassembler interface defined in dis-asm.h.  Several functions
2860484Sobrien   are provided because this file handles disassembly for the PowerPC
2960484Sobrien   in both big and little endian mode and also for the POWER (RS/6000)
3060484Sobrien   chip.  */
3160484Sobrien
3260484Sobrienstatic int print_insn_powerpc PARAMS ((bfd_vma, struct disassemble_info *,
3360484Sobrien				       int bigendian, int dialect));
3460484Sobrien
3560484Sobrien/* Print a big endian PowerPC instruction.  For convenience, also
3677298Sobrien   disassemble instructions supported by the Motorola PowerPC 601
3777298Sobrien   and the Altivec vector unit.  */
3860484Sobrien
3960484Sobrienint
4060484Sobrienprint_insn_big_powerpc (memaddr, info)
4160484Sobrien     bfd_vma memaddr;
4260484Sobrien     struct disassemble_info *info;
4360484Sobrien{
4460484Sobrien  return print_insn_powerpc (memaddr, info, 1,
4577298Sobrien			     PPC_OPCODE_PPC | PPC_OPCODE_601 |
4677298Sobrien			     PPC_OPCODE_ALTIVEC);
4760484Sobrien}
4860484Sobrien
4960484Sobrien/* Print a little endian PowerPC instruction.  For convenience, also
5077298Sobrien   disassemble instructions supported by the Motorola PowerPC 601
5177298Sobrien   and the Altivec vector unit.  */
5260484Sobrien
5360484Sobrienint
5460484Sobrienprint_insn_little_powerpc (memaddr, info)
5560484Sobrien     bfd_vma memaddr;
5660484Sobrien     struct disassemble_info *info;
5760484Sobrien{
5860484Sobrien  return print_insn_powerpc (memaddr, info, 0,
5977298Sobrien			     PPC_OPCODE_PPC | PPC_OPCODE_601 |
6077298Sobrien			     PPC_OPCODE_ALTIVEC);
6160484Sobrien}
6260484Sobrien
6360484Sobrien/* Print a POWER (RS/6000) instruction.  */
6460484Sobrien
6560484Sobrienint
6660484Sobrienprint_insn_rs6000 (memaddr, info)
6760484Sobrien     bfd_vma memaddr;
6860484Sobrien     struct disassemble_info *info;
6960484Sobrien{
7060484Sobrien  return print_insn_powerpc (memaddr, info, 1, PPC_OPCODE_POWER);
7160484Sobrien}
7260484Sobrien
7360484Sobrien/* Print a PowerPC or POWER instruction.  */
7460484Sobrien
7560484Sobrienstatic int
7660484Sobrienprint_insn_powerpc (memaddr, info, bigendian, dialect)
7760484Sobrien     bfd_vma memaddr;
7860484Sobrien     struct disassemble_info *info;
7960484Sobrien     int bigendian;
8060484Sobrien     int dialect;
8160484Sobrien{
8260484Sobrien  bfd_byte buffer[4];
8360484Sobrien  int status;
8460484Sobrien  unsigned long insn;
8560484Sobrien  const struct powerpc_opcode *opcode;
8660484Sobrien  const struct powerpc_opcode *opcode_end;
8760484Sobrien  unsigned long op;
8860484Sobrien
8960484Sobrien  status = (*info->read_memory_func) (memaddr, buffer, 4, info);
9060484Sobrien  if (status != 0)
9160484Sobrien    {
9260484Sobrien      (*info->memory_error_func) (status, memaddr, info);
9360484Sobrien      return -1;
9460484Sobrien    }
9560484Sobrien
9660484Sobrien  if (bigendian)
9760484Sobrien    insn = bfd_getb32 (buffer);
9860484Sobrien  else
9960484Sobrien    insn = bfd_getl32 (buffer);
10060484Sobrien
10160484Sobrien  /* Get the major opcode of the instruction.  */
10260484Sobrien  op = PPC_OP (insn);
10360484Sobrien
10460484Sobrien  /* Find the first match in the opcode table.  We could speed this up
10560484Sobrien     a bit by doing a binary search on the major opcode.  */
10660484Sobrien  opcode_end = powerpc_opcodes + powerpc_num_opcodes;
10760484Sobrien  for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
10860484Sobrien    {
10960484Sobrien      unsigned long table_op;
11060484Sobrien      const unsigned char *opindex;
11160484Sobrien      const struct powerpc_operand *operand;
11260484Sobrien      int invalid;
11360484Sobrien      int need_comma;
11460484Sobrien      int need_paren;
11560484Sobrien
11660484Sobrien      table_op = PPC_OP (opcode->opcode);
11760484Sobrien      if (op < table_op)
11860484Sobrien	break;
11960484Sobrien      if (op > table_op)
12060484Sobrien	continue;
12160484Sobrien
12260484Sobrien      if ((insn & opcode->mask) != opcode->opcode
12360484Sobrien	  || (opcode->flags & dialect) == 0)
12460484Sobrien	continue;
12560484Sobrien
12660484Sobrien      /* Make two passes over the operands.  First see if any of them
12760484Sobrien	 have extraction functions, and, if they do, make sure the
12860484Sobrien	 instruction is valid.  */
12960484Sobrien      invalid = 0;
13060484Sobrien      for (opindex = opcode->operands; *opindex != 0; opindex++)
13160484Sobrien	{
13260484Sobrien	  operand = powerpc_operands + *opindex;
13360484Sobrien	  if (operand->extract)
13460484Sobrien	    (*operand->extract) (insn, &invalid);
13560484Sobrien	}
13660484Sobrien      if (invalid)
13760484Sobrien	continue;
13860484Sobrien
13960484Sobrien      /* The instruction is valid.  */
14060484Sobrien      (*info->fprintf_func) (info->stream, "%s", opcode->name);
14160484Sobrien      if (opcode->operands[0] != 0)
14260484Sobrien	(*info->fprintf_func) (info->stream, "\t");
14360484Sobrien
14460484Sobrien      /* Now extract and print the operands.  */
14560484Sobrien      need_comma = 0;
14660484Sobrien      need_paren = 0;
14760484Sobrien      for (opindex = opcode->operands; *opindex != 0; opindex++)
14860484Sobrien	{
14960484Sobrien	  long value;
15060484Sobrien
15160484Sobrien	  operand = powerpc_operands + *opindex;
15260484Sobrien
15360484Sobrien	  /* Operands that are marked FAKE are simply ignored.  We
15460484Sobrien	     already made sure that the extract function considered
15560484Sobrien	     the instruction to be valid.  */
15660484Sobrien	  if ((operand->flags & PPC_OPERAND_FAKE) != 0)
15760484Sobrien	    continue;
15860484Sobrien
15960484Sobrien	  /* Extract the value from the instruction.  */
16060484Sobrien	  if (operand->extract)
16160484Sobrien	    value = (*operand->extract) (insn, (int *) NULL);
16260484Sobrien	  else
16360484Sobrien	    {
16460484Sobrien	      value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
16560484Sobrien	      if ((operand->flags & PPC_OPERAND_SIGNED) != 0
16660484Sobrien		  && (value & (1 << (operand->bits - 1))) != 0)
16760484Sobrien		value -= 1 << operand->bits;
16860484Sobrien	    }
16960484Sobrien
17060484Sobrien	  /* If the operand is optional, and the value is zero, don't
17160484Sobrien	     print anything.  */
17260484Sobrien	  if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
17360484Sobrien	      && (operand->flags & PPC_OPERAND_NEXT) == 0
17460484Sobrien	      && value == 0)
17560484Sobrien	    continue;
17660484Sobrien
17760484Sobrien	  if (need_comma)
17860484Sobrien	    {
17960484Sobrien	      (*info->fprintf_func) (info->stream, ",");
18060484Sobrien	      need_comma = 0;
18160484Sobrien	    }
18260484Sobrien
18360484Sobrien	  /* Print the operand as directed by the flags.  */
18460484Sobrien	  if ((operand->flags & PPC_OPERAND_GPR) != 0)
18560484Sobrien	    (*info->fprintf_func) (info->stream, "r%ld", value);
18660484Sobrien	  else if ((operand->flags & PPC_OPERAND_FPR) != 0)
18760484Sobrien	    (*info->fprintf_func) (info->stream, "f%ld", value);
18877298Sobrien	  else if ((operand->flags & PPC_OPERAND_VR) != 0)
18977298Sobrien	    (*info->fprintf_func) (info->stream, "v%ld", value);
19060484Sobrien	  else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
19160484Sobrien	    (*info->print_address_func) (memaddr + value, info);
19260484Sobrien	  else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
19360484Sobrien	    (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
19460484Sobrien	  else if ((operand->flags & PPC_OPERAND_CR) == 0
19560484Sobrien		   || (dialect & PPC_OPCODE_PPC) == 0)
19660484Sobrien	    (*info->fprintf_func) (info->stream, "%ld", value);
19760484Sobrien	  else
19860484Sobrien	    {
19960484Sobrien	      if (operand->bits == 3)
20060484Sobrien		(*info->fprintf_func) (info->stream, "cr%d", value);
20160484Sobrien	      else
20260484Sobrien		{
20360484Sobrien		  static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
20460484Sobrien		  int cr;
20560484Sobrien		  int cc;
20660484Sobrien
20760484Sobrien		  cr = value >> 2;
20860484Sobrien		  if (cr != 0)
20960484Sobrien		    (*info->fprintf_func) (info->stream, "4*cr%d", cr);
21060484Sobrien		  cc = value & 3;
21160484Sobrien		  if (cc != 0)
21260484Sobrien		    {
21360484Sobrien		      if (cr != 0)
21460484Sobrien			(*info->fprintf_func) (info->stream, "+");
21560484Sobrien		      (*info->fprintf_func) (info->stream, "%s", cbnames[cc]);
21660484Sobrien		    }
21760484Sobrien		}
21860484Sobrien	    }
21960484Sobrien
22060484Sobrien	  if (need_paren)
22160484Sobrien	    {
22260484Sobrien	      (*info->fprintf_func) (info->stream, ")");
22360484Sobrien	      need_paren = 0;
22460484Sobrien	    }
22560484Sobrien
22660484Sobrien	  if ((operand->flags & PPC_OPERAND_PARENS) == 0)
22760484Sobrien	    need_comma = 1;
22860484Sobrien	  else
22960484Sobrien	    {
23060484Sobrien	      (*info->fprintf_func) (info->stream, "(");
23160484Sobrien	      need_paren = 1;
23260484Sobrien	    }
23360484Sobrien	}
23460484Sobrien
23560484Sobrien      /* We have found and printed an instruction; return.  */
23660484Sobrien      return 4;
23760484Sobrien    }
23860484Sobrien
23960484Sobrien  /* We could not find a match.  */
24060484Sobrien  (*info->fprintf_func) (info->stream, ".long 0x%lx", insn);
24160484Sobrien
24260484Sobrien  return 4;
24360484Sobrien}
244