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