pru-dis.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /* TI PRU disassemble routines
  2. Copyright (C) 2014-2022 Free Software Foundation, Inc.
  3. Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
  4. This file is part of the GNU opcodes library.
  5. This library is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3, or (at your option)
  8. any later version.
  9. It is distributed in the hope that it will be useful, but WITHOUT
  10. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  12. License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this file; see the file COPYING. If not, write to the
  15. Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
  16. MA 02110-1301, USA. */
  17. #include "sysdep.h"
  18. #include "disassemble.h"
  19. #include "opcode/pru.h"
  20. #include "libiberty.h"
  21. #include <string.h>
  22. #include <assert.h>
  23. /* No symbol table is available when this code runs out in an embedded
  24. system as when it is used for disassembler support in a monitor. */
  25. #if !defined (EMBEDDED_ENV)
  26. #define SYMTAB_AVAILABLE 1
  27. #include "elf-bfd.h"
  28. #include "elf/pru.h"
  29. #endif
  30. /* Length of PRU instruction in bytes. */
  31. #define INSNLEN 4
  32. /* Return a pointer to an pru_opcode struct for a given instruction
  33. opcode, or NULL if there is an error. */
  34. const struct pru_opcode *
  35. pru_find_opcode (unsigned long opcode)
  36. {
  37. const struct pru_opcode *p;
  38. const struct pru_opcode *op = NULL;
  39. const struct pru_opcode *pseudo_op = NULL;
  40. for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++)
  41. {
  42. if ((p->mask & opcode) == p->match)
  43. {
  44. if ((p->pinfo & PRU_INSN_MACRO) == PRU_INSN_MACRO)
  45. pseudo_op = p;
  46. else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32)
  47. /* ignore - should be caught with regular patterns */;
  48. else
  49. op = p;
  50. }
  51. }
  52. return pseudo_op ? pseudo_op : op;
  53. }
  54. /* There are 32 regular registers, each with 8 possible subfield selectors. */
  55. #define NUMREGNAMES (32 * 8)
  56. static void
  57. pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
  58. disassemble_info *info)
  59. {
  60. unsigned int i = r * RSEL_NUM_ITEMS + sel;
  61. assert (i < (unsigned int)pru_num_regs);
  62. assert (i < NUMREGNAMES);
  63. (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
  64. }
  65. /* The function pru_print_insn_arg uses the character pointed
  66. to by ARGPTR to determine how it print the next token or separator
  67. character in the arguments to an instruction. */
  68. static int
  69. pru_print_insn_arg (const char *argptr,
  70. unsigned long opcode, bfd_vma address,
  71. disassemble_info *info)
  72. {
  73. long offs = 0;
  74. unsigned long i = 0;
  75. unsigned long io = 0;
  76. switch (*argptr)
  77. {
  78. case ',':
  79. (*info->fprintf_func) (info->stream, "%c ", *argptr);
  80. break;
  81. case 'd':
  82. pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
  83. GET_INSN_FIELD (RDSEL, opcode),
  84. info);
  85. break;
  86. case 'D':
  87. /* The first 4 values for RDB and RSEL are the same, so we
  88. can reuse some code. */
  89. pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
  90. GET_INSN_FIELD (RDB, opcode),
  91. info);
  92. break;
  93. case 's':
  94. pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
  95. GET_INSN_FIELD (RS1SEL, opcode),
  96. info);
  97. break;
  98. case 'S':
  99. pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
  100. RSEL_31_0,
  101. info);
  102. break;
  103. case 'b':
  104. io = GET_INSN_FIELD (IO, opcode);
  105. if (io)
  106. {
  107. i = GET_INSN_FIELD (IMM8, opcode);
  108. (*info->fprintf_func) (info->stream, "%ld", i);
  109. }
  110. else
  111. {
  112. pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
  113. GET_INSN_FIELD (RS2SEL, opcode),
  114. info);
  115. }
  116. break;
  117. case 'B':
  118. io = GET_INSN_FIELD (IO, opcode);
  119. if (io)
  120. {
  121. i = GET_INSN_FIELD (IMM8, opcode) + 1;
  122. (*info->fprintf_func) (info->stream, "%ld", i);
  123. }
  124. else
  125. {
  126. pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
  127. GET_INSN_FIELD (RS2SEL, opcode),
  128. info);
  129. }
  130. break;
  131. case 'j':
  132. io = GET_INSN_FIELD (IO, opcode);
  133. if (io)
  134. {
  135. /* For the sake of pretty-printing, dump text addresses with
  136. their "virtual" offset that we use for distinguishing
  137. PMEM vs DMEM. This is needed for printing the correct text
  138. labels. */
  139. bfd_vma text_offset = address & ~0x3fffff;
  140. i = GET_INSN_FIELD (IMM16, opcode) * 4;
  141. (*info->print_address_func) (i + text_offset, info);
  142. }
  143. else
  144. {
  145. pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
  146. GET_INSN_FIELD (RS2SEL, opcode),
  147. info);
  148. }
  149. break;
  150. case 'W':
  151. i = GET_INSN_FIELD (IMM16, opcode);
  152. (*info->fprintf_func) (info->stream, "%ld", i);
  153. break;
  154. case 'o':
  155. offs = GET_BROFF_SIGNED (opcode) * 4;
  156. (*info->print_address_func) (address + offs, info);
  157. break;
  158. case 'O':
  159. offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4;
  160. (*info->print_address_func) (address + offs, info);
  161. break;
  162. case 'l':
  163. i = GET_BURSTLEN (opcode);
  164. if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
  165. (*info->fprintf_func) (info->stream, "%ld", i + 1);
  166. else
  167. {
  168. i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
  169. (*info->fprintf_func) (info->stream, "r0.b%ld", i);
  170. }
  171. break;
  172. case 'n':
  173. i = GET_INSN_FIELD (XFR_LENGTH, opcode);
  174. if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
  175. (*info->fprintf_func) (info->stream, "%ld", i + 1);
  176. else
  177. {
  178. i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
  179. (*info->fprintf_func) (info->stream, "r0.b%ld", i);
  180. }
  181. break;
  182. case 'c':
  183. i = GET_INSN_FIELD (CB, opcode);
  184. (*info->fprintf_func) (info->stream, "%ld", i);
  185. break;
  186. case 'w':
  187. i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
  188. (*info->fprintf_func) (info->stream, "%ld", i);
  189. break;
  190. case 'x':
  191. i = GET_INSN_FIELD (XFR_WBA, opcode);
  192. (*info->fprintf_func) (info->stream, "%ld", i);
  193. break;
  194. default:
  195. (*info->fprintf_func) (info->stream, "unknown");
  196. break;
  197. }
  198. return 0;
  199. }
  200. /* pru_disassemble does all the work of disassembling a PRU
  201. instruction opcode. */
  202. static int
  203. pru_disassemble (bfd_vma address, unsigned long opcode,
  204. disassemble_info *info)
  205. {
  206. const struct pru_opcode *op;
  207. info->bytes_per_line = INSNLEN;
  208. info->bytes_per_chunk = INSNLEN;
  209. info->display_endian = info->endian;
  210. info->insn_info_valid = 1;
  211. info->branch_delay_insns = 0;
  212. info->data_size = 0;
  213. info->insn_type = dis_nonbranch;
  214. info->target = 0;
  215. info->target2 = 0;
  216. /* Find the major opcode and use this to disassemble
  217. the instruction and its arguments. */
  218. op = pru_find_opcode (opcode);
  219. if (op != NULL)
  220. {
  221. (*info->fprintf_func) (info->stream, "%s", op->name);
  222. const char *argstr = op->args;
  223. if (argstr != NULL && *argstr != '\0')
  224. {
  225. (*info->fprintf_func) (info->stream, "\t");
  226. while (*argstr != '\0')
  227. {
  228. pru_print_insn_arg (argstr, opcode, address, info);
  229. ++argstr;
  230. }
  231. }
  232. }
  233. else
  234. {
  235. /* Handle undefined instructions. */
  236. info->insn_type = dis_noninsn;
  237. (*info->fprintf_func) (info->stream, "0x%lx", opcode);
  238. }
  239. /* Tell the caller how far to advance the program counter. */
  240. return INSNLEN;
  241. }
  242. /* print_insn_pru is the main disassemble function for PRU. */
  243. int
  244. print_insn_pru (bfd_vma address, disassemble_info *info)
  245. {
  246. bfd_byte buffer[INSNLEN];
  247. int status;
  248. status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
  249. if (status == 0)
  250. {
  251. unsigned long insn;
  252. insn = (unsigned long) bfd_getl32 (buffer);
  253. status = pru_disassemble (address, insn, info);
  254. }
  255. else
  256. {
  257. (*info->memory_error_func) (status, address, info);
  258. status = -1;
  259. }
  260. return status;
  261. }