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