1 /* TI PRU disassemble routines
2 Copyright (C) 2014-2022 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
);