arc-dis.c revision 60484
1/* Instruction printing code for the ARC.
2   Copyright (C) 1994, 1995, 1997, 1998 Free Software Foundation, Inc.
3   Contributed by Doug Evans (dje@cygnus.com).
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19#include "sysdep.h"
20#include "dis-asm.h"
21#include "opcode/arc.h"
22#include "elf-bfd.h"
23#include "elf/arc.h"
24#include "opintl.h"
25
26static int print_insn_arc_base_little PARAMS ((bfd_vma, disassemble_info *));
27static int print_insn_arc_base_big PARAMS ((bfd_vma, disassemble_info *));
28
29static int print_insn PARAMS ((bfd_vma, disassemble_info *, int, int));
30
31/* Print one instruction from PC on INFO->STREAM.
32   Return the size of the instruction (4 or 8 for the ARC). */
33
34static int
35print_insn (pc, info, mach, big_p)
36     bfd_vma pc;
37     disassemble_info *info;
38     int mach;
39     int big_p;
40{
41  const struct arc_opcode *opcode;
42  bfd_byte buffer[4];
43  void *stream = info->stream;
44  fprintf_ftype func = info->fprintf_func;
45  int status;
46  /* First element is insn, second element is limm (if present).  */
47  arc_insn insn[2];
48  int got_limm_p = 0;
49  static int initialized = 0;
50  static int current_mach = 0;
51
52  if (!initialized || mach != current_mach)
53    {
54      initialized = 1;
55      current_mach = arc_get_opcode_mach (mach, big_p);
56      arc_opcode_init_tables (current_mach);
57    }
58
59  status = (*info->read_memory_func) (pc, buffer, 4, info);
60  if (status != 0)
61    {
62      (*info->memory_error_func) (status, pc, info);
63      return -1;
64    }
65  if (big_p)
66    insn[0] = bfd_getb32 (buffer);
67  else
68    insn[0] = bfd_getl32 (buffer);
69
70  (*func) (stream, "%08lx\t", insn[0]);
71
72  /* The instructions are stored in lists hashed by the insn code
73     (though we needn't care how they're hashed).  */
74
75  opcode = arc_opcode_lookup_dis (insn[0]);
76  for ( ; opcode != NULL; opcode = ARC_OPCODE_NEXT_DIS (opcode))
77    {
78      char *syn;
79      int mods,invalid;
80      long value;
81      const struct arc_operand *operand;
82      const struct arc_operand_value *opval;
83
84      /* Basic bit mask must be correct.  */
85      if ((insn[0] & opcode->mask) != opcode->value)
86	continue;
87
88      /* Supported by this cpu?  */
89      if (! arc_opcode_supported (opcode))
90	continue;
91
92      /* Make two passes over the operands.  First see if any of them
93	 have extraction functions, and, if they do, make sure the
94	 instruction is valid.  */
95
96      arc_opcode_init_extract ();
97      invalid = 0;
98
99      /* ??? Granted, this is slower than the `ppc' way.  Maybe when this is
100	 done it'll be clear what the right way to do this is.  */
101      /* Instructions like "add.f r0,r1,1" are tricky because the ".f" gets
102	 printed first, but we don't know how to print it until we've processed
103	 the regs.  Since we're scanning all the args before printing the insn
104	 anyways, it's actually quite easy.  */
105
106      for (syn = opcode->syntax; *syn; ++syn)
107	{
108	  int c;
109
110	  if (*syn != '%' || *++syn == '%')
111	    continue;
112	  mods = 0;
113	  c = *syn;
114	  while (ARC_MOD_P (arc_operands[arc_operand_map[c]].flags))
115	    {
116	      mods |= arc_operands[arc_operand_map[c]].flags & ARC_MOD_BITS;
117	      ++syn;
118	      c = *syn;
119	    }
120	  operand = arc_operands + arc_operand_map[c];
121	  if (operand->extract)
122	    (*operand->extract) (insn, operand, mods,
123				 (const struct arc_operand_value **) NULL,
124				 &invalid);
125	}
126      if (invalid)
127	continue;
128
129      /* The instruction is valid.  */
130
131      /* If we have an insn with a limm, fetch it now.  Scanning the insns
132	 twice lets us do this.  */
133      if (arc_opcode_limm_p (NULL))
134	{
135	  status = (*info->read_memory_func) (pc + 4, buffer, 4, info);
136	  if (status != 0)
137	    {
138	      (*info->memory_error_func) (status, pc, info);
139	      return -1;
140	    }
141	  if (big_p)
142	    insn[1] = bfd_getb32 (buffer);
143	  else
144	    insn[1] = bfd_getl32 (buffer);
145	  got_limm_p = 1;
146	}
147
148      for (syn = opcode->syntax; *syn; ++syn)
149	{
150	  int c;
151
152	  if (*syn != '%' || *++syn == '%')
153	    {
154	      (*func) (stream, "%c", *syn);
155	      continue;
156	    }
157
158	  /* We have an operand.  Fetch any special modifiers.  */
159	  mods = 0;
160	  c = *syn;
161	  while (ARC_MOD_P (arc_operands[arc_operand_map[c]].flags))
162	    {
163	      mods |= arc_operands[arc_operand_map[c]].flags & ARC_MOD_BITS;
164	      ++syn;
165	      c = *syn;
166	    }
167	  operand = arc_operands + arc_operand_map[c];
168
169	  /* Extract the value from the instruction.  */
170	  opval = NULL;
171	  if (operand->extract)
172	    {
173	      value = (*operand->extract) (insn, operand, mods,
174					   &opval, (int *) NULL);
175	    }
176	  else
177	    {
178	      value = (insn[0] >> operand->shift) & ((1 << operand->bits) - 1);
179	      if ((operand->flags & ARC_OPERAND_SIGNED)
180		  && (value & (1 << (operand->bits - 1))))
181		value -= 1 << operand->bits;
182
183	      /* If this is a suffix operand, set `opval'.  */
184	      if (operand->flags & ARC_OPERAND_SUFFIX)
185		opval = arc_opcode_lookup_suffix (operand, value);
186	    }
187
188	  /* Print the operand as directed by the flags.  */
189	  if (operand->flags & ARC_OPERAND_FAKE)
190	    ; /* nothing to do (??? at least not yet) */
191	  else if (operand->flags & ARC_OPERAND_SUFFIX)
192	    {
193	      /* Default suffixes aren't printed.  Fortunately, they all have
194		 zero values.  Also, zero values for boolean suffixes are
195		 represented by the absence of text.  */
196
197	      if (value != 0)
198		{
199		  /* ??? OPVAL should have a value.  If it doesn't just cope
200		     as we want disassembly to be reasonably robust.
201		     Also remember that several condition code values (16-31)
202		     aren't defined yet.  For these cases just print the
203		     number suitably decorated.  */
204		  if (opval)
205		    (*func) (stream, "%s%s",
206			     mods & ARC_MOD_DOT ? "." : "",
207			     opval->name);
208		  else
209		    (*func) (stream, "%s%c%d",
210			     mods & ARC_MOD_DOT ? "." : "",
211			     operand->fmt, value);
212		}
213	    }
214	  else if (operand->flags & ARC_OPERAND_RELATIVE_BRANCH)
215	    (*info->print_address_func) (pc + 4 + value, info);
216	  /* ??? Not all cases of this are currently caught.  */
217	  else if (operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH)
218	    (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
219	  else if (operand->flags & ARC_OPERAND_ADDRESS)
220	    (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
221	  else if (opval)
222	    /* Note that this case catches both normal and auxiliary regs.  */
223	    (*func) (stream, "%s", opval->name);
224	  else
225	    (*func) (stream, "%ld", value);
226	}
227
228      /* We have found and printed an instruction; return.  */
229      return got_limm_p ? 8 : 4;
230    }
231
232  (*func) (stream, _("*unknown*"));
233  return 4;
234}
235
236/* Given MACH, one of bfd_mach_arc_xxx, return the print_insn function to use.
237   This does things a non-standard way (the "standard" way would be to copy
238   this code into disassemble.c).  Since there are more than a couple of
239   variants, hiding all this crud here seems cleaner.  */
240
241disassembler_ftype
242arc_get_disassembler (mach, big_p)
243     int mach;
244     int big_p;
245{
246  switch (mach)
247    {
248    case bfd_mach_arc_base:
249      return big_p ? print_insn_arc_base_big : print_insn_arc_base_little;
250    }
251  return print_insn_arc_base_little;
252}
253
254static int
255print_insn_arc_base_little (pc, info)
256     bfd_vma pc;
257     disassemble_info *info;
258{
259  return print_insn (pc, info, bfd_mach_arc_base, 0);
260}
261
262static int
263print_insn_arc_base_big (pc, info)
264     bfd_vma pc;
265     disassemble_info *info;
266{
267  return print_insn (pc, info, bfd_mach_arc_base, 1);
268}
269