This commit was generated by cvs2svn to track changes on a CVS vendor
[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 used to generate @arch@-dis.c.
5
6 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
7
8 This file is part of the GNU Binutils and GDB, the GNU debugger.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
23
24 #include "sysdep.h"
25 #include <stdio.h>
26 #include "ansidecl.h"
27 #include "dis-asm.h"
28 #include "bfd.h"
29 #include "symcat.h"
30 #include "@arch@-opc.h"
31
32 /* ??? The layout of this stuff is still work in progress.
33    For speed in assembly/disassembly, we use inline functions.  That of course
34    will only work for GCC.  When this stuff is finished, we can decide whether
35    to keep the inline functions (and only get the performance increase when
36    compiled with GCC), or switch to macros, or use something else.
37 */
38
39 /* Default text to print if an instruction isn't recognized.  */
40 #define UNKNOWN_INSN_MSG "*unknown*"
41
42 /* FIXME: Machine generate.  */
43 #ifndef CGEN_PCREL_OFFSET
44 #define CGEN_PCREL_OFFSET 0
45 #endif
46
47 static int print_insn PARAMS ((bfd_vma, disassemble_info *, char *, int));
48
49 static int extract_insn_normal
50      PARAMS ((const CGEN_INSN *, void *, cgen_insn_t, CGEN_FIELDS *));
51 static void print_insn_normal
52      PARAMS ((void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int));
53 \f
54 /* Default extraction routine.
55
56    ATTRS is a mask of the boolean attributes.  We only need `unsigned',
57    but for generality we take a bitmask of all of them.  */
58
59 static int
60 extract_normal (buf_ctrl, insn_value, attrs, start, length, shift, total_length, valuep)
61      PTR buf_ctrl;
62      cgen_insn_t insn_value;
63      unsigned int attrs;
64      int start, length, shift, total_length;
65      long *valuep;
66 {
67   long value;
68
69 #ifdef CGEN_INT_INSN
70 #if 0
71   value = ((insn_value >> (CGEN_BASE_INSN_BITSIZE - (start + length)))
72            & ((1 << length) - 1));
73 #else
74   value = ((insn_value >> (total_length - (start + length)))
75            & ((1 << length) - 1));
76 #endif
77   if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED))
78       && (value & (1 << (length - 1))))
79     value -= 1 << length;
80 #else
81   /* FIXME: unfinished */
82 #endif
83
84   /* This is backwards as we undo the effects of insert_normal.  */
85   if (shift < 0)
86     value >>= -shift;
87   else
88     value <<= shift;
89
90   *valuep = value;
91
92   /* FIXME: for now */
93   return 1;
94 }
95
96 /* Default print handler.  */
97
98 static void
99 print_normal (dis_info, value, attrs, pc, length)
100      PTR dis_info;
101      long value;
102      unsigned int attrs;
103      unsigned long pc; /* FIXME: should be bfd_vma */
104      int length;
105 {
106   disassemble_info *info = dis_info;
107
108   /* Print the operand as directed by the attributes.  */
109   if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_FAKE))
110     ; /* nothing to do (??? at least not yet) */
111   else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_PCREL_ADDR))
112     (*info->print_address_func) (pc + CGEN_PCREL_OFFSET + value, info);
113   /* ??? Not all cases of this are currently caught.  */
114   else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_ABS_ADDR))
115     /* FIXME: Why & 0xffffffff?  */
116     (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
117   else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED))
118     (*info->fprintf_func) (info->stream, "0x%lx", value);
119   else
120     (*info->fprintf_func) (info->stream, "%ld", value);
121 }
122
123 /* Keyword print handler.  */
124
125 static void
126 print_keyword (dis_info, keyword_table, value, attrs)
127      PTR dis_info;
128      CGEN_KEYWORD *keyword_table;
129      long value;
130      CGEN_ATTR *attrs;
131 {
132   disassemble_info *info = dis_info;
133   const CGEN_KEYWORD_ENTRY *ke;
134
135   ke = cgen_keyword_lookup_value (keyword_table, value);
136   if (ke != NULL)
137     (*info->fprintf_func) (info->stream, "%s", ke->name);
138   else
139     (*info->fprintf_func) (info->stream, "???");
140 }
141 \f
142 /* -- disassembler routines inserted here */
143 \f
144 /* Default insn extractor.
145
146    The extracted fields are stored in DIS_FLDS.
147    BUF_CTRL is used to handle reading variable length insns (FIXME: not done).
148    Return the length of the insn in bits, or 0 if no match.  */
149
150 static int
151 extract_insn_normal (insn, buf_ctrl, insn_value, fields)
152      const CGEN_INSN *insn;
153      PTR buf_ctrl;
154      cgen_insn_t insn_value;
155      CGEN_FIELDS *fields;
156 {
157   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
158   const unsigned char *syn;
159
160   CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
161
162   CGEN_INIT_EXTRACT ();
163
164   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
165     {
166       int length;
167
168       if (CGEN_SYNTAX_CHAR_P (*syn))
169         continue;
170
171       length = @arch@_cgen_extract_operand (CGEN_SYNTAX_FIELD (*syn),
172                                             buf_ctrl, insn_value, fields);
173       if (length == 0)
174         return 0;
175     }
176
177   /* We recognized and successfully extracted this insn.  */
178   return CGEN_INSN_BITSIZE (insn);
179 }
180
181 /* Default insn printer.
182
183    DIS_INFO is defined as `PTR' so the disassembler needn't know anything
184    about disassemble_info.
185 */
186
187 static void
188 print_insn_normal (dis_info, insn, fields, pc, length)
189      PTR dis_info;
190      const CGEN_INSN *insn;
191      CGEN_FIELDS *fields;
192      bfd_vma pc;
193      int length;
194 {
195   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
196   disassemble_info *info = dis_info;
197   const unsigned char *syn;
198
199   CGEN_INIT_PRINT ();
200
201   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
202     {
203       if (CGEN_SYNTAX_MNEMONIC_P (*syn))
204         {
205           (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
206           continue;
207         }
208       if (CGEN_SYNTAX_CHAR_P (*syn))
209         {
210           (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
211           continue;
212         }
213
214       /* We have an operand.  */
215       @arch@_cgen_print_operand (CGEN_SYNTAX_FIELD (*syn), info,
216                                 fields, CGEN_INSN_ATTRS (insn), pc, length);
217     }
218 }
219 \f
220 /* Default value for CGEN_PRINT_INSN.
221    Given BUFLEN bits (target byte order) read into BUF, look up the
222    insn in the instruction table and disassemble it.
223
224    The result is the size of the insn in bytes.  */
225
226 #ifndef CGEN_PRINT_INSN
227 #define CGEN_PRINT_INSN print_insn
228 #endif
229
230 static int
231 print_insn (pc, info, buf, buflen)
232      bfd_vma pc;
233      disassemble_info *info;
234      char *buf;
235      int buflen;
236 {
237   unsigned long insn_value;
238   const CGEN_INSN_LIST *insn_list;
239
240   switch (buflen)
241     {
242     case 8:
243       insn_value = buf[0];
244       break;
245     case 16:
246       insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf);
247       break;
248     case 32:
249       insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf);
250       break;
251     default:
252       abort ();
253     }
254
255   /* The instructions are stored in hash lists.
256      Pick the first one and keep trying until we find the right one.  */
257
258   insn_list = CGEN_DIS_LOOKUP_INSN (buf, insn_value);
259   while (insn_list != NULL)
260     {
261       const CGEN_INSN *insn = insn_list->insn;
262       CGEN_FIELDS fields;
263       int length;
264
265 #if 0 /* not needed as insn shouldn't be in hash lists if not supported */
266       /* Supported by this cpu?  */
267       if (! @arch@_cgen_insn_supported (insn))
268         continue;
269 #endif
270
271       /* Basic bit mask must be correct.  */
272       /* ??? May wish to allow target to defer this check until the extract
273          handler.  */
274       if ((insn_value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn))
275         {
276           /* Printing is handled in two passes.  The first pass parses the
277              machine insn and extracts the fields.  The second pass prints
278              them.  */
279
280           length = (*CGEN_EXTRACT_FN (insn)) (insn, NULL, insn_value, &fields);
281           if (length > 0)
282             {
283               (*CGEN_PRINT_FN (insn)) (info, insn, &fields, pc, length);
284               /* length is in bits, result is in bytes */
285               return length / 8;
286             }
287         }
288
289       insn_list = CGEN_DIS_NEXT_INSN (insn_list);
290     }
291
292   return 0;
293 }
294
295 /* Main entry point.
296    Print one instruction from PC on INFO->STREAM.
297    Return the size of the instruction (in bytes).  */
298
299 int
300 print_insn_@arch@ (pc, info)
301      bfd_vma pc;
302      disassemble_info *info;
303 {
304   char buffer[CGEN_MAX_INSN_SIZE];
305   int status, length;
306   static int initialized = 0;
307   static int current_mach = 0;
308   static int current_big_p = 0;
309   int mach = info->mach;
310   int big_p = info->endian == BFD_ENDIAN_BIG;
311
312   /* If we haven't initialized yet, or if we've switched cpu's, initialize.  */
313   if (!initialized || mach != current_mach || big_p != current_big_p)
314     {
315       initialized = 1;
316       current_mach = mach;
317       current_big_p = big_p;
318       @arch@_cgen_init_dis (mach, big_p ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE);
319     }
320
321   /* Read enough of the insn so we can look it up in the hash lists.  */
322
323   status = (*info->read_memory_func) (pc, buffer, CGEN_BASE_INSN_SIZE, info);
324   if (status != 0)
325     {
326       (*info->memory_error_func) (status, pc, info);
327       return -1;
328     }
329
330   /* We try to have as much common code as possible.
331      But at this point some targets need to take over.  */
332   /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
333      but if not possible try to move this hook elsewhere rather than
334      have two hooks.  */
335   length = CGEN_PRINT_INSN (pc, info, buffer, CGEN_BASE_INSN_BITSIZE);
336   if (length)
337     return length;
338
339   (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
340   return CGEN_DEFAULT_INSN_SIZE;
341 }