Back to index

cell-binutils  2.17cvs20070401
d30v-dis.c
Go to the documentation of this file.
00001 /* Disassemble D30V instructions.
00002    Copyright 1997, 1998, 2000, 2001, 2005 Free Software Foundation, Inc.
00003 
00004    This program is free software; you can redistribute it and/or modify
00005    it under the terms of the GNU General Public License as published by
00006    the Free Software Foundation; either version 2 of the License, or
00007    (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU General Public License for more details.
00013 
00014    You should have received a copy of the GNU General Public License
00015    along with this program; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
00017    MA 02110-1301, USA.  */
00018 
00019 #include <stdio.h>
00020 #include "sysdep.h"
00021 #include "opcode/d30v.h"
00022 #include "dis-asm.h"
00023 #include "opintl.h"
00024 
00025 #define PC_MASK 0xFFFFFFFF
00026 
00027 /* Return 0 if lookup fails,
00028    1 if found and only one form,
00029    2 if found and there are short and long forms.  */
00030 
00031 static int
00032 lookup_opcode (struct d30v_insn *insn, long num, int is_long)
00033 {
00034   int i = 0, index;
00035   struct d30v_format *f;
00036   struct d30v_opcode *op = (struct d30v_opcode *) d30v_opcode_table;
00037   int op1 = (num >> 25) & 0x7;
00038   int op2 = (num >> 20) & 0x1f;
00039   int mod = (num >> 18) & 0x3;
00040 
00041   /* Find the opcode.  */
00042   do
00043     {
00044       if ((op->op1 == op1) && (op->op2 == op2))
00045        break;
00046       op++;
00047     }
00048   while (op->name);
00049 
00050   if (!op || !op->name)
00051     return 0;
00052 
00053   while (op->op1 == op1 && op->op2 == op2)
00054     {
00055       /* Scan through all the formats for the opcode.  */
00056       index = op->format[i++];
00057       do
00058        {
00059          f = (struct d30v_format *) &d30v_format_table[index];
00060          while (f->form == index)
00061            {
00062              if ((!is_long || f->form >= LONG) && (f->modifier == mod))
00063               {
00064                 insn->form = f;
00065                 break;
00066               }
00067              f++;
00068            }
00069          if (insn->form)
00070            break;
00071        }
00072       while ((index = op->format[i++]) != 0);
00073       if (insn->form)
00074        break;
00075       op++;
00076       i = 0;
00077     }
00078   if (insn->form == NULL)
00079     return 0;
00080 
00081   insn->op = op;
00082   insn->ecc = (num >> 28) & 0x7;
00083   if (op->format[1])
00084     return 2;
00085   else
00086     return 1;
00087 }
00088 
00089 static int
00090 extract_value (long long num, struct d30v_operand *oper, int is_long)
00091 {
00092   int val;
00093   int shift = 12 - oper->position;
00094   int mask = (0xFFFFFFFF >> (32 - oper->bits));
00095 
00096   if (is_long)
00097     {
00098       if (oper->bits == 32)
00099        /* Piece together 32-bit constant.  */
00100        val = ((num & 0x3FFFF)
00101               | ((num & 0xFF00000) >> 2)
00102               | ((num & 0x3F00000000LL) >> 6));
00103       else
00104        val = (num >> (32 + shift)) & mask;
00105     }
00106   else
00107     val = (num >> shift) & mask;
00108 
00109   if (oper->flags & OPERAND_SHIFT)
00110     val <<= 3;
00111 
00112   return val;
00113 }
00114 
00115 static void
00116 print_insn (struct disassemble_info *info,
00117            bfd_vma memaddr,
00118            long long num,
00119            struct d30v_insn *insn,
00120            int is_long,
00121            int show_ext)
00122 {
00123   int val, opnum, need_comma = 0;
00124   struct d30v_operand *oper;
00125   int i, match, opind = 0, need_paren = 0, found_control = 0;
00126 
00127   (*info->fprintf_func) (info->stream, "%s", insn->op->name);
00128 
00129   /* Check for CMP or CMPU.  */
00130   if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME)
00131     {
00132       opind++;
00133       val =
00134        extract_value (num,
00135                      (struct d30v_operand *) &d30v_operand_table[insn->form->operands[0]],
00136                      is_long);
00137       (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]);
00138     }
00139 
00140   /* Add in ".s" or ".l".  */
00141   if (show_ext == 2)
00142     {
00143       if (is_long)
00144        (*info->fprintf_func) (info->stream, ".l");
00145       else
00146        (*info->fprintf_func) (info->stream, ".s");
00147     }
00148 
00149   if (insn->ecc)
00150     (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]);
00151 
00152   (*info->fprintf_func) (info->stream, "\t");
00153 
00154   while ((opnum = insn->form->operands[opind++]) != 0)
00155     {
00156       int bits;
00157 
00158       oper = (struct d30v_operand *) &d30v_operand_table[opnum];
00159       bits = oper->bits;
00160       if (oper->flags & OPERAND_SHIFT)
00161        bits += 3;
00162 
00163       if (need_comma
00164          && oper->flags != OPERAND_PLUS
00165          && oper->flags != OPERAND_MINUS)
00166        {
00167          need_comma = 0;
00168          (*info->fprintf_func) (info->stream, ", ");
00169        }
00170 
00171       if (oper->flags == OPERAND_ATMINUS)
00172        {
00173          (*info->fprintf_func) (info->stream, "@-");
00174          continue;
00175        }
00176       if (oper->flags == OPERAND_MINUS)
00177        {
00178          (*info->fprintf_func) (info->stream, "-");
00179          continue;
00180        }
00181       if (oper->flags == OPERAND_PLUS)
00182        {
00183          (*info->fprintf_func) (info->stream, "+");
00184          continue;
00185        }
00186       if (oper->flags == OPERAND_ATSIGN)
00187        {
00188          (*info->fprintf_func) (info->stream, "@");
00189          continue;
00190        }
00191       if (oper->flags == OPERAND_ATPAR)
00192        {
00193          (*info->fprintf_func) (info->stream, "@(");
00194          need_paren = 1;
00195          continue;
00196        }
00197 
00198       if (oper->flags == OPERAND_SPECIAL)
00199        continue;
00200 
00201       val = extract_value (num, oper, is_long);
00202 
00203       if (oper->flags & OPERAND_REG)
00204        {
00205          match = 0;
00206          if (oper->flags & OPERAND_CONTROL)
00207            {
00208              struct d30v_operand *oper3 =
00209               (struct d30v_operand *) &d30v_operand_table[insn->form->operands[2]];
00210              int id = extract_value (num, oper3, is_long);
00211 
00212              found_control = 1;
00213              switch (id)
00214               {
00215               case 0:
00216                 val |= OPERAND_CONTROL;
00217                 break;
00218               case 1:
00219               case 2:
00220                 val = OPERAND_CONTROL + MAX_CONTROL_REG + id;
00221                 break;
00222               case 3:
00223                 val |= OPERAND_FLAG;
00224                 break;
00225               default:
00226                 fprintf (stderr, "illegal id (%d)\n", id);
00227               }
00228            }
00229          else if (oper->flags & OPERAND_ACC)
00230            val |= OPERAND_ACC;
00231          else if (oper->flags & OPERAND_FLAG)
00232            val |= OPERAND_FLAG;
00233          for (i = 0; i < reg_name_cnt (); i++)
00234            {
00235              if (val == pre_defined_registers[i].value)
00236               {
00237                 if (pre_defined_registers[i].pname)
00238                   (*info->fprintf_func)
00239                     (info->stream, "%s", pre_defined_registers[i].pname);
00240                 else
00241                   (*info->fprintf_func)
00242                     (info->stream, "%s", pre_defined_registers[i].name);
00243                 match = 1;
00244                 break;
00245               }
00246            }
00247          if (match == 0)
00248            {
00249              /* This would only get executed if a register was not in
00250                the register table.  */
00251              (*info->fprintf_func)
00252               (info->stream, _("<unknown register %d>"), val & 0x3F);
00253            }
00254        }
00255       /* repeati has a relocation, but its first argument is a plain
00256         immediate.  OTOH instructions like djsri have a pc-relative
00257         delay target, but an absolute jump target.  Therefore, a test
00258         of insn->op->reloc_flag is not specific enough; we must test
00259         if the actual operand we are handling now is pc-relative.  */
00260       else if (oper->flags & OPERAND_PCREL)
00261        {
00262          int neg = 0;
00263 
00264          /* IMM6S3 is unsigned.  */
00265          if (oper->flags & OPERAND_SIGNED || bits == 32)
00266            {
00267              long max;
00268              max = (1 << (bits - 1));
00269              if (val & max)
00270               {
00271                 if (bits == 32)
00272                   val = -val;
00273                 else
00274                   val = -val & ((1 << bits) - 1);
00275                 neg = 1;
00276               }
00277            }
00278          if (neg)
00279            {
00280              (*info->fprintf_func) (info->stream, "-%x\t(", val);
00281              (*info->print_address_func) ((memaddr - val) & PC_MASK, info);
00282              (*info->fprintf_func) (info->stream, ")");
00283            }
00284          else
00285            {
00286              (*info->fprintf_func) (info->stream, "%x\t(", val);
00287              (*info->print_address_func) ((memaddr + val) & PC_MASK, info);
00288              (*info->fprintf_func) (info->stream, ")");
00289            }
00290        }
00291       else if (insn->op->reloc_flag == RELOC_ABS)
00292        {
00293          (*info->print_address_func) (val, info);
00294        }
00295       else
00296        {
00297          if (oper->flags & OPERAND_SIGNED)
00298            {
00299              int max = (1 << (bits - 1));
00300 
00301              if (val & max)
00302               {
00303                 val = -val;
00304                 if (bits < 32)
00305                   val &= ((1 << bits) - 1);
00306                 (*info->fprintf_func) (info->stream, "-");
00307               }
00308            }
00309          (*info->fprintf_func) (info->stream, "0x%x", val);
00310        }
00311       /* If there is another operand, then write a comma and space.  */
00312       if (insn->form->operands[opind] && !(found_control && opind == 2))
00313        need_comma = 1;
00314     }
00315   if (need_paren)
00316     (*info->fprintf_func) (info->stream, ")");
00317 }
00318 
00319 int
00320 print_insn_d30v (bfd_vma memaddr, struct disassemble_info *info)
00321 {
00322   int status, result;
00323   bfd_byte buffer[12];
00324   unsigned long in1, in2;
00325   struct d30v_insn insn;
00326   long long num;
00327 
00328   insn.form = NULL;
00329 
00330   info->bytes_per_line = 8;
00331   info->bytes_per_chunk = 4;
00332   info->display_endian = BFD_ENDIAN_BIG;
00333 
00334   status = (*info->read_memory_func) (memaddr, buffer, 4, info);
00335   if (status != 0)
00336     {
00337       (*info->memory_error_func) (status, memaddr, info);
00338       return -1;
00339     }
00340   in1 = bfd_getb32 (buffer);
00341 
00342   status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info);
00343   if (status != 0)
00344     {
00345       info->bytes_per_line = 8;
00346       if (!(result = lookup_opcode (&insn, in1, 0)))
00347        (*info->fprintf_func) (info->stream, ".long\t0x%lx", in1);
00348       else
00349        print_insn (info, memaddr, (long long) in1, &insn, 0, result);
00350       return 4;
00351     }
00352   in2 = bfd_getb32 (buffer);
00353 
00354   if (in1 & in2 & FM01)
00355     {
00356       /* LONG instruction.  */
00357       if (!(result = lookup_opcode (&insn, in1, 1)))
00358        {
00359          (*info->fprintf_func) (info->stream, ".long\t0x%lx,0x%lx", in1, in2);
00360          return 8;
00361        }
00362       num = (long long) in1 << 32 | in2;
00363       print_insn (info, memaddr, num, &insn, 1, result);
00364     }
00365   else
00366     {
00367       num = in1;
00368       if (!(result = lookup_opcode (&insn, in1, 0)))
00369        (*info->fprintf_func) (info->stream, ".long\t0x%lx", in1);
00370       else
00371        print_insn (info, memaddr, num, &insn, 0, result);
00372 
00373       switch (((in1 >> 31) << 1) | (in2 >> 31))
00374        {
00375        case 0:
00376          (*info->fprintf_func) (info->stream, "\t||\t");
00377          break;
00378        case 1:
00379          (*info->fprintf_func) (info->stream, "\t->\t");
00380          break;
00381        case 2:
00382          (*info->fprintf_func) (info->stream, "\t<-\t");
00383        default:
00384          break;
00385        }
00386 
00387       insn.form = NULL;
00388       num = in2;
00389       if (!(result = lookup_opcode (&insn, in2, 0)))
00390        (*info->fprintf_func) (info->stream, ".long\t0x%lx", in2);
00391       else
00392        print_insn (info, memaddr, num, &insn, 0, result);
00393     }
00394   return 8;
00395 }