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