(root)/
binutils-2.41/
opcodes/
d10v-dis.c
       1  /* Disassemble D10V instructions.
       2     Copyright (C) 1996-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/d10v.h"
      24  #include "disassemble.h"
      25  
      26  /* The PC wraps at 18 bits, except for the segment number,
      27     so use this mask to keep the parts we want.  */
      28  #define PC_MASK	0x0303FFFF
      29  
      30  static void
      31  print_operand (struct d10v_operand *oper,
      32  	       unsigned long insn,
      33  	       struct d10v_opcode *op,
      34  	       bfd_vma memaddr,
      35  	       struct disassemble_info *info)
      36  {
      37    int num, shift;
      38  
      39    if (oper->flags == OPERAND_ATMINUS)
      40      {
      41        (*info->fprintf_func) (info->stream, "@-");
      42        return;
      43      }
      44    if (oper->flags == OPERAND_MINUS)
      45      {
      46        (*info->fprintf_func) (info->stream, "-");
      47        return;
      48      }
      49    if (oper->flags == OPERAND_PLUS)
      50      {
      51        (*info->fprintf_func) (info->stream, "+");
      52        return;
      53      }
      54    if (oper->flags == OPERAND_ATSIGN)
      55      {
      56        (*info->fprintf_func) (info->stream, "@");
      57        return;
      58      }
      59    if (oper->flags == OPERAND_ATPAR)
      60      {
      61        (*info->fprintf_func) (info->stream, "@(");
      62        return;
      63      }
      64  
      65    shift = oper->shift;
      66  
      67    /* The LONG_L format shifts registers over by 15.  */
      68    if (op->format == LONG_L && (oper->flags & OPERAND_REG))
      69      shift += 15;
      70  
      71    num = (insn >> shift) & (0x7FFFFFFF >> (31 - oper->bits));
      72  
      73    if (oper->flags & OPERAND_REG)
      74      {
      75        int i;
      76        int match = 0;
      77  
      78        num += (oper->flags
      79  	      & (OPERAND_GPR | OPERAND_FFLAG | OPERAND_CFLAG | OPERAND_CONTROL));
      80        if (oper->flags & (OPERAND_ACC0 | OPERAND_ACC1))
      81  	num += num ? OPERAND_ACC1 : OPERAND_ACC0;
      82        for (i = 0; i < d10v_reg_name_cnt (); i++)
      83  	{
      84  	  if (num == (d10v_predefined_registers[i].value & ~ OPERAND_SP))
      85  	    {
      86  	      if (d10v_predefined_registers[i].pname)
      87  		(*info->fprintf_func) (info->stream, "%s",
      88  				       d10v_predefined_registers[i].pname);
      89  	      else
      90  		(*info->fprintf_func) (info->stream, "%s",
      91  				       d10v_predefined_registers[i].name);
      92  	      match = 1;
      93  	      break;
      94  	    }
      95  	}
      96        if (match == 0)
      97  	{
      98  	  /* This would only get executed if a register was not in the
      99  	     register table.  */
     100  	  if (oper->flags & (OPERAND_ACC0 | OPERAND_ACC1))
     101  	    (*info->fprintf_func) (info->stream, "a");
     102  	  else if (oper->flags & OPERAND_CONTROL)
     103  	    (*info->fprintf_func) (info->stream, "cr");
     104  	  else if (oper->flags & OPERAND_REG)
     105  	    (*info->fprintf_func) (info->stream, "r");
     106  	  (*info->fprintf_func) (info->stream, "%d", num & REGISTER_MASK);
     107  	}
     108      }
     109    else
     110      {
     111        /* Addresses are right-shifted by 2.  */
     112        if (oper->flags & OPERAND_ADDR)
     113  	{
     114  	  long max;
     115  	  int neg = 0;
     116  
     117  	  max = (1 << (oper->bits - 1));
     118  	  if (num & max)
     119  	    {
     120  	      num = -num & ((1 << oper->bits) - 1);
     121  	      neg = 1;
     122  	    }
     123  	  num = num << 2;
     124  	  if (info->flags & INSN_HAS_RELOC)
     125  	    (*info->print_address_func) (num & PC_MASK, info);
     126  	  else
     127  	    {
     128  	      if (neg)
     129  		(*info->print_address_func) ((memaddr - num) & PC_MASK, info);
     130  	      else
     131  		(*info->print_address_func) ((memaddr + num) & PC_MASK, info);
     132  	    }
     133  	}
     134        else
     135  	{
     136  	  if (oper->flags & OPERAND_SIGNED)
     137  	    {
     138  	      int max = (1 << (oper->bits - 1));
     139  	      if (num & max)
     140  		{
     141  		  num = -num & ((1 << oper->bits) - 1);
     142  		  (*info->fprintf_func) (info->stream, "-");
     143  		}
     144  	    }
     145  	  (*info->fprintf_func) (info->stream, "0x%x", num);
     146  	}
     147      }
     148  }
     149  
     150  static void
     151  dis_long (unsigned long insn,
     152  	  bfd_vma memaddr,
     153  	  struct disassemble_info *info)
     154  {
     155    int i;
     156    struct d10v_opcode *op = (struct d10v_opcode *) d10v_opcodes;
     157    struct d10v_operand *oper;
     158    int need_paren = 0;
     159    int match = 0;
     160  
     161    while (op->name)
     162      {
     163        if ((op->format & LONG_OPCODE)
     164  	  && ((op->mask & insn) == (unsigned long) op->opcode))
     165  	{
     166  	  match = 1;
     167  	  (*info->fprintf_func) (info->stream, "%s\t", op->name);
     168  
     169  	  for (i = 0; op->operands[i]; i++)
     170  	    {
     171  	      oper = (struct d10v_operand *) &d10v_operands[op->operands[i]];
     172  	      if (oper->flags == OPERAND_ATPAR)
     173  		need_paren = 1;
     174  	      print_operand (oper, insn, op, memaddr, info);
     175  	      if (op->operands[i + 1] && oper->bits
     176  		  && d10v_operands[op->operands[i + 1]].flags != OPERAND_PLUS
     177  		  && d10v_operands[op->operands[i + 1]].flags != OPERAND_MINUS)
     178  		(*info->fprintf_func) (info->stream, ", ");
     179  	    }
     180  	  break;
     181  	}
     182        op++;
     183      }
     184  
     185    if (!match)
     186      (*info->fprintf_func) (info->stream, ".long\t0x%08lx", insn);
     187  
     188    if (need_paren)
     189      (*info->fprintf_func) (info->stream, ")");
     190  }
     191  
     192  static void
     193  dis_2_short (unsigned long insn,
     194  	     bfd_vma memaddr,
     195  	     struct disassemble_info *info,
     196  	     int order)
     197  {
     198    int i, j;
     199    unsigned int ins[2];
     200    struct d10v_opcode *op;
     201    int match, num_match = 0;
     202    struct d10v_operand *oper;
     203    int need_paren = 0;
     204  
     205    ins[0] = (insn & 0x3FFFFFFF) >> 15;
     206    ins[1] = insn & 0x00007FFF;
     207  
     208    for (j = 0; j < 2; j++)
     209      {
     210        op = (struct d10v_opcode *) d10v_opcodes;
     211        match = 0;
     212        while (op->name)
     213  	{
     214  	  if ((op->format & SHORT_OPCODE)
     215  	      && ((((unsigned int) op->mask) & ins[j])
     216  		  == (unsigned int) op->opcode))
     217  	    {
     218  	      (*info->fprintf_func) (info->stream, "%s\t", op->name);
     219  	      for (i = 0; op->operands[i]; i++)
     220  		{
     221  		  oper = (struct d10v_operand *) &d10v_operands[op->operands[i]];
     222  		  if (oper->flags == OPERAND_ATPAR)
     223  		    need_paren = 1;
     224  		  print_operand (oper, ins[j], op, memaddr, info);
     225  		  if (op->operands[i + 1] && oper->bits
     226  		      && d10v_operands[op->operands[i + 1]].flags != OPERAND_PLUS
     227  		      && d10v_operands[op->operands[i + 1]].flags != OPERAND_MINUS)
     228  		    (*info->fprintf_func) (info->stream, ", ");
     229  		}
     230  	      match = 1;
     231  	      num_match++;
     232  	      break;
     233  	    }
     234  	  op++;
     235  	}
     236        if (!match)
     237  	(*info->fprintf_func) (info->stream, "unknown");
     238  
     239        switch (order)
     240  	{
     241  	case 0:
     242  	  (*info->fprintf_func) (info->stream, "\t->\t");
     243  	  order = -1;
     244  	  break;
     245  	case 1:
     246  	  (*info->fprintf_func) (info->stream, "\t<-\t");
     247  	  order = -1;
     248  	  break;
     249  	case 2:
     250  	  (*info->fprintf_func) (info->stream, "\t||\t");
     251  	  order = -1;
     252  	  break;
     253  	default:
     254  	  break;
     255  	}
     256      }
     257  
     258    if (num_match == 0)
     259      (*info->fprintf_func) (info->stream, ".long\t0x%08lx", insn);
     260  
     261    if (need_paren)
     262      (*info->fprintf_func) (info->stream, ")");
     263  }
     264  
     265  int
     266  print_insn_d10v (bfd_vma memaddr, struct disassemble_info *info)
     267  {
     268    int status;
     269    bfd_byte buffer[4];
     270    unsigned long insn;
     271  
     272    status = (*info->read_memory_func) (memaddr, buffer, 4, info);
     273    if (status != 0)
     274      {
     275        (*info->memory_error_func) (status, memaddr, info);
     276        return -1;
     277      }
     278    insn = bfd_getb32 (buffer);
     279  
     280    status = insn & FM11;
     281    switch (status)
     282      {
     283      case 0:
     284        dis_2_short (insn, memaddr, info, 2);
     285        break;
     286      case FM01:
     287        dis_2_short (insn, memaddr, info, 0);
     288        break;
     289      case FM10:
     290        dis_2_short (insn, memaddr, info, 1);
     291        break;
     292      case FM11:
     293        dis_long (insn, memaddr, info);
     294        break;
     295      }
     296    return 4;
     297  }