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