Back to index

cell-binutils  2.17cvs20070401
openrisc-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 "openrisc-desc.h"
00037 #include "openrisc-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 
00062 void openrisc_cgen_print_operand
00063   (CGEN_CPU_DESC, int, PTR, CGEN_FIELDS *, void const *, bfd_vma, int);
00064 
00065 /* Main entry point for printing operands.
00066    XINFO is a `void *' and not a `disassemble_info *' to not put a requirement
00067    of dis-asm.h on cgen.h.
00068 
00069    This function is basically just a big switch statement.  Earlier versions
00070    used tables to look up the function to use, but
00071    - if the table contains both assembler and disassembler functions then
00072      the disassembler contains much of the assembler and vice-versa,
00073    - there's a lot of inlining possibilities as things grow,
00074    - using a switch statement avoids the function call overhead.
00075 
00076    This function could be moved into `print_insn_normal', but keeping it
00077    separate makes clear the interface between `print_insn_normal' and each of
00078    the handlers.  */
00079 
00080 void
00081 openrisc_cgen_print_operand (CGEN_CPU_DESC cd,
00082                         int opindex,
00083                         void * xinfo,
00084                         CGEN_FIELDS *fields,
00085                         void const *attrs ATTRIBUTE_UNUSED,
00086                         bfd_vma pc,
00087                         int length)
00088 {
00089   disassemble_info *info = (disassemble_info *) xinfo;
00090 
00091   switch (opindex)
00092     {
00093     case OPENRISC_OPERAND_ABS_26 :
00094       print_address (cd, info, fields->f_abs26, 0|(1<<CGEN_OPERAND_ABS_ADDR), pc, length);
00095       break;
00096     case OPENRISC_OPERAND_DISP_26 :
00097       print_address (cd, info, fields->f_disp26, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
00098       break;
00099     case OPENRISC_OPERAND_HI16 :
00100       print_normal (cd, info, fields->f_simm16, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_SIGN_OPT), pc, length);
00101       break;
00102     case OPENRISC_OPERAND_LO16 :
00103       print_normal (cd, info, fields->f_lo16, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_SIGN_OPT), pc, length);
00104       break;
00105     case OPENRISC_OPERAND_OP_F_23 :
00106       print_normal (cd, info, fields->f_op4, 0, pc, length);
00107       break;
00108     case OPENRISC_OPERAND_OP_F_3 :
00109       print_normal (cd, info, fields->f_op5, 0, pc, length);
00110       break;
00111     case OPENRISC_OPERAND_RA :
00112       print_keyword (cd, info, & openrisc_cgen_opval_h_gr, fields->f_r2, 0);
00113       break;
00114     case OPENRISC_OPERAND_RB :
00115       print_keyword (cd, info, & openrisc_cgen_opval_h_gr, fields->f_r3, 0);
00116       break;
00117     case OPENRISC_OPERAND_RD :
00118       print_keyword (cd, info, & openrisc_cgen_opval_h_gr, fields->f_r1, 0);
00119       break;
00120     case OPENRISC_OPERAND_SIMM_16 :
00121       print_normal (cd, info, fields->f_simm16, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
00122       break;
00123     case OPENRISC_OPERAND_UI16NC :
00124       print_normal (cd, info, fields->f_i16nc, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_SIGN_OPT)|(1<<CGEN_OPERAND_VIRTUAL), pc, length);
00125       break;
00126     case OPENRISC_OPERAND_UIMM_16 :
00127       print_normal (cd, info, fields->f_uimm16, 0, pc, length);
00128       break;
00129     case OPENRISC_OPERAND_UIMM_5 :
00130       print_normal (cd, info, fields->f_uimm5, 0, pc, length);
00131       break;
00132 
00133     default :
00134       /* xgettext:c-format */
00135       fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
00136               opindex);
00137     abort ();
00138   }
00139 }
00140 
00141 cgen_print_fn * const openrisc_cgen_print_handlers[] = 
00142 {
00143   print_insn_normal,
00144 };
00145 
00146 
00147 void
00148 openrisc_cgen_init_dis (CGEN_CPU_DESC cd)
00149 {
00150   openrisc_cgen_init_opcode_table (cd);
00151   openrisc_cgen_init_ibld_table (cd);
00152   cd->print_handlers = & openrisc_cgen_print_handlers[0];
00153   cd->print_operand = openrisc_cgen_print_operand;
00154 }
00155 
00156 
00157 /* Default print handler.  */
00158 
00159 static void
00160 print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00161              void *dis_info,
00162              long value,
00163              unsigned int attrs,
00164              bfd_vma pc ATTRIBUTE_UNUSED,
00165              int length ATTRIBUTE_UNUSED)
00166 {
00167   disassemble_info *info = (disassemble_info *) dis_info;
00168 
00169 #ifdef CGEN_PRINT_NORMAL
00170   CGEN_PRINT_NORMAL (cd, info, value, attrs, pc, length);
00171 #endif
00172 
00173   /* Print the operand as directed by the attributes.  */
00174   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
00175     ; /* nothing to do */
00176   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
00177     (*info->fprintf_func) (info->stream, "%ld", value);
00178   else
00179     (*info->fprintf_func) (info->stream, "0x%lx", value);
00180 }
00181 
00182 /* Default address handler.  */
00183 
00184 static void
00185 print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00186               void *dis_info,
00187               bfd_vma value,
00188               unsigned int attrs,
00189               bfd_vma pc ATTRIBUTE_UNUSED,
00190               int length ATTRIBUTE_UNUSED)
00191 {
00192   disassemble_info *info = (disassemble_info *) dis_info;
00193 
00194 #ifdef CGEN_PRINT_ADDRESS
00195   CGEN_PRINT_ADDRESS (cd, info, value, attrs, pc, length);
00196 #endif
00197 
00198   /* Print the operand as directed by the attributes.  */
00199   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
00200     ; /* Nothing to do.  */
00201   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
00202     (*info->print_address_func) (value, info);
00203   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
00204     (*info->print_address_func) (value, info);
00205   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
00206     (*info->fprintf_func) (info->stream, "%ld", (long) value);
00207   else
00208     (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
00209 }
00210 
00211 /* Keyword print handler.  */
00212 
00213 static void
00214 print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00215               void *dis_info,
00216               CGEN_KEYWORD *keyword_table,
00217               long value,
00218               unsigned int attrs ATTRIBUTE_UNUSED)
00219 {
00220   disassemble_info *info = (disassemble_info *) dis_info;
00221   const CGEN_KEYWORD_ENTRY *ke;
00222 
00223   ke = cgen_keyword_lookup_value (keyword_table, value);
00224   if (ke != NULL)
00225     (*info->fprintf_func) (info->stream, "%s", ke->name);
00226   else
00227     (*info->fprintf_func) (info->stream, "???");
00228 }
00229 
00230 /* Default insn printer.
00231 
00232    DIS_INFO is defined as `void *' so the disassembler needn't know anything
00233    about disassemble_info.  */
00234 
00235 static void
00236 print_insn_normal (CGEN_CPU_DESC cd,
00237                  void *dis_info,
00238                  const CGEN_INSN *insn,
00239                  CGEN_FIELDS *fields,
00240                  bfd_vma pc,
00241                  int length)
00242 {
00243   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
00244   disassemble_info *info = (disassemble_info *) dis_info;
00245   const CGEN_SYNTAX_CHAR_TYPE *syn;
00246 
00247   CGEN_INIT_PRINT (cd);
00248 
00249   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
00250     {
00251       if (CGEN_SYNTAX_MNEMONIC_P (*syn))
00252        {
00253          (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
00254          continue;
00255        }
00256       if (CGEN_SYNTAX_CHAR_P (*syn))
00257        {
00258          (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
00259          continue;
00260        }
00261 
00262       /* We have an operand.  */
00263       openrisc_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
00264                              fields, CGEN_INSN_ATTRS (insn), pc, length);
00265     }
00266 }
00267 
00268 /* Subroutine of print_insn. Reads an insn into the given buffers and updates
00269    the extract info.
00270    Returns 0 if all is well, non-zero otherwise.  */
00271 
00272 static int
00273 read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
00274           bfd_vma pc,
00275           disassemble_info *info,
00276           bfd_byte *buf,
00277           int buflen,
00278           CGEN_EXTRACT_INFO *ex_info,
00279           unsigned long *insn_value)
00280 {
00281   int status = (*info->read_memory_func) (pc, buf, buflen, info);
00282 
00283   if (status != 0)
00284     {
00285       (*info->memory_error_func) (status, pc, info);
00286       return -1;
00287     }
00288 
00289   ex_info->dis_info = info;
00290   ex_info->valid = (1 << buflen) - 1;
00291   ex_info->insn_bytes = buf;
00292 
00293   *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
00294   return 0;
00295 }
00296 
00297 /* Utility to print an insn.
00298    BUF is the base part of the insn, target byte order, BUFLEN bytes long.
00299    The result is the size of the insn in bytes or zero for an unknown insn
00300    or -1 if an error occurs fetching data (memory_error_func will have
00301    been called).  */
00302 
00303 static int
00304 print_insn (CGEN_CPU_DESC cd,
00305            bfd_vma pc,
00306            disassemble_info *info,
00307            bfd_byte *buf,
00308            unsigned int buflen)
00309 {
00310   CGEN_INSN_INT insn_value;
00311   const CGEN_INSN_LIST *insn_list;
00312   CGEN_EXTRACT_INFO ex_info;
00313   int basesize;
00314 
00315   /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
00316   basesize = cd->base_insn_bitsize < buflen * 8 ?
00317                                      cd->base_insn_bitsize : buflen * 8;
00318   insn_value = cgen_get_insn_value (cd, buf, basesize);
00319 
00320 
00321   /* Fill in ex_info fields like read_insn would.  Don't actually call
00322      read_insn, since the incoming buffer is already read (and possibly
00323      modified a la m32r).  */
00324   ex_info.valid = (1 << buflen) - 1;
00325   ex_info.dis_info = info;
00326   ex_info.insn_bytes = buf;
00327 
00328   /* The instructions are stored in hash lists.
00329      Pick the first one and keep trying until we find the right one.  */
00330 
00331   insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value);
00332   while (insn_list != NULL)
00333     {
00334       const CGEN_INSN *insn = insn_list->insn;
00335       CGEN_FIELDS fields;
00336       int length;
00337       unsigned long insn_value_cropped;
00338 
00339 #ifdef CGEN_VALIDATE_INSN_SUPPORTED 
00340       /* Not needed as insn shouldn't be in hash lists if not supported.  */
00341       /* Supported by this cpu?  */
00342       if (! openrisc_cgen_insn_supported (cd, insn))
00343         {
00344           insn_list = CGEN_DIS_NEXT_INSN (insn_list);
00345          continue;
00346         }
00347 #endif
00348 
00349       /* Basic bit mask must be correct.  */
00350       /* ??? May wish to allow target to defer this check until the extract
00351         handler.  */
00352 
00353       /* Base size may exceed this instruction's size.  Extract the
00354          relevant part from the buffer. */
00355       if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
00356          (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
00357        insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn), 
00358                                       info->endian == BFD_ENDIAN_BIG);
00359       else
00360        insn_value_cropped = insn_value;
00361 
00362       if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
00363          == CGEN_INSN_BASE_VALUE (insn))
00364        {
00365          /* Printing is handled in two passes.  The first pass parses the
00366             machine insn and extracts the fields.  The second pass prints
00367             them.  */
00368 
00369          /* Make sure the entire insn is loaded into insn_value, if it
00370             can fit.  */
00371          if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
00372              (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
00373            {
00374              unsigned long full_insn_value;
00375              int rc = read_insn (cd, pc, info, buf,
00376                               CGEN_INSN_BITSIZE (insn) / 8,
00377                               & ex_info, & full_insn_value);
00378              if (rc != 0)
00379               return rc;
00380              length = CGEN_EXTRACT_FN (cd, insn)
00381               (cd, insn, &ex_info, full_insn_value, &fields, pc);
00382            }
00383          else
00384            length = CGEN_EXTRACT_FN (cd, insn)
00385              (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
00386 
00387          /* Length < 0 -> error.  */
00388          if (length < 0)
00389            return length;
00390          if (length > 0)
00391            {
00392              CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
00393              /* Length is in bits, result is in bytes.  */
00394              return length / 8;
00395            }
00396        }
00397 
00398       insn_list = CGEN_DIS_NEXT_INSN (insn_list);
00399     }
00400 
00401   return 0;
00402 }
00403 
00404 /* Default value for CGEN_PRINT_INSN.
00405    The result is the size of the insn in bytes or zero for an unknown insn
00406    or -1 if an error occured fetching bytes.  */
00407 
00408 #ifndef CGEN_PRINT_INSN
00409 #define CGEN_PRINT_INSN default_print_insn
00410 #endif
00411 
00412 static int
00413 default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
00414 {
00415   bfd_byte buf[CGEN_MAX_INSN_SIZE];
00416   int buflen;
00417   int status;
00418 
00419   /* Attempt to read the base part of the insn.  */
00420   buflen = cd->base_insn_bitsize / 8;
00421   status = (*info->read_memory_func) (pc, buf, buflen, info);
00422 
00423   /* Try again with the minimum part, if min < base.  */
00424   if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
00425     {
00426       buflen = cd->min_insn_bitsize / 8;
00427       status = (*info->read_memory_func) (pc, buf, buflen, info);
00428     }
00429 
00430   if (status != 0)
00431     {
00432       (*info->memory_error_func) (status, pc, info);
00433       return -1;
00434     }
00435 
00436   return print_insn (cd, pc, info, buf, buflen);
00437 }
00438 
00439 /* Main entry point.
00440    Print one instruction from PC on INFO->STREAM.
00441    Return the size of the instruction (in bytes).  */
00442 
00443 typedef struct cpu_desc_list
00444 {
00445   struct cpu_desc_list *next;
00446   CGEN_BITSET *isa;
00447   int mach;
00448   int endian;
00449   CGEN_CPU_DESC cd;
00450 } cpu_desc_list;
00451 
00452 int
00453 print_insn_openrisc (bfd_vma pc, disassemble_info *info)
00454 {
00455   static cpu_desc_list *cd_list = 0;
00456   cpu_desc_list *cl = 0;
00457   static CGEN_CPU_DESC cd = 0;
00458   static CGEN_BITSET *prev_isa;
00459   static int prev_mach;
00460   static int prev_endian;
00461   int length;
00462   CGEN_BITSET *isa;
00463   int mach;
00464   int endian = (info->endian == BFD_ENDIAN_BIG
00465               ? CGEN_ENDIAN_BIG
00466               : CGEN_ENDIAN_LITTLE);
00467   enum bfd_architecture arch;
00468 
00469   /* ??? gdb will set mach but leave the architecture as "unknown" */
00470 #ifndef CGEN_BFD_ARCH
00471 #define CGEN_BFD_ARCH bfd_arch_openrisc
00472 #endif
00473   arch = info->arch;
00474   if (arch == bfd_arch_unknown)
00475     arch = CGEN_BFD_ARCH;
00476    
00477   /* There's no standard way to compute the machine or isa number
00478      so we leave it to the target.  */
00479 #ifdef CGEN_COMPUTE_MACH
00480   mach = CGEN_COMPUTE_MACH (info);
00481 #else
00482   mach = info->mach;
00483 #endif
00484 
00485 #ifdef CGEN_COMPUTE_ISA
00486   {
00487     static CGEN_BITSET *permanent_isa;
00488 
00489     if (!permanent_isa)
00490       permanent_isa = cgen_bitset_create (MAX_ISAS);
00491     isa = permanent_isa;
00492     cgen_bitset_clear (isa);
00493     cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info));
00494   }
00495 #else
00496   isa = info->insn_sets;
00497 #endif
00498 
00499   /* If we've switched cpu's, try to find a handle we've used before */
00500   if (cd
00501       && (cgen_bitset_compare (isa, prev_isa) != 0
00502          || mach != prev_mach
00503          || endian != prev_endian))
00504     {
00505       cd = 0;
00506       for (cl = cd_list; cl; cl = cl->next)
00507        {
00508          if (cgen_bitset_compare (cl->isa, isa) == 0 &&
00509              cl->mach == mach &&
00510              cl->endian == endian)
00511            {
00512              cd = cl->cd;
00513              prev_isa = cd->isas;
00514              break;
00515            }
00516        }
00517     } 
00518 
00519   /* If we haven't initialized yet, initialize the opcode table.  */
00520   if (! cd)
00521     {
00522       const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
00523       const char *mach_name;
00524 
00525       if (!arch_type)
00526        abort ();
00527       mach_name = arch_type->printable_name;
00528 
00529       prev_isa = cgen_bitset_copy (isa);
00530       prev_mach = mach;
00531       prev_endian = endian;
00532       cd = openrisc_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
00533                              CGEN_CPU_OPEN_BFDMACH, mach_name,
00534                              CGEN_CPU_OPEN_ENDIAN, prev_endian,
00535                              CGEN_CPU_OPEN_END);
00536       if (!cd)
00537        abort ();
00538 
00539       /* Save this away for future reference.  */
00540       cl = xmalloc (sizeof (struct cpu_desc_list));
00541       cl->cd = cd;
00542       cl->isa = prev_isa;
00543       cl->mach = mach;
00544       cl->endian = endian;
00545       cl->next = cd_list;
00546       cd_list = cl;
00547 
00548       openrisc_cgen_init_dis (cd);
00549     }
00550 
00551   /* We try to have as much common code as possible.
00552      But at this point some targets need to take over.  */
00553   /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
00554      but if not possible try to move this hook elsewhere rather than
00555      have two hooks.  */
00556   length = CGEN_PRINT_INSN (cd, pc, info);
00557   if (length > 0)
00558     return length;
00559   if (length < 0)
00560     return -1;
00561 
00562   (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
00563   return cd->default_insn_bitsize / 8;
00564 }