dismips.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*
  2. * very basic mips disassembler for MIPS32/MIPS64 Release 2, only for picodrive
  3. * Copyright (C) 2019 kub
  4. *
  5. * This work is licensed under the terms of MAME license.
  6. * See COPYING file in the top-level directory.
  7. */
  8. // unimplemented insns: SYNC, CACHE, PREF,
  9. // MOV[FT], LWC*/LDC*, SWC*/SDC*, and all of COP* (fpu, mmu, irq, exc, ...)
  10. // unimplemented variants of insns: EHB, SSNOP (both SLL zero), JALR.HB, JR.HB
  11. // however, it's certainly good enough for anything picodrive DRC throws at it.
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <stdint.h>
  15. #include <string.h>
  16. #include "dismips.h"
  17. static char *const register_names[32] = {
  18. "$zero",
  19. "$at",
  20. "$v0",
  21. "$v1",
  22. "$a0",
  23. "$a1",
  24. "$a2",
  25. "$a3",
  26. "$t0",
  27. "$t1",
  28. "$t2",
  29. "$t3",
  30. "$t4",
  31. "$t5",
  32. "$t6",
  33. "$t7",
  34. "$s0",
  35. "$s1",
  36. "$s2",
  37. "$s3",
  38. "$s4",
  39. "$s5",
  40. "$s6",
  41. "$s7",
  42. "$t8",
  43. "$t9",
  44. "$k0",
  45. "$k1",
  46. "$gp",
  47. "$sp",
  48. "$fp",
  49. "$ra"
  50. };
  51. enum insn_type {
  52. REG_DST, REG_ST, REG_TD, // 3, 2, or 1 regs
  53. REG_DS, REG_DT, REG_D, REG_S,
  54. S_IMM_DT, // 2 regs with shift amount
  55. F_IMM_TS, // 2 regs with bitfield spec
  56. B_IMM_S, B_IMM_TS, // pc-relative branches with 1 or 2 regs
  57. T_IMM_S, // trap insns with immediate
  58. J_IMM, // region-relative jump
  59. A_IMM_TS, // arithmetic immediate with 2 regs
  60. L_IMM_T, L_IMM_TS, // logical immediate with 1 or 2 regs
  61. M_IMM_TS, M_IMM_S, // memory indexed with 2 regs
  62. SB_CODE, // code parameter (syscall, break, sdbbp)
  63. SR_BIT = 0x80 // shift right with R-bit
  64. };
  65. struct insn {
  66. unsigned char op;
  67. enum insn_type type;
  68. char *name;
  69. };
  70. // ATTN: these array MUST be sorted by op (decode relies on it)
  71. // instructions with opcode SPECIAL (R-type)
  72. #define OP_SPECIAL 0x00
  73. static const struct insn special_insns[] = {
  74. {0x00, S_IMM_DT, "sll"},
  75. // {0x01, , "movf\0movt"},
  76. {0x02, S_IMM_DT|SR_BIT, "srl\0rotr"},
  77. {0x03, S_IMM_DT, "sra"},
  78. {0x04, REG_DST, "sllv"},
  79. {0x06, REG_DST|SR_BIT, "srlv\0rotrv"},
  80. {0x07, REG_DST, "srav"},
  81. {0x08, REG_S, "jr"},
  82. {0x09, REG_DS, "jalr"},
  83. {0x0a, REG_DST, "movz"},
  84. {0x0b, REG_DST, "movn"},
  85. {0x0c, SB_CODE, "syscall"},
  86. {0x0d, SB_CODE, "break"},
  87. // {0x0f, , "sync"},
  88. {0x10, REG_D, "mfhi"},
  89. {0x11, REG_S, "mthi"},
  90. {0x12, REG_D, "mflo"},
  91. {0x13, REG_S, "mtlo"},
  92. {0x14, REG_DST, "dsllv"},
  93. {0x16, REG_DST|SR_BIT, "dsrlv\0drotrv"},
  94. {0x17, REG_DST, "dsrav"},
  95. {0x18, REG_ST, "mult"},
  96. {0x19, REG_ST, "multu"},
  97. {0x1A, REG_ST, "div"},
  98. {0x1B, REG_ST, "divu"},
  99. {0x1C, REG_ST, "dmult"},
  100. {0x1D, REG_ST, "dmultu"},
  101. {0x1E, REG_ST, "ddiv"},
  102. {0x1F, REG_ST, "ddivu"},
  103. {0x20, REG_DST, "add"},
  104. {0x21, REG_DST, "addu"},
  105. {0x22, REG_DST, "sub"},
  106. {0x23, REG_DST, "subu"},
  107. {0x24, REG_DST, "and"},
  108. {0x25, REG_DST, "or"},
  109. {0x26, REG_DST, "xor"},
  110. {0x27, REG_DST, "nor"},
  111. {0x2A, REG_DST, "slt"},
  112. {0x2B, REG_DST, "sltu"},
  113. {0x2C, REG_DST, "dadd"},
  114. {0x2D, REG_DST, "daddu"},
  115. {0x2E, REG_DST, "dsub"},
  116. {0x2F, REG_DST, "dsubu"},
  117. {0x30, REG_ST, "tge" },
  118. {0x31, REG_ST, "tgeu" },
  119. {0x32, REG_ST, "tlt" },
  120. {0x33, REG_ST, "tltu" },
  121. {0x34, REG_ST, "teq" },
  122. {0x36, REG_ST, "tne" },
  123. {0x38, S_IMM_DT, "dsll"},
  124. {0x3A, S_IMM_DT|SR_BIT, "dsrl\0drotrv"},
  125. {0x3B, S_IMM_DT, "dsra"},
  126. {0x3C, S_IMM_DT, "dsll32"},
  127. {0x3E, S_IMM_DT|SR_BIT, "dsrl32\0drotr32"},
  128. {0x3F, S_IMM_DT, "dsra32"},
  129. };
  130. // instructions with opcode SPECIAL2 (R-type)
  131. #define OP_SPECIAL2 0x1C
  132. static const struct insn special2_insns[] = {
  133. {0x00, REG_ST, "madd" },
  134. {0x01, REG_ST, "maddu" },
  135. {0x02, REG_ST, "mul" },
  136. {0x04, REG_ST, "msub" },
  137. {0x05, REG_ST, "msubu" },
  138. {0x20, REG_DS, "clz" },
  139. {0x21, REG_DS, "clo" },
  140. {0x24, REG_DS, "dclz" },
  141. {0x25, REG_DS, "dclo" },
  142. {0x37, SB_CODE, "sdbbp" },
  143. };
  144. // instructions with opcode SPECIAL3 (R-type)
  145. #define OP_SPECIAL3 0x1F
  146. static const struct insn special3_insns[] = {
  147. {0x00, F_IMM_TS, "ext" },
  148. {0x01, F_IMM_TS, "dextm" },
  149. {0x02, F_IMM_TS, "dextu" },
  150. {0x03, F_IMM_TS, "dext" },
  151. {0x04, F_IMM_TS, "ins" },
  152. {0x05, F_IMM_TS, "dinsm" },
  153. {0x06, F_IMM_TS, "dinsu" },
  154. {0x07, F_IMM_TS, "dins" },
  155. {0x3b, REG_TD, "rdhwr" },
  156. };
  157. // instruction with opcode SPECIAL3 and function *BSHFL
  158. #define FN_BSHFL 0x20
  159. static const struct insn bshfl_insns[] = {
  160. {0x02, REG_DT, "wsbh" },
  161. {0x10, REG_DT, "seb" },
  162. {0x18, REG_DT, "seh" },
  163. };
  164. #define FN_DBSHFL 0x24
  165. static const struct insn dbshfl_insns[] = {
  166. {0x02, REG_DT, "dsbh" },
  167. {0x05, REG_DT, "dshd" },
  168. };
  169. // instructions with opcode REGIMM (I-type)
  170. #define OP_REGIMM 0x01
  171. static const struct insn regimm_insns[] = {
  172. {0x00, B_IMM_S, "bltz"},
  173. {0x01, B_IMM_S, "bgez"},
  174. {0x02, B_IMM_S, "bltzl"},
  175. {0x03, B_IMM_S, "bgezl"},
  176. {0x08, T_IMM_S, "tgei"},
  177. {0x09, T_IMM_S, "tgeiu"},
  178. {0x0a, T_IMM_S, "tlti"},
  179. {0x0b, T_IMM_S, "tltiu"},
  180. {0x0c, T_IMM_S, "teqi"},
  181. {0x0e, T_IMM_S, "tnei"},
  182. {0x10, B_IMM_S, "bltzal"},
  183. {0x11, B_IMM_S, "bgezal"},
  184. {0x12, B_IMM_S, "bltzall"},
  185. {0x13, B_IMM_S, "bgezall"},
  186. {0x1f, M_IMM_S, "synci" },
  187. };
  188. // instructions with other opcodes (I-type)
  189. static const struct insn immediate_insns[] = {
  190. {0x02, J_IMM, "j"},
  191. {0x03, J_IMM, "jal"},
  192. {0x04, B_IMM_TS, "beq"},
  193. {0x05, B_IMM_TS, "bne"},
  194. {0x06, B_IMM_S, "blez"},
  195. {0x07, B_IMM_S, "bgtz"},
  196. {0x08, A_IMM_TS, "addi"},
  197. {0x09, A_IMM_TS, "addiu"},
  198. {0x0A, A_IMM_TS, "slti"},
  199. {0x0B, A_IMM_TS, "sltiu"},
  200. {0x0C, L_IMM_TS, "andi"},
  201. {0x0D, L_IMM_TS, "ori"},
  202. {0x0E, L_IMM_TS, "xori"},
  203. {0x0F, L_IMM_T, "lui"},
  204. {0x14, B_IMM_TS, "beql"},
  205. {0x15, B_IMM_TS, "bnel"},
  206. {0x16, B_IMM_S, "blezl"},
  207. {0x17, B_IMM_S, "bgtzl"},
  208. {0x18, A_IMM_TS, "daddi"},
  209. {0x19, A_IMM_TS, "daddiu"},
  210. {0x1A, M_IMM_TS, "ldl"},
  211. {0x1B, M_IMM_TS, "ldr"},
  212. {0x20, M_IMM_TS, "lb"},
  213. {0x21, M_IMM_TS, "lh"},
  214. {0x22, M_IMM_TS, "lwl"},
  215. {0x23, M_IMM_TS, "lw"},
  216. {0x24, M_IMM_TS, "lbu"},
  217. {0x25, M_IMM_TS, "lhu"},
  218. {0x26, M_IMM_TS, "lwr"},
  219. {0x27, M_IMM_TS, "lwu"},
  220. {0x28, M_IMM_TS, "sb"},
  221. {0x29, M_IMM_TS, "sh"},
  222. {0x2A, M_IMM_TS, "swl"},
  223. {0x2B, M_IMM_TS, "sw"},
  224. {0x2C, M_IMM_TS, "sdl"},
  225. {0x2D, M_IMM_TS, "sdr"},
  226. {0x2E, M_IMM_TS, "swr"},
  227. // {0x2F, , "cache"},
  228. {0x30, M_IMM_TS, "ll"},
  229. // {0x31, , "lwc1"},
  230. // {0x32, , "lwc2"},
  231. // {0x33, , "pref"},
  232. {0x34, M_IMM_TS, "lld"},
  233. // {0x35, , "ldc1"},
  234. // {0x36, , "ldc2"},
  235. {0x37, M_IMM_TS, "ld"},
  236. {0x38, M_IMM_TS, "sc"},
  237. // {0x39, , "swc1"},
  238. // {0x3A, , "swc2"},
  239. {0x3C, M_IMM_TS, "scd"},
  240. // {0x3D, , "sdc1"},
  241. // {0x3E, , "sdc2"},
  242. {0x3F, M_IMM_TS, "sd"},
  243. };
  244. #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*a))
  245. // find instruction description for insn
  246. static const struct insn *decode_insn(uint32_t insn)
  247. {
  248. uint32_t op = insn >> 26;
  249. const struct insn *pi;
  250. int l = 0, r = 0;
  251. if (op == OP_SPECIAL) {
  252. op = insn & 0x3f;
  253. pi = special_insns;
  254. r = ARRAY_SIZE(special_insns)-1;
  255. } else if (op == OP_SPECIAL2) {
  256. op = insn & 0x3f;
  257. pi = special2_insns;
  258. r = ARRAY_SIZE(special2_insns)-1;
  259. } else if (op == OP_SPECIAL3) {
  260. op = insn & 0x3f;
  261. if (op == FN_BSHFL) {
  262. op = (insn >> 6) & 0x1f;
  263. pi = bshfl_insns;
  264. r = ARRAY_SIZE(bshfl_insns)-1;
  265. } else if (op == FN_DBSHFL) {
  266. op = (insn >> 6) & 0x1f;
  267. pi = dbshfl_insns;
  268. r = ARRAY_SIZE(dbshfl_insns)-1;
  269. } else {
  270. pi = special3_insns;
  271. r = ARRAY_SIZE(special3_insns)-1;
  272. }
  273. } else if (op == OP_REGIMM) {
  274. op = (insn>>16) & 0x1f;
  275. pi = regimm_insns;
  276. r = ARRAY_SIZE(regimm_insns)-1;
  277. } else {
  278. pi = immediate_insns;
  279. r = ARRAY_SIZE(immediate_insns)-1;
  280. }
  281. while (l <= r) {
  282. int m = (l+r) / 2;
  283. if (pi[m].op == op)
  284. return pi+m;
  285. else if (pi[m].op < op)
  286. l = m+1;
  287. else
  288. r = m-1;
  289. }
  290. return NULL;
  291. }
  292. // calculate target for pc-relative branches
  293. static unsigned long b_target(unsigned long pc, uint32_t insn)
  294. {
  295. return pc + 4 + (int16_t)insn * 4;
  296. }
  297. // calculate target for region-relative branches
  298. static unsigned long j_target(unsigned long pc, uint32_t insn)
  299. {
  300. return (pc & ~0x0fffffffL) | ((insn & 0x03ffffff) << 2);
  301. }
  302. // main disassembler function
  303. int dismips(uintptr_t pc, uint32_t insn, char *buf, size_t buflen, unsigned long *sym)
  304. {
  305. const struct insn *pi = decode_insn(insn);
  306. char *rs = register_names[(insn >> 21) & 0x1f];
  307. char *rt = register_names[(insn >> 16) & 0x1f];
  308. char *rd = register_names[(insn >> 11) & 0x1f];
  309. int sa = (insn >> 6) & 0x1f, sb = (insn >> 11) & 0x1f;
  310. int imm = (int16_t) insn;
  311. *sym = 0;
  312. if (pi == NULL) {
  313. snprintf(buf, buflen, "0x%08lx", (unsigned long)insn);
  314. return 0;
  315. }
  316. switch (pi->type & ~SR_BIT) {
  317. case REG_DST:
  318. if ((insn & 0x3f) == 0x25 /*OR*/ && (insn & 0x1f0000) == 0 /*zero*/)
  319. snprintf(buf, buflen, "move %s, %s", rd, rs);
  320. else if ((pi->type & SR_BIT) && (insn & (1<<6)))
  321. snprintf(buf, buflen, "%s %s, %s, %s", pi->name+strlen(pi->name)+1, rd, rs, rt);
  322. else
  323. snprintf(buf, buflen, "%s %s, %s, %s", pi->name, rd, rs, rt);
  324. break;
  325. case REG_ST:
  326. if ((insn & 0x38) == 0x30 /*T..*/)
  327. snprintf(buf, buflen, "%s %s, %s (code %d)", pi->name, rs, rt, (int)(insn>>6) & 0x3ff);
  328. else
  329. snprintf(buf, buflen, "%s %s, %s", pi->name, rs, rt);
  330. break;
  331. case REG_TD:
  332. snprintf(buf, buflen, "%s %s, %s", pi->name, rt, rd);
  333. break;
  334. case REG_DS:
  335. snprintf(buf, buflen, "%s %s, %s", pi->name, rd, rs);
  336. break;
  337. case REG_DT:
  338. snprintf(buf, buflen, "%s %s, %s", pi->name, rd, rt);
  339. break;
  340. case REG_D:
  341. snprintf(buf, buflen, "%s %s", pi->name, rd);
  342. break;
  343. case REG_S:
  344. snprintf(buf, buflen, "%s %s", pi->name, rs);
  345. break;
  346. case S_IMM_DT:
  347. if (insn == 0x00000000)
  348. snprintf(buf, buflen, "nop");
  349. else if ((pi->type & SR_BIT) && (insn & (1<<21)))
  350. snprintf(buf, buflen, "%s %s, %s, %d", pi->name+strlen(pi->name)+1, rd, rt, sa);
  351. else
  352. snprintf(buf, buflen, "%s %s, %s, %d", pi->name, rd, rt, sa);
  353. break;
  354. //dext: pos,size-1 dextm: pos,size-33 dextu: pos-32,size-1
  355. //dins: pos,pos+size-1 dinsm: pos,pos+size-33 dinsu: pos-32,pos+size-33
  356. case F_IMM_TS:
  357. if (insn & 0x01) sb+=32; // ...m
  358. if (insn & 0x02) sa+=32; // ...u
  359. if (insn & 0x04) sb-=sa; // ins
  360. snprintf(buf, buflen, "%s %s, %s, %d, %d", pi->name, rt, rs, sa, sb+1);
  361. break;
  362. case B_IMM_S:
  363. *sym = b_target(pc, insn);
  364. snprintf(buf, buflen, "%s %s, 0x%lx", pi->name, rs, *sym);
  365. break;
  366. case B_IMM_TS:
  367. *sym = b_target(pc, insn);
  368. snprintf(buf, buflen, "%s %s, %s, 0x%lx", pi->name, rs, rt, *sym);
  369. break;
  370. case J_IMM:
  371. *sym = j_target(pc, insn);
  372. snprintf(buf, buflen, "%s 0x%lx", pi->name, *sym);
  373. break;
  374. case A_IMM_TS:
  375. if (abs(imm) < 1000)
  376. snprintf(buf, buflen, "%s %s, %s, %d", pi->name, rt, rs, imm);
  377. else
  378. snprintf(buf, buflen, "%s %s, %s, 0x%x", pi->name, rt, rs, imm);
  379. break;
  380. case L_IMM_T:
  381. snprintf(buf, buflen, "%s %s, 0x%x", pi->name, rt, (uint16_t)imm);
  382. break;
  383. case L_IMM_TS:
  384. if ((insn >> 26) == 0x34 /*ORI*/ && (insn & 0x03e00000) == 0 /*zero*/)
  385. snprintf(buf, buflen, "li %s, 0x%x", rt, (uint16_t)imm);
  386. else
  387. snprintf(buf, buflen, "%s %s, %s, 0x%x", pi->name, rt, rs, (uint16_t)imm);
  388. break;
  389. case M_IMM_TS:
  390. snprintf(buf, buflen, "%s %s, %d(%s)", pi->name, rt, imm, rs);
  391. break;
  392. case M_IMM_S:
  393. snprintf(buf, buflen, "%s %d(%s)", pi->name, imm, rs);
  394. break;
  395. case T_IMM_S:
  396. snprintf(buf, buflen, "%s %s, %d", pi->name, rs, imm);
  397. break;
  398. case SB_CODE:
  399. snprintf(buf, buflen, "%s %ld", pi->name, (unsigned long)(insn>>6) & 0xfffff);
  400. break;
  401. }
  402. return 1;
  403. }