microblaze-dis.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /* Disassemble Xilinx microblaze instructions.
  2. Copyright (C) 2009-2022 Free Software Foundation, Inc.
  3. This file is part of the GNU opcodes library.
  4. This library is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3, or (at your option)
  7. any later version.
  8. It is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  11. License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this file; see the file COPYING. If not, write to the
  14. Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
  15. MA 02110-1301, USA. */
  16. #include "sysdep.h"
  17. #define STATIC_TABLE
  18. #define DEFINE_TABLE
  19. #include "disassemble.h"
  20. #include <strings.h>
  21. #include "microblaze-opc.h"
  22. #include "microblaze-dis.h"
  23. #define get_field_rd(buf, instr) get_field (buf, instr, RD_MASK, RD_LOW)
  24. #define get_field_r1(buf, instr) get_field (buf, instr, RA_MASK, RA_LOW)
  25. #define get_field_r2(buf, instr) get_field (buf, instr, RB_MASK, RB_LOW)
  26. #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW)
  27. #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW)
  28. #define NUM_STRBUFS 3
  29. #define STRBUF_SIZE 25
  30. struct string_buf
  31. {
  32. unsigned int which;
  33. char str[NUM_STRBUFS][STRBUF_SIZE];
  34. };
  35. static inline char *
  36. strbuf (struct string_buf *buf)
  37. {
  38. #ifdef ENABLE_CHECKING
  39. if (buf->which >= NUM_STRBUFS)
  40. abort ();
  41. #endif
  42. return buf->str[buf->which++];
  43. }
  44. static char *
  45. get_field (struct string_buf *buf, long instr, long mask, unsigned short low)
  46. {
  47. char *p = strbuf (buf);
  48. sprintf (p, "%s%d", register_prefix, (int)((instr & mask) >> low));
  49. return p;
  50. }
  51. static char *
  52. get_field_imm (struct string_buf *buf, long instr)
  53. {
  54. char *p = strbuf (buf);
  55. sprintf (p, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
  56. return p;
  57. }
  58. static char *
  59. get_field_imm5 (struct string_buf *buf, long instr)
  60. {
  61. char *p = strbuf (buf);
  62. sprintf (p, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
  63. return p;
  64. }
  65. static char *
  66. get_field_imm5_mbar (struct string_buf *buf, long instr)
  67. {
  68. char *p = strbuf (buf);
  69. sprintf (p, "%d", (short)((instr & IMM5_MBAR_MASK) >> IMM_MBAR));
  70. return p;
  71. }
  72. static char *
  73. get_field_rfsl (struct string_buf *buf, long instr)
  74. {
  75. char *p = strbuf (buf);
  76. sprintf (p, "%s%d", fsl_register_prefix,
  77. (short)((instr & RFSL_MASK) >> IMM_LOW));
  78. return p;
  79. }
  80. static char *
  81. get_field_imm15 (struct string_buf *buf, long instr)
  82. {
  83. char *p = strbuf (buf);
  84. sprintf (p, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
  85. return p;
  86. }
  87. static char *
  88. get_field_special (struct string_buf *buf, long instr,
  89. const struct op_code_struct *op)
  90. {
  91. char *p = strbuf (buf);
  92. char *spr;
  93. switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
  94. {
  95. case REG_MSR_MASK :
  96. spr = "msr";
  97. break;
  98. case REG_PC_MASK :
  99. spr = "pc";
  100. break;
  101. case REG_EAR_MASK :
  102. spr = "ear";
  103. break;
  104. case REG_ESR_MASK :
  105. spr = "esr";
  106. break;
  107. case REG_FSR_MASK :
  108. spr = "fsr";
  109. break;
  110. case REG_BTR_MASK :
  111. spr = "btr";
  112. break;
  113. case REG_EDR_MASK :
  114. spr = "edr";
  115. break;
  116. case REG_PID_MASK :
  117. spr = "pid";
  118. break;
  119. case REG_ZPR_MASK :
  120. spr = "zpr";
  121. break;
  122. case REG_TLBX_MASK :
  123. spr = "tlbx";
  124. break;
  125. case REG_TLBLO_MASK :
  126. spr = "tlblo";
  127. break;
  128. case REG_TLBHI_MASK :
  129. spr = "tlbhi";
  130. break;
  131. case REG_TLBSX_MASK :
  132. spr = "tlbsx";
  133. break;
  134. case REG_SHR_MASK :
  135. spr = "shr";
  136. break;
  137. case REG_SLR_MASK :
  138. spr = "slr";
  139. break;
  140. default :
  141. if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
  142. == REG_PVR_MASK)
  143. {
  144. sprintf (p, "%spvr%d", register_prefix,
  145. (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
  146. ^ op->immval_mask) ^ REG_PVR_MASK);
  147. return p;
  148. }
  149. else
  150. spr = "pc";
  151. break;
  152. }
  153. sprintf (p, "%s%s", register_prefix, spr);
  154. return p;
  155. }
  156. static unsigned long
  157. read_insn_microblaze (bfd_vma memaddr,
  158. struct disassemble_info *info,
  159. const struct op_code_struct **opr)
  160. {
  161. unsigned char ibytes[4];
  162. int status;
  163. const struct op_code_struct *op;
  164. unsigned long inst;
  165. status = info->read_memory_func (memaddr, ibytes, 4, info);
  166. if (status != 0)
  167. {
  168. info->memory_error_func (status, memaddr, info);
  169. return 0;
  170. }
  171. if (info->endian == BFD_ENDIAN_BIG)
  172. inst = (((unsigned) ibytes[0] << 24) | (ibytes[1] << 16)
  173. | (ibytes[2] << 8) | ibytes[3]);
  174. else if (info->endian == BFD_ENDIAN_LITTLE)
  175. inst = (((unsigned) ibytes[3] << 24) | (ibytes[2] << 16)
  176. | (ibytes[1] << 8) | ibytes[0]);
  177. else
  178. abort ();
  179. /* Just a linear search of the table. */
  180. for (op = microblaze_opcodes; op->name != 0; op ++)
  181. if (op->bit_sequence == (inst & op->opcode_mask))
  182. break;
  183. *opr = op;
  184. return inst;
  185. }
  186. int
  187. print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
  188. {
  189. fprintf_ftype print_func = info->fprintf_func;
  190. void *stream = info->stream;
  191. unsigned long inst, prev_inst;
  192. const struct op_code_struct *op, *pop;
  193. int immval = 0;
  194. bool immfound = false;
  195. static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */
  196. static int prev_insn_vma = -1; /* Init the prev insn vma. */
  197. int curr_insn_vma = info->buffer_vma;
  198. struct string_buf buf;
  199. buf.which = 0;
  200. info->bytes_per_chunk = 4;
  201. inst = read_insn_microblaze (memaddr, info, &op);
  202. if (inst == 0)
  203. return -1;
  204. if (prev_insn_vma == curr_insn_vma)
  205. {
  206. if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
  207. {
  208. prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
  209. if (prev_inst == 0)
  210. return -1;
  211. if (pop->instr == imm)
  212. {
  213. immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
  214. immfound = true;
  215. }
  216. else
  217. {
  218. immval = 0;
  219. immfound = false;
  220. }
  221. }
  222. }
  223. /* Make curr insn as prev insn. */
  224. prev_insn_addr = memaddr;
  225. prev_insn_vma = curr_insn_vma;
  226. if (op->name == NULL)
  227. print_func (stream, ".short 0x%04x", (unsigned int) inst);
  228. else
  229. {
  230. print_func (stream, "%s", op->name);
  231. switch (op->inst_type)
  232. {
  233. case INST_TYPE_RD_R1_R2:
  234. print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst),
  235. get_field_r1 (&buf, inst), get_field_r2 (&buf, inst));
  236. break;
  237. case INST_TYPE_RD_R1_IMM:
  238. print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst),
  239. get_field_r1 (&buf, inst), get_field_imm (&buf, inst));
  240. if (info->print_address_func && get_int_field_r1 (inst) == 0
  241. && info->symbol_at_address_func)
  242. {
  243. if (immfound)
  244. immval |= (get_int_field_imm (inst) & 0x0000ffff);
  245. else
  246. {
  247. immval = get_int_field_imm (inst);
  248. if (immval & 0x8000)
  249. immval |= 0xFFFF0000;
  250. }
  251. if (immval > 0 && info->symbol_at_address_func (immval, info))
  252. {
  253. print_func (stream, "\t// ");
  254. info->print_address_func (immval, info);
  255. }
  256. }
  257. break;
  258. case INST_TYPE_RD_R1_IMM5:
  259. print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst),
  260. get_field_r1 (&buf, inst), get_field_imm5 (&buf, inst));
  261. break;
  262. case INST_TYPE_RD_RFSL:
  263. print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
  264. get_field_rfsl (&buf, inst));
  265. break;
  266. case INST_TYPE_R1_RFSL:
  267. print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
  268. get_field_rfsl (&buf, inst));
  269. break;
  270. case INST_TYPE_RD_SPECIAL:
  271. print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
  272. get_field_special (&buf, inst, op));
  273. break;
  274. case INST_TYPE_SPECIAL_R1:
  275. print_func (stream, "\t%s, %s", get_field_special (&buf, inst, op),
  276. get_field_r1 (&buf, inst));
  277. break;
  278. case INST_TYPE_RD_R1:
  279. print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
  280. get_field_r1 (&buf, inst));
  281. break;
  282. case INST_TYPE_R1_R2:
  283. print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
  284. get_field_r2 (&buf, inst));
  285. break;
  286. case INST_TYPE_R1_IMM:
  287. print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
  288. get_field_imm (&buf, inst));
  289. /* The non-pc relative instructions are returns, which shouldn't
  290. have a label printed. */
  291. if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
  292. && info->symbol_at_address_func)
  293. {
  294. if (immfound)
  295. immval |= (get_int_field_imm (inst) & 0x0000ffff);
  296. else
  297. {
  298. immval = get_int_field_imm (inst);
  299. if (immval & 0x8000)
  300. immval |= 0xFFFF0000;
  301. }
  302. immval += memaddr;
  303. if (immval > 0 && info->symbol_at_address_func (immval, info))
  304. {
  305. print_func (stream, "\t// ");
  306. info->print_address_func (immval, info);
  307. }
  308. else
  309. {
  310. print_func (stream, "\t\t// ");
  311. print_func (stream, "%x", immval);
  312. }
  313. }
  314. break;
  315. case INST_TYPE_RD_IMM:
  316. print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
  317. get_field_imm (&buf, inst));
  318. if (info->print_address_func && info->symbol_at_address_func)
  319. {
  320. if (immfound)
  321. immval |= (get_int_field_imm (inst) & 0x0000ffff);
  322. else
  323. {
  324. immval = get_int_field_imm (inst);
  325. if (immval & 0x8000)
  326. immval |= 0xFFFF0000;
  327. }
  328. if (op->inst_offset_type == INST_PC_OFFSET)
  329. immval += (int) memaddr;
  330. if (info->symbol_at_address_func (immval, info))
  331. {
  332. print_func (stream, "\t// ");
  333. info->print_address_func (immval, info);
  334. }
  335. }
  336. break;
  337. case INST_TYPE_IMM:
  338. print_func (stream, "\t%s", get_field_imm (&buf, inst));
  339. if (info->print_address_func && info->symbol_at_address_func
  340. && op->instr != imm)
  341. {
  342. if (immfound)
  343. immval |= (get_int_field_imm (inst) & 0x0000ffff);
  344. else
  345. {
  346. immval = get_int_field_imm (inst);
  347. if (immval & 0x8000)
  348. immval |= 0xFFFF0000;
  349. }
  350. if (op->inst_offset_type == INST_PC_OFFSET)
  351. immval += (int) memaddr;
  352. if (immval > 0 && info->symbol_at_address_func (immval, info))
  353. {
  354. print_func (stream, "\t// ");
  355. info->print_address_func (immval, info);
  356. }
  357. else if (op->inst_offset_type == INST_PC_OFFSET)
  358. {
  359. print_func (stream, "\t\t// ");
  360. print_func (stream, "%x", immval);
  361. }
  362. }
  363. break;
  364. case INST_TYPE_RD_R2:
  365. print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
  366. get_field_r2 (&buf, inst));
  367. break;
  368. case INST_TYPE_R2:
  369. print_func (stream, "\t%s", get_field_r2 (&buf, inst));
  370. break;
  371. case INST_TYPE_R1:
  372. print_func (stream, "\t%s", get_field_r1 (&buf, inst));
  373. break;
  374. case INST_TYPE_R1_R2_SPECIAL:
  375. print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst),
  376. get_field_r2 (&buf, inst));
  377. break;
  378. case INST_TYPE_RD_IMM15:
  379. print_func (stream, "\t%s, %s", get_field_rd (&buf, inst),
  380. get_field_imm15 (&buf, inst));
  381. break;
  382. /* For mbar insn. */
  383. case INST_TYPE_IMM5:
  384. print_func (stream, "\t%s", get_field_imm5_mbar (&buf, inst));
  385. break;
  386. /* For mbar 16 or sleep insn. */
  387. case INST_TYPE_NONE:
  388. break;
  389. /* For tuqula instruction */
  390. case INST_TYPE_RD:
  391. print_func (stream, "\t%s", get_field_rd (&buf, inst));
  392. break;
  393. case INST_TYPE_RFSL:
  394. print_func (stream, "\t%s", get_field_rfsl (&buf, inst));
  395. break;
  396. default:
  397. /* If the disassembler lags the instruction set. */
  398. print_func (stream, "\tundecoded operands, inst is 0x%04x",
  399. (unsigned int) inst);
  400. break;
  401. }
  402. }
  403. /* Say how many bytes we consumed. */
  404. return 4;
  405. }
  406. enum microblaze_instr
  407. get_insn_microblaze (long inst,
  408. bool *isunsignedimm,
  409. enum microblaze_instr_type *insn_type,
  410. short *delay_slots)
  411. {
  412. const struct op_code_struct *op;
  413. *isunsignedimm = false;
  414. /* Just a linear search of the table. */
  415. for (op = microblaze_opcodes; op->name != 0; op ++)
  416. if (op->bit_sequence == (inst & op->opcode_mask))
  417. break;
  418. if (op->name == 0)
  419. return invalid_inst;
  420. else
  421. {
  422. *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
  423. *insn_type = op->instr_type;
  424. *delay_slots = op->delay_slots;
  425. return op->instr;
  426. }
  427. }
  428. enum microblaze_instr
  429. microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed)
  430. {
  431. enum microblaze_instr op;
  432. bool t1;
  433. enum microblaze_instr_type t2;
  434. short t3;
  435. op = get_insn_microblaze (insn, &t1, &t2, &t3);
  436. *rd = (insn & RD_MASK) >> RD_LOW;
  437. *ra = (insn & RA_MASK) >> RA_LOW;
  438. *rb = (insn & RB_MASK) >> RB_LOW;
  439. t3 = (insn & IMM_MASK) >> IMM_LOW;
  440. *immed = (int) t3;
  441. return (op);
  442. }
  443. unsigned long
  444. microblaze_get_target_address (long inst, bool immfound, int immval,
  445. long pcval, long r1val, long r2val,
  446. bool *targetvalid,
  447. bool *unconditionalbranch)
  448. {
  449. const struct op_code_struct *op;
  450. long targetaddr = 0;
  451. *unconditionalbranch = false;
  452. /* Just a linear search of the table. */
  453. for (op = microblaze_opcodes; op->name != 0; op ++)
  454. if (op->bit_sequence == (inst & op->opcode_mask))
  455. break;
  456. if (op->name == 0)
  457. {
  458. *targetvalid = false;
  459. }
  460. else if (op->instr_type == branch_inst)
  461. {
  462. switch (op->inst_type)
  463. {
  464. case INST_TYPE_R2:
  465. *unconditionalbranch = true;
  466. /* Fall through. */
  467. case INST_TYPE_RD_R2:
  468. case INST_TYPE_R1_R2:
  469. targetaddr = r2val;
  470. *targetvalid = true;
  471. if (op->inst_offset_type == INST_PC_OFFSET)
  472. targetaddr += pcval;
  473. break;
  474. case INST_TYPE_IMM:
  475. *unconditionalbranch = true;
  476. /* Fall through. */
  477. case INST_TYPE_RD_IMM:
  478. case INST_TYPE_R1_IMM:
  479. if (immfound)
  480. {
  481. targetaddr = (immval << 16) & 0xffff0000;
  482. targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
  483. }
  484. else
  485. {
  486. targetaddr = get_int_field_imm (inst);
  487. if (targetaddr & 0x8000)
  488. targetaddr |= 0xFFFF0000;
  489. }
  490. if (op->inst_offset_type == INST_PC_OFFSET)
  491. targetaddr += pcval;
  492. *targetvalid = true;
  493. break;
  494. default:
  495. *targetvalid = false;
  496. break;
  497. }
  498. }
  499. else if (op->instr_type == return_inst)
  500. {
  501. if (immfound)
  502. {
  503. targetaddr = (immval << 16) & 0xffff0000;
  504. targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
  505. }
  506. else
  507. {
  508. targetaddr = get_int_field_imm (inst);
  509. if (targetaddr & 0x8000)
  510. targetaddr |= 0xFFFF0000;
  511. }
  512. targetaddr += r1val;
  513. *targetvalid = true;
  514. }
  515. else
  516. *targetvalid = false;
  517. return targetaddr;
  518. }