2000-08-04 Ben Elliston <bje@redhat.com>
[platform/upstream/binutils.git] / opcodes / cgen-dis.in
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 (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
8
9 This file is part of the GNU Binutils and GDB, the GNU debugger.
10
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.
15
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.
20
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.  */
24
25 /* ??? Eventually more and more of this stuff can go to cpu-independent files.
26    Keep that in mind.  */
27
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"
37
38 /* Default text to print if an instruction isn't recognized.  */
39 #define UNKNOWN_INSN_MSG _("*unknown*")
40
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 PARAMS ((CGEN_CPU_DESC, bfd_vma,
51                                disassemble_info *, char *, int));
52 static int default_print_insn
53      PARAMS ((CGEN_CPU_DESC, bfd_vma, disassemble_info *));
54 \f
55 /* -- disassembler routines inserted here */
56 \f
57 /* Default print handler.  */
58
59 static void
60 print_normal (cd, dis_info, value, attrs, pc, length)
61 #ifdef CGEN_PRINT_NORMAL
62      CGEN_CPU_DESC cd;
63 #else
64      CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
65 #endif
66      PTR dis_info;
67      long value;
68      unsigned int attrs;
69 #ifdef CGEN_PRINT_NORMAL
70      bfd_vma pc;
71      int length;
72 #else
73      bfd_vma pc ATTRIBUTE_UNUSED;
74      int length ATTRIBUTE_UNUSED;
75 #endif
76 {
77   disassemble_info *info = (disassemble_info *) dis_info;
78
79 #ifdef CGEN_PRINT_NORMAL
80   CGEN_PRINT_NORMAL (cd, info, value, attrs, pc, length);
81 #endif
82
83   /* Print the operand as directed by the attributes.  */
84   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
85     ; /* nothing to do */
86   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
87     (*info->fprintf_func) (info->stream, "%ld", value);
88   else
89     (*info->fprintf_func) (info->stream, "0x%lx", value);
90 }
91
92 /* Default address handler.  */
93
94 static void
95 print_address (cd, dis_info, value, attrs, pc, length)
96 #ifdef CGEN_PRINT_NORMAL
97      CGEN_CPU_DESC cd;
98 #else
99      CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
100 #endif
101      PTR dis_info;
102      bfd_vma value;
103      unsigned int attrs;
104 #ifdef CGEN_PRINT_NORMAL
105      bfd_vma pc;
106      int length;
107 #else
108      bfd_vma pc ATTRIBUTE_UNUSED;
109      int length ATTRIBUTE_UNUSED;
110 #endif
111 {
112   disassemble_info *info = (disassemble_info *) dis_info;
113
114 #ifdef CGEN_PRINT_ADDRESS
115   CGEN_PRINT_ADDRESS (cd, info, value, attrs, pc, length);
116 #endif
117
118   /* Print the operand as directed by the attributes.  */
119   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
120     ; /* nothing to do */
121   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
122     (*info->print_address_func) (value, info);
123   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
124     (*info->print_address_func) (value, info);
125   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
126     (*info->fprintf_func) (info->stream, "%ld", (long) value);
127   else
128     (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
129 }
130
131 /* Keyword print handler.  */
132
133 static void
134 print_keyword (cd, dis_info, keyword_table, value, attrs)
135      CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
136      PTR dis_info;
137      CGEN_KEYWORD *keyword_table;
138      long value;
139      unsigned int attrs ATTRIBUTE_UNUSED;
140 {
141   disassemble_info *info = (disassemble_info *) dis_info;
142   const CGEN_KEYWORD_ENTRY *ke;
143
144   ke = cgen_keyword_lookup_value (keyword_table, value);
145   if (ke != NULL)
146     (*info->fprintf_func) (info->stream, "%s", ke->name);
147   else
148     (*info->fprintf_func) (info->stream, "???");
149 }
150 \f
151 /* Default insn printer.
152
153    DIS_INFO is defined as `PTR' so the disassembler needn't know anything
154    about disassemble_info.  */
155
156 static void
157 print_insn_normal (cd, dis_info, insn, fields, pc, length)
158      CGEN_CPU_DESC cd;
159      PTR dis_info;
160      const CGEN_INSN *insn;
161      CGEN_FIELDS *fields;
162      bfd_vma pc;
163      int length;
164 {
165   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
166   disassemble_info *info = (disassemble_info *) dis_info;
167   const unsigned char *syn;
168
169   CGEN_INIT_PRINT (cd);
170
171   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
172     {
173       if (CGEN_SYNTAX_MNEMONIC_P (*syn))
174         {
175           (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
176           continue;
177         }
178       if (CGEN_SYNTAX_CHAR_P (*syn))
179         {
180           (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
181           continue;
182         }
183
184       /* We have an operand.  */
185       @arch@_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
186                                  fields, CGEN_INSN_ATTRS (insn), pc, length);
187     }
188 }
189 \f
190 /* Utility to print an insn.
191    BUF is the base part of the insn, target byte order, BUFLEN bytes long.
192    The result is the size of the insn in bytes or zero for an unknown insn
193    or -1 if an error occurs fetching data (memory_error_func will have
194    been called).  */
195
196 static int
197 print_insn (cd, pc, info, buf, buflen)
198      CGEN_CPU_DESC cd;
199      bfd_vma pc;
200      disassemble_info *info;
201      char *buf;
202      int buflen;
203 {
204   unsigned long insn_value;
205   const CGEN_INSN_LIST *insn_list;
206   CGEN_EXTRACT_INFO ex_info;
207
208   ex_info.dis_info = info;
209   ex_info.valid = (1 << (cd->base_insn_bitsize / 8)) - 1;
210   ex_info.insn_bytes = buf;
211
212   switch (buflen)
213     {
214     case 1:
215       insn_value = buf[0];
216       break;
217     case 2:
218       insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf);
219       break;
220     case 4:
221       insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf);
222       break;
223     default:
224       abort ();
225     }
226
227   /* The instructions are stored in hash lists.
228      Pick the first one and keep trying until we find the right one.  */
229
230   insn_list = CGEN_DIS_LOOKUP_INSN (cd, buf, insn_value);
231   while (insn_list != NULL)
232     {
233       const CGEN_INSN *insn = insn_list->insn;
234       CGEN_FIELDS fields;
235       int length;
236
237 #ifdef CGEN_VALIDATE_INSN_SUPPORTED 
238       /* not needed as insn shouldn't be in hash lists if not supported */
239       /* Supported by this cpu?  */
240       if (! @arch@_cgen_insn_supported (cd, insn))
241         {
242           insn_list = CGEN_DIS_NEXT_INSN (insn_list);
243           continue;
244         }
245 #endif
246
247       /* Basic bit mask must be correct.  */
248       /* ??? May wish to allow target to defer this check until the extract
249          handler.  */
250       if ((insn_value & CGEN_INSN_BASE_MASK (insn))
251           == CGEN_INSN_BASE_VALUE (insn))
252         {
253           /* Printing is handled in two passes.  The first pass parses the
254              machine insn and extracts the fields.  The second pass prints
255              them.  */
256
257           length = CGEN_EXTRACT_FN (cd, insn)
258             (cd, insn, &ex_info, insn_value, &fields, pc);
259           /* length < 0 -> error */
260           if (length < 0)
261             return length;
262           if (length > 0)
263             {
264               CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
265               /* length is in bits, result is in bytes */
266               return length / 8;
267             }
268         }
269
270       insn_list = CGEN_DIS_NEXT_INSN (insn_list);
271     }
272
273   return 0;
274 }
275
276 /* Default value for CGEN_PRINT_INSN.
277    The result is the size of the insn in bytes or zero for an unknown insn
278    or -1 if an error occured fetching bytes.  */
279
280 #ifndef CGEN_PRINT_INSN
281 #define CGEN_PRINT_INSN default_print_insn
282 #endif
283
284 static int
285 default_print_insn (cd, pc, info)
286      CGEN_CPU_DESC cd;
287      bfd_vma pc;
288      disassemble_info *info;
289 {
290   char buf[CGEN_MAX_INSN_SIZE];
291   int status;
292
293   /* Read the base part of the insn.  */
294
295   status = (*info->read_memory_func) (pc, buf, cd->base_insn_bitsize / 8, info);
296   if (status != 0)
297     {
298       (*info->memory_error_func) (status, pc, info);
299       return -1;
300     }
301
302   return print_insn (cd, pc, info, buf, cd->base_insn_bitsize / 8);
303 }
304
305 /* Main entry point.
306    Print one instruction from PC on INFO->STREAM.
307    Return the size of the instruction (in bytes).  */
308
309 int
310 print_insn_@arch@ (pc, info)
311      bfd_vma pc;
312      disassemble_info *info;
313 {
314   static CGEN_CPU_DESC cd = 0;
315   static int prev_isa;
316   static int prev_mach;
317   static int prev_endian;
318   int length;
319   int isa,mach;
320   int endian = (info->endian == BFD_ENDIAN_BIG
321                 ? CGEN_ENDIAN_BIG
322                 : CGEN_ENDIAN_LITTLE);
323   enum bfd_architecture arch;
324
325   /* ??? gdb will set mach but leave the architecture as "unknown" */
326 #ifndef CGEN_BFD_ARCH
327 #define CGEN_BFD_ARCH bfd_arch_@arch@
328 #endif
329   arch = info->arch;
330   if (arch == bfd_arch_unknown)
331     arch = CGEN_BFD_ARCH;
332       
333   /* There's no standard way to compute the isa number (e.g. for arm thumb)
334      so we leave it to the target.  */
335 #ifdef CGEN_COMPUTE_ISA
336   isa = CGEN_COMPUTE_ISA (info);
337 #else
338   isa = 0;
339 #endif
340
341   mach = info->mach;
342
343   /* If we've switched cpu's, close the current table and open a new one.  */
344   if (cd
345       && (isa != prev_isa
346           || mach != prev_mach
347           || endian != prev_endian))
348     {
349       @arch@_cgen_cpu_close (cd);
350       cd = 0;
351     }
352
353   /* If we haven't initialized yet, initialize the opcode table.  */
354   if (! cd)
355     {
356       const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
357       const char *mach_name;
358
359       if (!arch_type)
360         abort ();
361       mach_name = arch_type->printable_name;
362
363       prev_isa = isa;
364       prev_mach = mach;
365       prev_endian = endian;
366       cd = @arch@_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
367                                  CGEN_CPU_OPEN_BFDMACH, mach_name,
368                                  CGEN_CPU_OPEN_ENDIAN, prev_endian,
369                                  CGEN_CPU_OPEN_END);
370       if (!cd)
371         abort ();
372       @arch@_cgen_init_dis (cd);
373     }
374
375   /* We try to have as much common code as possible.
376      But at this point some targets need to take over.  */
377   /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
378      but if not possible try to move this hook elsewhere rather than
379      have two hooks.  */
380   length = CGEN_PRINT_INSN (cd, pc, info);
381   if (length > 0)
382     return length;
383   if (length < 0)
384     return -1;
385
386   (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
387   return cd->default_insn_bitsize / 8;
388 }