(root)/
binutils-2.41/
opcodes/
pru-dis.c
       1  /* TI PRU disassemble routines
       2     Copyright (C) 2014-2023 Free Software Foundation, Inc.
       3     Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
       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 file; see the file COPYING.  If not, write to the
      19     Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
      20     MA 02110-1301, USA.  */
      21  
      22  #include "sysdep.h"
      23  #include "disassemble.h"
      24  #include "opcode/pru.h"
      25  #include "libiberty.h"
      26  #include <string.h>
      27  #include <assert.h>
      28  
      29  /* No symbol table is available when this code runs out in an embedded
      30     system as when it is used for disassembler support in a monitor.  */
      31  #if !defined (EMBEDDED_ENV)
      32  #define SYMTAB_AVAILABLE 1
      33  #include "elf-bfd.h"
      34  #include "elf/pru.h"
      35  #endif
      36  
      37  /* Length of PRU instruction in bytes.  */
      38  #define INSNLEN 4
      39  
      40  /* Return a pointer to an pru_opcode struct for a given instruction
      41     opcode, or NULL if there is an error.  */
      42  const struct pru_opcode *
      43  pru_find_opcode (unsigned long opcode)
      44  {
      45    const struct pru_opcode *p;
      46    const struct pru_opcode *op = NULL;
      47    const struct pru_opcode *pseudo_op = NULL;
      48  
      49    for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++)
      50      {
      51        if ((p->mask & opcode) == p->match)
      52  	{
      53  	  if ((p->pinfo & PRU_INSN_MACRO) == PRU_INSN_MACRO)
      54  	    pseudo_op = p;
      55  	  else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32)
      56  	    /* ignore - should be caught with regular patterns */;
      57  	  else
      58  	    op = p;
      59  	}
      60      }
      61  
      62    return pseudo_op ? pseudo_op : op;
      63  }
      64  
      65  /* There are 32 regular registers, each with 8 possible subfield selectors.  */
      66  #define NUMREGNAMES (32 * 8)
      67  
      68  static void
      69  pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
      70  			disassemble_info *info)
      71  {
      72    unsigned int i = r * RSEL_NUM_ITEMS + sel;
      73    assert (i < (unsigned int)pru_num_regs);
      74    assert (i < NUMREGNAMES);
      75    (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
      76  }
      77  
      78  /* The function pru_print_insn_arg uses the character pointed
      79     to by ARGPTR to determine how it print the next token or separator
      80     character in the arguments to an instruction.  */
      81  static int
      82  pru_print_insn_arg (const char *argptr,
      83  		      unsigned long opcode, bfd_vma address,
      84  		      disassemble_info *info)
      85  {
      86    long offs = 0;
      87    unsigned long i = 0;
      88    unsigned long io = 0;
      89  
      90    switch (*argptr)
      91      {
      92      case ',':
      93        (*info->fprintf_func) (info->stream, "%c ", *argptr);
      94        break;
      95      case 'd':
      96        pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
      97  			      GET_INSN_FIELD (RDSEL, opcode),
      98  			      info);
      99        break;
     100      case 'D':
     101        /* The first 4 values for RDB and RSEL are the same, so we
     102  	 can reuse some code.  */
     103        pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
     104  			      GET_INSN_FIELD (RDB, opcode),
     105  			      info);
     106        break;
     107      case 's':
     108        pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
     109  			      GET_INSN_FIELD (RS1SEL, opcode),
     110  			      info);
     111        break;
     112      case 'S':
     113        pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
     114  			      RSEL_31_0,
     115  			      info);
     116        break;
     117      case 'b':
     118        io = GET_INSN_FIELD (IO, opcode);
     119  
     120        if (io)
     121  	{
     122  	  i = GET_INSN_FIELD (IMM8, opcode);
     123  	  (*info->fprintf_func) (info->stream, "%ld", i);
     124  	}
     125        else
     126  	{
     127  	pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
     128  				GET_INSN_FIELD (RS2SEL, opcode),
     129  				info);
     130  	}
     131        break;
     132      case 'B':
     133        io = GET_INSN_FIELD (IO, opcode);
     134  
     135        if (io)
     136  	{
     137  	  i = GET_INSN_FIELD (IMM8, opcode) + 1;
     138  	  (*info->fprintf_func) (info->stream, "%ld", i);
     139  	}
     140        else
     141  	{
     142  	pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
     143  				GET_INSN_FIELD (RS2SEL, opcode),
     144  				info);
     145  	}
     146        break;
     147      case 'j':
     148        io = GET_INSN_FIELD (IO, opcode);
     149  
     150        if (io)
     151  	{
     152  	  /* For the sake of pretty-printing, dump text addresses with
     153  	     their "virtual" offset that we use for distinguishing
     154  	     PMEM vs DMEM. This is needed for printing the correct text
     155  	     labels.  */
     156  	  bfd_vma text_offset = address & ~0x3fffff;
     157  	  i = GET_INSN_FIELD (IMM16, opcode) * 4;
     158  	  (*info->print_address_func) (i + text_offset, info);
     159  	}
     160        else
     161  	{
     162  	  pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
     163  				GET_INSN_FIELD (RS2SEL, opcode),
     164  				info);
     165  	}
     166        break;
     167      case 'W':
     168        i = GET_INSN_FIELD (IMM16, opcode);
     169        (*info->fprintf_func) (info->stream, "%ld", i);
     170        break;
     171      case 'o':
     172        offs = GET_BROFF_SIGNED (opcode) * 4;
     173        (*info->print_address_func) (address + offs, info);
     174        break;
     175      case 'O':
     176        offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4;
     177        (*info->print_address_func) (address + offs, info);
     178        break;
     179      case 'l':
     180        i = GET_BURSTLEN (opcode);
     181        if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
     182  	(*info->fprintf_func) (info->stream, "%ld", i + 1);
     183        else
     184  	{
     185  	  i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
     186  	  (*info->fprintf_func) (info->stream, "r0.b%ld", i);
     187  	}
     188        break;
     189      case 'n':
     190        i = GET_INSN_FIELD (XFR_LENGTH, opcode);
     191        if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
     192  	(*info->fprintf_func) (info->stream, "%ld", i + 1);
     193        else
     194  	{
     195  	  i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
     196  	  (*info->fprintf_func) (info->stream, "r0.b%ld", i);
     197  	}
     198        break;
     199      case 'c':
     200        i = GET_INSN_FIELD (CB, opcode);
     201        (*info->fprintf_func) (info->stream, "%ld", i);
     202        break;
     203      case 'w':
     204        i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
     205        (*info->fprintf_func) (info->stream, "%ld", i);
     206        break;
     207      case 'x':
     208        i = GET_INSN_FIELD (XFR_WBA, opcode);
     209        (*info->fprintf_func) (info->stream, "%ld", i);
     210        break;
     211      default:
     212        (*info->fprintf_func) (info->stream, "unknown");
     213        break;
     214      }
     215    return 0;
     216  }
     217  
     218  /* pru_disassemble does all the work of disassembling a PRU
     219     instruction opcode.  */
     220  static int
     221  pru_disassemble (bfd_vma address, unsigned long opcode,
     222  		   disassemble_info *info)
     223  {
     224    const struct pru_opcode *op;
     225  
     226    info->bytes_per_line = INSNLEN;
     227    info->bytes_per_chunk = INSNLEN;
     228    info->display_endian = info->endian;
     229    info->insn_info_valid = 1;
     230    info->branch_delay_insns = 0;
     231    info->data_size = 0;
     232    info->insn_type = dis_nonbranch;
     233    info->target = 0;
     234    info->target2 = 0;
     235  
     236    /* Find the major opcode and use this to disassemble
     237       the instruction and its arguments.  */
     238    op = pru_find_opcode (opcode);
     239  
     240    if (op != NULL)
     241      {
     242        (*info->fprintf_func) (info->stream, "%s", op->name);
     243  
     244        const char *argstr = op->args;
     245        if (argstr != NULL && *argstr != '\0')
     246  	{
     247  	  (*info->fprintf_func) (info->stream, "\t");
     248  	  while (*argstr != '\0')
     249  	    {
     250  	      pru_print_insn_arg (argstr, opcode, address, info);
     251  	      ++argstr;
     252  	    }
     253  	}
     254      }
     255    else
     256      {
     257        /* Handle undefined instructions.  */
     258        info->insn_type = dis_noninsn;
     259        (*info->fprintf_func) (info->stream, "0x%lx", opcode);
     260      }
     261    /* Tell the caller how far to advance the program counter.  */
     262    return INSNLEN;
     263  }
     264  
     265  
     266  /* print_insn_pru is the main disassemble function for PRU.  */
     267  int
     268  print_insn_pru (bfd_vma address, disassemble_info *info)
     269  {
     270    bfd_byte buffer[INSNLEN];
     271    int status;
     272  
     273    status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
     274    if (status == 0)
     275      {
     276        unsigned long insn;
     277        insn = (unsigned long) bfd_getl32 (buffer);
     278        status = pru_disassemble (address, insn, info);
     279      }
     280    else
     281      {
     282        (*info->memory_error_func) (status, address, info);
     283        status = -1;
     284      }
     285    return status;
     286  }