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 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005
8   Free Software Foundation, Inc.
9
10   This file is part of the GNU Binutils and GDB, the GNU debugger.
11
12   This program is free software; you can redistribute it and/or modify
13   it under the terms of the GNU General Public License as published by
14   the Free Software Foundation; either version 2, or (at your option)
15   any later version.
16
17   This program is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20   GNU General Public License for more details.
21
22   You should have received a copy of the GNU General Public License
23   along with this program; if not, write to the Free Software Foundation, Inc.,
24   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
25
26/* ??? Eventually more and more of this stuff can go to cpu-independent files.
27   Keep that in mind.  */
28
29#include "sysdep.h"
30#include <stdio.h>
31#include "ansidecl.h"
32#include "dis-asm.h"
33#include "bfd.h"
34#include "symcat.h"
35#include "libiberty.h"
36#include "mt-desc.h"
37#include "mt-opc.h"
38#include "opintl.h"
39
40/* Default text to print if an instruction isn't recognized.  */
41#define UNKNOWN_INSN_MSG _("*unknown*")
42
43static void print_normal
44  (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
45static void print_address
46  (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED;
47static void print_keyword
48  (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED;
49static void print_insn_normal
50  (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
51static int print_insn
52  (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, bfd_byte *, unsigned);
53static int default_print_insn
54  (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED;
55static int read_insn
56  (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *,
57   unsigned long *);
58
59/* -- disassembler routines inserted here.  */
60
61/* -- dis.c */
62static void print_dollarhex (CGEN_CPU_DESC, PTR, long, unsigned, bfd_vma, int);
63static void print_pcrel (CGEN_CPU_DESC, PTR, long, unsigned, bfd_vma, int);
64
65static void
66print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
67		 void * dis_info,
68		 long value,
69		 unsigned int attrs ATTRIBUTE_UNUSED,
70		 bfd_vma pc ATTRIBUTE_UNUSED,
71		 int length ATTRIBUTE_UNUSED)
72{
73  disassemble_info *info = (disassemble_info *) dis_info;
74
75  info->fprintf_func (info->stream, "$%lx", value);
76
77  if (0)
78    print_normal (cd, dis_info, value, attrs, pc, length);
79}
80
81static void
82print_pcrel (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
83	     void * dis_info,
84	     long value,
85	     unsigned int attrs ATTRIBUTE_UNUSED,
86	     bfd_vma pc ATTRIBUTE_UNUSED,
87	     int length ATTRIBUTE_UNUSED)
88{
89  print_address (cd, dis_info, value + pc, attrs, pc, length);
90}
91
92/* -- */
93
94void mt_cgen_print_operand
95  (CGEN_CPU_DESC, int, PTR, CGEN_FIELDS *, void const *, bfd_vma, int);
96
97/* Main entry point for printing operands.
98   XINFO is a `void *' and not a `disassemble_info *' to not put a requirement
99   of dis-asm.h on cgen.h.
100
101   This function is basically just a big switch statement.  Earlier versions
102   used tables to look up the function to use, but
103   - if the table contains both assembler and disassembler functions then
104     the disassembler contains much of the assembler and vice-versa,
105   - there's a lot of inlining possibilities as things grow,
106   - using a switch statement avoids the function call overhead.
107
108   This function could be moved into `print_insn_normal', but keeping it
109   separate makes clear the interface between `print_insn_normal' and each of
110   the handlers.  */
111
112void
113mt_cgen_print_operand (CGEN_CPU_DESC cd,
114			   int opindex,
115			   void * xinfo,
116			   CGEN_FIELDS *fields,
117			   void const *attrs ATTRIBUTE_UNUSED,
118			   bfd_vma pc,
119			   int length)
120{
121  disassemble_info *info = (disassemble_info *) xinfo;
122
123  switch (opindex)
124    {
125    case MT_OPERAND_A23 :
126      print_dollarhex (cd, info, fields->f_a23, 0, pc, length);
127      break;
128    case MT_OPERAND_BALL :
129      print_dollarhex (cd, info, fields->f_ball, 0, pc, length);
130      break;
131    case MT_OPERAND_BALL2 :
132      print_dollarhex (cd, info, fields->f_ball2, 0, pc, length);
133      break;
134    case MT_OPERAND_BANKADDR :
135      print_dollarhex (cd, info, fields->f_bankaddr, 0, pc, length);
136      break;
137    case MT_OPERAND_BRC :
138      print_dollarhex (cd, info, fields->f_brc, 0, pc, length);
139      break;
140    case MT_OPERAND_BRC2 :
141      print_dollarhex (cd, info, fields->f_brc2, 0, pc, length);
142      break;
143    case MT_OPERAND_CB1INCR :
144      print_dollarhex (cd, info, fields->f_cb1incr, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
145      break;
146    case MT_OPERAND_CB1SEL :
147      print_dollarhex (cd, info, fields->f_cb1sel, 0, pc, length);
148      break;
149    case MT_OPERAND_CB2INCR :
150      print_dollarhex (cd, info, fields->f_cb2incr, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
151      break;
152    case MT_OPERAND_CB2SEL :
153      print_dollarhex (cd, info, fields->f_cb2sel, 0, pc, length);
154      break;
155    case MT_OPERAND_CBRB :
156      print_dollarhex (cd, info, fields->f_cbrb, 0, pc, length);
157      break;
158    case MT_OPERAND_CBS :
159      print_dollarhex (cd, info, fields->f_cbs, 0, pc, length);
160      break;
161    case MT_OPERAND_CBX :
162      print_dollarhex (cd, info, fields->f_cbx, 0, pc, length);
163      break;
164    case MT_OPERAND_CCB :
165      print_dollarhex (cd, info, fields->f_ccb, 0, pc, length);
166      break;
167    case MT_OPERAND_CDB :
168      print_dollarhex (cd, info, fields->f_cdb, 0, pc, length);
169      break;
170    case MT_OPERAND_CELL :
171      print_dollarhex (cd, info, fields->f_cell, 0, pc, length);
172      break;
173    case MT_OPERAND_COLNUM :
174      print_dollarhex (cd, info, fields->f_colnum, 0, pc, length);
175      break;
176    case MT_OPERAND_CONTNUM :
177      print_dollarhex (cd, info, fields->f_contnum, 0, pc, length);
178      break;
179    case MT_OPERAND_CR :
180      print_dollarhex (cd, info, fields->f_cr, 0, pc, length);
181      break;
182    case MT_OPERAND_CTXDISP :
183      print_dollarhex (cd, info, fields->f_ctxdisp, 0, pc, length);
184      break;
185    case MT_OPERAND_DUP :
186      print_dollarhex (cd, info, fields->f_dup, 0, pc, length);
187      break;
188    case MT_OPERAND_FBDISP :
189      print_dollarhex (cd, info, fields->f_fbdisp, 0, pc, length);
190      break;
191    case MT_OPERAND_FBINCR :
192      print_dollarhex (cd, info, fields->f_fbincr, 0, pc, length);
193      break;
194    case MT_OPERAND_FRDR :
195      print_keyword (cd, info, & mt_cgen_opval_h_spr, fields->f_dr, 0|(1<<CGEN_OPERAND_ABS_ADDR));
196      break;
197    case MT_OPERAND_FRDRRR :
198      print_keyword (cd, info, & mt_cgen_opval_h_spr, fields->f_drrr, 0|(1<<CGEN_OPERAND_ABS_ADDR));
199      break;
200    case MT_OPERAND_FRSR1 :
201      print_keyword (cd, info, & mt_cgen_opval_h_spr, fields->f_sr1, 0|(1<<CGEN_OPERAND_ABS_ADDR));
202      break;
203    case MT_OPERAND_FRSR2 :
204      print_keyword (cd, info, & mt_cgen_opval_h_spr, fields->f_sr2, 0|(1<<CGEN_OPERAND_ABS_ADDR));
205      break;
206    case MT_OPERAND_ID :
207      print_dollarhex (cd, info, fields->f_id, 0, pc, length);
208      break;
209    case MT_OPERAND_IMM16 :
210      print_dollarhex (cd, info, fields->f_imm16s, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
211      break;
212    case MT_OPERAND_IMM16L :
213      print_dollarhex (cd, info, fields->f_imm16l, 0, pc, length);
214      break;
215    case MT_OPERAND_IMM16O :
216      print_pcrel (cd, info, fields->f_imm16s, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
217      break;
218    case MT_OPERAND_IMM16Z :
219      print_dollarhex (cd, info, fields->f_imm16u, 0, pc, length);
220      break;
221    case MT_OPERAND_INCAMT :
222      print_dollarhex (cd, info, fields->f_incamt, 0, pc, length);
223      break;
224    case MT_OPERAND_INCR :
225      print_dollarhex (cd, info, fields->f_incr, 0, pc, length);
226      break;
227    case MT_OPERAND_LENGTH :
228      print_dollarhex (cd, info, fields->f_length, 0, pc, length);
229      break;
230    case MT_OPERAND_LOOPSIZE :
231      print_pcrel (cd, info, fields->f_loopo, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
232      break;
233    case MT_OPERAND_MASK :
234      print_dollarhex (cd, info, fields->f_mask, 0, pc, length);
235      break;
236    case MT_OPERAND_MASK1 :
237      print_dollarhex (cd, info, fields->f_mask1, 0, pc, length);
238      break;
239    case MT_OPERAND_MODE :
240      print_dollarhex (cd, info, fields->f_mode, 0, pc, length);
241      break;
242    case MT_OPERAND_PERM :
243      print_dollarhex (cd, info, fields->f_perm, 0, pc, length);
244      break;
245    case MT_OPERAND_RBBC :
246      print_dollarhex (cd, info, fields->f_rbbc, 0, pc, length);
247      break;
248    case MT_OPERAND_RC :
249      print_dollarhex (cd, info, fields->f_rc, 0, pc, length);
250      break;
251    case MT_OPERAND_RC1 :
252      print_dollarhex (cd, info, fields->f_rc1, 0, pc, length);
253      break;
254    case MT_OPERAND_RC2 :
255      print_dollarhex (cd, info, fields->f_rc2, 0, pc, length);
256      break;
257    case MT_OPERAND_RC3 :
258      print_dollarhex (cd, info, fields->f_rc3, 0, pc, length);
259      break;
260    case MT_OPERAND_RCNUM :
261      print_dollarhex (cd, info, fields->f_rcnum, 0, pc, length);
262      break;
263    case MT_OPERAND_RDA :
264      print_dollarhex (cd, info, fields->f_rda, 0, pc, length);
265      break;
266    case MT_OPERAND_ROWNUM :
267      print_dollarhex (cd, info, fields->f_rownum, 0, pc, length);
268      break;
269    case MT_OPERAND_ROWNUM1 :
270      print_dollarhex (cd, info, fields->f_rownum1, 0, pc, length);
271      break;
272    case MT_OPERAND_ROWNUM2 :
273      print_dollarhex (cd, info, fields->f_rownum2, 0, pc, length);
274      break;
275    case MT_OPERAND_SIZE :
276      print_dollarhex (cd, info, fields->f_size, 0, pc, length);
277      break;
278    case MT_OPERAND_TYPE :
279      print_dollarhex (cd, info, fields->f_type, 0, pc, length);
280      break;
281    case MT_OPERAND_WR :
282      print_dollarhex (cd, info, fields->f_wr, 0, pc, length);
283      break;
284    case MT_OPERAND_XMODE :
285      print_dollarhex (cd, info, fields->f_xmode, 0, pc, length);
286      break;
287
288    default :
289      /* xgettext:c-format */
290      fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
291	       opindex);
292    abort ();
293  }
294}
295
296cgen_print_fn * const mt_cgen_print_handlers[] =
297{
298  print_insn_normal,
299};
300
301
302void
303mt_cgen_init_dis (CGEN_CPU_DESC cd)
304{
305  mt_cgen_init_opcode_table (cd);
306  mt_cgen_init_ibld_table (cd);
307  cd->print_handlers = & mt_cgen_print_handlers[0];
308  cd->print_operand = mt_cgen_print_operand;
309}
310
311
312/* Default print handler.  */
313
314static void
315print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
316	      void *dis_info,
317	      long value,
318	      unsigned int attrs,
319	      bfd_vma pc ATTRIBUTE_UNUSED,
320	      int length ATTRIBUTE_UNUSED)
321{
322  disassemble_info *info = (disassemble_info *) dis_info;
323
324#ifdef CGEN_PRINT_NORMAL
325  CGEN_PRINT_NORMAL (cd, info, value, attrs, pc, length);
326#endif
327
328  /* Print the operand as directed by the attributes.  */
329  if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
330    ; /* nothing to do */
331  else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
332    (*info->fprintf_func) (info->stream, "%ld", value);
333  else
334    (*info->fprintf_func) (info->stream, "0x%lx", value);
335}
336
337/* Default address handler.  */
338
339static void
340print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
341	       void *dis_info,
342	       bfd_vma value,
343	       unsigned int attrs,
344	       bfd_vma pc ATTRIBUTE_UNUSED,
345	       int length ATTRIBUTE_UNUSED)
346{
347  disassemble_info *info = (disassemble_info *) dis_info;
348
349#ifdef CGEN_PRINT_ADDRESS
350  CGEN_PRINT_ADDRESS (cd, info, value, attrs, pc, length);
351#endif
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      mt_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 (! mt_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_mt (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_mt
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 = mt_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      mt_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