cgen-dis.in revision 130562
1214571Sdim/* Disassembler interface for targets using CGEN. -*- C -*-
2214571Sdim   CGEN: Cpu tools GENerator
3214571Sdim
4214571SdimTHIS FILE IS MACHINE GENERATED WITH CGEN.
5214571Sdim- the resultant file is machine generated, cgen-dis.in isn't
6214571Sdim
7214571SdimCopyright 1996, 1997, 1998, 1999, 2000, 2001, 2002
8214571SdimFree Software Foundation, Inc.
9214571Sdim
10214571SdimThis file is part of the GNU Binutils and GDB, the GNU debugger.
11214571Sdim
12214571SdimThis program is free software; you can redistribute it and/or modify
13214571Sdimit under the terms of the GNU General Public License as published by
14214571Sdimthe Free Software Foundation; either version 2, or (at your option)
15214571Sdimany later version.
16214571Sdim
17214571SdimThis program is distributed in the hope that it will be useful,
18214571Sdimbut WITHOUT ANY WARRANTY; without even the implied warranty of
19214571SdimMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20214571SdimGNU General Public License for more details.
21214571Sdim
22214571SdimYou should have received a copy of the GNU General Public License
23214571Sdimalong with this program; if not, write to the Free Software Foundation, Inc.,
24214571Sdim59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
25214571Sdim
26214571Sdim/* ??? Eventually more and more of this stuff can go to cpu-independent files.
27214571Sdim   Keep that in mind.  */
28214571Sdim
29214571Sdim#include "sysdep.h"
30214571Sdim#include <stdio.h>
31214571Sdim#include "ansidecl.h"
32214571Sdim#include "dis-asm.h"
33214571Sdim#include "bfd.h"
34214571Sdim#include "symcat.h"
35214571Sdim#include "libiberty.h"
36214571Sdim#include "@prefix@-desc.h"
37214571Sdim#include "@prefix@-opc.h"
38214571Sdim#include "opintl.h"
39214571Sdim
40214571Sdim/* Default text to print if an instruction isn't recognized.  */
41214571Sdim#define UNKNOWN_INSN_MSG _("*unknown*")
42214571Sdim
43214571Sdimstatic void print_normal
44214571Sdim  (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
45214571Sdimstatic void print_address
46214571Sdim  (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int);
47214571Sdimstatic void print_keyword
48214571Sdim  (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int);
49214571Sdimstatic void print_insn_normal
50214571Sdim  (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
51214571Sdimstatic int print_insn
52214571Sdim  (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, char *, unsigned);
53214571Sdimstatic int default_print_insn
54214571Sdim  (CGEN_CPU_DESC, bfd_vma, disassemble_info *);
55214571Sdimstatic int read_insn
56214571Sdim  (CGEN_CPU_DESC, bfd_vma, disassemble_info *, char *, int, CGEN_EXTRACT_INFO *,
57214571Sdim   unsigned long *);
58214571Sdim
59214571Sdim/* -- disassembler routines inserted here */
60214571Sdim
61214571Sdim/* Default print handler.  */
62214571Sdim
63214571Sdimstatic void
64214571Sdimprint_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
65214571Sdim	      void *dis_info,
66214571Sdim	      long value,
67214571Sdim	      unsigned int attrs,
68214571Sdim	      bfd_vma pc ATTRIBUTE_UNUSED,
69214571Sdim	      int length ATTRIBUTE_UNUSED)
70214571Sdim{
71214571Sdim  disassemble_info *info = (disassemble_info *) dis_info;
72214571Sdim
73214571Sdim#ifdef CGEN_PRINT_NORMAL
74214571Sdim  CGEN_PRINT_NORMAL (cd, info, value, attrs, pc, length);
75214571Sdim#endif
76214571Sdim
77214571Sdim  /* Print the operand as directed by the attributes.  */
78214571Sdim  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
79214571Sdim    ; /* nothing to do */
80214571Sdim  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
81214571Sdim    (*info->fprintf_func) (info->stream, "%ld", value);
82214571Sdim  else
83214571Sdim    (*info->fprintf_func) (info->stream, "0x%lx", value);
84214571Sdim}
85214571Sdim
86214571Sdim/* Default address handler.  */
87214571Sdim
88214571Sdimstatic void
89214571Sdimprint_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
90214571Sdim	       void *dis_info,
91214571Sdim	       bfd_vma value,
92214571Sdim	       unsigned int attrs,
93214571Sdim	       bfd_vma pc ATTRIBUTE_UNUSED,
94214571Sdim	       int length ATTRIBUTE_UNUSED)
95214571Sdim{
96214571Sdim  disassemble_info *info = (disassemble_info *) dis_info;
97214571Sdim
98214571Sdim#ifdef CGEN_PRINT_ADDRESS
99214571Sdim  CGEN_PRINT_ADDRESS (cd, info, value, attrs, pc, length);
100214571Sdim#endif
101214571Sdim
102214571Sdim  /* Print the operand as directed by the attributes.  */
103214571Sdim  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
104214571Sdim    ; /* nothing to do */
105214571Sdim  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
106214571Sdim    (*info->print_address_func) (value, info);
107214571Sdim  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
108214571Sdim    (*info->print_address_func) (value, info);
109214571Sdim  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
110214571Sdim    (*info->fprintf_func) (info->stream, "%ld", (long) value);
111214571Sdim  else
112214571Sdim    (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
113214571Sdim}
114214571Sdim
115214571Sdim/* Keyword print handler.  */
116214571Sdim
117214571Sdimstatic void
118214571Sdimprint_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
119214571Sdim	       void *dis_info,
120214571Sdim	       CGEN_KEYWORD *keyword_table,
121214571Sdim	       long value,
122214571Sdim	       unsigned int attrs ATTRIBUTE_UNUSED)
123214571Sdim{
124214571Sdim  disassemble_info *info = (disassemble_info *) dis_info;
125214571Sdim  const CGEN_KEYWORD_ENTRY *ke;
126214571Sdim
127214571Sdim  ke = cgen_keyword_lookup_value (keyword_table, value);
128214571Sdim  if (ke != NULL)
129214571Sdim    (*info->fprintf_func) (info->stream, "%s", ke->name);
130214571Sdim  else
131214571Sdim    (*info->fprintf_func) (info->stream, "???");
132214571Sdim}
133214571Sdim
134214571Sdim/* Default insn printer.
135214571Sdim
136214571Sdim   DIS_INFO is defined as `void *' so the disassembler needn't know anything
137214571Sdim   about disassemble_info.  */
138214571Sdim
139214571Sdimstatic void
140214571Sdimprint_insn_normal (CGEN_CPU_DESC cd,
141214571Sdim		   void *dis_info,
142214571Sdim		   const CGEN_INSN *insn,
143214571Sdim		   CGEN_FIELDS *fields,
144214571Sdim		   bfd_vma pc,
145214571Sdim		   int length)
146214571Sdim{
147214571Sdim  const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
148214571Sdim  disassemble_info *info = (disassemble_info *) dis_info;
149214571Sdim  const CGEN_SYNTAX_CHAR_TYPE *syn;
150214571Sdim
151214571Sdim  CGEN_INIT_PRINT (cd);
152214571Sdim
153214571Sdim  for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
154214571Sdim    {
155214571Sdim      if (CGEN_SYNTAX_MNEMONIC_P (*syn))
156214571Sdim	{
157214571Sdim	  (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
158214571Sdim	  continue;
159214571Sdim	}
160214571Sdim      if (CGEN_SYNTAX_CHAR_P (*syn))
161214571Sdim	{
162214571Sdim	  (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
163214571Sdim	  continue;
164214571Sdim	}
165214571Sdim
166214571Sdim      /* We have an operand.  */
167214571Sdim      @arch@_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
168214571Sdim				 fields, CGEN_INSN_ATTRS (insn), pc, length);
169214571Sdim    }
170214571Sdim}
171214571Sdim
172214571Sdim/* Subroutine of print_insn. Reads an insn into the given buffers and updates
173214571Sdim   the extract info.
174214571Sdim   Returns 0 if all is well, non-zero otherwise.  */
175214571Sdim
176214571Sdimstatic int
177214571Sdimread_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
178214571Sdim	   bfd_vma pc,
179214571Sdim	   disassemble_info *info,
180214571Sdim	   char *buf,
181214571Sdim	   int buflen,
182214571Sdim	   CGEN_EXTRACT_INFO *ex_info,
183214571Sdim	   unsigned long *insn_value)
184214571Sdim{
185214571Sdim  int status = (*info->read_memory_func) (pc, buf, buflen, info);
186214571Sdim  if (status != 0)
187214571Sdim    {
188214571Sdim      (*info->memory_error_func) (status, pc, info);
189214571Sdim      return -1;
190214571Sdim    }
191214571Sdim
192214571Sdim  ex_info->dis_info = info;
193214571Sdim  ex_info->valid = (1 << buflen) - 1;
194214571Sdim  ex_info->insn_bytes = buf;
195214571Sdim
196214571Sdim  *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
197214571Sdim  return 0;
198214571Sdim}
199214571Sdim
200214571Sdim/* Utility to print an insn.
201214571Sdim   BUF is the base part of the insn, target byte order, BUFLEN bytes long.
202214571Sdim   The result is the size of the insn in bytes or zero for an unknown insn
203214571Sdim   or -1 if an error occurs fetching data (memory_error_func will have
204214571Sdim   been called).  */
205214571Sdim
206214571Sdimstatic int
207214571Sdimprint_insn (CGEN_CPU_DESC cd,
208214571Sdim	    bfd_vma pc,
209214571Sdim	    disassemble_info *info,
210214571Sdim	    char *buf,
211214571Sdim	    unsigned int buflen)
212214571Sdim{
213214571Sdim  CGEN_INSN_INT insn_value;
214214571Sdim  const CGEN_INSN_LIST *insn_list;
215214571Sdim  CGEN_EXTRACT_INFO ex_info;
216214571Sdim  int basesize;
217214571Sdim
218214571Sdim  /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
219214571Sdim  basesize = cd->base_insn_bitsize < buflen * 8 ?
220214571Sdim                                     cd->base_insn_bitsize : buflen * 8;
221214571Sdim  insn_value = cgen_get_insn_value (cd, buf, basesize);
222214571Sdim
223214571Sdim
224214571Sdim  /* Fill in ex_info fields like read_insn would.  Don't actually call
225214571Sdim     read_insn, since the incoming buffer is already read (and possibly
226214571Sdim     modified a la m32r).  */
227214571Sdim  ex_info.valid = (1 << buflen) - 1;
228214571Sdim  ex_info.dis_info = info;
229214571Sdim  ex_info.insn_bytes = buf;
230214571Sdim
231214571Sdim  /* The instructions are stored in hash lists.
232214571Sdim     Pick the first one and keep trying until we find the right one.  */
233214571Sdim
234214571Sdim  insn_list = CGEN_DIS_LOOKUP_INSN (cd, buf, insn_value);
235214571Sdim  while (insn_list != NULL)
236214571Sdim    {
237214571Sdim      const CGEN_INSN *insn = insn_list->insn;
238214571Sdim      CGEN_FIELDS fields;
239214571Sdim      int length;
240214571Sdim      unsigned long insn_value_cropped;
241214571Sdim
242214571Sdim#ifdef CGEN_VALIDATE_INSN_SUPPORTED 
243214571Sdim      /* Not needed as insn shouldn't be in hash lists if not supported.  */
244214571Sdim      /* Supported by this cpu?  */
245214571Sdim      if (! @arch@_cgen_insn_supported (cd, insn))
246214571Sdim        {
247214571Sdim          insn_list = CGEN_DIS_NEXT_INSN (insn_list);
248214571Sdim	  continue;
249214571Sdim        }
250214571Sdim#endif
251214571Sdim
252214571Sdim      /* Basic bit mask must be correct.  */
253214571Sdim      /* ??? May wish to allow target to defer this check until the extract
254214571Sdim	 handler.  */
255214571Sdim
256214571Sdim      /* Base size may exceed this instruction's size.  Extract the
257214571Sdim         relevant part from the buffer. */
258214571Sdim      if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
259214571Sdim	  (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
260214571Sdim	insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn), 
261214571Sdim					   info->endian == BFD_ENDIAN_BIG);
262214571Sdim      else
263214571Sdim	insn_value_cropped = insn_value;
264214571Sdim
265214571Sdim      if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
266214571Sdim	  == CGEN_INSN_BASE_VALUE (insn))
267214571Sdim	{
268214571Sdim	  /* Printing is handled in two passes.  The first pass parses the
269214571Sdim	     machine insn and extracts the fields.  The second pass prints
270214571Sdim	     them.  */
271214571Sdim
272214571Sdim	  /* Make sure the entire insn is loaded into insn_value, if it
273214571Sdim	     can fit.  */
274214571Sdim	  if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
275214571Sdim	      (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
276214571Sdim	    {
277214571Sdim	      unsigned long full_insn_value;
278214571Sdim	      int rc = read_insn (cd, pc, info, buf,
279214571Sdim				  CGEN_INSN_BITSIZE (insn) / 8,
280214571Sdim				  & ex_info, & full_insn_value);
281214571Sdim	      if (rc != 0)
282214571Sdim		return rc;
283214571Sdim	      length = CGEN_EXTRACT_FN (cd, insn)
284214571Sdim		(cd, insn, &ex_info, full_insn_value, &fields, pc);
285214571Sdim	    }
286214571Sdim	  else
287214571Sdim	    length = CGEN_EXTRACT_FN (cd, insn)
288214571Sdim	      (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
289214571Sdim
290214571Sdim	  /* length < 0 -> error */
291214571Sdim	  if (length < 0)
292214571Sdim	    return length;
293214571Sdim	  if (length > 0)
294214571Sdim	    {
295214571Sdim	      CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
296214571Sdim	      /* length is in bits, result is in bytes */
297214571Sdim	      return length / 8;
298214571Sdim	    }
299214571Sdim	}
300214571Sdim
301214571Sdim      insn_list = CGEN_DIS_NEXT_INSN (insn_list);
302214571Sdim    }
303214571Sdim
304214571Sdim  return 0;
305214571Sdim}
306214571Sdim
307214571Sdim/* Default value for CGEN_PRINT_INSN.
308214571Sdim   The result is the size of the insn in bytes or zero for an unknown insn
309214571Sdim   or -1 if an error occured fetching bytes.  */
310214571Sdim
311214571Sdim#ifndef CGEN_PRINT_INSN
312214571Sdim#define CGEN_PRINT_INSN default_print_insn
313214571Sdim#endif
314214571Sdim
315214571Sdimstatic int
316214571Sdimdefault_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
317214571Sdim{
318214571Sdim  char buf[CGEN_MAX_INSN_SIZE];
319214571Sdim  int buflen;
320214571Sdim  int status;
321214571Sdim
322214571Sdim  /* Attempt to read the base part of the insn.  */
323214571Sdim  buflen = cd->base_insn_bitsize / 8;
324214571Sdim  status = (*info->read_memory_func) (pc, buf, buflen, info);
325214571Sdim
326214571Sdim  /* Try again with the minimum part, if min < base.  */
327214571Sdim  if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
328214571Sdim    {
329214571Sdim      buflen = cd->min_insn_bitsize / 8;
330214571Sdim      status = (*info->read_memory_func) (pc, buf, buflen, info);
331214571Sdim    }
332214571Sdim
333214571Sdim  if (status != 0)
334214571Sdim    {
335214571Sdim      (*info->memory_error_func) (status, pc, info);
336214571Sdim      return -1;
337214571Sdim    }
338214571Sdim
339214571Sdim  return print_insn (cd, pc, info, buf, buflen);
340214571Sdim}
341214571Sdim
342214571Sdim/* Main entry point.
343214571Sdim   Print one instruction from PC on INFO->STREAM.
344214571Sdim   Return the size of the instruction (in bytes).  */
345214571Sdim
346214571Sdimtypedef struct cpu_desc_list {
347214571Sdim  struct cpu_desc_list *next;
348214571Sdim  int isa;
349214571Sdim  int mach;
350214571Sdim  int endian;
351214571Sdim  CGEN_CPU_DESC cd;
352214571Sdim} cpu_desc_list;
353214571Sdim
354214571Sdimint
355214571Sdimprint_insn_@arch@ (bfd_vma pc, disassemble_info *info)
356214571Sdim{
357214571Sdim  static cpu_desc_list *cd_list = 0;
358214571Sdim  cpu_desc_list *cl = 0;
359214571Sdim  static CGEN_CPU_DESC cd = 0;
360214571Sdim  static int prev_isa;
361214571Sdim  static int prev_mach;
362214571Sdim  static int prev_endian;
363214571Sdim  int length;
364214571Sdim  int isa,mach;
365214571Sdim  int endian = (info->endian == BFD_ENDIAN_BIG
366214571Sdim		? CGEN_ENDIAN_BIG
367214571Sdim		: CGEN_ENDIAN_LITTLE);
368214571Sdim  enum bfd_architecture arch;
369214571Sdim
370214571Sdim  /* ??? gdb will set mach but leave the architecture as "unknown" */
371214571Sdim#ifndef CGEN_BFD_ARCH
372214571Sdim#define CGEN_BFD_ARCH bfd_arch_@arch@
373214571Sdim#endif
374214571Sdim  arch = info->arch;
375214571Sdim  if (arch == bfd_arch_unknown)
376214571Sdim    arch = CGEN_BFD_ARCH;
377214571Sdim   
378214571Sdim  /* There's no standard way to compute the machine or isa number
379214571Sdim     so we leave it to the target.  */
380214571Sdim#ifdef CGEN_COMPUTE_MACH
381214571Sdim  mach = CGEN_COMPUTE_MACH (info);
382214571Sdim#else
383214571Sdim  mach = info->mach;
384214571Sdim#endif
385214571Sdim
386214571Sdim#ifdef CGEN_COMPUTE_ISA
387214571Sdim  isa = CGEN_COMPUTE_ISA (info);
388214571Sdim#else
389214571Sdim  isa = info->insn_sets;
390214571Sdim#endif
391214571Sdim
392214571Sdim  /* If we've switched cpu's, try to find a handle we've used before */
393214571Sdim  if (cd
394214571Sdim      && (isa != prev_isa
395214571Sdim	  || mach != prev_mach
396214571Sdim	  || endian != prev_endian))
397214571Sdim    {
398214571Sdim      cd = 0;
399214571Sdim      for (cl = cd_list; cl; cl = cl->next)
400214571Sdim	{
401214571Sdim	  if (cl->isa == isa &&
402214571Sdim	      cl->mach == mach &&
403214571Sdim	      cl->endian == endian)
404214571Sdim	    {
405214571Sdim	      cd = cl->cd;
406214571Sdim	      break;
407214571Sdim	    }
408214571Sdim	}
409214571Sdim    } 
410214571Sdim
411214571Sdim  /* If we haven't initialized yet, initialize the opcode table.  */
412214571Sdim  if (! cd)
413214571Sdim    {
414214571Sdim      const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
415214571Sdim      const char *mach_name;
416214571Sdim
417214571Sdim      if (!arch_type)
418214571Sdim	abort ();
419214571Sdim      mach_name = arch_type->printable_name;
420214571Sdim
421214571Sdim      prev_isa = isa;
422214571Sdim      prev_mach = mach;
423214571Sdim      prev_endian = endian;
424214571Sdim      cd = @arch@_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
425214571Sdim				 CGEN_CPU_OPEN_BFDMACH, mach_name,
426214571Sdim				 CGEN_CPU_OPEN_ENDIAN, prev_endian,
427214571Sdim				 CGEN_CPU_OPEN_END);
428214571Sdim      if (!cd)
429214571Sdim	abort ();
430214571Sdim
431214571Sdim      /* save this away for future reference */
432214571Sdim      cl = xmalloc (sizeof (struct cpu_desc_list));
433214571Sdim      cl->cd = cd;
434214571Sdim      cl->isa = isa;
435214571Sdim      cl->mach = mach;
436214571Sdim      cl->endian = endian;
437214571Sdim      cl->next = cd_list;
438214571Sdim      cd_list = cl;
439214571Sdim
440214571Sdim      @arch@_cgen_init_dis (cd);
441214571Sdim    }
442214571Sdim
443214571Sdim  /* We try to have as much common code as possible.
444214571Sdim     But at this point some targets need to take over.  */
445214571Sdim  /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
446214571Sdim     but if not possible try to move this hook elsewhere rather than
447214571Sdim     have two hooks.  */
448214571Sdim  length = CGEN_PRINT_INSN (cd, pc, info);
449214571Sdim  if (length > 0)
450214571Sdim    return length;
451214571Sdim  if (length < 0)
452214571Sdim    return -1;
453214571Sdim
454214571Sdim  (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
455214571Sdim  return cd->default_insn_bitsize / 8;
456214571Sdim}
457214571Sdim