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