1/* Disassembler interface for targets using CGEN. -*- C -*-
2   CGEN: Cpu tools GENerator
3
4   THIS FILE IS MACHINE GENERATED WITH CGEN.
5   - the resultant file is machine generated, cgen-dis.in isn't
6
7   Copyright (C) 1996-2017 Free Software Foundation, Inc.
8
9   This file is part of libopcodes.
10
11   This library is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation; either version 3, or (at your option)
14   any later version.
15
16   It is distributed in the hope that it will be useful, but WITHOUT
17   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19   License for more details.
20
21   You should have received a copy of the GNU General Public License
22   along with this program; if not, write to the Free Software Foundation, Inc.,
23   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
24
25/* ??? Eventually more and more of this stuff can go to cpu-independent files.
26   Keep that in mind.  */
27
28#include "sysdep.h"
29#include <stdio.h>
30#include "ansidecl.h"
31#include "dis-asm.h"
32#include "bfd.h"
33#include "symcat.h"
34#include "libiberty.h"
35#include "fr30-desc.h"
36#include "fr30-opc.h"
37#include "opintl.h"
38
39/* Default text to print if an instruction isn't recognized.  */
40#define UNKNOWN_INSN_MSG _("*unknown*")
41
42static void print_normal
43  (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
44static void print_address
45  (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED;
46static void print_keyword
47  (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED;
48static void print_insn_normal
49  (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
50static int print_insn
51  (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, bfd_byte *, unsigned);
52static int default_print_insn
53  (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED;
54static int read_insn
55  (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *,
56   unsigned long *);
57
58/* -- disassembler routines inserted here.  */
59
60/* -- dis.c */
61static void
62print_register_list (void * dis_info,
63		     long value,
64		     long offset,
65		     int load_store) /* 0 == load, 1 == store.  */
66{
67  disassemble_info *info = dis_info;
68  int mask;
69  int reg_index = 0;
70  char * comma = "";
71
72  if (load_store)
73    mask = 0x80;
74  else
75    mask = 1;
76
77  if (value & mask)
78    {
79      (*info->fprintf_func) (info->stream, "r%li", reg_index + offset);
80      comma = ",";
81    }
82
83  for (reg_index = 1; reg_index <= 7; ++reg_index)
84    {
85      if (load_store)
86	mask >>= 1;
87      else
88	mask <<= 1;
89
90      if (value & mask)
91	{
92	  (*info->fprintf_func) (info->stream, "%sr%li", comma, reg_index + offset);
93	  comma = ",";
94	}
95    }
96}
97
98static void
99print_hi_register_list_ld (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
100			   void * dis_info,
101			   long value,
102			   unsigned int attrs ATTRIBUTE_UNUSED,
103			   bfd_vma pc ATTRIBUTE_UNUSED,
104			   int length ATTRIBUTE_UNUSED)
105{
106  print_register_list (dis_info, value, 8, 0 /* Load.  */);
107}
108
109static void
110print_low_register_list_ld (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
111			    void * dis_info,
112			    long value,
113			    unsigned int attrs ATTRIBUTE_UNUSED,
114			    bfd_vma pc ATTRIBUTE_UNUSED,
115			    int length ATTRIBUTE_UNUSED)
116{
117  print_register_list (dis_info, value, 0, 0 /* Load.  */);
118}
119
120static void
121print_hi_register_list_st (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
122			   void * dis_info,
123			   long value,
124			   unsigned int attrs ATTRIBUTE_UNUSED,
125			   bfd_vma pc ATTRIBUTE_UNUSED,
126			   int length ATTRIBUTE_UNUSED)
127{
128  print_register_list (dis_info, value, 8, 1 /* Store.  */);
129}
130
131static void
132print_low_register_list_st (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
133			    void * dis_info,
134			    long value,
135			    unsigned int attrs ATTRIBUTE_UNUSED,
136			    bfd_vma pc ATTRIBUTE_UNUSED,
137			    int length ATTRIBUTE_UNUSED)
138{
139  print_register_list (dis_info, value, 0, 1 /* Store.  */);
140}
141
142static void
143print_m4 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
144	  void * dis_info,
145	  long value,
146	  unsigned int attrs ATTRIBUTE_UNUSED,
147	  bfd_vma pc ATTRIBUTE_UNUSED,
148	  int length ATTRIBUTE_UNUSED)
149{
150  disassemble_info *info = (disassemble_info *) dis_info;
151
152  (*info->fprintf_func) (info->stream, "%ld", value);
153}
154/* -- */
155
156void fr30_cgen_print_operand
157  (CGEN_CPU_DESC, int, PTR, CGEN_FIELDS *, void const *, bfd_vma, int);
158
159/* Main entry point for printing operands.
160   XINFO is a `void *' and not a `disassemble_info *' to not put a requirement
161   of dis-asm.h on cgen.h.
162
163   This function is basically just a big switch statement.  Earlier versions
164   used tables to look up the function to use, but
165   - if the table contains both assembler and disassembler functions then
166     the disassembler contains much of the assembler and vice-versa,
167   - there's a lot of inlining possibilities as things grow,
168   - using a switch statement avoids the function call overhead.
169
170   This function could be moved into `print_insn_normal', but keeping it
171   separate makes clear the interface between `print_insn_normal' and each of
172   the handlers.  */
173
174void
175fr30_cgen_print_operand (CGEN_CPU_DESC cd,
176			   int opindex,
177			   void * xinfo,
178			   CGEN_FIELDS *fields,
179			   void const *attrs ATTRIBUTE_UNUSED,
180			   bfd_vma pc,
181			   int length)
182{
183  disassemble_info *info = (disassemble_info *) xinfo;
184
185  switch (opindex)
186    {
187    case FR30_OPERAND_CRI :
188      print_keyword (cd, info, & fr30_cgen_opval_cr_names, fields->f_CRi, 0);
189      break;
190    case FR30_OPERAND_CRJ :
191      print_keyword (cd, info, & fr30_cgen_opval_cr_names, fields->f_CRj, 0);
192      break;
193    case FR30_OPERAND_R13 :
194      print_keyword (cd, info, & fr30_cgen_opval_h_r13, 0, 0);
195      break;
196    case FR30_OPERAND_R14 :
197      print_keyword (cd, info, & fr30_cgen_opval_h_r14, 0, 0);
198      break;
199    case FR30_OPERAND_R15 :
200      print_keyword (cd, info, & fr30_cgen_opval_h_r15, 0, 0);
201      break;
202    case FR30_OPERAND_RI :
203      print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Ri, 0);
204      break;
205    case FR30_OPERAND_RIC :
206      print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Ric, 0);
207      break;
208    case FR30_OPERAND_RJ :
209      print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Rj, 0);
210      break;
211    case FR30_OPERAND_RJC :
212      print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Rjc, 0);
213      break;
214    case FR30_OPERAND_RS1 :
215      print_keyword (cd, info, & fr30_cgen_opval_dr_names, fields->f_Rs1, 0);
216      break;
217    case FR30_OPERAND_RS2 :
218      print_keyword (cd, info, & fr30_cgen_opval_dr_names, fields->f_Rs2, 0);
219      break;
220    case FR30_OPERAND_CC :
221      print_normal (cd, info, fields->f_cc, 0, pc, length);
222      break;
223    case FR30_OPERAND_CCC :
224      print_normal (cd, info, fields->f_ccc, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
225      break;
226    case FR30_OPERAND_DIR10 :
227      print_normal (cd, info, fields->f_dir10, 0, pc, length);
228      break;
229    case FR30_OPERAND_DIR8 :
230      print_normal (cd, info, fields->f_dir8, 0, pc, length);
231      break;
232    case FR30_OPERAND_DIR9 :
233      print_normal (cd, info, fields->f_dir9, 0, pc, length);
234      break;
235    case FR30_OPERAND_DISP10 :
236      print_normal (cd, info, fields->f_disp10, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
237      break;
238    case FR30_OPERAND_DISP8 :
239      print_normal (cd, info, fields->f_disp8, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
240      break;
241    case FR30_OPERAND_DISP9 :
242      print_normal (cd, info, fields->f_disp9, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
243      break;
244    case FR30_OPERAND_I20 :
245      print_normal (cd, info, fields->f_i20, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_VIRTUAL), pc, length);
246      break;
247    case FR30_OPERAND_I32 :
248      print_normal (cd, info, fields->f_i32, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_SIGN_OPT), pc, length);
249      break;
250    case FR30_OPERAND_I8 :
251      print_normal (cd, info, fields->f_i8, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
252      break;
253    case FR30_OPERAND_LABEL12 :
254      print_address (cd, info, fields->f_rel12, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
255      break;
256    case FR30_OPERAND_LABEL9 :
257      print_address (cd, info, fields->f_rel9, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
258      break;
259    case FR30_OPERAND_M4 :
260      print_m4 (cd, info, fields->f_m4, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
261      break;
262    case FR30_OPERAND_PS :
263      print_keyword (cd, info, & fr30_cgen_opval_h_ps, 0, 0);
264      break;
265    case FR30_OPERAND_REGLIST_HI_LD :
266      print_hi_register_list_ld (cd, info, fields->f_reglist_hi_ld, 0, pc, length);
267      break;
268    case FR30_OPERAND_REGLIST_HI_ST :
269      print_hi_register_list_st (cd, info, fields->f_reglist_hi_st, 0, pc, length);
270      break;
271    case FR30_OPERAND_REGLIST_LOW_LD :
272      print_low_register_list_ld (cd, info, fields->f_reglist_low_ld, 0, pc, length);
273      break;
274    case FR30_OPERAND_REGLIST_LOW_ST :
275      print_low_register_list_st (cd, info, fields->f_reglist_low_st, 0, pc, length);
276      break;
277    case FR30_OPERAND_S10 :
278      print_normal (cd, info, fields->f_s10, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
279      break;
280    case FR30_OPERAND_U10 :
281      print_normal (cd, info, fields->f_u10, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
282      break;
283    case FR30_OPERAND_U4 :
284      print_normal (cd, info, fields->f_u4, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
285      break;
286    case FR30_OPERAND_U4C :
287      print_normal (cd, info, fields->f_u4c, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
288      break;
289    case FR30_OPERAND_U8 :
290      print_normal (cd, info, fields->f_u8, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
291      break;
292    case FR30_OPERAND_UDISP6 :
293      print_normal (cd, info, fields->f_udisp6, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
294      break;
295
296    default :
297      /* xgettext:c-format */
298      fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
299	       opindex);
300    abort ();
301  }
302}
303
304cgen_print_fn * const fr30_cgen_print_handlers[] =
305{
306  print_insn_normal,
307};
308
309
310void
311fr30_cgen_init_dis (CGEN_CPU_DESC cd)
312{
313  fr30_cgen_init_opcode_table (cd);
314  fr30_cgen_init_ibld_table (cd);
315  cd->print_handlers = & fr30_cgen_print_handlers[0];
316  cd->print_operand = fr30_cgen_print_operand;
317}
318
319
320/* Default print handler.  */
321
322static void
323print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
324	      void *dis_info,
325	      long value,
326	      unsigned int attrs,
327	      bfd_vma pc ATTRIBUTE_UNUSED,
328	      int length ATTRIBUTE_UNUSED)
329{
330  disassemble_info *info = (disassemble_info *) dis_info;
331
332  /* Print the operand as directed by the attributes.  */
333  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
334    ; /* nothing to do */
335  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
336    (*info->fprintf_func) (info->stream, "%ld", value);
337  else
338    (*info->fprintf_func) (info->stream, "0x%lx", value);
339}
340
341/* Default address handler.  */
342
343static void
344print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
345	       void *dis_info,
346	       bfd_vma value,
347	       unsigned int attrs,
348	       bfd_vma pc ATTRIBUTE_UNUSED,
349	       int length ATTRIBUTE_UNUSED)
350{
351  disassemble_info *info = (disassemble_info *) dis_info;
352
353  /* Print the operand as directed by the attributes.  */
354  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
355    ; /* Nothing to do.  */
356  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
357    (*info->print_address_func) (value, info);
358  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
359    (*info->print_address_func) (value, info);
360  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
361    (*info->fprintf_func) (info->stream, "%ld", (long) value);
362  else
363    (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
364}
365
366/* Keyword print handler.  */
367
368static void
369print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
370	       void *dis_info,
371	       CGEN_KEYWORD *keyword_table,
372	       long value,
373	       unsigned int attrs ATTRIBUTE_UNUSED)
374{
375  disassemble_info *info = (disassemble_info *) dis_info;
376  const CGEN_KEYWORD_ENTRY *ke;
377
378  ke = cgen_keyword_lookup_value (keyword_table, value);
379  if (ke != NULL)
380    (*info->fprintf_func) (info->stream, "%s", ke->name);
381  else
382    (*info->fprintf_func) (info->stream, "???");
383}
384
385/* Default insn printer.
386
387   DIS_INFO is defined as `void *' so the disassembler needn't know anything
388   about disassemble_info.  */
389
390static void
391print_insn_normal (CGEN_CPU_DESC cd,
392		   void *dis_info,
393		   const CGEN_INSN *insn,
394		   CGEN_FIELDS *fields,
395		   bfd_vma pc,
396		   int length)
397{
398  const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
399  disassemble_info *info = (disassemble_info *) dis_info;
400  const CGEN_SYNTAX_CHAR_TYPE *syn;
401
402  CGEN_INIT_PRINT (cd);
403
404  for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
405    {
406      if (CGEN_SYNTAX_MNEMONIC_P (*syn))
407	{
408	  (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
409	  continue;
410	}
411      if (CGEN_SYNTAX_CHAR_P (*syn))
412	{
413	  (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
414	  continue;
415	}
416
417      /* We have an operand.  */
418      fr30_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
419				 fields, CGEN_INSN_ATTRS (insn), pc, length);
420    }
421}
422
423/* Subroutine of print_insn. Reads an insn into the given buffers and updates
424   the extract info.
425   Returns 0 if all is well, non-zero otherwise.  */
426
427static int
428read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
429	   bfd_vma pc,
430	   disassemble_info *info,
431	   bfd_byte *buf,
432	   int buflen,
433	   CGEN_EXTRACT_INFO *ex_info,
434	   unsigned long *insn_value)
435{
436  int status = (*info->read_memory_func) (pc, buf, buflen, info);
437
438  if (status != 0)
439    {
440      (*info->memory_error_func) (status, pc, info);
441      return -1;
442    }
443
444  ex_info->dis_info = info;
445  ex_info->valid = (1 << buflen) - 1;
446  ex_info->insn_bytes = buf;
447
448  *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
449  return 0;
450}
451
452/* Utility to print an insn.
453   BUF is the base part of the insn, target byte order, BUFLEN bytes long.
454   The result is the size of the insn in bytes or zero for an unknown insn
455   or -1 if an error occurs fetching data (memory_error_func will have
456   been called).  */
457
458static int
459print_insn (CGEN_CPU_DESC cd,
460	    bfd_vma pc,
461	    disassemble_info *info,
462	    bfd_byte *buf,
463	    unsigned int buflen)
464{
465  CGEN_INSN_INT insn_value;
466  const CGEN_INSN_LIST *insn_list;
467  CGEN_EXTRACT_INFO ex_info;
468  int basesize;
469
470  /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
471  basesize = cd->base_insn_bitsize < buflen * 8 ?
472                                     cd->base_insn_bitsize : buflen * 8;
473  insn_value = cgen_get_insn_value (cd, buf, basesize);
474
475
476  /* Fill in ex_info fields like read_insn would.  Don't actually call
477     read_insn, since the incoming buffer is already read (and possibly
478     modified a la m32r).  */
479  ex_info.valid = (1 << buflen) - 1;
480  ex_info.dis_info = info;
481  ex_info.insn_bytes = buf;
482
483  /* The instructions are stored in hash lists.
484     Pick the first one and keep trying until we find the right one.  */
485
486  insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value);
487  while (insn_list != NULL)
488    {
489      const CGEN_INSN *insn = insn_list->insn;
490      CGEN_FIELDS fields;
491      int length;
492      unsigned long insn_value_cropped;
493
494#ifdef CGEN_VALIDATE_INSN_SUPPORTED
495      /* Not needed as insn shouldn't be in hash lists if not supported.  */
496      /* Supported by this cpu?  */
497      if (! fr30_cgen_insn_supported (cd, insn))
498        {
499          insn_list = CGEN_DIS_NEXT_INSN (insn_list);
500	  continue;
501        }
502#endif
503
504      /* Basic bit mask must be correct.  */
505      /* ??? May wish to allow target to defer this check until the extract
506	 handler.  */
507
508      /* Base size may exceed this instruction's size.  Extract the
509         relevant part from the buffer. */
510      if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
511	  (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
512	insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn),
513					   info->endian == BFD_ENDIAN_BIG);
514      else
515	insn_value_cropped = insn_value;
516
517      if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
518	  == CGEN_INSN_BASE_VALUE (insn))
519	{
520	  /* Printing is handled in two passes.  The first pass parses the
521	     machine insn and extracts the fields.  The second pass prints
522	     them.  */
523
524	  /* Make sure the entire insn is loaded into insn_value, if it
525	     can fit.  */
526	  if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
527	      (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
528	    {
529	      unsigned long full_insn_value;
530	      int rc = read_insn (cd, pc, info, buf,
531				  CGEN_INSN_BITSIZE (insn) / 8,
532				  & ex_info, & full_insn_value);
533	      if (rc != 0)
534		return rc;
535	      length = CGEN_EXTRACT_FN (cd, insn)
536		(cd, insn, &ex_info, full_insn_value, &fields, pc);
537	    }
538	  else
539	    length = CGEN_EXTRACT_FN (cd, insn)
540	      (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
541
542	  /* Length < 0 -> error.  */
543	  if (length < 0)
544	    return length;
545	  if (length > 0)
546	    {
547	      CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
548	      /* Length is in bits, result is in bytes.  */
549	      return length / 8;
550	    }
551	}
552
553      insn_list = CGEN_DIS_NEXT_INSN (insn_list);
554    }
555
556  return 0;
557}
558
559/* Default value for CGEN_PRINT_INSN.
560   The result is the size of the insn in bytes or zero for an unknown insn
561   or -1 if an error occured fetching bytes.  */
562
563#ifndef CGEN_PRINT_INSN
564#define CGEN_PRINT_INSN default_print_insn
565#endif
566
567static int
568default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
569{
570  bfd_byte buf[CGEN_MAX_INSN_SIZE];
571  int buflen;
572  int status;
573
574  /* Attempt to read the base part of the insn.  */
575  buflen = cd->base_insn_bitsize / 8;
576  status = (*info->read_memory_func) (pc, buf, buflen, info);
577
578  /* Try again with the minimum part, if min < base.  */
579  if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
580    {
581      buflen = cd->min_insn_bitsize / 8;
582      status = (*info->read_memory_func) (pc, buf, buflen, info);
583    }
584
585  if (status != 0)
586    {
587      (*info->memory_error_func) (status, pc, info);
588      return -1;
589    }
590
591  return print_insn (cd, pc, info, buf, buflen);
592}
593
594/* Main entry point.
595   Print one instruction from PC on INFO->STREAM.
596   Return the size of the instruction (in bytes).  */
597
598typedef struct cpu_desc_list
599{
600  struct cpu_desc_list *next;
601  CGEN_BITSET *isa;
602  int mach;
603  int endian;
604  CGEN_CPU_DESC cd;
605} cpu_desc_list;
606
607int
608print_insn_fr30 (bfd_vma pc, disassemble_info *info)
609{
610  static cpu_desc_list *cd_list = 0;
611  cpu_desc_list *cl = 0;
612  static CGEN_CPU_DESC cd = 0;
613  static CGEN_BITSET *prev_isa;
614  static int prev_mach;
615  static int prev_endian;
616  int length;
617  CGEN_BITSET *isa;
618  int mach;
619  int endian = (info->endian == BFD_ENDIAN_BIG
620		? CGEN_ENDIAN_BIG
621		: CGEN_ENDIAN_LITTLE);
622  enum bfd_architecture arch;
623
624  /* ??? gdb will set mach but leave the architecture as "unknown" */
625#ifndef CGEN_BFD_ARCH
626#define CGEN_BFD_ARCH bfd_arch_fr30
627#endif
628  arch = info->arch;
629  if (arch == bfd_arch_unknown)
630    arch = CGEN_BFD_ARCH;
631
632  /* There's no standard way to compute the machine or isa number
633     so we leave it to the target.  */
634#ifdef CGEN_COMPUTE_MACH
635  mach = CGEN_COMPUTE_MACH (info);
636#else
637  mach = info->mach;
638#endif
639
640#ifdef CGEN_COMPUTE_ISA
641  {
642    static CGEN_BITSET *permanent_isa;
643
644    if (!permanent_isa)
645      permanent_isa = cgen_bitset_create (MAX_ISAS);
646    isa = permanent_isa;
647    cgen_bitset_clear (isa);
648    cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info));
649  }
650#else
651  isa = info->insn_sets;
652#endif
653
654  /* If we've switched cpu's, try to find a handle we've used before */
655  if (cd
656      && (cgen_bitset_compare (isa, prev_isa) != 0
657	  || mach != prev_mach
658	  || endian != prev_endian))
659    {
660      cd = 0;
661      for (cl = cd_list; cl; cl = cl->next)
662	{
663	  if (cgen_bitset_compare (cl->isa, isa) == 0 &&
664	      cl->mach == mach &&
665	      cl->endian == endian)
666	    {
667	      cd = cl->cd;
668 	      prev_isa = cd->isas;
669	      break;
670	    }
671	}
672    }
673
674  /* If we haven't initialized yet, initialize the opcode table.  */
675  if (! cd)
676    {
677      const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
678      const char *mach_name;
679
680      if (!arch_type)
681	abort ();
682      mach_name = arch_type->printable_name;
683
684      prev_isa = cgen_bitset_copy (isa);
685      prev_mach = mach;
686      prev_endian = endian;
687      cd = fr30_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
688				 CGEN_CPU_OPEN_BFDMACH, mach_name,
689				 CGEN_CPU_OPEN_ENDIAN, prev_endian,
690				 CGEN_CPU_OPEN_END);
691      if (!cd)
692	abort ();
693
694      /* Save this away for future reference.  */
695      cl = xmalloc (sizeof (struct cpu_desc_list));
696      cl->cd = cd;
697      cl->isa = prev_isa;
698      cl->mach = mach;
699      cl->endian = endian;
700      cl->next = cd_list;
701      cd_list = cl;
702
703      fr30_cgen_init_dis (cd);
704    }
705
706  /* We try to have as much common code as possible.
707     But at this point some targets need to take over.  */
708  /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
709     but if not possible try to move this hook elsewhere rather than
710     have two hooks.  */
711  length = CGEN_PRINT_INSN (cd, pc, info);
712  if (length > 0)
713    return length;
714  if (length < 0)
715    return -1;
716
717  (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
718  return cd->default_insn_bitsize / 8;
719}
720