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