xref: /netbsd/external/gpl3/gdb/dist/opcodes/pru-dis.c (revision 1424dfb3)
11c468f90Schristos /* TI PRU disassemble routines
2*1424dfb3Schristos    Copyright (C) 2014-2020 Free Software Foundation, Inc.
31c468f90Schristos    Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
41c468f90Schristos 
51c468f90Schristos    This file is part of the GNU opcodes library.
61c468f90Schristos 
71c468f90Schristos    This library is free software; you can redistribute it and/or modify
81c468f90Schristos    it under the terms of the GNU General Public License as published by
91c468f90Schristos    the Free Software Foundation; either version 3, or (at your option)
101c468f90Schristos    any later version.
111c468f90Schristos 
121c468f90Schristos    It is distributed in the hope that it will be useful, but WITHOUT
131c468f90Schristos    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
141c468f90Schristos    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
151c468f90Schristos    License for more details.
161c468f90Schristos 
171c468f90Schristos    You should have received a copy of the GNU General Public License
181c468f90Schristos    along with this file; see the file COPYING.  If not, write to the
191c468f90Schristos    Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
201c468f90Schristos    MA 02110-1301, USA.  */
211c468f90Schristos 
221c468f90Schristos #include "sysdep.h"
2307163879Schristos #include "disassemble.h"
241c468f90Schristos #include "opcode/pru.h"
251c468f90Schristos #include "libiberty.h"
261c468f90Schristos #include <string.h>
271c468f90Schristos #include <assert.h>
281c468f90Schristos 
291c468f90Schristos /* No symbol table is available when this code runs out in an embedded
301c468f90Schristos    system as when it is used for disassembler support in a monitor.  */
311c468f90Schristos #if !defined (EMBEDDED_ENV)
321c468f90Schristos #define SYMTAB_AVAILABLE 1
331c468f90Schristos #include "elf-bfd.h"
341c468f90Schristos #include "elf/pru.h"
351c468f90Schristos #endif
361c468f90Schristos 
371c468f90Schristos /* Length of PRU instruction in bytes.  */
381c468f90Schristos #define INSNLEN 4
391c468f90Schristos 
401c468f90Schristos /* Return a pointer to an pru_opcode struct for a given instruction
411c468f90Schristos    opcode, or NULL if there is an error.  */
421c468f90Schristos const struct pru_opcode *
pru_find_opcode(unsigned long opcode)431c468f90Schristos pru_find_opcode (unsigned long opcode)
441c468f90Schristos {
451c468f90Schristos   const struct pru_opcode *p;
461c468f90Schristos   const struct pru_opcode *op = NULL;
471c468f90Schristos   const struct pru_opcode *pseudo_op = NULL;
481c468f90Schristos 
491c468f90Schristos   for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++)
501c468f90Schristos     {
511c468f90Schristos       if ((p->mask & opcode) == p->match)
521c468f90Schristos 	{
531c468f90Schristos 	  if ((p->pinfo & PRU_INSN_MACRO) == PRU_INSN_MACRO)
541c468f90Schristos 	    pseudo_op = p;
551c468f90Schristos 	  else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32)
561c468f90Schristos 	    /* ignore - should be caught with regular patterns */;
571c468f90Schristos 	  else
581c468f90Schristos 	    op = p;
591c468f90Schristos 	}
601c468f90Schristos     }
611c468f90Schristos 
621c468f90Schristos   return pseudo_op ? pseudo_op : op;
631c468f90Schristos }
641c468f90Schristos 
651c468f90Schristos /* There are 32 regular registers, each with 8 possible subfield selectors.  */
661c468f90Schristos #define NUMREGNAMES (32 * 8)
671c468f90Schristos 
681c468f90Schristos static void
pru_print_insn_arg_reg(unsigned int r,unsigned int sel,disassemble_info * info)691c468f90Schristos pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
701c468f90Schristos 			disassemble_info *info)
711c468f90Schristos {
721c468f90Schristos   unsigned int i = r * RSEL_NUM_ITEMS + sel;
731c468f90Schristos   assert (i < (unsigned int)pru_num_regs);
741c468f90Schristos   assert (i < NUMREGNAMES);
751c468f90Schristos   (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
761c468f90Schristos }
771c468f90Schristos 
781c468f90Schristos /* The function pru_print_insn_arg uses the character pointed
791c468f90Schristos    to by ARGPTR to determine how it print the next token or separator
801c468f90Schristos    character in the arguments to an instruction.  */
811c468f90Schristos static int
pru_print_insn_arg(const char * argptr,unsigned long opcode,bfd_vma address,disassemble_info * info)821c468f90Schristos pru_print_insn_arg (const char *argptr,
831c468f90Schristos 		      unsigned long opcode, bfd_vma address,
841c468f90Schristos 		      disassemble_info *info)
851c468f90Schristos {
861c468f90Schristos   long offs = 0;
871c468f90Schristos   unsigned long i = 0;
881c468f90Schristos   unsigned long io = 0;
891c468f90Schristos 
901c468f90Schristos   switch (*argptr)
911c468f90Schristos     {
921c468f90Schristos     case ',':
931c468f90Schristos       (*info->fprintf_func) (info->stream, "%c ", *argptr);
941c468f90Schristos       break;
951c468f90Schristos     case 'd':
961c468f90Schristos       pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
971c468f90Schristos 			      GET_INSN_FIELD (RDSEL, opcode),
981c468f90Schristos 			      info);
991c468f90Schristos       break;
1001c468f90Schristos     case 'D':
1011c468f90Schristos       /* The first 4 values for RDB and RSEL are the same, so we
1021c468f90Schristos 	 can reuse some code.  */
1031c468f90Schristos       pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
1041c468f90Schristos 			      GET_INSN_FIELD (RDB, opcode),
1051c468f90Schristos 			      info);
1061c468f90Schristos       break;
1071c468f90Schristos     case 's':
1081c468f90Schristos       pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
1091c468f90Schristos 			      GET_INSN_FIELD (RS1SEL, opcode),
1101c468f90Schristos 			      info);
1111c468f90Schristos       break;
1121c468f90Schristos     case 'S':
1131c468f90Schristos       pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
1141c468f90Schristos 			      RSEL_31_0,
1151c468f90Schristos 			      info);
1161c468f90Schristos       break;
1171c468f90Schristos     case 'b':
1181c468f90Schristos       io = GET_INSN_FIELD (IO, opcode);
1191c468f90Schristos 
1201c468f90Schristos       if (io)
1211c468f90Schristos 	{
1221c468f90Schristos 	  i = GET_INSN_FIELD (IMM8, opcode);
1231c468f90Schristos 	  (*info->fprintf_func) (info->stream, "%ld", i);
1241c468f90Schristos 	}
1251c468f90Schristos       else
1261c468f90Schristos 	{
1271c468f90Schristos 	pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
1281c468f90Schristos 				GET_INSN_FIELD (RS2SEL, opcode),
1291c468f90Schristos 				info);
1301c468f90Schristos 	}
1311c468f90Schristos       break;
1321c468f90Schristos     case 'B':
1331c468f90Schristos       io = GET_INSN_FIELD (IO, opcode);
1341c468f90Schristos 
1351c468f90Schristos       if (io)
1361c468f90Schristos 	{
1371c468f90Schristos 	  i = GET_INSN_FIELD (IMM8, opcode) + 1;
1381c468f90Schristos 	  (*info->fprintf_func) (info->stream, "%ld", i);
1391c468f90Schristos 	}
1401c468f90Schristos       else
1411c468f90Schristos 	{
1421c468f90Schristos 	pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
1431c468f90Schristos 				GET_INSN_FIELD (RS2SEL, opcode),
1441c468f90Schristos 				info);
1451c468f90Schristos 	}
1461c468f90Schristos       break;
1471c468f90Schristos     case 'j':
1481c468f90Schristos       io = GET_INSN_FIELD (IO, opcode);
1491c468f90Schristos 
1501c468f90Schristos       if (io)
1511c468f90Schristos 	{
1521c468f90Schristos 	  /* For the sake of pretty-printing, dump text addresses with
1531c468f90Schristos 	     their "virtual" offset that we use for distinguishing
1541c468f90Schristos 	     PMEM vs DMEM. This is needed for printing the correct text
1551c468f90Schristos 	     labels.  */
1561c468f90Schristos 	  bfd_vma text_offset = address & ~0x3fffff;
1571c468f90Schristos 	  i = GET_INSN_FIELD (IMM16, opcode) * 4;
1581c468f90Schristos 	  (*info->print_address_func) (i + text_offset, info);
1591c468f90Schristos 	}
1601c468f90Schristos       else
1611c468f90Schristos 	{
1621c468f90Schristos 	  pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
1631c468f90Schristos 				GET_INSN_FIELD (RS2SEL, opcode),
1641c468f90Schristos 				info);
1651c468f90Schristos 	}
1661c468f90Schristos       break;
1671c468f90Schristos     case 'W':
1681c468f90Schristos       i = GET_INSN_FIELD (IMM16, opcode);
1691c468f90Schristos       (*info->fprintf_func) (info->stream, "%ld", i);
1701c468f90Schristos       break;
1711c468f90Schristos     case 'o':
1721c468f90Schristos       offs = GET_BROFF_SIGNED (opcode) * 4;
1731c468f90Schristos       (*info->print_address_func) (address + offs, info);
1741c468f90Schristos       break;
1751c468f90Schristos     case 'O':
1761c468f90Schristos       offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4;
1771c468f90Schristos       (*info->print_address_func) (address + offs, info);
1781c468f90Schristos       break;
1791c468f90Schristos     case 'l':
1801c468f90Schristos       i = GET_BURSTLEN (opcode);
1811c468f90Schristos       if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
1821c468f90Schristos 	(*info->fprintf_func) (info->stream, "%ld", i + 1);
1831c468f90Schristos       else
1841c468f90Schristos 	{
1851c468f90Schristos 	  i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
1861c468f90Schristos 	  (*info->fprintf_func) (info->stream, "r0.b%ld", i);
1871c468f90Schristos 	}
1881c468f90Schristos       break;
1891c468f90Schristos     case 'n':
1901c468f90Schristos       i = GET_INSN_FIELD (XFR_LENGTH, opcode);
1911c468f90Schristos       if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
1921c468f90Schristos 	(*info->fprintf_func) (info->stream, "%ld", i + 1);
1931c468f90Schristos       else
1941c468f90Schristos 	{
1951c468f90Schristos 	  i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
1961c468f90Schristos 	  (*info->fprintf_func) (info->stream, "r0.b%ld", i);
1971c468f90Schristos 	}
1981c468f90Schristos       break;
1991c468f90Schristos     case 'c':
2001c468f90Schristos       i = GET_INSN_FIELD (CB, opcode);
2011c468f90Schristos       (*info->fprintf_func) (info->stream, "%ld", i);
2021c468f90Schristos       break;
2031c468f90Schristos     case 'w':
2041c468f90Schristos       i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
2051c468f90Schristos       (*info->fprintf_func) (info->stream, "%ld", i);
2061c468f90Schristos       break;
2071c468f90Schristos     case 'x':
2081c468f90Schristos       i = GET_INSN_FIELD (XFR_WBA, opcode);
2091c468f90Schristos       (*info->fprintf_func) (info->stream, "%ld", i);
2101c468f90Schristos       break;
2111c468f90Schristos     default:
2121c468f90Schristos       (*info->fprintf_func) (info->stream, "unknown");
2131c468f90Schristos       break;
2141c468f90Schristos     }
2151c468f90Schristos   return 0;
2161c468f90Schristos }
2171c468f90Schristos 
2181c468f90Schristos /* pru_disassemble does all the work of disassembling a PRU
2191c468f90Schristos    instruction opcode.  */
2201c468f90Schristos static int
pru_disassemble(bfd_vma address,unsigned long opcode,disassemble_info * info)2211c468f90Schristos pru_disassemble (bfd_vma address, unsigned long opcode,
2221c468f90Schristos 		   disassemble_info *info)
2231c468f90Schristos {
2241c468f90Schristos   const struct pru_opcode *op;
2251c468f90Schristos 
2261c468f90Schristos   info->bytes_per_line = INSNLEN;
2271c468f90Schristos   info->bytes_per_chunk = INSNLEN;
2281c468f90Schristos   info->display_endian = info->endian;
2291c468f90Schristos   info->insn_info_valid = 1;
2301c468f90Schristos   info->branch_delay_insns = 0;
2311c468f90Schristos   info->data_size = 0;
2321c468f90Schristos   info->insn_type = dis_nonbranch;
2331c468f90Schristos   info->target = 0;
2341c468f90Schristos   info->target2 = 0;
2351c468f90Schristos 
2361c468f90Schristos   /* Find the major opcode and use this to disassemble
2371c468f90Schristos      the instruction and its arguments.  */
2381c468f90Schristos   op = pru_find_opcode (opcode);
2391c468f90Schristos 
2401c468f90Schristos   if (op != NULL)
2411c468f90Schristos     {
2421c468f90Schristos       (*info->fprintf_func) (info->stream, "%s", op->name);
2431c468f90Schristos 
2441c468f90Schristos       const char *argstr = op->args;
2451c468f90Schristos       if (argstr != NULL && *argstr != '\0')
2461c468f90Schristos 	{
2471c468f90Schristos 	  (*info->fprintf_func) (info->stream, "\t");
2481c468f90Schristos 	  while (*argstr != '\0')
2491c468f90Schristos 	    {
2501c468f90Schristos 	      pru_print_insn_arg (argstr, opcode, address, info);
2511c468f90Schristos 	      ++argstr;
2521c468f90Schristos 	    }
2531c468f90Schristos 	}
2541c468f90Schristos     }
2551c468f90Schristos   else
2561c468f90Schristos     {
2571c468f90Schristos       /* Handle undefined instructions.  */
2581c468f90Schristos       info->insn_type = dis_noninsn;
2591c468f90Schristos       (*info->fprintf_func) (info->stream, "0x%lx", opcode);
2601c468f90Schristos     }
2611c468f90Schristos   /* Tell the caller how far to advance the program counter.  */
2621c468f90Schristos   return INSNLEN;
2631c468f90Schristos }
2641c468f90Schristos 
2651c468f90Schristos 
2661c468f90Schristos /* print_insn_pru is the main disassemble function for PRU.  */
2671c468f90Schristos int
print_insn_pru(bfd_vma address,disassemble_info * info)2681c468f90Schristos print_insn_pru (bfd_vma address, disassemble_info *info)
2691c468f90Schristos {
2701c468f90Schristos   bfd_byte buffer[INSNLEN];
2711c468f90Schristos   int status;
2721c468f90Schristos 
2731c468f90Schristos   status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
2741c468f90Schristos   if (status == 0)
2751c468f90Schristos     {
2761c468f90Schristos       unsigned long insn;
2771c468f90Schristos       insn = (unsigned long) bfd_getl32 (buffer);
2781c468f90Schristos       status = pru_disassemble (address, insn, info);
2791c468f90Schristos     }
2801c468f90Schristos   else
2811c468f90Schristos     {
2821c468f90Schristos       (*info->memory_error_func) (status, address, info);
2831c468f90Schristos       status = -1;
2841c468f90Schristos     }
2851c468f90Schristos   return status;
2861c468f90Schristos }
287