Support for disassembling parallel instructions added.
[external/binutils.git] / opcodes / m32r-dis.c
1 /* Disassembler interface for targets using CGEN. -*- C -*-
2    CGEN: Cpu tools GENerator
3
4 This file is used to generate m32r-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 "m32r-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
53 CGEN_INLINE void
54 m32r_cgen_print_operand
55      PARAMS ((int opindex, disassemble_info * info, CGEN_FIELDS * fields, void const * attrs, bfd_vma pc, int length));
56
57 \f
58 /* Default extraction routine.
59
60    ATTRS is a mask of the boolean attributes.  We only need `unsigned',
61    but for generality we take a bitmask of all of them.  */
62
63 static int
64 extract_normal (buf_ctrl, insn_value, attrs, start, length, shift, total_length, valuep)
65      void *       buf_ctrl;
66      cgen_insn_t  insn_value;
67      unsigned int attrs;
68      int          start;
69      int          length;
70      int          shift;
71      int          total_length;
72      long *       valuep;
73 {
74   long value;
75
76 #ifdef CGEN_INT_INSN
77 #if 0
78   value = ((insn_value >> (CGEN_BASE_INSN_BITSIZE - (start + length)))
79            & ((1 << length) - 1));
80 #else
81   value = ((insn_value >> (total_length - (start + length)))
82            & ((1 << length) - 1));
83 #endif
84   if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED))
85       && (value & (1 << (length - 1))))
86     value -= 1 << length;
87 #else
88   /* FIXME: unfinished */
89 #endif
90
91   /* This is backwards as we undo the effects of insert_normal.  */
92   if (shift < 0)
93     value >>= -shift;
94   else
95     value <<= shift;
96
97   * valuep = value;
98   return 1;
99 }
100
101 /* Default print handler.  */
102
103 static void
104 print_normal (dis_info, value, attrs, pc, length)
105      void *        dis_info;
106      long          value;
107      unsigned int  attrs;
108      unsigned long pc; /* FIXME: should be bfd_vma */
109      int           length;
110 {
111   disassemble_info * info = dis_info;
112
113   /* Print the operand as directed by the attributes.  */
114   if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_FAKE))
115     ; /* nothing to do (??? at least not yet) */
116   else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_PCREL_ADDR))
117     (*info->print_address_func) (pc + CGEN_PCREL_OFFSET + value, info);
118   /* ??? Not all cases of this are currently caught.  */
119   else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_ABS_ADDR))
120     /* FIXME: Why & 0xffffffff?  */
121     (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
122   else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED))
123     (*info->fprintf_func) (info->stream, "0x%lx", value);
124   else
125     (*info->fprintf_func) (info->stream, "%ld", value);
126 }
127
128 /* Keyword print handler.  */
129
130 static void
131 print_keyword (dis_info, keyword_table, value, attrs)
132      void *         dis_info;
133      CGEN_KEYWORD * keyword_table;
134      long           value;
135      CGEN_ATTR *    attrs;
136 {
137   disassemble_info * info = dis_info;
138   const CGEN_KEYWORD_ENTRY * ke;
139
140   ke = cgen_keyword_lookup_value (keyword_table, value);
141   info->fprintf_func (info->stream, "%s", ke == NULL ? "???" : ke->name);
142 }
143 \f
144 /* -- disassembler routines inserted here */
145 \f
146 /* Default insn extractor.
147
148    The extracted fields are stored in DIS_FLDS.
149    BUF_CTRL is used to handle reading variable length insns (FIXME: not done).
150    Return the length of the insn in bits, or 0 if no match.  */
151
152 static int
153 extract_insn_normal (insn, buf_ctrl, insn_value, fields)
154      const CGEN_INSN *  insn;
155      void *             buf_ctrl;
156      cgen_insn_t        insn_value;
157      CGEN_FIELDS *      fields;
158 {
159   const CGEN_SYNTAX *   syntax = CGEN_INSN_SYNTAX (insn);
160   const unsigned char * syn;
161
162   CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
163
164   CGEN_INIT_EXTRACT ();
165
166   for (syn = CGEN_SYNTAX_STRING (syntax); * syn; ++ syn)
167     {
168       int length;
169
170       if (CGEN_SYNTAX_CHAR_P (* syn))
171         continue;
172
173       length = m32r_cgen_extract_operand (CGEN_SYNTAX_FIELD (* syn),
174                                            buf_ctrl, insn_value, fields);
175       if (length == 0)
176         return 0;
177     }
178
179   /* We recognized and successfully extracted this insn.  */
180   return CGEN_INSN_BITSIZE (insn);
181 }
182
183 /* Default insn printer.
184
185    DIS_INFO is defined as `void *' so the disassembler needn't know anything
186    about disassemble_info.
187 */
188
189 static void
190 print_insn_normal (dis_info, insn, fields, pc, length)
191      void *             dis_info;
192      const CGEN_INSN *  insn;
193      CGEN_FIELDS *      fields;
194      bfd_vma            pc;
195      int                length;
196 {
197   const CGEN_SYNTAX *   syntax = CGEN_INSN_SYNTAX (insn);
198   disassemble_info *    info = dis_info;
199   const unsigned char * syn;
200
201   CGEN_INIT_PRINT ();
202
203   for (syn = CGEN_SYNTAX_STRING (syntax); * syn; ++ syn)
204     {
205       if (CGEN_SYNTAX_MNEMONIC_P (* syn))
206         {
207           info->fprintf_func (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
208           continue;
209         }
210       if (CGEN_SYNTAX_CHAR_P (* syn))
211         {
212           info->fprintf_func (info->stream, "%c", CGEN_SYNTAX_CHAR (* syn));
213           continue;
214         }
215
216       /* We have an operand.  */
217       m32r_cgen_print_operand (CGEN_SYNTAX_FIELD (* syn), info,
218                                 fields, CGEN_INSN_ATTRS (insn), pc, length);
219     }
220 }
221 \f
222 /* Default value for CGEN_PRINT_INSN.
223    Given BUFLEN bytes (target byte order) read into BUF, look up the
224    insn in the instruction table and disassemble it.
225
226    The result is the size of the insn in bytes.  */
227
228 #ifndef CGEN_PRINT_INSN
229 #define CGEN_PRINT_INSN print_insn
230 #endif
231
232 static int
233 print_insn (pc, info, buf, buflen)
234      bfd_vma            pc;
235      disassemble_info * info;
236      char *             buf;
237      int                buflen;
238 {
239   int                    i;
240   unsigned long          insn_value;
241   const CGEN_INSN_LIST * insn_list;
242   int                    extra_bytes;
243   
244   switch (buflen)
245     {
246     case 8:
247       insn_value = buf[0];
248       break;
249     case 16:
250       insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf);
251       break;
252     case 32:
253       insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf);
254       break;
255     default:
256       abort ();
257     }
258
259   /* Special case - a 32 bit instruction which is actually two 16 bit instructions
260      being executed in parallel.  */
261   if (buflen == 32
262       && ((insn_value & 0x80008000) == 0x00008000))
263     {
264       if (info->endian == BFD_ENDIAN_BIG)
265         {
266           static char buf2 [4];
267           
268           print_insn (pc, info, buf, 16);
269
270           info->fprintf_func (info->stream, " || ");
271
272           buf2 [0] = buf [2] & ~ 0x80;
273           buf2 [1] = buf [3];
274           buf2 [2] = 0;
275           buf2 [3] = 0;
276           buf = buf2;
277           
278           insn_value <<= 17;
279           insn_value >>= 1;
280         }
281       else
282         {
283           print_insn (pc, info, buf + 2, 16);
284
285           info->fprintf_func (info->stream, " || ");
286
287           insn_value &= 0x7fff;
288         }
289
290       pc += 2;
291       extra_bytes = 2;
292     }
293   else
294     extra_bytes = 0;
295   
296   /* The instructions are stored in hash lists.
297      Pick the first one and keep trying until we find the right one.  */
298
299   insn_list = CGEN_DIS_LOOKUP_INSN (buf, insn_value);
300
301   while (insn_list != NULL)
302     {
303       const CGEN_INSN *   insn = insn_list->insn;
304       unsigned long       value;
305
306 #if 0 /* not needed as insn shouldn't be in hash lists if not supported */
307       /* Supported by this cpu?  */
308       if (! m32r_cgen_insn_supported (insn))
309         continue;
310 #endif
311
312       /* If we are looking at a 16 bit insn we may have to adjust the value being examined.  */
313       value = insn_value;
314       if (CGEN_INSN_BITSIZE (insn) == 16)
315         {
316         /* If this is a big endian target,
317            and we have read 32 bits for the instruction value,
318            then we must examine the top 16 bits, not the bottom.  */
319           if (buflen == 32 && info->endian == BFD_ENDIAN_BIG)
320             value >>= 16;
321         }
322       
323       /* Basic bit mask must be correct.  */
324       /* ??? May wish to allow target to defer this check until the extract
325          handler.  */
326       if ((value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn))
327         {
328           CGEN_FIELDS fields;
329           int         length;
330           
331           /* Printing is handled in two passes.  The first pass parses the
332              machine insn and extracts the fields.  The second pass prints
333              them.  */
334
335           length = CGEN_EXTRACT_FN (insn) (insn, NULL, value, & fields);
336           if (length > 0)
337             {
338               CGEN_PRINT_FN (insn) (info, insn, & fields, pc, length);
339               
340               /* length is in bits, result is in bytes */
341               return (length / 8) + extra_bytes;
342             }
343         }
344       
345       insn_list = CGEN_DIS_NEXT_INSN (insn_list);
346     }
347
348   return extra_bytes;
349 }
350
351 /* Main entry point.
352    Print one instruction from PC on INFO->STREAM.
353    Return the size of the instruction (in bytes).  */
354
355 int
356 print_insn_m32r (pc, info)
357      bfd_vma            pc;
358      disassemble_info * info;
359 {
360   char       buffer [CGEN_MAX_INSN_SIZE];
361   int        status;
362   int        length;
363   static int initialized    = 0;
364   static int current_mach   = 0;
365   static int current_bigend = 0;
366   int        mach           = info->mach;
367   int        bigend         = info->endian == BFD_ENDIAN_BIG;
368
369   /* If we haven't initialized yet, or if we've switched cpu's, initialize.  */
370   if (!initialized || mach != current_mach || bigend != current_bigend)
371     {
372       initialized    = 1;
373       current_mach   = mach;
374       current_bigend = bigend;
375       
376       m32r_cgen_init_dis (mach, bigend ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE);
377     }
378
379   /* Read enough of the insn so we can look it up in the hash lists.  */
380
381   status = info->read_memory_func (pc, buffer, CGEN_BASE_INSN_SIZE, info);
382   if (status != 0)
383     {
384       /* Try reading a 16 bit instruction.  */
385       info->bytes_per_chunk = 2;
386       status = info->read_memory_func (pc, buffer, CGEN_BASE_INSN_SIZE / 2, info);
387       buffer [2] = buffer [3] = 0;
388     }
389   if (status != 0)
390     {
391       info->memory_error_func (status, pc, info);
392       return -1;
393     }
394
395   /* We try to have as much common code as possible.
396      But at this point some targets need to take over.  */
397   /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
398      but if not possible try to move this hook elsewhere rather than
399      have two hooks.  */
400   length = CGEN_PRINT_INSN (pc, info, buffer, CGEN_BASE_INSN_BITSIZE);
401   
402   if (length)
403     return length;
404
405   info->fprintf_func (info->stream, UNKNOWN_INSN_MSG);
406   
407   return CGEN_DEFAULT_INSN_SIZE;
408 }
409
410 /* Get the generate machine specific code.  */
411 #include "m32r-dis.in"