Back to index

cell-binutils  2.17cvs20070401
ip2k-dis.c
Go to the documentation of this file.
00001 /* Disassembler interface for targets using CGEN. -*- C -*-
00002    CGEN: Cpu tools GENerator
00003 
00004    THIS FILE IS MACHINE GENERATED WITH CGEN.
00005    - the resultant file is machine generated, cgen-dis.in isn't
00006 
00007    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005
00008    Free Software Foundation, Inc.
00009 
00010    This file is part of the GNU Binutils and GDB, the GNU debugger.
00011 
00012    This program is free software; you can redistribute it and/or modify
00013    it under the terms of the GNU General Public License as published by
00014    the Free Software Foundation; either version 2, or (at your option)
00015    any later version.
00016 
00017    This program is distributed in the hope that it will be useful,
00018    but WITHOUT ANY WARRANTY; without even the implied warranty of
00019    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020    GNU General Public License for more details.
00021 
00022    You should have received a copy of the GNU General Public License
00023    along with this program; if not, write to the Free Software Foundation, Inc.,
00024    51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
00025 
00026 /* ??? Eventually more and more of this stuff can go to cpu-independent files.
00027    Keep that in mind.  */
00028 
00029 #include "sysdep.h"
00030 #include <stdio.h>
00031 #include "ansidecl.h"
00032 #include "dis-asm.h"
00033 #include "bfd.h"
00034 #include "symcat.h"
00035 #include "libiberty.h"
00036 #include "ip2k-desc.h"
00037 #include "ip2k-opc.h"
00038 #include "opintl.h"
00039 
00040 /* Default text to print if an instruction isn't recognized.  */
00041 #define UNKNOWN_INSN_MSG _("*unknown*")
00042 
00043 static void print_normal
00044   (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
00045 static void print_address
00046   (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED;
00047 static void print_keyword
00048   (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED;
00049 static void print_insn_normal
00050   (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
00051 static int print_insn
00052   (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, bfd_byte *, unsigned);
00053 static int default_print_insn
00054   (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED;
00055 static int read_insn
00056   (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *,
00057    unsigned long *);
00058 
00059 /* -- disassembler routines inserted here.  */
00060 
00061 /* -- dis.c */
00062 
00063 static void
00064 print_fr (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00065          void * dis_info,
00066          long value,
00067          unsigned int attrs ATTRIBUTE_UNUSED,
00068          bfd_vma pc ATTRIBUTE_UNUSED,
00069          int length ATTRIBUTE_UNUSED)
00070 {
00071   disassemble_info *info = (disassemble_info *) dis_info;
00072   const CGEN_KEYWORD_ENTRY *ke;
00073   extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
00074   long offsettest;
00075   long offsetvalue;
00076 
00077   if (value == 0) /* This is (IP).  */
00078     {
00079       (*info->fprintf_func) (info->stream, "%s", "(IP)");
00080       return;
00081     }
00082 
00083   offsettest = value >> 7;
00084   offsetvalue = value & 0x7F;
00085 
00086   /* Check to see if first two bits are 10 -> (DP).  */
00087   if (offsettest == 2)
00088     {
00089       if (offsetvalue == 0)
00090        (*info->fprintf_func) (info->stream, "%s","(DP)");
00091       else
00092        (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue, "(DP)");
00093       return;
00094     }
00095 
00096   /* Check to see if first two bits are 11 -> (SP).  */
00097   if (offsettest == 3)
00098     {
00099       if (offsetvalue == 0)
00100        (*info->fprintf_func) (info->stream, "%s", "(SP)");
00101       else
00102        (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue,"(SP)");
00103       return;
00104     }
00105 
00106   /* Attempt to print as a register keyword.  */
00107   ke = cgen_keyword_lookup_value (& ip2k_cgen_opval_register_names, value);
00108 
00109   if (ke != NULL)
00110     (*info->fprintf_func) (info->stream, "%s", ke->name);
00111   else
00112     /* Print as an address literal.  */
00113     (*info->fprintf_func) (info->stream, "$%02lx", value);
00114 }
00115 
00116 static void
00117 print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00118                void * dis_info,
00119                long value,
00120                unsigned int attrs ATTRIBUTE_UNUSED,
00121                bfd_vma pc ATTRIBUTE_UNUSED,
00122                int length ATTRIBUTE_UNUSED)
00123 {
00124   disassemble_info *info = (disassemble_info *) dis_info;
00125 
00126   (*info->fprintf_func) (info->stream, "$%lx", value);
00127 }
00128 
00129 static void
00130 print_dollarhex8 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00131                 void * dis_info,
00132                 long value,
00133                 unsigned int attrs ATTRIBUTE_UNUSED,
00134                 bfd_vma pc ATTRIBUTE_UNUSED,
00135                 int length ATTRIBUTE_UNUSED)
00136 {
00137   disassemble_info *info = (disassemble_info *) dis_info;
00138 
00139   (*info->fprintf_func) (info->stream, "$%02lx", value);
00140 }
00141 
00142 static void
00143 print_dollarhex_addr16h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00144                       void * dis_info,
00145                       long value,
00146                       unsigned int attrs ATTRIBUTE_UNUSED,
00147                       bfd_vma pc ATTRIBUTE_UNUSED,
00148                       int length ATTRIBUTE_UNUSED)
00149 {
00150   disassemble_info *info = (disassemble_info *) dis_info;
00151 
00152   /* This is a loadh instruction. Shift the value to the left
00153      by 8 bits so that disassembled code will reassemble properly.  */
00154   value = ((value << 8) & 0xFF00);
00155 
00156   (*info->fprintf_func) (info->stream, "$%04lx", value);
00157 }
00158 
00159 static void
00160 print_dollarhex_addr16l (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00161                       void * dis_info,
00162                       long value,
00163                       unsigned int attrs ATTRIBUTE_UNUSED,
00164                       bfd_vma pc ATTRIBUTE_UNUSED,
00165                       int length ATTRIBUTE_UNUSED)
00166 {
00167   disassemble_info *info = (disassemble_info *) dis_info;
00168 
00169   (*info->fprintf_func) (info->stream, "$%04lx", value);
00170 }
00171 
00172 static void
00173 print_dollarhex_p (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00174                  void * dis_info,
00175                  long value,
00176                  unsigned int attrs ATTRIBUTE_UNUSED,
00177                  bfd_vma pc ATTRIBUTE_UNUSED,
00178                  int length ATTRIBUTE_UNUSED)
00179 {
00180   disassemble_info *info = (disassemble_info *) dis_info;
00181 
00182   value = ((value << 14) & 0x1C000);
00183   ;value = (value  & 0x1FFFF);
00184   (*info->fprintf_func) (info->stream, "$%05lx", value);
00185 }
00186 
00187 static void
00188 print_dollarhex_cj (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00189                   void * dis_info,
00190                   long value,
00191                   unsigned int attrs ATTRIBUTE_UNUSED,
00192                   bfd_vma pc ATTRIBUTE_UNUSED,
00193                   int length ATTRIBUTE_UNUSED)
00194 {
00195   disassemble_info *info = (disassemble_info *) dis_info;
00196 
00197   value = ((value << 1) & 0x1FFFF);
00198   (*info->fprintf_func) (info->stream, "$%05lx", value);
00199 }
00200 
00201 static void
00202 print_decimal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00203               void * dis_info,
00204               long value,
00205               unsigned int attrs ATTRIBUTE_UNUSED,
00206               bfd_vma pc ATTRIBUTE_UNUSED,
00207               int length ATTRIBUTE_UNUSED)
00208 {
00209   disassemble_info *info = (disassemble_info *) dis_info;
00210 
00211   (*info->fprintf_func) (info->stream, "%ld", value);
00212 }
00213 
00214 
00215 
00216 /* -- */
00217 
00218 void ip2k_cgen_print_operand
00219   (CGEN_CPU_DESC, int, PTR, CGEN_FIELDS *, void const *, bfd_vma, int);
00220 
00221 /* Main entry point for printing operands.
00222    XINFO is a `void *' and not a `disassemble_info *' to not put a requirement
00223    of dis-asm.h on cgen.h.
00224 
00225    This function is basically just a big switch statement.  Earlier versions
00226    used tables to look up the function to use, but
00227    - if the table contains both assembler and disassembler functions then
00228      the disassembler contains much of the assembler and vice-versa,
00229    - there's a lot of inlining possibilities as things grow,
00230    - using a switch statement avoids the function call overhead.
00231 
00232    This function could be moved into `print_insn_normal', but keeping it
00233    separate makes clear the interface between `print_insn_normal' and each of
00234    the handlers.  */
00235 
00236 void
00237 ip2k_cgen_print_operand (CGEN_CPU_DESC cd,
00238                         int opindex,
00239                         void * xinfo,
00240                         CGEN_FIELDS *fields,
00241                         void const *attrs ATTRIBUTE_UNUSED,
00242                         bfd_vma pc,
00243                         int length)
00244 {
00245   disassemble_info *info = (disassemble_info *) xinfo;
00246 
00247   switch (opindex)
00248     {
00249     case IP2K_OPERAND_ADDR16CJP :
00250       print_dollarhex_cj (cd, info, fields->f_addr16cjp, 0|(1<<CGEN_OPERAND_ABS_ADDR), pc, length);
00251       break;
00252     case IP2K_OPERAND_ADDR16H :
00253       print_dollarhex_addr16h (cd, info, fields->f_imm8, 0, pc, length);
00254       break;
00255     case IP2K_OPERAND_ADDR16L :
00256       print_dollarhex_addr16l (cd, info, fields->f_imm8, 0, pc, length);
00257       break;
00258     case IP2K_OPERAND_ADDR16P :
00259       print_dollarhex_p (cd, info, fields->f_page3, 0, pc, length);
00260       break;
00261     case IP2K_OPERAND_BITNO :
00262       print_decimal (cd, info, fields->f_bitno, 0, pc, length);
00263       break;
00264     case IP2K_OPERAND_CBIT :
00265       print_normal (cd, info, 0, 0, pc, length);
00266       break;
00267     case IP2K_OPERAND_DCBIT :
00268       print_normal (cd, info, 0, 0, pc, length);
00269       break;
00270     case IP2K_OPERAND_FR :
00271       print_fr (cd, info, fields->f_reg, 0|(1<<CGEN_OPERAND_ABS_ADDR), pc, length);
00272       break;
00273     case IP2K_OPERAND_LIT8 :
00274       print_dollarhex8 (cd, info, fields->f_imm8, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
00275       break;
00276     case IP2K_OPERAND_PABITS :
00277       print_normal (cd, info, 0, 0, pc, length);
00278       break;
00279     case IP2K_OPERAND_RETI3 :
00280       print_dollarhex (cd, info, fields->f_reti3, 0, pc, length);
00281       break;
00282     case IP2K_OPERAND_ZBIT :
00283       print_normal (cd, info, 0, 0, pc, length);
00284       break;
00285 
00286     default :
00287       /* xgettext:c-format */
00288       fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
00289               opindex);
00290     abort ();
00291   }
00292 }
00293 
00294 cgen_print_fn * const ip2k_cgen_print_handlers[] = 
00295 {
00296   print_insn_normal,
00297 };
00298 
00299 
00300 void
00301 ip2k_cgen_init_dis (CGEN_CPU_DESC cd)
00302 {
00303   ip2k_cgen_init_opcode_table (cd);
00304   ip2k_cgen_init_ibld_table (cd);
00305   cd->print_handlers = & ip2k_cgen_print_handlers[0];
00306   cd->print_operand = ip2k_cgen_print_operand;
00307 }
00308 
00309 
00310 /* Default print handler.  */
00311 
00312 static void
00313 print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00314              void *dis_info,
00315              long value,
00316              unsigned int attrs,
00317              bfd_vma pc ATTRIBUTE_UNUSED,
00318              int length ATTRIBUTE_UNUSED)
00319 {
00320   disassemble_info *info = (disassemble_info *) dis_info;
00321 
00322 #ifdef CGEN_PRINT_NORMAL
00323   CGEN_PRINT_NORMAL (cd, info, value, attrs, pc, length);
00324 #endif
00325 
00326   /* Print the operand as directed by the attributes.  */
00327   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
00328     ; /* nothing to do */
00329   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
00330     (*info->fprintf_func) (info->stream, "%ld", value);
00331   else
00332     (*info->fprintf_func) (info->stream, "0x%lx", value);
00333 }
00334 
00335 /* Default address handler.  */
00336 
00337 static void
00338 print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00339               void *dis_info,
00340               bfd_vma value,
00341               unsigned int attrs,
00342               bfd_vma pc ATTRIBUTE_UNUSED,
00343               int length ATTRIBUTE_UNUSED)
00344 {
00345   disassemble_info *info = (disassemble_info *) dis_info;
00346 
00347 #ifdef CGEN_PRINT_ADDRESS
00348   CGEN_PRINT_ADDRESS (cd, info, value, attrs, pc, length);
00349 #endif
00350 
00351   /* Print the operand as directed by the attributes.  */
00352   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
00353     ; /* Nothing to do.  */
00354   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
00355     (*info->print_address_func) (value, info);
00356   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
00357     (*info->print_address_func) (value, info);
00358   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
00359     (*info->fprintf_func) (info->stream, "%ld", (long) value);
00360   else
00361     (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
00362 }
00363 
00364 /* Keyword print handler.  */
00365 
00366 static void
00367 print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00368               void *dis_info,
00369               CGEN_KEYWORD *keyword_table,
00370               long value,
00371               unsigned int attrs ATTRIBUTE_UNUSED)
00372 {
00373   disassemble_info *info = (disassemble_info *) dis_info;
00374   const CGEN_KEYWORD_ENTRY *ke;
00375 
00376   ke = cgen_keyword_lookup_value (keyword_table, value);
00377   if (ke != NULL)
00378     (*info->fprintf_func) (info->stream, "%s", ke->name);
00379   else
00380     (*info->fprintf_func) (info->stream, "???");
00381 }
00382 
00383 /* Default insn printer.
00384 
00385    DIS_INFO is defined as `void *' so the disassembler needn't know anything
00386    about disassemble_info.  */
00387 
00388 static void
00389 print_insn_normal (CGEN_CPU_DESC cd,
00390                  void *dis_info,
00391                  const CGEN_INSN *insn,
00392                  CGEN_FIELDS *fields,
00393                  bfd_vma pc,
00394                  int length)
00395 {
00396   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
00397   disassemble_info *info = (disassemble_info *) dis_info;
00398   const CGEN_SYNTAX_CHAR_TYPE *syn;
00399 
00400   CGEN_INIT_PRINT (cd);
00401 
00402   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
00403     {
00404       if (CGEN_SYNTAX_MNEMONIC_P (*syn))
00405        {
00406          (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
00407          continue;
00408        }
00409       if (CGEN_SYNTAX_CHAR_P (*syn))
00410        {
00411          (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
00412          continue;
00413        }
00414 
00415       /* We have an operand.  */
00416       ip2k_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
00417                              fields, CGEN_INSN_ATTRS (insn), pc, length);
00418     }
00419 }
00420 
00421 /* Subroutine of print_insn. Reads an insn into the given buffers and updates
00422    the extract info.
00423    Returns 0 if all is well, non-zero otherwise.  */
00424 
00425 static int
00426 read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00427           bfd_vma pc,
00428           disassemble_info *info,
00429           bfd_byte *buf,
00430           int buflen,
00431           CGEN_EXTRACT_INFO *ex_info,
00432           unsigned long *insn_value)
00433 {
00434   int status = (*info->read_memory_func) (pc, buf, buflen, info);
00435 
00436   if (status != 0)
00437     {
00438       (*info->memory_error_func) (status, pc, info);
00439       return -1;
00440     }
00441 
00442   ex_info->dis_info = info;
00443   ex_info->valid = (1 << buflen) - 1;
00444   ex_info->insn_bytes = buf;
00445 
00446   *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
00447   return 0;
00448 }
00449 
00450 /* Utility to print an insn.
00451    BUF is the base part of the insn, target byte order, BUFLEN bytes long.
00452    The result is the size of the insn in bytes or zero for an unknown insn
00453    or -1 if an error occurs fetching data (memory_error_func will have
00454    been called).  */
00455 
00456 static int
00457 print_insn (CGEN_CPU_DESC cd,
00458            bfd_vma pc,
00459            disassemble_info *info,
00460            bfd_byte *buf,
00461            unsigned int buflen)
00462 {
00463   CGEN_INSN_INT insn_value;
00464   const CGEN_INSN_LIST *insn_list;
00465   CGEN_EXTRACT_INFO ex_info;
00466   int basesize;
00467 
00468   /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
00469   basesize = cd->base_insn_bitsize < buflen * 8 ?
00470                                      cd->base_insn_bitsize : buflen * 8;
00471   insn_value = cgen_get_insn_value (cd, buf, basesize);
00472 
00473 
00474   /* Fill in ex_info fields like read_insn would.  Don't actually call
00475      read_insn, since the incoming buffer is already read (and possibly
00476      modified a la m32r).  */
00477   ex_info.valid = (1 << buflen) - 1;
00478   ex_info.dis_info = info;
00479   ex_info.insn_bytes = buf;
00480 
00481   /* The instructions are stored in hash lists.
00482      Pick the first one and keep trying until we find the right one.  */
00483 
00484   insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value);
00485   while (insn_list != NULL)
00486     {
00487       const CGEN_INSN *insn = insn_list->insn;
00488       CGEN_FIELDS fields;
00489       int length;
00490       unsigned long insn_value_cropped;
00491 
00492 #ifdef CGEN_VALIDATE_INSN_SUPPORTED 
00493       /* Not needed as insn shouldn't be in hash lists if not supported.  */
00494       /* Supported by this cpu?  */
00495       if (! ip2k_cgen_insn_supported (cd, insn))
00496         {
00497           insn_list = CGEN_DIS_NEXT_INSN (insn_list);
00498          continue;
00499         }
00500 #endif
00501 
00502       /* Basic bit mask must be correct.  */
00503       /* ??? May wish to allow target to defer this check until the extract
00504         handler.  */
00505 
00506       /* Base size may exceed this instruction's size.  Extract the
00507          relevant part from the buffer. */
00508       if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
00509          (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
00510        insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn), 
00511                                       info->endian == BFD_ENDIAN_BIG);
00512       else
00513        insn_value_cropped = insn_value;
00514 
00515       if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
00516          == CGEN_INSN_BASE_VALUE (insn))
00517        {
00518          /* Printing is handled in two passes.  The first pass parses the
00519             machine insn and extracts the fields.  The second pass prints
00520             them.  */
00521 
00522          /* Make sure the entire insn is loaded into insn_value, if it
00523             can fit.  */
00524          if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
00525              (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
00526            {
00527              unsigned long full_insn_value;
00528              int rc = read_insn (cd, pc, info, buf,
00529                               CGEN_INSN_BITSIZE (insn) / 8,
00530                               & ex_info, & full_insn_value);
00531              if (rc != 0)
00532               return rc;
00533              length = CGEN_EXTRACT_FN (cd, insn)
00534               (cd, insn, &ex_info, full_insn_value, &fields, pc);
00535            }
00536          else
00537            length = CGEN_EXTRACT_FN (cd, insn)
00538              (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
00539 
00540          /* Length < 0 -> error.  */
00541          if (length < 0)
00542            return length;
00543          if (length > 0)
00544            {
00545              CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
00546              /* Length is in bits, result is in bytes.  */
00547              return length / 8;
00548            }
00549        }
00550 
00551       insn_list = CGEN_DIS_NEXT_INSN (insn_list);
00552     }
00553 
00554   return 0;
00555 }
00556 
00557 /* Default value for CGEN_PRINT_INSN.
00558    The result is the size of the insn in bytes or zero for an unknown insn
00559    or -1 if an error occured fetching bytes.  */
00560 
00561 #ifndef CGEN_PRINT_INSN
00562 #define CGEN_PRINT_INSN default_print_insn
00563 #endif
00564 
00565 static int
00566 default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
00567 {
00568   bfd_byte buf[CGEN_MAX_INSN_SIZE];
00569   int buflen;
00570   int status;
00571 
00572   /* Attempt to read the base part of the insn.  */
00573   buflen = cd->base_insn_bitsize / 8;
00574   status = (*info->read_memory_func) (pc, buf, buflen, info);
00575 
00576   /* Try again with the minimum part, if min < base.  */
00577   if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
00578     {
00579       buflen = cd->min_insn_bitsize / 8;
00580       status = (*info->read_memory_func) (pc, buf, buflen, info);
00581     }
00582 
00583   if (status != 0)
00584     {
00585       (*info->memory_error_func) (status, pc, info);
00586       return -1;
00587     }
00588 
00589   return print_insn (cd, pc, info, buf, buflen);
00590 }
00591 
00592 /* Main entry point.
00593    Print one instruction from PC on INFO->STREAM.
00594    Return the size of the instruction (in bytes).  */
00595 
00596 typedef struct cpu_desc_list
00597 {
00598   struct cpu_desc_list *next;
00599   CGEN_BITSET *isa;
00600   int mach;
00601   int endian;
00602   CGEN_CPU_DESC cd;
00603 } cpu_desc_list;
00604 
00605 int
00606 print_insn_ip2k (bfd_vma pc, disassemble_info *info)
00607 {
00608   static cpu_desc_list *cd_list = 0;
00609   cpu_desc_list *cl = 0;
00610   static CGEN_CPU_DESC cd = 0;
00611   static CGEN_BITSET *prev_isa;
00612   static int prev_mach;
00613   static int prev_endian;
00614   int length;
00615   CGEN_BITSET *isa;
00616   int mach;
00617   int endian = (info->endian == BFD_ENDIAN_BIG
00618               ? CGEN_ENDIAN_BIG
00619               : CGEN_ENDIAN_LITTLE);
00620   enum bfd_architecture arch;
00621 
00622   /* ??? gdb will set mach but leave the architecture as "unknown" */
00623 #ifndef CGEN_BFD_ARCH
00624 #define CGEN_BFD_ARCH bfd_arch_ip2k
00625 #endif
00626   arch = info->arch;
00627   if (arch == bfd_arch_unknown)
00628     arch = CGEN_BFD_ARCH;
00629    
00630   /* There's no standard way to compute the machine or isa number
00631      so we leave it to the target.  */
00632 #ifdef CGEN_COMPUTE_MACH
00633   mach = CGEN_COMPUTE_MACH (info);
00634 #else
00635   mach = info->mach;
00636 #endif
00637 
00638 #ifdef CGEN_COMPUTE_ISA
00639   {
00640     static CGEN_BITSET *permanent_isa;
00641 
00642     if (!permanent_isa)
00643       permanent_isa = cgen_bitset_create (MAX_ISAS);
00644     isa = permanent_isa;
00645     cgen_bitset_clear (isa);
00646     cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info));
00647   }
00648 #else
00649   isa = info->insn_sets;
00650 #endif
00651 
00652   /* If we've switched cpu's, try to find a handle we've used before */
00653   if (cd
00654       && (cgen_bitset_compare (isa, prev_isa) != 0
00655          || mach != prev_mach
00656          || endian != prev_endian))
00657     {
00658       cd = 0;
00659       for (cl = cd_list; cl; cl = cl->next)
00660        {
00661          if (cgen_bitset_compare (cl->isa, isa) == 0 &&
00662              cl->mach == mach &&
00663              cl->endian == endian)
00664            {
00665              cd = cl->cd;
00666              prev_isa = cd->isas;
00667              break;
00668            }
00669        }
00670     } 
00671 
00672   /* If we haven't initialized yet, initialize the opcode table.  */
00673   if (! cd)
00674     {
00675       const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
00676       const char *mach_name;
00677 
00678       if (!arch_type)
00679        abort ();
00680       mach_name = arch_type->printable_name;
00681 
00682       prev_isa = cgen_bitset_copy (isa);
00683       prev_mach = mach;
00684       prev_endian = endian;
00685       cd = ip2k_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
00686                              CGEN_CPU_OPEN_BFDMACH, mach_name,
00687                              CGEN_CPU_OPEN_ENDIAN, prev_endian,
00688                              CGEN_CPU_OPEN_END);
00689       if (!cd)
00690        abort ();
00691 
00692       /* Save this away for future reference.  */
00693       cl = xmalloc (sizeof (struct cpu_desc_list));
00694       cl->cd = cd;
00695       cl->isa = prev_isa;
00696       cl->mach = mach;
00697       cl->endian = endian;
00698       cl->next = cd_list;
00699       cd_list = cl;
00700 
00701       ip2k_cgen_init_dis (cd);
00702     }
00703 
00704   /* We try to have as much common code as possible.
00705      But at this point some targets need to take over.  */
00706   /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
00707      but if not possible try to move this hook elsewhere rather than
00708      have two hooks.  */
00709   length = CGEN_PRINT_INSN (cd, pc, info);
00710   if (length > 0)
00711     return length;
00712   if (length < 0)
00713     return -1;
00714 
00715   (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
00716   return cd->default_insn_bitsize / 8;
00717 }