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