xref: /netbsd-src/external/gpl3/binutils/dist/opcodes/cgen-dis.in (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
1/* Disassembler interface for targets using CGEN. -*- C -*-
2   CGEN: Cpu tools GENerator
3
4   THIS FILE IS MACHINE GENERATED WITH CGEN.
5   - the resultant file is machine generated, cgen-dis.in isn't
6
7   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2007,
8   2008, 2010  Free Software Foundation, Inc.
9
10   This file is part of libopcodes.
11
12   This library is free software; you can redistribute it and/or modify
13   it under the terms of the GNU General Public License as published by
14   the Free Software Foundation; either version 3, or (at your option)
15   any later version.
16
17   It is distributed in the hope that it will be useful, but WITHOUT
18   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
20   License for more details.
21
22   You should have received a copy of the GNU General Public License
23   along with this program; if not, write to the Free Software Foundation, Inc.,
24   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
25
26/* ??? Eventually more and more of this stuff can go to cpu-independent files.
27   Keep that in mind.  */
28
29#include "sysdep.h"
30#include <stdio.h>
31#include "ansidecl.h"
32#include "dis-asm.h"
33#include "bfd.h"
34#include "symcat.h"
35#include "libiberty.h"
36#include "@prefix@-desc.h"
37#include "@prefix@-opc.h"
38#include "opintl.h"
39
40/* Default text to print if an instruction isn't recognized.  */
41#define UNKNOWN_INSN_MSG _("*unknown*")
42
43static void print_normal
44  (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
45static void print_address
46  (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED;
47static void print_keyword
48  (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED;
49static void print_insn_normal
50  (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
51static int print_insn
52  (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, bfd_byte *, unsigned);
53static int default_print_insn
54  (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED;
55static int read_insn
56  (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *,
57   unsigned long *);
58
59/* -- disassembler routines inserted here.  */
60
61/* Default print handler.  */
62
63static void
64print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
65	      void *dis_info,
66	      long value,
67	      unsigned int attrs,
68	      bfd_vma pc ATTRIBUTE_UNUSED,
69	      int length ATTRIBUTE_UNUSED)
70{
71  disassemble_info *info = (disassemble_info *) dis_info;
72
73  /* Print the operand as directed by the attributes.  */
74  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
75    ; /* nothing to do */
76  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
77    (*info->fprintf_func) (info->stream, "%ld", value);
78  else
79    (*info->fprintf_func) (info->stream, "0x%lx", value);
80}
81
82/* Default address handler.  */
83
84static void
85print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
86	       void *dis_info,
87	       bfd_vma value,
88	       unsigned int attrs,
89	       bfd_vma pc ATTRIBUTE_UNUSED,
90	       int length ATTRIBUTE_UNUSED)
91{
92  disassemble_info *info = (disassemble_info *) dis_info;
93
94  /* Print the operand as directed by the attributes.  */
95  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
96    ; /* Nothing to do.  */
97  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
98    (*info->print_address_func) (value, info);
99  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
100    (*info->print_address_func) (value, info);
101  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
102    (*info->fprintf_func) (info->stream, "%ld", (long) value);
103  else
104    (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
105}
106
107/* Keyword print handler.  */
108
109static void
110print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
111	       void *dis_info,
112	       CGEN_KEYWORD *keyword_table,
113	       long value,
114	       unsigned int attrs ATTRIBUTE_UNUSED)
115{
116  disassemble_info *info = (disassemble_info *) dis_info;
117  const CGEN_KEYWORD_ENTRY *ke;
118
119  ke = cgen_keyword_lookup_value (keyword_table, value);
120  if (ke != NULL)
121    (*info->fprintf_func) (info->stream, "%s", ke->name);
122  else
123    (*info->fprintf_func) (info->stream, "???");
124}
125
126/* Default insn printer.
127
128   DIS_INFO is defined as `void *' so the disassembler needn't know anything
129   about disassemble_info.  */
130
131static void
132print_insn_normal (CGEN_CPU_DESC cd,
133		   void *dis_info,
134		   const CGEN_INSN *insn,
135		   CGEN_FIELDS *fields,
136		   bfd_vma pc,
137		   int length)
138{
139  const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
140  disassemble_info *info = (disassemble_info *) dis_info;
141  const CGEN_SYNTAX_CHAR_TYPE *syn;
142
143  CGEN_INIT_PRINT (cd);
144
145  for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
146    {
147      if (CGEN_SYNTAX_MNEMONIC_P (*syn))
148	{
149	  (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
150	  continue;
151	}
152      if (CGEN_SYNTAX_CHAR_P (*syn))
153	{
154	  (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
155	  continue;
156	}
157
158      /* We have an operand.  */
159      @arch@_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
160				 fields, CGEN_INSN_ATTRS (insn), pc, length);
161    }
162}
163
164/* Subroutine of print_insn. Reads an insn into the given buffers and updates
165   the extract info.
166   Returns 0 if all is well, non-zero otherwise.  */
167
168static int
169read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
170	   bfd_vma pc,
171	   disassemble_info *info,
172	   bfd_byte *buf,
173	   int buflen,
174	   CGEN_EXTRACT_INFO *ex_info,
175	   unsigned long *insn_value)
176{
177  int status = (*info->read_memory_func) (pc, buf, buflen, info);
178
179  if (status != 0)
180    {
181      (*info->memory_error_func) (status, pc, info);
182      return -1;
183    }
184
185  ex_info->dis_info = info;
186  ex_info->valid = (1 << buflen) - 1;
187  ex_info->insn_bytes = buf;
188
189  *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
190  return 0;
191}
192
193/* Utility to print an insn.
194   BUF is the base part of the insn, target byte order, BUFLEN bytes long.
195   The result is the size of the insn in bytes or zero for an unknown insn
196   or -1 if an error occurs fetching data (memory_error_func will have
197   been called).  */
198
199static int
200print_insn (CGEN_CPU_DESC cd,
201	    bfd_vma pc,
202	    disassemble_info *info,
203	    bfd_byte *buf,
204	    unsigned int buflen)
205{
206  CGEN_INSN_INT insn_value;
207  const CGEN_INSN_LIST *insn_list;
208  CGEN_EXTRACT_INFO ex_info;
209  int basesize;
210
211  /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
212  basesize = cd->base_insn_bitsize < buflen * 8 ?
213                                     cd->base_insn_bitsize : buflen * 8;
214  insn_value = cgen_get_insn_value (cd, buf, basesize);
215
216
217  /* Fill in ex_info fields like read_insn would.  Don't actually call
218     read_insn, since the incoming buffer is already read (and possibly
219     modified a la m32r).  */
220  ex_info.valid = (1 << buflen) - 1;
221  ex_info.dis_info = info;
222  ex_info.insn_bytes = buf;
223
224  /* The instructions are stored in hash lists.
225     Pick the first one and keep trying until we find the right one.  */
226
227  insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value);
228  while (insn_list != NULL)
229    {
230      const CGEN_INSN *insn = insn_list->insn;
231      CGEN_FIELDS fields;
232      int length;
233      unsigned long insn_value_cropped;
234
235#ifdef CGEN_VALIDATE_INSN_SUPPORTED
236      /* Not needed as insn shouldn't be in hash lists if not supported.  */
237      /* Supported by this cpu?  */
238      if (! @arch@_cgen_insn_supported (cd, insn))
239        {
240          insn_list = CGEN_DIS_NEXT_INSN (insn_list);
241	  continue;
242        }
243#endif
244
245      /* Basic bit mask must be correct.  */
246      /* ??? May wish to allow target to defer this check until the extract
247	 handler.  */
248
249      /* Base size may exceed this instruction's size.  Extract the
250         relevant part from the buffer. */
251      if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
252	  (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
253	insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn),
254					   info->endian == BFD_ENDIAN_BIG);
255      else
256	insn_value_cropped = insn_value;
257
258      if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
259	  == CGEN_INSN_BASE_VALUE (insn))
260	{
261	  /* Printing is handled in two passes.  The first pass parses the
262	     machine insn and extracts the fields.  The second pass prints
263	     them.  */
264
265	  /* Make sure the entire insn is loaded into insn_value, if it
266	     can fit.  */
267	  if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
268	      (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
269	    {
270	      unsigned long full_insn_value;
271	      int rc = read_insn (cd, pc, info, buf,
272				  CGEN_INSN_BITSIZE (insn) / 8,
273				  & ex_info, & full_insn_value);
274	      if (rc != 0)
275		return rc;
276	      length = CGEN_EXTRACT_FN (cd, insn)
277		(cd, insn, &ex_info, full_insn_value, &fields, pc);
278	    }
279	  else
280	    length = CGEN_EXTRACT_FN (cd, insn)
281	      (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
282
283	  /* Length < 0 -> error.  */
284	  if (length < 0)
285	    return length;
286	  if (length > 0)
287	    {
288	      CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
289	      /* Length is in bits, result is in bytes.  */
290	      return length / 8;
291	    }
292	}
293
294      insn_list = CGEN_DIS_NEXT_INSN (insn_list);
295    }
296
297  return 0;
298}
299
300/* Default value for CGEN_PRINT_INSN.
301   The result is the size of the insn in bytes or zero for an unknown insn
302   or -1 if an error occured fetching bytes.  */
303
304#ifndef CGEN_PRINT_INSN
305#define CGEN_PRINT_INSN default_print_insn
306#endif
307
308static int
309default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
310{
311  bfd_byte buf[CGEN_MAX_INSN_SIZE];
312  int buflen;
313  int status;
314
315  /* Attempt to read the base part of the insn.  */
316  buflen = cd->base_insn_bitsize / 8;
317  status = (*info->read_memory_func) (pc, buf, buflen, info);
318
319  /* Try again with the minimum part, if min < base.  */
320  if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
321    {
322      buflen = cd->min_insn_bitsize / 8;
323      status = (*info->read_memory_func) (pc, buf, buflen, info);
324    }
325
326  if (status != 0)
327    {
328      (*info->memory_error_func) (status, pc, info);
329      return -1;
330    }
331
332  return print_insn (cd, pc, info, buf, buflen);
333}
334
335/* Main entry point.
336   Print one instruction from PC on INFO->STREAM.
337   Return the size of the instruction (in bytes).  */
338
339typedef struct cpu_desc_list
340{
341  struct cpu_desc_list *next;
342  CGEN_BITSET *isa;
343  int mach;
344  int endian;
345  CGEN_CPU_DESC cd;
346} cpu_desc_list;
347
348int
349print_insn_@arch@ (bfd_vma pc, disassemble_info *info)
350{
351  static cpu_desc_list *cd_list = 0;
352  cpu_desc_list *cl = 0;
353  static CGEN_CPU_DESC cd = 0;
354  static CGEN_BITSET *prev_isa;
355  static int prev_mach;
356  static int prev_endian;
357  int length;
358  CGEN_BITSET *isa;
359  int mach;
360  int endian = (info->endian == BFD_ENDIAN_BIG
361		? CGEN_ENDIAN_BIG
362		: CGEN_ENDIAN_LITTLE);
363  enum bfd_architecture arch;
364
365  /* ??? gdb will set mach but leave the architecture as "unknown" */
366#ifndef CGEN_BFD_ARCH
367#define CGEN_BFD_ARCH bfd_arch_@arch@
368#endif
369  arch = info->arch;
370  if (arch == bfd_arch_unknown)
371    arch = CGEN_BFD_ARCH;
372
373  /* There's no standard way to compute the machine or isa number
374     so we leave it to the target.  */
375#ifdef CGEN_COMPUTE_MACH
376  mach = CGEN_COMPUTE_MACH (info);
377#else
378  mach = info->mach;
379#endif
380
381#ifdef CGEN_COMPUTE_ISA
382  {
383    static CGEN_BITSET *permanent_isa;
384
385    if (!permanent_isa)
386      permanent_isa = cgen_bitset_create (MAX_ISAS);
387    isa = permanent_isa;
388    cgen_bitset_clear (isa);
389    cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info));
390  }
391#else
392  isa = info->insn_sets;
393#endif
394
395  /* If we've switched cpu's, try to find a handle we've used before */
396  if (cd
397      && (cgen_bitset_compare (isa, prev_isa) != 0
398	  || mach != prev_mach
399	  || endian != prev_endian))
400    {
401      cd = 0;
402      for (cl = cd_list; cl; cl = cl->next)
403	{
404	  if (cgen_bitset_compare (cl->isa, isa) == 0 &&
405	      cl->mach == mach &&
406	      cl->endian == endian)
407	    {
408	      cd = cl->cd;
409 	      prev_isa = cd->isas;
410	      break;
411	    }
412	}
413    }
414
415  /* If we haven't initialized yet, initialize the opcode table.  */
416  if (! cd)
417    {
418      const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
419      const char *mach_name;
420
421      if (!arch_type)
422	abort ();
423      mach_name = arch_type->printable_name;
424
425      prev_isa = cgen_bitset_copy (isa);
426      prev_mach = mach;
427      prev_endian = endian;
428      cd = @arch@_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
429				 CGEN_CPU_OPEN_BFDMACH, mach_name,
430				 CGEN_CPU_OPEN_ENDIAN, prev_endian,
431				 CGEN_CPU_OPEN_END);
432      if (!cd)
433	abort ();
434
435      /* Save this away for future reference.  */
436      cl = xmalloc (sizeof (struct cpu_desc_list));
437      cl->cd = cd;
438      cl->isa = prev_isa;
439      cl->mach = mach;
440      cl->endian = endian;
441      cl->next = cd_list;
442      cd_list = cl;
443
444      @arch@_cgen_init_dis (cd);
445    }
446
447  /* We try to have as much common code as possible.
448     But at this point some targets need to take over.  */
449  /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
450     but if not possible try to move this hook elsewhere rather than
451     have two hooks.  */
452  length = CGEN_PRINT_INSN (cd, pc, info);
453  if (length > 0)
454    return length;
455  if (length < 0)
456    return -1;
457
458  (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
459  return cd->default_insn_bitsize / 8;
460}
461