Automatic date update in version.in
[binutils-gdb.git] / opcodes / pru-dis.c
blobc564bc0a2cf24cc10371f261c4b23a0881ac08bd
1 /* TI PRU disassemble routines
2 Copyright (C) 2014-2024 Free Software Foundation, Inc.
3 Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
5 This file is part of the GNU opcodes library.
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.
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.
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. */
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>
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
37 /* Length of PRU instruction in bytes. */
38 #define INSNLEN 4
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)
45 const struct pru_opcode *p;
46 const struct pru_opcode *op = NULL;
47 const struct pru_opcode *pseudo_op = NULL;
49 for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++)
51 if ((p->mask & opcode) == p->match)
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;
62 return pseudo_op ? pseudo_op : op;
65 /* There are 32 regular registers, each with 8 possible subfield selectors. */
66 #define NUMREGNAMES (32 * 8)
68 static void
69 pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
70 disassemble_info *info)
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);
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)
86 long offs = 0;
87 unsigned long i = 0;
88 unsigned long io = 0;
90 switch (*argptr)
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);
120 if (io)
122 i = GET_INSN_FIELD (IMM8, opcode);
123 (*info->fprintf_func) (info->stream, "%ld", i);
125 else
127 pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
128 GET_INSN_FIELD (RS2SEL, opcode),
129 info);
131 break;
132 case 'B':
133 io = GET_INSN_FIELD (IO, opcode);
135 if (io)
137 i = GET_INSN_FIELD (IMM8, opcode) + 1;
138 (*info->fprintf_func) (info->stream, "%ld", i);
140 else
142 pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
143 GET_INSN_FIELD (RS2SEL, opcode),
144 info);
146 break;
147 case 'j':
148 io = GET_INSN_FIELD (IO, opcode);
150 if (io)
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);
160 else
162 pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
163 GET_INSN_FIELD (RS2SEL, opcode),
164 info);
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
185 i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
186 (*info->fprintf_func) (info->stream, "r0.b%ld", i);
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
195 i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
196 (*info->fprintf_func) (info->stream, "r0.b%ld", i);
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;
215 return 0;
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)
224 const struct pru_opcode *op;
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;
236 /* Find the major opcode and use this to disassemble
237 the instruction and its arguments. */
238 op = pru_find_opcode (opcode);
240 if (op != NULL)
242 (*info->fprintf_func) (info->stream, "%s", op->name);
244 const char *argstr = op->args;
245 if (argstr != NULL && *argstr != '\0')
247 (*info->fprintf_func) (info->stream, "\t");
248 while (*argstr != '\0')
250 pru_print_insn_arg (argstr, opcode, address, info);
251 ++argstr;
255 else
257 /* Handle undefined instructions. */
258 info->insn_type = dis_noninsn;
259 (*info->fprintf_func) (info->stream, "0x%lx", opcode);
261 /* Tell the caller how far to advance the program counter. */
262 return INSNLEN;
266 /* print_insn_pru is the main disassemble function for PRU. */
268 print_insn_pru (bfd_vma address, disassemble_info *info)
270 bfd_byte buffer[INSNLEN];
271 int status;
273 status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
274 if (status == 0)
276 unsigned long insn;
277 insn = (unsigned long) bfd_getl32 (buffer);
278 status = pru_disassemble (address, insn, info);
280 else
282 (*info->memory_error_func) (status, address, info);
283 status = -1;
285 return status;