]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - opcodes/pru-dis.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / opcodes / pru-dis.c
CommitLineData
11146849 1/* TI PRU disassemble routines
fd67aa11 2 Copyright (C) 2014-2024 Free Software Foundation, Inc.
11146849
DD
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"
88c1242d 23#include "disassemble.h"
11146849
DD
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. */
42const struct pru_opcode *
43pru_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
68static void
69pru_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. */
81static int
82pru_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. */
220static int
221pru_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. */
267int
268print_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}