1/* alpha-dis.c -- Disassemble Alpha AXP instructions
2   Copyright 1996, 1998, 1999, 2000, 2001, 2002
3   Free Software Foundation, Inc.
4   Contributed by Richard Henderson <rth@tamu.edu>,
5   patterned after the PPC opcode handling written by Ian Lance Taylor.
6
7This file is part of GDB, GAS, and the GNU binutils.
8
9GDB, GAS, and the GNU binutils are free software; you can redistribute
10them and/or modify them under the terms of the GNU General Public
11License as published by the Free Software Foundation; either version
122, or (at your option) any later version.
13
14GDB, GAS, and the GNU binutils are distributed in the hope that they
15will be useful, but WITHOUT ANY WARRANTY; without even the implied
16warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17the GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this file; see the file COPYING.  If not, write to the Free
21Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
2202110-1301, USA.  */
23
24#include <stdio.h>
25#include "sysdep.h"
26#include "dis-asm.h"
27#include "opcode/alpha.h"
28
29/* OSF register names.  */
30
31static const char * const osf_regnames[64] = {
32  "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
33  "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp",
34  "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9",
35  "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero",
36  "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
37  "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
38  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
39  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
40};
41
42/* VMS register names.  */
43
44static const char * const vms_regnames[64] = {
45  "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
46  "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
47  "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
48  "R24", "AI", "RA", "PV", "AT", "FP", "SP", "RZ",
49  "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
50  "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15",
51  "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23",
52  "F24", "F25", "F26", "F27", "F28", "F29", "F30", "FZ"
53};
54
55/* Disassemble Alpha instructions.  */
56
57int
58print_insn_alpha (memaddr, info)
59     bfd_vma memaddr;
60     struct disassemble_info *info;
61{
62  static const struct alpha_opcode *opcode_index[AXP_NOPS+1];
63  const char * const * regnames;
64  const struct alpha_opcode *opcode, *opcode_end;
65  const unsigned char *opindex;
66  unsigned insn, op, isa_mask;
67  int need_comma;
68
69  /* Initialize the majorop table the first time through */
70  if (!opcode_index[0])
71    {
72      opcode = alpha_opcodes;
73      opcode_end = opcode + alpha_num_opcodes;
74
75      for (op = 0; op < AXP_NOPS; ++op)
76	{
77	  opcode_index[op] = opcode;
78	  while (opcode < opcode_end && op == AXP_OP (opcode->opcode))
79	    ++opcode;
80	}
81      opcode_index[op] = opcode;
82    }
83
84  if (info->flavour == bfd_target_evax_flavour)
85    regnames = vms_regnames;
86  else
87    regnames = osf_regnames;
88
89  isa_mask = AXP_OPCODE_NOPAL;
90  switch (info->mach)
91    {
92    case bfd_mach_alpha_ev4:
93      isa_mask |= AXP_OPCODE_EV4;
94      break;
95    case bfd_mach_alpha_ev5:
96      isa_mask |= AXP_OPCODE_EV5;
97      break;
98    case bfd_mach_alpha_ev6:
99      isa_mask |= AXP_OPCODE_EV6;
100      break;
101    }
102
103  /* Read the insn into a host word */
104  {
105    bfd_byte buffer[4];
106    int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
107    if (status != 0)
108      {
109	(*info->memory_error_func) (status, memaddr, info);
110	return -1;
111      }
112    insn = bfd_getl32 (buffer);
113  }
114
115  /* Get the major opcode of the instruction.  */
116  op = AXP_OP (insn);
117
118  /* Find the first match in the opcode table.  */
119  opcode_end = opcode_index[op + 1];
120  for (opcode = opcode_index[op]; opcode < opcode_end; ++opcode)
121    {
122      if ((insn ^ opcode->opcode) & opcode->mask)
123	continue;
124
125      if (!(opcode->flags & isa_mask))
126	continue;
127
128      /* Make two passes over the operands.  First see if any of them
129	 have extraction functions, and, if they do, make sure the
130	 instruction is valid.  */
131      {
132	int invalid = 0;
133	for (opindex = opcode->operands; *opindex != 0; opindex++)
134	  {
135	    const struct alpha_operand *operand = alpha_operands + *opindex;
136	    if (operand->extract)
137	      (*operand->extract) (insn, &invalid);
138	  }
139	if (invalid)
140	  continue;
141      }
142
143      /* The instruction is valid.  */
144      goto found;
145    }
146
147  /* No instruction found */
148  (*info->fprintf_func) (info->stream, ".long %#08x", insn);
149
150  return 4;
151
152found:
153  (*info->fprintf_func) (info->stream, "%s", opcode->name);
154  if (opcode->operands[0] != 0)
155    (*info->fprintf_func) (info->stream, "\t");
156
157  /* Now extract and print the operands.  */
158  need_comma = 0;
159  for (opindex = opcode->operands; *opindex != 0; opindex++)
160    {
161      const struct alpha_operand *operand = alpha_operands + *opindex;
162      int value;
163
164      /* Operands that are marked FAKE are simply ignored.  We
165	 already made sure that the extract function considered
166	 the instruction to be valid.  */
167      if ((operand->flags & AXP_OPERAND_FAKE) != 0)
168	continue;
169
170      /* Extract the value from the instruction.  */
171      if (operand->extract)
172	value = (*operand->extract) (insn, (int *) NULL);
173      else
174	{
175	  value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
176	  if (operand->flags & AXP_OPERAND_SIGNED)
177	    {
178	      int signbit = 1 << (operand->bits - 1);
179	      value = (value ^ signbit) - signbit;
180	    }
181	}
182
183      if (need_comma &&
184	  ((operand->flags & (AXP_OPERAND_PARENS | AXP_OPERAND_COMMA))
185	   != AXP_OPERAND_PARENS))
186	{
187	  (*info->fprintf_func) (info->stream, ",");
188	}
189      if (operand->flags & AXP_OPERAND_PARENS)
190	(*info->fprintf_func) (info->stream, "(");
191
192      /* Print the operand as directed by the flags.  */
193      if (operand->flags & AXP_OPERAND_IR)
194	(*info->fprintf_func) (info->stream, "%s", regnames[value]);
195      else if (operand->flags & AXP_OPERAND_FPR)
196	(*info->fprintf_func) (info->stream, "%s", regnames[value + 32]);
197      else if (operand->flags & AXP_OPERAND_RELATIVE)
198	(*info->print_address_func) (memaddr + 4 + value, info);
199      else if (operand->flags & AXP_OPERAND_SIGNED)
200	(*info->fprintf_func) (info->stream, "%d", value);
201      else
202	(*info->fprintf_func) (info->stream, "%#x", value);
203
204      if (operand->flags & AXP_OPERAND_PARENS)
205	(*info->fprintf_func) (info->stream, ")");
206      need_comma = 1;
207    }
208
209  return 4;
210}
211