/src/capstonenext/arch/SH/SHInstPrinter.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Capstone Disassembly Engine */ |
2 | | /* By Yoshinori Sato, 2022 */ |
3 | | |
4 | | #include <string.h> |
5 | | |
6 | | #include "../../Mapping.h" |
7 | | #include "SHInstPrinter.h" |
8 | | |
9 | | #ifndef CAPSTONE_DIET |
10 | | static const char *const s_reg_names[] = { |
11 | | "invalid", "r0", "r1", "r2", "r3", "r4", |
12 | | "r5", "r6", "r7", "r8", "r9", "r10", |
13 | | "r11", "r12", "r13", "r14", "r15", "r0_bank", |
14 | | "r1_bank", "r2_bank", "r3_bank", "r4_bank", "r5_bank", "r6_bank", |
15 | | "r7_bank", "fr0", "fr1", "fr2", "fr3", "fr4", |
16 | | "fr5", "fr6", "fr7", "fr8", "fr9", "fr10", |
17 | | "fr11", "fr12", "fr13", "fr14", "fr15", "dr0", |
18 | | "dr2", "dr4", "dr6", "dr8", "dr10", "dr12", |
19 | | "dr14", "xd0", "xd2", "xd4", "xd6", "xd8", |
20 | | "xd10", "xd12", "xd14", "xf0", "xf1", "xf2", |
21 | | "xf3", "xf4", "xf5", "xf6", "xf7", "xf8", |
22 | | "xf9", "xf10", "xf11", "xf12", "xf13", "xf14", |
23 | | "xf15", "fv0", "fv4", "fv8", "fv12", "xmtrx", |
24 | | "pc", "pr", "mach", "macl", "sr", "gbr", |
25 | | "ssr", "spc", "sgr", "dbr", "vbr", "tbr", |
26 | | "rs", "re", "mod", "fpul", "fpscr", "x0", |
27 | | "x1", "y0", "y1", "a0", "a1", "a0g", |
28 | | "a1g", "m0", "m1", "dsr", "0x0", "0x1", |
29 | | "0x2", "0x3", "0x4", "0x5", "0x6", "0x7", |
30 | | "0x8", "0x9", "0xa", "0xb", "0xc", "0xd", |
31 | | "0xe", "0xf", |
32 | | }; |
33 | | #endif |
34 | | |
35 | | const char *SH_reg_name(csh handle, unsigned int reg) |
36 | 0 | { |
37 | | #ifdef CAPSTONE_DIET |
38 | | return NULL; |
39 | | #else |
40 | 0 | if (reg >= ARR_SIZE(s_reg_names)) { |
41 | 0 | return NULL; |
42 | 0 | } |
43 | 0 | return s_reg_names[(int)reg]; |
44 | 0 | #endif |
45 | 0 | } |
46 | | |
47 | | void SH_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) |
48 | 0 | { |
49 | 0 | insn->id = id; // These id's matches for sh |
50 | 0 | } |
51 | | |
52 | | #ifndef CAPSTONE_DIET |
53 | | static const char *const s_insn_names[] = { |
54 | | "unknown", "add", "add", "addc", "addv", "and", |
55 | | "band", "bandnot", "bclr", "bf", "bf/s", "bld", |
56 | | "bldnot", "bor", "bornot", "bra", "braf", "bset", |
57 | | "bsr", "bsrf", "bst", "bt", "bt/s", "bxor", |
58 | | "clips", "clipu", "clrdmxy", "clrmac", "clrs", "clrt", |
59 | | "cmp/eq", "cmp/ge", "cmp/gt", "cmp/hi", "cmp/hs", "cmp/pl", |
60 | | "cmp/pz", "cmp/str", "div0s", "div0u", "div1", "divs", |
61 | | "divu", "dmuls.l", "dmulu.l", "dt", "exts", "exts", |
62 | | "extu", "extu", "fabs", "fadd", "fcmp/eq", "fcmp/gt", |
63 | | "fcnvds", "fcnvsd", "fdiv", "fipr", "fldi0", "fldi1", |
64 | | "flds", "float", "fmac", "fmov", "fmul", "fneg", |
65 | | "fpchg", "frchg", "fsca", "fschg", "fsqrt", "fsrra", |
66 | | "fsts", "fsub", "ftrc", "ftrv", "icbi", "jmp", |
67 | | "jsr", "jsr/n", "ldbank", "ldc", "ldrc", "ldre", |
68 | | "ldrs", "lds", "ldtlb", "mac.l", "mac.w", "mov", |
69 | | "mova", "movca", "movco", "movi20", "movi20s", "movli", |
70 | | "movml", "movmu", "movrt", "movt", "movu", "movua", |
71 | | "mul.l", "mulr", "muls", "mulu", "neg", "negc", |
72 | | "nop", "not", "nott", "ocbi", "ocbp", "ocbwb", |
73 | | "or", "pref", "prefi", "resbank", "rotcl", "rotcr", |
74 | | "rotl", "rotr", "rte", "rts", "rts/n", "rtv/n", |
75 | | "setdmx", "setdmy", "setrc", "sets", "sett", "shad", |
76 | | "shal", "shar", "shld", "shll", "shll16", "shll2", |
77 | | "shll8", "shlr", "shlr16", "shlr2", "shlr8", "sleep", |
78 | | "stbank", "stc", "sts", "sub", "subc", "subv", |
79 | | "swap", "swap", "synco", "tas", "trapa", "tst", |
80 | | "xor", "xtrct", |
81 | | }; |
82 | | #endif |
83 | | |
84 | | const char *SH_insn_name(csh handle, unsigned int id) |
85 | 0 | { |
86 | | #ifdef CAPSTONE_DIET |
87 | | return NULL; |
88 | | #else |
89 | 0 | if (id >= ARR_SIZE(s_insn_names)) { |
90 | 0 | return NULL; |
91 | 0 | } |
92 | 0 | return s_insn_names[id]; |
93 | 0 | #endif |
94 | 0 | } |
95 | | |
96 | | #ifndef CAPSTONE_DIET |
97 | | #endif |
98 | | |
99 | | #ifndef CAPSTONE_DIET |
100 | | static void print_dsp_double(SStream *O, sh_info *info, int xy) |
101 | 0 | { |
102 | 0 | char suffix_xy = 'x' + xy; |
103 | 0 | int i; |
104 | 0 | if (info->op.operands[xy].dsp.insn == SH_INS_DSP_NOP) { |
105 | 0 | if ((info->op.operands[0].dsp.insn == SH_INS_DSP_NOP) && |
106 | 0 | (info->op.operands[1].dsp.insn == SH_INS_DSP_NOP)) { |
107 | 0 | SStream_concat(O, "nop%c", suffix_xy); |
108 | 0 | } |
109 | 0 | } else { |
110 | 0 | SStream_concat(O, "mov%c", suffix_xy); |
111 | 0 | switch (info->op.operands[xy].dsp.size) { |
112 | 0 | case 16: |
113 | 0 | SStream_concat0(O, ".w "); |
114 | 0 | break; |
115 | 0 | case 32: |
116 | 0 | SStream_concat0(O, ".l "); |
117 | 0 | break; |
118 | 0 | } |
119 | | |
120 | 0 | for (i = 0; i < 2; i++) { |
121 | 0 | switch (info->op.operands[xy].dsp.operand[i]) { |
122 | 0 | default: |
123 | 0 | break; |
124 | 0 | case SH_OP_DSP_REG_IND: |
125 | 0 | SStream_concat(O, "@%s", |
126 | 0 | s_reg_names[info->op.operands[xy] |
127 | 0 | .dsp.r[i]]); |
128 | 0 | break; |
129 | 0 | case SH_OP_DSP_REG_POST: |
130 | 0 | SStream_concat(O, "@%s+", |
131 | 0 | s_reg_names[info->op.operands[xy] |
132 | 0 | .dsp.r[i]]); |
133 | 0 | break; |
134 | 0 | case SH_OP_DSP_REG_INDEX: |
135 | 0 | SStream_concat(O, "@%s+%s", |
136 | 0 | s_reg_names[info->op.operands[xy] |
137 | 0 | .dsp.r[i]], |
138 | 0 | s_reg_names[SH_REG_R8 + xy]); |
139 | 0 | break; |
140 | 0 | case SH_OP_DSP_REG: |
141 | 0 | SStream_concat(O, "%s", |
142 | 0 | s_reg_names[info->op.operands[xy] |
143 | 0 | .dsp.r[i]]); |
144 | 0 | break; |
145 | 0 | } |
146 | 0 | if (i == 0) |
147 | 0 | SStream_concat0(O, ","); |
148 | 0 | } |
149 | 0 | } |
150 | 0 | if (xy == 0) |
151 | 0 | SStream_concat0(O, " "); |
152 | 0 | } |
153 | | |
154 | | static const char *s_dsp_insns[] = { |
155 | | "invalid", "nop", "mov", "pshl", "psha", "pmuls", |
156 | | "pclr_pmuls", "psub_pmuls", "padd_pmuls", "psubc", "paddc", "pcmp", |
157 | | "pabs", "prnd", "psub", "psub", "padd", "pand", |
158 | | "pxor", "por", "pdec", "pinc", "pclr", "pdmsb", |
159 | | "pneg", "pcopy", "psts", "plds", "pswap", "pwad", |
160 | | "pwsb", |
161 | | }; |
162 | | |
163 | | static void print_dsp(SStream *O, sh_info *info) |
164 | 0 | { |
165 | 0 | int i; |
166 | 0 | switch (info->op.op_count) { |
167 | 0 | case 1: |
168 | | // single transfer |
169 | 0 | SStream_concat0(O, "movs"); |
170 | 0 | switch (info->op.operands[0].dsp.size) { |
171 | 0 | case 16: |
172 | 0 | SStream_concat0(O, ".w "); |
173 | 0 | break; |
174 | 0 | case 32: |
175 | 0 | SStream_concat0(O, ".l "); |
176 | 0 | break; |
177 | 0 | } |
178 | 0 | for (i = 0; i < 2; i++) { |
179 | 0 | switch (info->op.operands[0].dsp.operand[i]) { |
180 | 0 | default: |
181 | 0 | break; |
182 | 0 | case SH_OP_DSP_REG_PRE: |
183 | 0 | SStream_concat(O, "@-%s", |
184 | 0 | s_reg_names[info->op.operands[0] |
185 | 0 | .dsp.r[i]]); |
186 | 0 | break; |
187 | 0 | case SH_OP_DSP_REG_IND: |
188 | 0 | SStream_concat(O, "@%s", |
189 | 0 | s_reg_names[info->op.operands[0] |
190 | 0 | .dsp.r[i]]); |
191 | 0 | break; |
192 | 0 | case SH_OP_DSP_REG_POST: |
193 | 0 | SStream_concat(O, "@%s+", |
194 | 0 | s_reg_names[info->op.operands[0] |
195 | 0 | .dsp.r[i]]); |
196 | 0 | break; |
197 | 0 | case SH_OP_DSP_REG_INDEX: |
198 | 0 | SStream_concat(O, "@%s+%s", |
199 | 0 | s_reg_names[info->op.operands[0] |
200 | 0 | .dsp.r[i]], |
201 | 0 | s_reg_names[SH_REG_R8]); |
202 | 0 | break; |
203 | 0 | case SH_OP_DSP_REG: |
204 | 0 | SStream_concat(O, "%s", |
205 | 0 | s_reg_names[info->op.operands[0] |
206 | 0 | .dsp.r[i]]); |
207 | 0 | } |
208 | 0 | if (i == 0) |
209 | 0 | SStream_concat0(O, ","); |
210 | 0 | } |
211 | 0 | break; |
212 | 0 | case 2: // Double transfer |
213 | 0 | print_dsp_double(O, info, 0); |
214 | 0 | print_dsp_double(O, info, 1); |
215 | 0 | break; |
216 | 0 | case 3: // Parallel |
217 | 0 | switch (info->op.operands[2].dsp.cc) { |
218 | 0 | default: |
219 | 0 | break; |
220 | 0 | case SH_DSP_CC_DCT: |
221 | 0 | SStream_concat0(O, "dct "); |
222 | 0 | break; |
223 | 0 | case SH_DSP_CC_DCF: |
224 | 0 | SStream_concat0(O, "dcf "); |
225 | 0 | break; |
226 | 0 | } |
227 | 0 | switch (info->op.operands[2].dsp.insn) { |
228 | 0 | case SH_INS_DSP_PSUB_PMULS: |
229 | 0 | case SH_INS_DSP_PADD_PMULS: |
230 | 0 | switch (info->op.operands[2].dsp.insn) { |
231 | 0 | default: |
232 | 0 | break; |
233 | 0 | case SH_INS_DSP_PSUB_PMULS: |
234 | 0 | SStream_concat0(O, "psub "); |
235 | 0 | break; |
236 | 0 | case SH_INS_DSP_PADD_PMULS: |
237 | 0 | SStream_concat0(O, "padd "); |
238 | 0 | break; |
239 | 0 | } |
240 | 0 | for (i = 0; i < 6; i++) { |
241 | 0 | SStream_concat(O, "%s", |
242 | 0 | s_reg_names[info->op.operands[2] |
243 | 0 | .dsp.r[i]]); |
244 | 0 | if ((i % 3) < 2) |
245 | 0 | SStream_concat0(O, ","); |
246 | 0 | if (i == 2) |
247 | 0 | SStream_concat( |
248 | 0 | O, " %s ", |
249 | 0 | s_dsp_insns[SH_INS_DSP_PMULS]); |
250 | 0 | } |
251 | 0 | break; |
252 | 0 | case SH_INS_DSP_PCLR_PMULS: |
253 | 0 | SStream_concat0(O, s_dsp_insns[SH_INS_DSP_PCLR]); |
254 | 0 | SStream_concat( |
255 | 0 | O, " %s ", |
256 | 0 | s_reg_names[info->op.operands[2].dsp.r[3]]); |
257 | 0 | SStream_concat(O, "%s ", s_dsp_insns[SH_INS_DSP_PMULS]); |
258 | 0 | for (i = 0; i < 3; i++) { |
259 | 0 | SStream_concat(O, "%s", |
260 | 0 | s_reg_names[info->op.operands[2] |
261 | 0 | .dsp.r[i]]); |
262 | 0 | if (i < 2) |
263 | 0 | SStream_concat0(O, ","); |
264 | 0 | } |
265 | 0 | break; |
266 | | |
267 | 0 | default: |
268 | 0 | SStream_concat0( |
269 | 0 | O, s_dsp_insns[info->op.operands[2].dsp.insn]); |
270 | 0 | SStream_concat0(O, " "); |
271 | 0 | for (i = 0; i < 3; i++) { |
272 | 0 | if (info->op.operands[2].dsp.r[i] == |
273 | 0 | SH_REG_INVALID) { |
274 | 0 | if (i == 0) { |
275 | 0 | SStream_concat( |
276 | 0 | O, "#%d", |
277 | 0 | info->op.operands[2] |
278 | 0 | .dsp.imm); |
279 | 0 | } |
280 | 0 | } else |
281 | 0 | SStream_concat( |
282 | 0 | O, "%s", |
283 | 0 | s_reg_names[info->op.operands[2] |
284 | 0 | .dsp.r[i]]); |
285 | 0 | if (i < 2 && |
286 | 0 | info->op.operands[2].dsp.r[i + 1] != |
287 | 0 | SH_REG_INVALID) |
288 | 0 | SStream_concat0(O, ","); |
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | 0 | if (info->op.operands[0].dsp.insn != SH_INS_DSP_NOP) { |
293 | 0 | SStream_concat0(O, " "); |
294 | 0 | print_dsp_double(O, info, 0); |
295 | 0 | } |
296 | 0 | if (info->op.operands[1].dsp.insn != SH_INS_DSP_NOP) { |
297 | 0 | SStream_concat0(O, " "); |
298 | 0 | print_dsp_double(O, info, 1); |
299 | 0 | } |
300 | 0 | break; |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | | static void PrintMemop(SStream *O, sh_op_mem *op) |
305 | 0 | { |
306 | 0 | switch (op->address) { |
307 | 0 | case SH_OP_MEM_INVALID: |
308 | 0 | break; |
309 | 0 | case SH_OP_MEM_REG_IND: |
310 | 0 | SStream_concat(O, "@%s", s_reg_names[op->reg]); |
311 | 0 | break; |
312 | 0 | case SH_OP_MEM_REG_POST: |
313 | 0 | SStream_concat(O, "@%s+", s_reg_names[op->reg]); |
314 | 0 | break; |
315 | 0 | case SH_OP_MEM_REG_PRE: |
316 | 0 | SStream_concat(O, "@-%s", s_reg_names[op->reg]); |
317 | 0 | break; |
318 | 0 | case SH_OP_MEM_REG_DISP: |
319 | 0 | SStream_concat(O, "@(%d,%s)", op->disp, s_reg_names[op->reg]); |
320 | 0 | break; |
321 | 0 | case SH_OP_MEM_REG_R0: /// <= R0 indexed |
322 | 0 | SStream_concat(O, "@(%s,%s)", s_reg_names[SH_REG_R0], |
323 | 0 | s_reg_names[op->reg]); |
324 | 0 | break; |
325 | 0 | case SH_OP_MEM_GBR_DISP: /// <= GBR based displaysment |
326 | 0 | SStream_concat(O, "@(%d,%s)", op->disp, |
327 | 0 | s_reg_names[SH_REG_GBR]); |
328 | 0 | break; |
329 | 0 | case SH_OP_MEM_GBR_R0: /// <= GBR based R0 indexed |
330 | 0 | SStream_concat(O, "@(%s,%s)", s_reg_names[SH_REG_R0], |
331 | 0 | s_reg_names[SH_REG_GBR]); |
332 | 0 | break; |
333 | 0 | case SH_OP_MEM_PCR: /// <= PC relative |
334 | 0 | SStream_concat(O, "0x%x", op->disp); |
335 | 0 | break; |
336 | 0 | case SH_OP_MEM_TBR_DISP: /// <= GBR based displaysment |
337 | 0 | SStream_concat(O, "@@(%d,%s)", op->disp, |
338 | 0 | s_reg_names[SH_REG_TBR]); |
339 | 0 | break; |
340 | 0 | } |
341 | 0 | } |
342 | | #endif |
343 | | |
344 | | void SH_printInst(MCInst *MI, SStream *O, void *PrinterInfo) |
345 | 0 | { |
346 | 0 | #ifndef CAPSTONE_DIET |
347 | 0 | sh_info *info = (sh_info *)PrinterInfo; |
348 | 0 | int i; |
349 | 0 | int imm; |
350 | |
|
351 | 0 | if (MI->Opcode == SH_INS_DSP) { |
352 | 0 | print_dsp(O, info); |
353 | 0 | return; |
354 | 0 | } |
355 | | |
356 | 0 | SStream_concat0(O, (char *)s_insn_names[MI->Opcode]); |
357 | 0 | switch (info->op.size) { |
358 | 0 | case 8: |
359 | 0 | SStream_concat0(O, ".b"); |
360 | 0 | break; |
361 | 0 | case 16: |
362 | 0 | SStream_concat0(O, ".w"); |
363 | 0 | break; |
364 | 0 | case 32: |
365 | 0 | SStream_concat0(O, ".l"); |
366 | 0 | break; |
367 | 0 | case 64: |
368 | 0 | SStream_concat0(O, ".d"); |
369 | 0 | break; |
370 | 0 | } |
371 | 0 | SStream_concat0(O, " "); |
372 | 0 | for (i = 0; i < info->op.op_count; i++) { |
373 | 0 | switch (info->op.operands[i].type) { |
374 | 0 | case SH_OP_INVALID: |
375 | 0 | break; |
376 | 0 | case SH_OP_REG: |
377 | 0 | SStream_concat0(O, |
378 | 0 | s_reg_names[info->op.operands[i].reg]); |
379 | 0 | break; |
380 | 0 | case SH_OP_IMM: |
381 | 0 | imm = info->op.operands[i].imm; |
382 | 0 | SStream_concat(O, "#%d", imm); |
383 | 0 | break; |
384 | 0 | case SH_OP_MEM: |
385 | 0 | PrintMemop(O, &info->op.operands[i].mem); |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | if (i < (info->op.op_count - 1)) { |
389 | 0 | SStream_concat0(O, ","); |
390 | 0 | } |
391 | 0 | } |
392 | 0 | #endif |
393 | 0 | } |
394 | | |
395 | | #ifndef CAPSTONE_DIET |
396 | | static const name_map group_name_maps[] = { |
397 | | { SH_GRP_INVALID, NULL }, |
398 | | { SH_GRP_JUMP, "jump" }, |
399 | | { SH_GRP_CALL, "call" }, |
400 | | { SH_GRP_INT, "int" }, |
401 | | { SH_GRP_RET, "ret" }, |
402 | | { SH_GRP_IRET, "iret" }, |
403 | | { SH_GRP_PRIVILEGE, "privilege" }, |
404 | | { SH_GRP_BRANCH_RELATIVE, "branch_relative" }, |
405 | | { SH_GRP_SH2, "SH2" }, |
406 | | { SH_GRP_SH2E, "SH2E" }, |
407 | | { SH_GRP_SH2DSP, "SH2-DSP" }, |
408 | | { SH_GRP_SH2A, "SH2A" }, |
409 | | { SH_GRP_SH2AFPU, "SH2A-FPU" }, |
410 | | { SH_GRP_SH3, "SH3" }, |
411 | | { SH_GRP_SH3DSP, "SH3-DSP" }, |
412 | | { SH_GRP_SH4, "SH4" }, |
413 | | { SH_GRP_SH4A, "SH4A" }, |
414 | | }; |
415 | | #endif |
416 | | |
417 | | const char *SH_group_name(csh handle, unsigned int id) |
418 | 0 | { |
419 | 0 | #ifndef CAPSTONE_DIET |
420 | 0 | return id2name(group_name_maps, ARR_SIZE(group_name_maps), id); |
421 | | #else |
422 | | return NULL; |
423 | | #endif |
424 | 0 | } |