184865Sobrien/* ia64-dis.c -- Disassemble ia64 instructions
2218822Sdim   Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
384865Sobrien   Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
484865Sobrien
584865Sobrien   This file is part of GDB, GAS, and the GNU binutils.
684865Sobrien
784865Sobrien   GDB, GAS, and the GNU binutils are free software; you can redistribute
884865Sobrien   them and/or modify them under the terms of the GNU General Public
984865Sobrien   License as published by the Free Software Foundation; either version
1084865Sobrien   2, or (at your option) any later version.
1184865Sobrien
1284865Sobrien   GDB, GAS, and the GNU binutils are distributed in the hope that they
1384865Sobrien   will be useful, but WITHOUT ANY WARRANTY; without even the implied
1484865Sobrien   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
1584865Sobrien   the GNU General Public License for more details.
1684865Sobrien
1784865Sobrien   You should have received a copy of the GNU General Public License
1884865Sobrien   along with this file; see the file COPYING.  If not, write to the
19218822Sdim   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
20218822Sdim   02110-1301, USA.  */
2184865Sobrien
2284865Sobrien#include <assert.h>
2384865Sobrien#include <string.h>
2484865Sobrien
2584865Sobrien#include "dis-asm.h"
2684865Sobrien#include "opcode/ia64.h"
2784865Sobrien
2884865Sobrien#define NELEMS(a)	((int) (sizeof (a) / sizeof (a[0])))
2984865Sobrien
3084865Sobrien/* Disassemble ia64 instruction.  */
3184865Sobrien
3284865Sobrien/* Return the instruction type for OPCODE found in unit UNIT. */
3384865Sobrien
3484865Sobrienstatic enum ia64_insn_type
3584865Sobrienunit_to_type (ia64_insn opcode, enum ia64_unit unit)
3684865Sobrien{
3784865Sobrien  enum ia64_insn_type type;
3884865Sobrien  int op;
3984865Sobrien
4084865Sobrien  op = IA64_OP (opcode);
4184865Sobrien
4284865Sobrien  if (op >= 8 && (unit == IA64_UNIT_I || unit == IA64_UNIT_M))
4384865Sobrien    {
4484865Sobrien      type = IA64_TYPE_A;
4584865Sobrien    }
4684865Sobrien  else
4784865Sobrien    {
4884865Sobrien      switch (unit)
4984865Sobrien	{
5084865Sobrien	case IA64_UNIT_I:
5184865Sobrien	  type = IA64_TYPE_I; break;
5284865Sobrien	case IA64_UNIT_M:
5384865Sobrien	  type = IA64_TYPE_M; break;
5484865Sobrien	case IA64_UNIT_B:
5584865Sobrien	  type = IA64_TYPE_B; break;
5684865Sobrien	case IA64_UNIT_F:
5784865Sobrien	  type = IA64_TYPE_F; break;
5884865Sobrien        case IA64_UNIT_L:
5984865Sobrien	case IA64_UNIT_X:
6084865Sobrien	  type = IA64_TYPE_X; break;
6184865Sobrien	default:
6284865Sobrien	  type = -1;
6384865Sobrien	}
6484865Sobrien    }
6584865Sobrien  return type;
6684865Sobrien}
6784865Sobrien
6884865Sobrienint
6984865Sobrienprint_insn_ia64 (bfd_vma memaddr, struct disassemble_info *info)
7084865Sobrien{
7184865Sobrien  ia64_insn t0, t1, slot[3], template, s_bit, insn;
7284865Sobrien  int slotnum, j, status, need_comma, retval, slot_multiplier;
7384865Sobrien  const struct ia64_operand *odesc;
7484865Sobrien  const struct ia64_opcode *idesc;
7584865Sobrien  const char *err, *str, *tname;
7684865Sobrien  BFD_HOST_U_64_BIT value;
7784865Sobrien  bfd_byte bundle[16];
7884865Sobrien  enum ia64_unit unit;
7984865Sobrien  char regname[16];
8084865Sobrien
8184865Sobrien  if (info->bytes_per_line == 0)
8284865Sobrien    info->bytes_per_line = 6;
8384865Sobrien  info->display_endian = info->endian;
8484865Sobrien
8584865Sobrien  slot_multiplier = info->bytes_per_line;
8684865Sobrien  retval = slot_multiplier;
8784865Sobrien
8884865Sobrien  slotnum = (((long) memaddr) & 0xf) / slot_multiplier;
8984865Sobrien  if (slotnum > 2)
9084865Sobrien    return -1;
9184865Sobrien
9284865Sobrien  memaddr -= (memaddr & 0xf);
9384865Sobrien  status = (*info->read_memory_func) (memaddr, bundle, sizeof (bundle), info);
9484865Sobrien  if (status != 0)
9584865Sobrien    {
9684865Sobrien      (*info->memory_error_func) (status, memaddr, info);
9784865Sobrien      return -1;
9884865Sobrien    }
9984865Sobrien  /* bundles are always in little-endian byte order */
10084865Sobrien  t0 = bfd_getl64 (bundle);
10184865Sobrien  t1 = bfd_getl64 (bundle + 8);
10284865Sobrien  s_bit = t0 & 1;
10384865Sobrien  template = (t0 >> 1) & 0xf;
10484865Sobrien  slot[0] = (t0 >>  5) & 0x1ffffffffffLL;
10584865Sobrien  slot[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18);
10684865Sobrien  slot[2] = (t1 >> 23) & 0x1ffffffffffLL;
10784865Sobrien
10884865Sobrien  tname = ia64_templ_desc[template].name;
10984865Sobrien  if (slotnum == 0)
11084865Sobrien    (*info->fprintf_func) (info->stream, "[%s] ", tname);
11184865Sobrien  else
112218822Sdim    (*info->fprintf_func) (info->stream, "      ");
11384865Sobrien
11484865Sobrien  unit = ia64_templ_desc[template].exec_unit[slotnum];
11584865Sobrien
11684865Sobrien  if (template == 2 && slotnum == 1)
11784865Sobrien    {
11884865Sobrien      /* skip L slot in MLI template: */
11984865Sobrien      slotnum = 2;
12084865Sobrien      retval += slot_multiplier;
12184865Sobrien    }
12284865Sobrien
12384865Sobrien  insn = slot[slotnum];
12484865Sobrien
12584865Sobrien  if (unit == IA64_UNIT_NIL)
12684865Sobrien    goto decoding_failed;
12784865Sobrien
12884865Sobrien  idesc = ia64_dis_opcode (insn, unit_to_type (insn, unit));
12984865Sobrien  if (idesc == NULL)
13084865Sobrien    goto decoding_failed;
13184865Sobrien
13284865Sobrien  /* print predicate, if any: */
13384865Sobrien
13484865Sobrien  if ((idesc->flags & IA64_OPCODE_NO_PRED)
13584865Sobrien      || (insn & 0x3f) == 0)
13684865Sobrien    (*info->fprintf_func) (info->stream, "      ");
13784865Sobrien  else
13884865Sobrien    (*info->fprintf_func) (info->stream, "(p%02d) ", (int)(insn & 0x3f));
13984865Sobrien
14084865Sobrien  /* now the actual instruction: */
14184865Sobrien
14284865Sobrien  (*info->fprintf_func) (info->stream, "%s", idesc->name);
14384865Sobrien  if (idesc->operands[0])
14484865Sobrien    (*info->fprintf_func) (info->stream, " ");
14584865Sobrien
14684865Sobrien  need_comma = 0;
14784865Sobrien  for (j = 0; j < NELEMS (idesc->operands) && idesc->operands[j]; ++j)
14884865Sobrien    {
14984865Sobrien      odesc = elf64_ia64_operands + idesc->operands[j];
15084865Sobrien
15184865Sobrien      if (need_comma)
15284865Sobrien	(*info->fprintf_func) (info->stream, ",");
15384865Sobrien
15484865Sobrien      if (odesc - elf64_ia64_operands == IA64_OPND_IMMU64)
15584865Sobrien	{
15684865Sobrien	  /* special case of 64 bit immediate load: */
15784865Sobrien	  value = ((insn >> 13) & 0x7f) | (((insn >> 27) & 0x1ff) << 7)
15884865Sobrien	    | (((insn >> 22) & 0x1f) << 16) | (((insn >> 21) & 0x1) << 21)
15984865Sobrien	    | (slot[1] << 22) | (((insn >> 36) & 0x1) << 63);
16084865Sobrien	}
16184865Sobrien      else if (odesc - elf64_ia64_operands == IA64_OPND_IMMU62)
16284865Sobrien        {
16384865Sobrien          /* 62-bit immediate for nop.x/break.x */
16484865Sobrien          value = ((slot[1] & 0x1ffffffffffLL) << 21)
16584865Sobrien            | (((insn >> 36) & 0x1) << 20)
16684865Sobrien            | ((insn >> 6) & 0xfffff);
16784865Sobrien        }
16884865Sobrien      else if (odesc - elf64_ia64_operands == IA64_OPND_TGT64)
16984865Sobrien	{
170130561Sobrien	  /* 60-bit immediate for long branches. */
17184865Sobrien	  value = (((insn >> 13) & 0xfffff)
17284865Sobrien		   | (((insn >> 36) & 1) << 59)
173130561Sobrien		   | (((slot[1] >> 2) & 0x7fffffffffLL) << 20)) << 4;
17484865Sobrien	}
17584865Sobrien      else
17684865Sobrien	{
17784865Sobrien	  err = (*odesc->extract) (odesc, insn, &value);
17884865Sobrien	  if (err)
17984865Sobrien	    {
18084865Sobrien	      (*info->fprintf_func) (info->stream, "%s", err);
18184865Sobrien	      goto done;
18284865Sobrien	    }
18384865Sobrien	}
18484865Sobrien
18584865Sobrien	switch (odesc->class)
18684865Sobrien	  {
18784865Sobrien	  case IA64_OPND_CLASS_CST:
18884865Sobrien	    (*info->fprintf_func) (info->stream, "%s", odesc->str);
18984865Sobrien	    break;
19084865Sobrien
19184865Sobrien	  case IA64_OPND_CLASS_REG:
19284865Sobrien	    if (odesc->str[0] == 'a' && odesc->str[1] == 'r')
19384865Sobrien	      {
19484865Sobrien		switch (value)
19584865Sobrien		  {
19684865Sobrien		  case 0: case 1: case 2: case 3:
19784865Sobrien		  case 4: case 5: case 6: case 7:
19884865Sobrien		    sprintf (regname, "ar.k%u", (unsigned int) value);
19984865Sobrien		    break;
20084865Sobrien		  case 16:	strcpy (regname, "ar.rsc"); break;
20184865Sobrien		  case 17:	strcpy (regname, "ar.bsp"); break;
20284865Sobrien		  case 18:	strcpy (regname, "ar.bspstore"); break;
20384865Sobrien		  case 19:	strcpy (regname, "ar.rnat"); break;
20484865Sobrien		  case 32:	strcpy (regname, "ar.ccv"); break;
20584865Sobrien		  case 36:	strcpy (regname, "ar.unat"); break;
20684865Sobrien		  case 40:	strcpy (regname, "ar.fpsr"); break;
20784865Sobrien		  case 44:	strcpy (regname, "ar.itc"); break;
20884865Sobrien		  case 64:	strcpy (regname, "ar.pfs"); break;
20984865Sobrien		  case 65:	strcpy (regname, "ar.lc"); break;
21084865Sobrien		  case 66:	strcpy (regname, "ar.ec"); break;
21184865Sobrien		  default:
21284865Sobrien		    sprintf (regname, "ar%u", (unsigned int) value);
21384865Sobrien		    break;
21484865Sobrien		  }
21584865Sobrien		(*info->fprintf_func) (info->stream, "%s", regname);
21684865Sobrien	      }
21784865Sobrien	    else
21884865Sobrien	      (*info->fprintf_func) (info->stream, "%s%d", odesc->str, (int)value);
21984865Sobrien	    break;
22084865Sobrien
22184865Sobrien	  case IA64_OPND_CLASS_IND:
22284865Sobrien	    (*info->fprintf_func) (info->stream, "%s[r%d]", odesc->str, (int)value);
22384865Sobrien	    break;
22484865Sobrien
22584865Sobrien	  case IA64_OPND_CLASS_ABS:
22684865Sobrien	    str = 0;
22784865Sobrien	    if (odesc - elf64_ia64_operands == IA64_OPND_MBTYPE4)
22884865Sobrien	      switch (value)
22984865Sobrien		{
23084865Sobrien		case 0x0: str = "@brcst"; break;
23184865Sobrien		case 0x8: str = "@mix"; break;
23284865Sobrien		case 0x9: str = "@shuf"; break;
23384865Sobrien		case 0xa: str = "@alt"; break;
23484865Sobrien		case 0xb: str = "@rev"; break;
23584865Sobrien		}
23684865Sobrien
23784865Sobrien	    if (str)
23884865Sobrien	      (*info->fprintf_func) (info->stream, "%s", str);
23984865Sobrien	    else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_SIGNED)
240218822Sdim	      (*info->fprintf_func) (info->stream, "%lld", (long long) value);
24184865Sobrien	    else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_UNSIGNED)
242218822Sdim	      (*info->fprintf_func) (info->stream, "%llu", (long long) value);
24384865Sobrien	    else
244218822Sdim	      (*info->fprintf_func) (info->stream, "0x%llx", (long long) value);
24584865Sobrien	    break;
24684865Sobrien
24784865Sobrien	  case IA64_OPND_CLASS_REL:
24884865Sobrien	    (*info->print_address_func) (memaddr + value, info);
24984865Sobrien	    break;
25084865Sobrien	  }
25184865Sobrien
25284865Sobrien      need_comma = 1;
25384865Sobrien      if (j + 1 == idesc->num_outputs)
25484865Sobrien	{
25584865Sobrien	  (*info->fprintf_func) (info->stream, "=");
25684865Sobrien	  need_comma = 0;
25784865Sobrien	}
25884865Sobrien    }
25984865Sobrien  if (slotnum + 1 == ia64_templ_desc[template].group_boundary
26084865Sobrien      || ((slotnum == 2) && s_bit))
26184865Sobrien    (*info->fprintf_func) (info->stream, ";;");
26284865Sobrien
26384865Sobrien done:
26484865Sobrien  ia64_free_opcode ((struct ia64_opcode *)idesc);
26584865Sobrien failed:
26684865Sobrien  if (slotnum == 2)
26784865Sobrien    retval += 16 - 3*slot_multiplier;
26884865Sobrien  return retval;
26984865Sobrien
27084865Sobrien decoding_failed:
271218822Sdim  (*info->fprintf_func) (info->stream, "      data8 %#011llx", (long long) insn);
27284865Sobrien  goto failed;
27384865Sobrien}
274