(root)/
binutils-2.41/
opcodes/
d30v-dis.c
       1  /* Disassemble D30V instructions.
       2     Copyright (C) 1997-2023 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  #include "libiberty.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 (uint64_t num, const struct d30v_operand *oper, int is_long)
      94  {
      95    unsigned int val;
      96    int shift = 12 - oper->position;
      97    unsigned 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  	    uint64_t num,
     122  	    struct d30v_insn *insn,
     123  	    int is_long,
     124  	    int show_ext)
     125  {
     126    unsigned int val, opnum;
     127    const struct d30v_operand *oper;
     128    int i, match, need_comma = 0, need_paren = 0, found_control = 0;
     129    unsigned int opind = 0;
     130  
     131    (*info->fprintf_func) (info->stream, "%s", insn->op->name);
     132  
     133    /* Check for CMP or CMPU.  */
     134    if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME)
     135      {
     136        opind++;
     137        val =
     138  	extract_value (num,
     139  		       &d30v_operand_table[insn->form->operands[0]],
     140  		       is_long);
     141        (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]);
     142      }
     143  
     144    /* Add in ".s" or ".l".  */
     145    if (show_ext == 2)
     146      {
     147        if (is_long)
     148  	(*info->fprintf_func) (info->stream, ".l");
     149        else
     150  	(*info->fprintf_func) (info->stream, ".s");
     151      }
     152  
     153    if (insn->ecc)
     154      (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]);
     155  
     156    (*info->fprintf_func) (info->stream, "\t");
     157  
     158    while (opind < ARRAY_SIZE (insn->form->operands)
     159  	 && (opnum = insn->form->operands[opind++]) != 0)
     160      {
     161        int bits;
     162  
     163        oper = &d30v_operand_table[opnum];
     164        bits = oper->bits;
     165        if (oper->flags & OPERAND_SHIFT)
     166  	bits += 3;
     167  
     168        if (need_comma
     169  	  && oper->flags != OPERAND_PLUS
     170  	  && oper->flags != OPERAND_MINUS)
     171  	{
     172  	  need_comma = 0;
     173  	  (*info->fprintf_func) (info->stream, ", ");
     174  	}
     175  
     176        if (oper->flags == OPERAND_ATMINUS)
     177  	{
     178  	  (*info->fprintf_func) (info->stream, "@-");
     179  	  continue;
     180  	}
     181        if (oper->flags == OPERAND_MINUS)
     182  	{
     183  	  (*info->fprintf_func) (info->stream, "-");
     184  	  continue;
     185  	}
     186        if (oper->flags == OPERAND_PLUS)
     187  	{
     188  	  (*info->fprintf_func) (info->stream, "+");
     189  	  continue;
     190  	}
     191        if (oper->flags == OPERAND_ATSIGN)
     192  	{
     193  	  (*info->fprintf_func) (info->stream, "@");
     194  	  continue;
     195  	}
     196        if (oper->flags == OPERAND_ATPAR)
     197  	{
     198  	  (*info->fprintf_func) (info->stream, "@(");
     199  	  need_paren = 1;
     200  	  continue;
     201  	}
     202  
     203        if (oper->flags == OPERAND_SPECIAL)
     204  	continue;
     205  
     206        val = extract_value (num, oper, is_long);
     207  
     208        if (oper->flags & OPERAND_REG)
     209  	{
     210  	  match = 0;
     211  	  if (oper->flags & OPERAND_CONTROL)
     212  	    {
     213  	      const struct d30v_operand *oper3
     214  		= &d30v_operand_table[insn->form->operands[2]];
     215  	      int id = extract_value (num, oper3, is_long);
     216  
     217  	      found_control = 1;
     218  	      switch (id)
     219  		{
     220  		case 0:
     221  		  val |= OPERAND_CONTROL;
     222  		  break;
     223  		case 1:
     224  		case 2:
     225  		  val = OPERAND_CONTROL + MAX_CONTROL_REG + id;
     226  		  break;
     227  		case 3:
     228  		  val |= OPERAND_FLAG;
     229  		  break;
     230  		default:
     231  		  /* xgettext: c-format */
     232  		  opcodes_error_handler (_("illegal id (%d)"), id);
     233  		  abort ();
     234  		}
     235  	    }
     236  	  else if (oper->flags & OPERAND_ACC)
     237  	    val |= OPERAND_ACC;
     238  	  else if (oper->flags & OPERAND_FLAG)
     239  	    val |= OPERAND_FLAG;
     240  	  for (i = 0; i < reg_name_cnt (); i++)
     241  	    {
     242  	      if (val == pre_defined_registers[i].value)
     243  		{
     244  		  if (pre_defined_registers[i].pname)
     245  		    (*info->fprintf_func)
     246  		      (info->stream, "%s", pre_defined_registers[i].pname);
     247  		  else
     248  		    (*info->fprintf_func)
     249  		      (info->stream, "%s", pre_defined_registers[i].name);
     250  		  match = 1;
     251  		  break;
     252  		}
     253  	    }
     254  	  if (match == 0)
     255  	    {
     256  	      /* This would only get executed if a register was not in
     257  		 the register table.  */
     258  	      (*info->fprintf_func)
     259  		(info->stream, _("<unknown register %d>"), val & 0x3F);
     260  	    }
     261  	}
     262        /* repeati has a relocation, but its first argument is a plain
     263  	 immediate.  OTOH instructions like djsri have a pc-relative
     264  	 delay target, but an absolute jump target.  Therefore, a test
     265  	 of insn->op->reloc_flag is not specific enough; we must test
     266  	 if the actual operand we are handling now is pc-relative.  */
     267        else if (oper->flags & OPERAND_PCREL)
     268  	{
     269  	  int neg = 0;
     270  
     271  	  /* IMM6S3 is unsigned.  */
     272  	  if (oper->flags & OPERAND_SIGNED || bits == 32)
     273  	    {
     274  	      unsigned int sign = 1u << (bits - 1);
     275  	      if (val & sign)
     276  		{
     277  		  val = -val & (sign + sign - 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  	      unsigned int sign = 1u << (bits - 1);
     303  
     304  	      if (val & sign)
     305  		{
     306  		  val = -val & (sign + sign - 1);
     307  		  (*info->fprintf_func) (info->stream, "-");
     308  		}
     309  	    }
     310  	  (*info->fprintf_func) (info->stream, "0x%x", val);
     311  	}
     312        /* If there is another operand, then write a comma and space.  */
     313        if (opind < ARRAY_SIZE (insn->form->operands)
     314  	  && insn->form->operands[opind]
     315  	  && !(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    uint32_t in1, in2;
     328    struct d30v_insn insn;
     329    uint64_t 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%x", in1);
     351        else
     352  	print_insn (info, memaddr, (uint64_t) 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%x,0x%x", in1, in2);
     363  	  return 8;
     364  	}
     365        num = (uint64_t) 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%x", 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%x", in2);
     394        else
     395  	print_insn (info, memaddr, num, &insn, 0, result);
     396      }
     397    return 8;
     398  }