]>
Commit | Line | Data |
---|---|---|
a86481d3 DB |
1 | /* Disassembler interface for targets using CGEN. -*- C -*- |
2 | CGEN: Cpu tools GENerator | |
3 | ||
4 | THIS FILE IS USED TO GENERATE fr30-dis.c. | |
5 | ||
6 | Copyright (C) 1996, 1997, 1998 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 Foundation, Inc., | |
22 | 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 "fr30-opc.h" | |
31 | #include "opintl.h" | |
32 | ||
33 | #undef INLINE | |
34 | #ifdef __GNUC__ | |
35 | #define INLINE __inline__ | |
36 | #else | |
37 | #define INLINE | |
38 | #endif | |
39 | ||
40 | /* Default text to print if an instruction isn't recognized. */ | |
41 | #define UNKNOWN_INSN_MSG _("*unknown*") | |
42 | ||
43 | static int extract_normal | |
44 | PARAMS ((CGEN_OPCODE_DESC, CGEN_EXTRACT_INFO *, CGEN_INSN_BYTES, | |
45 | unsigned int, int, int, int, long *)); | |
46 | static void print_normal | |
47 | PARAMS ((CGEN_OPCODE_DESC, PTR, long, unsigned int, bfd_vma, int)); | |
48 | static void print_address | |
49 | PARAMS ((CGEN_OPCODE_DESC, PTR, bfd_vma, unsigned int, bfd_vma, int)); | |
50 | static void print_keyword | |
51 | PARAMS ((CGEN_OPCODE_DESC, PTR, CGEN_KEYWORD *, long, unsigned int)); | |
52 | static int extract_insn_normal | |
53 | PARAMS ((CGEN_OPCODE_DESC, const CGEN_INSN *, CGEN_EXTRACT_INFO *, | |
54 | unsigned long, CGEN_FIELDS *, bfd_vma)); | |
55 | static void print_insn_normal | |
56 | PARAMS ((CGEN_OPCODE_DESC, PTR, const CGEN_INSN *, CGEN_FIELDS *, | |
57 | bfd_vma, int)); | |
58 | static int print_insn PARAMS ((CGEN_OPCODE_DESC, bfd_vma, | |
59 | disassemble_info *, char *, int)); | |
60 | static int default_print_insn | |
61 | PARAMS ((CGEN_OPCODE_DESC, bfd_vma, disassemble_info *)); | |
62 | \f | |
63 | /* -- disassembler routines inserted here */ | |
64 | ||
65 | /* Main entry point for operand extraction. | |
66 | ||
67 | This function is basically just a big switch statement. Earlier versions | |
68 | used tables to look up the function to use, but | |
69 | - if the table contains both assembler and disassembler functions then | |
70 | the disassembler contains much of the assembler and vice-versa, | |
71 | - there's a lot of inlining possibilities as things grow, | |
72 | - using a switch statement avoids the function call overhead. | |
73 | ||
74 | This function could be moved into `print_insn_normal', but keeping it | |
75 | separate makes clear the interface between `print_insn_normal' and each of | |
76 | the handlers. | |
77 | */ | |
78 | ||
79 | int | |
80 | fr30_cgen_extract_operand (od, opindex, ex_info, insn_value, fields, pc) | |
81 | CGEN_OPCODE_DESC od; | |
82 | int opindex; | |
83 | CGEN_EXTRACT_INFO *ex_info; | |
84 | CGEN_INSN_BYTES insn_value; | |
85 | CGEN_FIELDS * fields; | |
86 | bfd_vma pc; | |
87 | { | |
88 | int length; | |
89 | ||
90 | switch (opindex) | |
91 | { | |
92 | case FR30_OPERAND_RI : | |
93 | length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 12, 4, CGEN_FIELDS_BITSIZE (fields), & fields->f_Ri); | |
94 | break; | |
95 | case FR30_OPERAND_RJ : | |
96 | length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 8, 4, CGEN_FIELDS_BITSIZE (fields), & fields->f_Rj); | |
97 | break; | |
98 | ||
99 | default : | |
100 | /* xgettext:c-format */ | |
101 | fprintf (stderr, _("Unrecognized field %d while decoding insn.\n"), | |
102 | opindex); | |
103 | abort (); | |
104 | } | |
105 | ||
106 | return length; | |
107 | } | |
108 | ||
109 | /* Main entry point for printing operands. | |
110 | ||
111 | This function is basically just a big switch statement. Earlier versions | |
112 | used tables to look up the function to use, but | |
113 | - if the table contains both assembler and disassembler functions then | |
114 | the disassembler contains much of the assembler and vice-versa, | |
115 | - there's a lot of inlining possibilities as things grow, | |
116 | - using a switch statement avoids the function call overhead. | |
117 | ||
118 | This function could be moved into `print_insn_normal', but keeping it | |
119 | separate makes clear the interface between `print_insn_normal' and each of | |
120 | the handlers. | |
121 | */ | |
122 | ||
123 | void | |
124 | fr30_cgen_print_operand (od, opindex, info, fields, attrs, pc, length) | |
125 | CGEN_OPCODE_DESC od; | |
126 | int opindex; | |
127 | disassemble_info * info; | |
128 | CGEN_FIELDS * fields; | |
129 | void const * attrs; | |
130 | bfd_vma pc; | |
131 | int length; | |
132 | { | |
133 | switch (opindex) | |
134 | { | |
135 | case FR30_OPERAND_RI : | |
136 | print_keyword (od, info, & fr30_cgen_opval_h_gr, fields->f_Ri, 0|(1<<CGEN_OPERAND_UNSIGNED)); | |
137 | break; | |
138 | case FR30_OPERAND_RJ : | |
139 | print_keyword (od, info, & fr30_cgen_opval_h_gr, fields->f_Rj, 0|(1<<CGEN_OPERAND_UNSIGNED)); | |
140 | break; | |
141 | ||
142 | default : | |
143 | /* xgettext:c-format */ | |
144 | fprintf (stderr, _("Unrecognized field %d while printing insn.\n"), | |
145 | opindex); | |
146 | abort (); | |
147 | } | |
148 | } | |
149 | ||
150 | cgen_extract_fn * const fr30_cgen_extract_handlers[] = | |
151 | { | |
152 | 0, /* default */ | |
153 | extract_insn_normal, | |
154 | }; | |
155 | ||
156 | cgen_print_fn * const fr30_cgen_print_handlers[] = | |
157 | { | |
158 | 0, /* default */ | |
159 | print_insn_normal, | |
160 | }; | |
161 | ||
162 | ||
163 | void | |
164 | fr30_cgen_init_dis (od) | |
165 | CGEN_OPCODE_DESC od; | |
166 | { | |
167 | } | |
168 | ||
169 | \f | |
170 | #if ! CGEN_INT_INSN_P | |
171 | ||
172 | /* Subroutine of extract_normal. */ | |
173 | ||
174 | static INLINE long | |
175 | extract_1 (od, ex_info, start, length, word_length, bufp) | |
176 | CGEN_OPCODE_DESC od; | |
177 | CGEN_EXTRACT_INFO *info; | |
178 | int start,length,word_length; | |
179 | unsigned char *bufp; | |
180 | { | |
181 | unsigned long x,mask; | |
182 | int shift; | |
183 | int big_p = CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG; | |
184 | ||
185 | /* FIXME: Need to use ex_info to ensure bytes have been fetched. */ | |
186 | ||
187 | switch (word_length) | |
188 | { | |
189 | case 8: | |
190 | x = *bufp; | |
191 | break; | |
192 | case 16: | |
193 | if (big_p) | |
194 | x = bfd_getb16 (bufp); | |
195 | else | |
196 | x = bfd_getl16 (bufp); | |
197 | break; | |
198 | case 24: | |
199 | /* ??? This may need reworking as these cases don't necessarily | |
200 | want the first byte and the last two bytes handled like this. */ | |
201 | if (big_p) | |
202 | x = (bfd_getb8 (bufp) << 16) | bfd_getb16 (bufp + 1); | |
203 | else | |
204 | x = bfd_getl16 (bufp) | (bfd_getb8 (bufp + 2) << 16); | |
205 | break; | |
206 | case 32: | |
207 | if (big_p) | |
208 | x = bfd_getb32 (bufp); | |
209 | else | |
210 | x = bfd_getl32 (bufp); | |
211 | break; | |
212 | default : | |
213 | abort (); | |
214 | } | |
215 | ||
216 | /* Written this way to avoid undefined behaviour. */ | |
217 | mask = (((1L << (length - 1)) - 1) << 1) | 1; | |
218 | if (CGEN_INSN_LSB0_P) | |
219 | shift = start; | |
220 | else | |
221 | shift = (word_length - (start + length)); | |
222 | return (x >> shift) & mask; | |
223 | } | |
224 | ||
225 | #endif /* ! CGEN_INT_INSN_P */ | |
226 | ||
227 | /* Default extraction routine. | |
228 | ||
229 | ATTRS is a mask of the boolean attributes. We only need `unsigned', | |
230 | but for generality we take a bitmask of all of them. */ | |
231 | ||
232 | /* ??? This doesn't handle bfd_vma's. Create another function when | |
233 | necessary. */ | |
234 | ||
235 | static int | |
236 | extract_normal (od, ex_info, insn_value, attrs, start, length, total_length, valuep) | |
237 | CGEN_OPCODE_DESC od; | |
238 | CGEN_EXTRACT_INFO *ex_info; | |
239 | CGEN_INSN_BYTES insn_value; | |
240 | unsigned int attrs; | |
241 | int start, length, total_length; | |
242 | long *valuep; | |
243 | { | |
244 | unsigned long value; | |
245 | ||
246 | /* If LENGTH is zero, this operand doesn't contribute to the value | |
247 | so give it a standard value of zero. */ | |
248 | if (length == 0) | |
249 | { | |
250 | *valuep = 0; | |
251 | return 1; | |
252 | } | |
253 | ||
254 | #if CGEN_INT_INSN_P | |
255 | ||
256 | { | |
257 | /* Written this way to avoid undefined behaviour. */ | |
258 | unsigned long mask = (((1L << (length - 1)) - 1) << 1) | 1; | |
259 | ||
260 | if (CGEN_INSN_LSB0_P) | |
261 | value = insn_value >> start; | |
262 | else | |
263 | value = insn_value >> (total_length - (start + length)); | |
264 | value &= mask; | |
265 | /* sign extend? */ | |
266 | if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) | |
267 | && (value & (1L << (length - 1)))) | |
268 | value |= ~mask; | |
269 | } | |
270 | ||
271 | #else | |
272 | ||
273 | /* The hard case is probably too slow for the normal cases. | |
274 | It's certainly more difficult to understand than the normal case. | |
275 | Thus this is split into two. Keep it that way. The hard case is defined | |
276 | to be when a field straddles a (loosely defined) word boundary | |
277 | (??? which may require target specific help to determine). */ | |
278 | ||
279 | #if 0 /*wip*/ | |
280 | ||
281 | #define HARD_CASE_P 0 /* FIXME:wip */ | |
282 | ||
283 | if (HARD_CASE_P) | |
284 | { | |
285 | } | |
286 | #endif | |
287 | else | |
288 | { | |
289 | unsigned char *bufp = (unsigned char *) insn_value; | |
290 | ||
291 | if (length > 32) | |
292 | abort (); | |
293 | ||
294 | /* Adjust start,total_length,bufp to point to the pseudo-word that holds | |
295 | the value. For example in a 48 bit insn where the value to insert | |
296 | (say an immediate value) is the last 16 bits then word_length here | |
297 | would be 16. To handle a 24 bit insn with an 18 bit immediate, | |
298 | extract_1 handles 24 bits (using a combination of bfd_get8,16). */ | |
299 | ||
300 | if (total_length > 32) | |
301 | { | |
302 | int needed_width = start % 8 + length; | |
303 | int fetch_length = (needed_width <= 8 ? 8 | |
304 | : needed_width <= 16 ? 16 | |
305 | : 32); | |
306 | ||
307 | if (CGEN_INSN_LSB0_P) | |
308 | { | |
309 | if (CGEN_INSN_WORD_ENDIAN (od) == CGEN_ENDIAN_BIG) | |
310 | { | |
311 | abort (); /* wip */ | |
312 | } | |
313 | else | |
314 | { | |
315 | int offset = start & ~7; | |
316 | ||
317 | bufp += offset / 8; | |
318 | start -= offset; | |
319 | total_length -= offset; | |
320 | } | |
321 | } | |
322 | else | |
323 | { | |
324 | if (CGEN_INSN_WORD_ENDIAN (od) == CGEN_ENDIAN_BIG) | |
325 | { | |
326 | int offset = start & ~7; | |
327 | ||
328 | bufp += offset / 8; | |
329 | start -= offset; | |
330 | total_length -= offset; | |
331 | } | |
332 | else | |
333 | { | |
334 | abort (); /* wip */ | |
335 | } | |
336 | } | |
337 | } | |
338 | ||
339 | /* FIXME: which bytes are being extracted have been lost. */ | |
340 | value = extract_1 (od, ex_info, start, length, total_length, bufp); | |
341 | } | |
342 | ||
343 | #endif /* ! CGEN_INT_INSN_P */ | |
344 | ||
345 | *valuep = value; | |
346 | ||
347 | /* FIXME: for now */ | |
348 | return 1; | |
349 | } | |
350 | ||
351 | /* Default print handler. */ | |
352 | ||
353 | static void | |
354 | print_normal (od, dis_info, value, attrs, pc, length) | |
355 | CGEN_OPCODE_DESC od; | |
356 | PTR dis_info; | |
357 | long value; | |
358 | unsigned int attrs; | |
359 | bfd_vma pc; | |
360 | int length; | |
361 | { | |
362 | disassemble_info *info = (disassemble_info *) dis_info; | |
363 | ||
364 | #ifdef CGEN_PRINT_NORMAL | |
365 | CGEN_PRINT_NORMAL (od, info, value, attrs, pc, length); | |
366 | #endif | |
367 | ||
368 | /* Print the operand as directed by the attributes. */ | |
1c8f439e | 369 | if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY)) |
a86481d3 DB |
370 | ; /* nothing to do */ |
371 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_UNSIGNED)) | |
372 | (*info->fprintf_func) (info->stream, "0x%lx", value); | |
373 | else | |
374 | (*info->fprintf_func) (info->stream, "%ld", value); | |
375 | } | |
376 | ||
377 | /* Default address handler. */ | |
378 | ||
379 | static void | |
380 | print_address (od, dis_info, value, attrs, pc, length) | |
381 | CGEN_OPCODE_DESC od; | |
382 | PTR dis_info; | |
383 | bfd_vma value; | |
384 | unsigned int attrs; | |
385 | bfd_vma pc; | |
386 | int length; | |
387 | { | |
388 | disassemble_info *info = (disassemble_info *) dis_info; | |
389 | ||
390 | #ifdef CGEN_PRINT_ADDRESS | |
391 | CGEN_PRINT_ADDRESS (od, info, value, attrs, pc, length); | |
392 | #endif | |
393 | ||
394 | /* Print the operand as directed by the attributes. */ | |
1c8f439e | 395 | if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY)) |
a86481d3 DB |
396 | ; /* nothing to do */ |
397 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR)) | |
398 | (*info->print_address_func) (value, info); | |
399 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR)) | |
400 | (*info->print_address_func) (value, info); | |
401 | else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_UNSIGNED)) | |
402 | (*info->fprintf_func) (info->stream, "0x%lx", (long) value); | |
403 | else | |
404 | (*info->fprintf_func) (info->stream, "%ld", (long) value); | |
405 | } | |
406 | ||
407 | /* Keyword print handler. */ | |
408 | ||
409 | static void | |
410 | print_keyword (od, dis_info, keyword_table, value, attrs) | |
411 | CGEN_OPCODE_DESC od; | |
412 | PTR dis_info; | |
413 | CGEN_KEYWORD *keyword_table; | |
414 | long value; | |
415 | unsigned int attrs; | |
416 | { | |
417 | disassemble_info *info = (disassemble_info *) dis_info; | |
418 | const CGEN_KEYWORD_ENTRY *ke; | |
419 | ||
420 | ke = cgen_keyword_lookup_value (keyword_table, value); | |
421 | if (ke != NULL) | |
422 | (*info->fprintf_func) (info->stream, "%s", ke->name); | |
423 | else | |
424 | (*info->fprintf_func) (info->stream, "???"); | |
425 | } | |
426 | \f | |
427 | /* Default insn extractor. | |
428 | ||
429 | INSN_VALUE is the first CGEN_BASE_INSN_SIZE bytes, translated to host order. | |
430 | The extracted fields are stored in FIELDS. | |
431 | EX_INFO is used to handle reading variable length insns. | |
432 | Return the length of the insn in bits, or 0 if no match, | |
433 | or -1 if an error occurs fetching data (memory_error_func will have | |
434 | been called). */ | |
435 | ||
436 | static int | |
437 | extract_insn_normal (od, insn, ex_info, insn_value, fields, pc) | |
438 | CGEN_OPCODE_DESC od; | |
439 | const CGEN_INSN *insn; | |
440 | CGEN_EXTRACT_INFO *ex_info; | |
441 | unsigned long insn_value; | |
442 | CGEN_FIELDS *fields; | |
443 | bfd_vma pc; | |
444 | { | |
445 | const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); | |
446 | const unsigned char *syn; | |
447 | ||
448 | CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn); | |
449 | ||
450 | CGEN_INIT_EXTRACT (od); | |
451 | ||
452 | for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn) | |
453 | { | |
454 | int length; | |
455 | ||
456 | if (CGEN_SYNTAX_CHAR_P (*syn)) | |
457 | continue; | |
458 | ||
459 | length = fr30_cgen_extract_operand (od, CGEN_SYNTAX_FIELD (*syn), | |
460 | ex_info, insn_value, fields, pc); | |
461 | if (length <= 0) | |
462 | return length; | |
463 | } | |
464 | ||
465 | /* We recognized and successfully extracted this insn. */ | |
466 | return CGEN_INSN_BITSIZE (insn); | |
467 | } | |
468 | ||
469 | /* Default insn printer. | |
470 | ||
471 | DIS_INFO is defined as `PTR' so the disassembler needn't know anything | |
472 | about disassemble_info. */ | |
473 | ||
474 | static void | |
475 | print_insn_normal (od, dis_info, insn, fields, pc, length) | |
476 | CGEN_OPCODE_DESC od; | |
477 | PTR dis_info; | |
478 | const CGEN_INSN *insn; | |
479 | CGEN_FIELDS *fields; | |
480 | bfd_vma pc; | |
481 | int length; | |
482 | { | |
483 | const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); | |
484 | disassemble_info *info = (disassemble_info *) dis_info; | |
485 | const unsigned char *syn; | |
486 | ||
487 | CGEN_INIT_PRINT (od); | |
488 | ||
489 | for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn) | |
490 | { | |
491 | if (CGEN_SYNTAX_MNEMONIC_P (*syn)) | |
492 | { | |
493 | (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn)); | |
494 | continue; | |
495 | } | |
496 | if (CGEN_SYNTAX_CHAR_P (*syn)) | |
497 | { | |
498 | (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn)); | |
499 | continue; | |
500 | } | |
501 | ||
502 | /* We have an operand. */ | |
503 | fr30_cgen_print_operand (od, CGEN_SYNTAX_FIELD (*syn), info, | |
504 | fields, CGEN_INSN_ATTRS (insn), pc, length); | |
505 | } | |
506 | } | |
507 | \f | |
508 | /* Utility to print an insn. | |
509 | BUF is the base part of the insn, target byte order, BUFLEN bytes long. | |
510 | The result is the size of the insn in bytes or zero for an unknown insn | |
511 | or -1 if an error occurs fetching data (memory_error_func will have | |
512 | been called). */ | |
513 | ||
514 | static int | |
515 | print_insn (od, pc, info, buf, buflen) | |
516 | CGEN_OPCODE_DESC od; | |
517 | bfd_vma pc; | |
518 | disassemble_info *info; | |
519 | char *buf; | |
520 | int buflen; | |
521 | { | |
522 | unsigned long insn_value; | |
523 | const CGEN_INSN_LIST *insn_list; | |
524 | CGEN_EXTRACT_INFO ex_info; | |
525 | ||
526 | ex_info.dis_info = info; | |
527 | ex_info.valid = (1 << CGEN_BASE_INSN_SIZE) - 1; | |
528 | ex_info.bytes = buf; | |
529 | ||
530 | switch (buflen) | |
531 | { | |
532 | case 1: | |
533 | insn_value = buf[0]; | |
534 | break; | |
535 | case 2: | |
536 | insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf); | |
537 | break; | |
538 | case 4: | |
539 | insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf); | |
540 | break; | |
541 | default: | |
542 | abort (); | |
543 | } | |
544 | ||
545 | /* The instructions are stored in hash lists. | |
546 | Pick the first one and keep trying until we find the right one. */ | |
547 | ||
548 | insn_list = CGEN_DIS_LOOKUP_INSN (od, buf, insn_value); | |
549 | while (insn_list != NULL) | |
550 | { | |
551 | const CGEN_INSN *insn = insn_list->insn; | |
552 | CGEN_FIELDS fields; | |
553 | int length; | |
554 | ||
555 | #if 0 /* not needed as insn shouldn't be in hash lists if not supported */ | |
556 | /* Supported by this cpu? */ | |
557 | if (! fr30_cgen_insn_supported (od, insn)) | |
558 | continue; | |
559 | #endif | |
560 | ||
561 | /* Basic bit mask must be correct. */ | |
562 | /* ??? May wish to allow target to defer this check until the extract | |
563 | handler. */ | |
564 | if ((insn_value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn)) | |
565 | { | |
566 | /* Printing is handled in two passes. The first pass parses the | |
567 | machine insn and extracts the fields. The second pass prints | |
568 | them. */ | |
569 | ||
570 | length = (*CGEN_EXTRACT_FN (insn)) (od, insn, &ex_info, insn_value, | |
571 | &fields, pc); | |
572 | /* length < 0 -> error */ | |
573 | if (length < 0) | |
574 | return length; | |
575 | if (length > 0) | |
576 | { | |
577 | (*CGEN_PRINT_FN (insn)) (od, info, insn, &fields, pc, length); | |
578 | /* length is in bits, result is in bytes */ | |
579 | return length / 8; | |
580 | } | |
581 | } | |
582 | ||
583 | insn_list = CGEN_DIS_NEXT_INSN (insn_list); | |
584 | } | |
585 | ||
586 | return 0; | |
587 | } | |
588 | ||
589 | /* Default value for CGEN_PRINT_INSN. | |
590 | The result is the size of the insn in bytes or zero for an unknown insn | |
591 | or -1 if an error occured fetching bytes. */ | |
592 | ||
593 | #ifndef CGEN_PRINT_INSN | |
594 | #define CGEN_PRINT_INSN default_print_insn | |
595 | #endif | |
596 | ||
597 | static int | |
598 | default_print_insn (od, pc, info) | |
599 | CGEN_OPCODE_DESC od; | |
600 | bfd_vma pc; | |
601 | disassemble_info *info; | |
602 | { | |
603 | char buf[CGEN_MAX_INSN_SIZE]; | |
604 | int status; | |
605 | ||
606 | /* Read the base part of the insn. */ | |
607 | ||
608 | status = (*info->read_memory_func) (pc, buf, CGEN_BASE_INSN_SIZE, info); | |
609 | if (status != 0) | |
610 | { | |
611 | (*info->memory_error_func) (status, pc, info); | |
612 | return -1; | |
613 | } | |
614 | ||
615 | return print_insn (od, pc, info, buf, CGEN_BASE_INSN_SIZE); | |
616 | } | |
617 | ||
618 | /* Main entry point. | |
619 | Print one instruction from PC on INFO->STREAM. | |
620 | Return the size of the instruction (in bytes). */ | |
621 | ||
622 | int | |
623 | print_insn_fr30 (pc, info) | |
624 | bfd_vma pc; | |
625 | disassemble_info *info; | |
626 | { | |
627 | int length; | |
628 | static CGEN_OPCODE_DESC od = 0; | |
629 | int mach = info->mach; | |
630 | int big_p = info->endian == BFD_ENDIAN_BIG; | |
631 | ||
632 | /* If we haven't initialized yet, initialize the opcode table. */ | |
633 | if (! od) | |
634 | { | |
635 | od = fr30_cgen_opcode_open (mach, | |
636 | big_p ? | |
637 | CGEN_ENDIAN_BIG | |
638 | : CGEN_ENDIAN_LITTLE); | |
639 | fr30_cgen_init_dis (od); | |
640 | } | |
641 | /* If we've switched cpu's, re-initialize. */ | |
642 | /* ??? Perhaps we should use BFD_ENDIAN. */ | |
643 | else if (mach != CGEN_OPCODE_MACH (od) | |
644 | || (CGEN_OPCODE_ENDIAN (od) | |
645 | != (big_p ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE))) | |
646 | { | |
647 | cgen_set_cpu (od, mach, big_p ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE); | |
648 | } | |
649 | ||
650 | /* We try to have as much common code as possible. | |
651 | But at this point some targets need to take over. */ | |
652 | /* ??? Some targets may need a hook elsewhere. Try to avoid this, | |
653 | but if not possible try to move this hook elsewhere rather than | |
654 | have two hooks. */ | |
655 | length = CGEN_PRINT_INSN (od, pc, info); | |
656 | if (length > 0) | |
657 | return length; | |
658 | if (length < 0) | |
659 | return -1; | |
660 | ||
661 | (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG); | |
662 | return CGEN_DEFAULT_INSN_SIZE; | |
663 | } |