Support for disassembling parallel instructions added.
[deliverable/binutils-gdb.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"
This page took 0.037512 seconds and 5 git commands to generate.