Home | History | Annotate | Line # | Download | only in opcodes
d30v-dis.c revision 1.1
      1 /* Disassemble D30V instructions.
      2    Copyright 1997, 1998, 2000, 2001, 2005, 2007, 2012
      3    Free Software Foundation, Inc.
      4 
      5    This file is part of the GNU opcodes library.
      6 
      7    This library is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3, or (at your option)
     10    any later version.
     11 
     12    It is distributed in the hope that it will be useful, but WITHOUT
     13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     15    License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program; if not, write to the Free Software
     19    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
     20    MA 02110-1301, USA.  */
     21 
     22 #include "sysdep.h"
     23 #include <stdio.h>
     24 #include "opcode/d30v.h"
     25 #include "dis-asm.h"
     26 #include "opintl.h"
     27 
     28 #define PC_MASK 0xFFFFFFFF
     29 
     30 /* Return 0 if lookup fails,
     31    1 if found and only one form,
     32    2 if found and there are short and long forms.  */
     33 
     34 static int
     35 lookup_opcode (struct d30v_insn *insn, long num, int is_long)
     36 {
     37   int i = 0, op_index;
     38   struct d30v_format *f;
     39   struct d30v_opcode *op = (struct d30v_opcode *) d30v_opcode_table;
     40   int op1 = (num >> 25) & 0x7;
     41   int op2 = (num >> 20) & 0x1f;
     42   int mod = (num >> 18) & 0x3;
     43 
     44   /* Find the opcode.  */
     45   do
     46     {
     47       if ((op->op1 == op1) && (op->op2 == op2))
     48 	break;
     49       op++;
     50     }
     51   while (op->name);
     52 
     53   if (!op || !op->name)
     54     return 0;
     55 
     56   while (op->op1 == op1 && op->op2 == op2)
     57     {
     58       /* Scan through all the formats for the opcode.  */
     59       op_index = op->format[i++];
     60       do
     61 	{
     62 	  f = (struct d30v_format *) &d30v_format_table[op_index];
     63 	  while (f->form == op_index)
     64 	    {
     65 	      if ((!is_long || f->form >= LONG) && (f->modifier == mod))
     66 		{
     67 		  insn->form = f;
     68 		  break;
     69 		}
     70 	      f++;
     71 	    }
     72 	  if (insn->form)
     73 	    break;
     74 	}
     75       while ((op_index = op->format[i++]) != 0);
     76       if (insn->form)
     77 	break;
     78       op++;
     79       i = 0;
     80     }
     81   if (insn->form == NULL)
     82     return 0;
     83 
     84   insn->op = op;
     85   insn->ecc = (num >> 28) & 0x7;
     86   if (op->format[1])
     87     return 2;
     88   else
     89     return 1;
     90 }
     91 
     92 static int
     93 extract_value (long long num, struct d30v_operand *oper, int is_long)
     94 {
     95   int val;
     96   int shift = 12 - oper->position;
     97   int mask = (0xFFFFFFFF >> (32 - oper->bits));
     98 
     99   if (is_long)
    100     {
    101       if (oper->bits == 32)
    102 	/* Piece together 32-bit constant.  */
    103 	val = ((num & 0x3FFFF)
    104 	       | ((num & 0xFF00000) >> 2)
    105 	       | ((num & 0x3F00000000LL) >> 6));
    106       else
    107 	val = (num >> (32 + shift)) & mask;
    108     }
    109   else
    110     val = (num >> shift) & mask;
    111 
    112   if (oper->flags & OPERAND_SHIFT)
    113     val <<= 3;
    114 
    115   return val;
    116 }
    117 
    118 static void
    119 print_insn (struct disassemble_info *info,
    120 	    bfd_vma memaddr,
    121 	    long long num,
    122 	    struct d30v_insn *insn,
    123 	    int is_long,
    124 	    int show_ext)
    125 {
    126   int val, opnum, need_comma = 0;
    127   struct d30v_operand *oper;
    128   int i, match, opind = 0, need_paren = 0, found_control = 0;
    129 
    130   (*info->fprintf_func) (info->stream, "%s", insn->op->name);
    131 
    132   /* Check for CMP or CMPU.  */
    133   if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME)
    134     {
    135       opind++;
    136       val =
    137 	extract_value (num,
    138 		       (struct d30v_operand *) &d30v_operand_table[insn->form->operands[0]],
    139 		       is_long);
    140       (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]);
    141     }
    142 
    143   /* Add in ".s" or ".l".  */
    144   if (show_ext == 2)
    145     {
    146       if (is_long)
    147 	(*info->fprintf_func) (info->stream, ".l");
    148       else
    149 	(*info->fprintf_func) (info->stream, ".s");
    150     }
    151 
    152   if (insn->ecc)
    153     (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]);
    154 
    155   (*info->fprintf_func) (info->stream, "\t");
    156 
    157   while ((opnum = insn->form->operands[opind++]) != 0)
    158     {
    159       int bits;
    160 
    161       oper = (struct d30v_operand *) &d30v_operand_table[opnum];
    162       bits = oper->bits;
    163       if (oper->flags & OPERAND_SHIFT)
    164 	bits += 3;
    165 
    166       if (need_comma
    167 	  && oper->flags != OPERAND_PLUS
    168 	  && oper->flags != OPERAND_MINUS)
    169 	{
    170 	  need_comma = 0;
    171 	  (*info->fprintf_func) (info->stream, ", ");
    172 	}
    173 
    174       if (oper->flags == OPERAND_ATMINUS)
    175 	{
    176 	  (*info->fprintf_func) (info->stream, "@-");
    177 	  continue;
    178 	}
    179       if (oper->flags == OPERAND_MINUS)
    180 	{
    181 	  (*info->fprintf_func) (info->stream, "-");
    182 	  continue;
    183 	}
    184       if (oper->flags == OPERAND_PLUS)
    185 	{
    186 	  (*info->fprintf_func) (info->stream, "+");
    187 	  continue;
    188 	}
    189       if (oper->flags == OPERAND_ATSIGN)
    190 	{
    191 	  (*info->fprintf_func) (info->stream, "@");
    192 	  continue;
    193 	}
    194       if (oper->flags == OPERAND_ATPAR)
    195 	{
    196 	  (*info->fprintf_func) (info->stream, "@(");
    197 	  need_paren = 1;
    198 	  continue;
    199 	}
    200 
    201       if (oper->flags == OPERAND_SPECIAL)
    202 	continue;
    203 
    204       val = extract_value (num, oper, is_long);
    205 
    206       if (oper->flags & OPERAND_REG)
    207 	{
    208 	  match = 0;
    209 	  if (oper->flags & OPERAND_CONTROL)
    210 	    {
    211 	      struct d30v_operand *oper3 =
    212 		(struct d30v_operand *) &d30v_operand_table[insn->form->operands[2]];
    213 	      int id = extract_value (num, oper3, is_long);
    214 
    215 	      found_control = 1;
    216 	      switch (id)
    217 		{
    218 		case 0:
    219 		  val |= OPERAND_CONTROL;
    220 		  break;
    221 		case 1:
    222 		case 2:
    223 		  val = OPERAND_CONTROL + MAX_CONTROL_REG + id;
    224 		  break;
    225 		case 3:
    226 		  val |= OPERAND_FLAG;
    227 		  break;
    228 		default:
    229 		  fprintf (stderr, "illegal id (%d)\n", id);
    230 		}
    231 	    }
    232 	  else if (oper->flags & OPERAND_ACC)
    233 	    val |= OPERAND_ACC;
    234 	  else if (oper->flags & OPERAND_FLAG)
    235 	    val |= OPERAND_FLAG;
    236 	  for (i = 0; i < reg_name_cnt (); i++)
    237 	    {
    238 	      if (val == pre_defined_registers[i].value)
    239 		{
    240 		  if (pre_defined_registers[i].pname)
    241 		    (*info->fprintf_func)
    242 		      (info->stream, "%s", pre_defined_registers[i].pname);
    243 		  else
    244 		    (*info->fprintf_func)
    245 		      (info->stream, "%s", pre_defined_registers[i].name);
    246 		  match = 1;
    247 		  break;
    248 		}
    249 	    }
    250 	  if (match == 0)
    251 	    {
    252 	      /* This would only get executed if a register was not in
    253 		 the register table.  */
    254 	      (*info->fprintf_func)
    255 		(info->stream, _("<unknown register %d>"), val & 0x3F);
    256 	    }
    257 	}
    258       /* repeati has a relocation, but its first argument is a plain
    259 	 immediate.  OTOH instructions like djsri have a pc-relative
    260 	 delay target, but an absolute jump target.  Therefore, a test
    261 	 of insn->op->reloc_flag is not specific enough; we must test
    262 	 if the actual operand we are handling now is pc-relative.  */
    263       else if (oper->flags & OPERAND_PCREL)
    264 	{
    265 	  int neg = 0;
    266 
    267 	  /* IMM6S3 is unsigned.  */
    268 	  if (oper->flags & OPERAND_SIGNED || bits == 32)
    269 	    {
    270 	      long max;
    271 	      max = (1 << (bits - 1));
    272 	      if (val & max)
    273 		{
    274 		  if (bits == 32)
    275 		    val = -val;
    276 		  else
    277 		    val = -val & ((1 << bits) - 1);
    278 		  neg = 1;
    279 		}
    280 	    }
    281 	  if (neg)
    282 	    {
    283 	      (*info->fprintf_func) (info->stream, "-%x\t(", val);
    284 	      (*info->print_address_func) ((memaddr - val) & PC_MASK, info);
    285 	      (*info->fprintf_func) (info->stream, ")");
    286 	    }
    287 	  else
    288 	    {
    289 	      (*info->fprintf_func) (info->stream, "%x\t(", val);
    290 	      (*info->print_address_func) ((memaddr + val) & PC_MASK, info);
    291 	      (*info->fprintf_func) (info->stream, ")");
    292 	    }
    293 	}
    294       else if (insn->op->reloc_flag == RELOC_ABS)
    295 	{
    296 	  (*info->print_address_func) (val, info);
    297 	}
    298       else
    299 	{
    300 	  if (oper->flags & OPERAND_SIGNED)
    301 	    {
    302 	      int max = (1 << (bits - 1));
    303 
    304 	      if (val & max)
    305 		{
    306 		  val = -val;
    307 		  if (bits < 32)
    308 		    val &= ((1 << bits) - 1);
    309 		  (*info->fprintf_func) (info->stream, "-");
    310 		}
    311 	    }
    312 	  (*info->fprintf_func) (info->stream, "0x%x", val);
    313 	}
    314       /* If there is another operand, then write a comma and space.  */
    315       if (insn->form->operands[opind] && !(found_control && opind == 2))
    316 	need_comma = 1;
    317     }
    318   if (need_paren)
    319     (*info->fprintf_func) (info->stream, ")");
    320 }
    321 
    322 int
    323 print_insn_d30v (bfd_vma memaddr, struct disassemble_info *info)
    324 {
    325   int status, result;
    326   bfd_byte buffer[12];
    327   unsigned long in1, in2;
    328   struct d30v_insn insn;
    329   long long num;
    330 
    331   insn.form = NULL;
    332 
    333   info->bytes_per_line = 8;
    334   info->bytes_per_chunk = 4;
    335   info->display_endian = BFD_ENDIAN_BIG;
    336 
    337   status = (*info->read_memory_func) (memaddr, buffer, 4, info);
    338   if (status != 0)
    339     {
    340       (*info->memory_error_func) (status, memaddr, info);
    341       return -1;
    342     }
    343   in1 = bfd_getb32 (buffer);
    344 
    345   status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info);
    346   if (status != 0)
    347     {
    348       info->bytes_per_line = 8;
    349       if (!(result = lookup_opcode (&insn, in1, 0)))
    350 	(*info->fprintf_func) (info->stream, ".long\t0x%lx", in1);
    351       else
    352 	print_insn (info, memaddr, (long long) in1, &insn, 0, result);
    353       return 4;
    354     }
    355   in2 = bfd_getb32 (buffer);
    356 
    357   if (in1 & in2 & FM01)
    358     {
    359       /* LONG instruction.  */
    360       if (!(result = lookup_opcode (&insn, in1, 1)))
    361 	{
    362 	  (*info->fprintf_func) (info->stream, ".long\t0x%lx,0x%lx", in1, in2);
    363 	  return 8;
    364 	}
    365       num = (long long) in1 << 32 | in2;
    366       print_insn (info, memaddr, num, &insn, 1, result);
    367     }
    368   else
    369     {
    370       num = in1;
    371       if (!(result = lookup_opcode (&insn, in1, 0)))
    372 	(*info->fprintf_func) (info->stream, ".long\t0x%lx", in1);
    373       else
    374 	print_insn (info, memaddr, num, &insn, 0, result);
    375 
    376       switch (((in1 >> 31) << 1) | (in2 >> 31))
    377 	{
    378 	case 0:
    379 	  (*info->fprintf_func) (info->stream, "\t||\t");
    380 	  break;
    381 	case 1:
    382 	  (*info->fprintf_func) (info->stream, "\t->\t");
    383 	  break;
    384 	case 2:
    385 	  (*info->fprintf_func) (info->stream, "\t<-\t");
    386 	default:
    387 	  break;
    388 	}
    389 
    390       insn.form = NULL;
    391       num = in2;
    392       if (!(result = lookup_opcode (&insn, in2, 0)))
    393 	(*info->fprintf_func) (info->stream, ".long\t0x%lx", in2);
    394       else
    395 	print_insn (info, memaddr, num, &insn, 0, result);
    396     }
    397   return 8;
    398 }
    399