1 /* TI PRU disassemble routines
2 Copyright (C) 2014-2019 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)
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. */
23 #include "disassemble.h"
24 #include "opcode/pru.h"
25 #include "libiberty.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
37 /* Length of PRU instruction in bytes. */
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)
55 else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32)
56 /* ignore - should be caught with regular patterns */;
62 return pseudo_op ? pseudo_op : op;
65 /* There are 32 regular registers, each with 8 possible subfield selectors. */
66 #define NUMREGNAMES (32 * 8)
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. */
82 pru_print_insn_arg (const char *argptr,
83 unsigned long opcode, bfd_vma address,
84 disassemble_info *info)
93 (*info->fprintf_func) (info->stream, "%c ", *argptr);
96 pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
97 GET_INSN_FIELD (RDSEL, opcode),
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),
108 pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
109 GET_INSN_FIELD (RS1SEL, opcode),
113 pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
118 io = GET_INSN_FIELD (IO, opcode);
122 i = GET_INSN_FIELD (IMM8, opcode);
123 (*info->fprintf_func) (info->stream, "%ld", i);
127 pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
128 GET_INSN_FIELD (RS2SEL, opcode),
133 io = GET_INSN_FIELD (IO, opcode);
137 i = GET_INSN_FIELD (IMM8, opcode) + 1;
138 (*info->fprintf_func) (info->stream, "%ld", i);
142 pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
143 GET_INSN_FIELD (RS2SEL, opcode),
148 io = GET_INSN_FIELD (IO, opcode);
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
156 bfd_vma text_offset = address & ~0x3fffff;
157 i = GET_INSN_FIELD (IMM16, opcode) * 4;
158 (*info->print_address_func) (i + text_offset, info);
162 pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
163 GET_INSN_FIELD (RS2SEL, opcode),
168 i = GET_INSN_FIELD (IMM16, opcode);
169 (*info->fprintf_func) (info->stream, "%ld", i);
172 offs = GET_BROFF_SIGNED (opcode) * 4;
173 (*info->print_address_func) (address + offs, info);
176 offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4;
177 (*info->print_address_func) (address + offs, info);
180 i = GET_BURSTLEN (opcode);
181 if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
182 (*info->fprintf_func) (info->stream, "%ld", i + 1);
185 i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
186 (*info->fprintf_func) (info->stream, "r0.b%ld", i);
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);
195 i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
196 (*info->fprintf_func) (info->stream, "r0.b%ld", i);
200 i = GET_INSN_FIELD (CB, opcode);
201 (*info->fprintf_func) (info->stream, "%ld", i);
204 i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
205 (*info->fprintf_func) (info->stream, "%ld", i);
208 i = GET_INSN_FIELD (XFR_WBA, opcode);
209 (*info->fprintf_func) (info->stream, "%ld", i);
212 (*info->fprintf_func) (info->stream, "unknown");
218 /* pru_disassemble does all the work of disassembling a PRU
219 instruction opcode. */
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;
232 info->insn_type = dis_nonbranch;
236 /* Find the major opcode and use this to disassemble
237 the instruction and its arguments. */
238 op = pru_find_opcode (opcode);
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);
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. */
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];
273 status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
277 insn = (unsigned long) bfd_getl32 (buffer);
278 status = pru_disassemble (address, insn, info);
282 (*info->memory_error_func) (status, address, info);