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