/src/capstonenext/arch/M680X/M680XInstPrinter.c
Line | Count | Source |
1 | | /* Capstone Disassembly Engine */ |
2 | | /* M680X Backend by Wolfgang Schwotzer <wolfgang.schwotzer@gmx.net> 2017 */ |
3 | | |
4 | | #ifdef CAPSTONE_HAS_M680X |
5 | | #include <stdio.h> |
6 | | #include <stdlib.h> |
7 | | #include <string.h> |
8 | | #include <capstone/platform.h> |
9 | | |
10 | | #include "../../cs_priv.h" |
11 | | #include "../../MCInst.h" |
12 | | #include "../../SStream.h" |
13 | | #include "../../MCRegisterInfo.h" |
14 | | #include "../../Mapping.h" |
15 | | #include "../../MCInstPrinter.h" |
16 | | #include "../../utils.h" |
17 | | #include "M680XInstPrinter.h" |
18 | | #include "M680XDisassembler.h" |
19 | | #include "M680XDisassemblerInternals.h" |
20 | | |
21 | | #ifndef CAPSTONE_DIET |
22 | | static const char s_reg_names[][10] = { |
23 | | "<invalid>", "a", "b", "e", "f", "0", "d", "w", |
24 | | "cc", "dp", "md", "hx", "h", "x", "y", "s", |
25 | | "u", "v", "q", "pc", "spc", "tmp2", "tmp3", |
26 | | }; |
27 | | |
28 | | static const char s_instruction_names[][6] = { |
29 | | "invld", "aba", "abx", "aby", "adc", "adca", "adcb", "adcd", |
30 | | "adcr", "add", "adda", "addb", "addd", "adde", "addf", "addr", |
31 | | "addw", "addx", "addy", "aded", "adex", "adey", "aim", "ais", |
32 | | "aix", "and", "anda", "andb", "andx", "andy", "andcc", "andd", |
33 | | "andr", "asl", "asla", "aslb", "asld", "aslw", "aslx", "asly", |
34 | | "asr", "asra", "asrb", "asrd", "asrw", "asrx", "asry", "band", |
35 | | "bcc", "bclr", "bcs", "beor", "beq", "bge", "bgnd", "bgt", |
36 | | "bhcc", "bhcs", "bhi", "biand", "bieor", "bih", "bil", "bior", |
37 | | "bit", "bita", "bitb", "bitx", "bity", "bitd", "bitmd", "ble", |
38 | | "bls", "blt", "bmc", "bmi", "bms", "bne", "bor", "bpl", |
39 | | "brclr", "brset", "bra", "brn", "bset", "bsr", "btas", "bvc", |
40 | | "bvs", "call", "cba", "cbeq", "cbeqa", "cbeqx", "clc", "cli", |
41 | | "clr", "clra", "clrb", "clrd", "clre", "clrf", "clrh", "clrw", |
42 | | "clrx", "clry", "clv", "cmp", "cmpa", "cmpb", "cmpd", "cmpe", |
43 | | "cmpf", "cmpr", "cmps", "cmpu", "cmpw", "cmpx", "cmpy", "com", |
44 | | "coma", "comb", "comd", "come", "comf", "comw", "comx", "comy", |
45 | | "cpd", "cped", "cpes", "cpex", "cpey", "cphx", "cps", "cpx", |
46 | | "cpy", "cwai", "daa", "dbeq", "dbne", "dbnz", "dbnza", "dbnzx", |
47 | | "dec", "deca", "decb", "decd", "dece", "decf", "decw", "decx", |
48 | | "decy", "des", "dex", "dey", "div", "divd", "divq", "ediv", |
49 | | "edivs", "eim", "emacs", "emaxd", "emaxm", "emind", "eminm", "emul", |
50 | | "emuls", "eor", "eora", "eorb", "eord", "eorr", "eorx", "eory", |
51 | | "etbl", "exg", "fdiv", "gldaa", "gldab", "gldd", "glds", "gldx", |
52 | | "gldy", "gstaa", "gstab", "gstd", "gsts", "gstx", "gsty", "ibeq", |
53 | | "ibne", "idiv", "idivs", "illgl", "inc", "inca", "incb", "incd", |
54 | | "ince", "incf", "incw", "incx", "incy", "ins", "inx", "iny", |
55 | | "jmp", "jsr", "lbcc", "lbcs", "lbeq", "lbge", "lbgt", "lbhi", |
56 | | "lble", "lbls", "lblt", "lbmi", "lbne", "lbpl", "lbra", "lbrn", |
57 | | "lbsr", "lbvc", "lbvs", "lda", "ldaa", "ldab", "ldb", "ldbt", |
58 | | "ldd", "lde", "ldf", "ldhx", "ldmd", "ldq", "lds", "ldu", |
59 | | "ldw", "ldx", "ldy", "leas", "leau", "leax", "leay", "lsl", |
60 | | "lsla", "lslb", "lsld", "lslx", "lsr", "lsra", "lsrb", "lsrd", |
61 | | "lsrw", "lsrx", "lsry", "maxa", "maxm", "mem", "mina", "minm", |
62 | | "mov", "movb", "movw", "mul", "muld", "neg", "nega", "negb", |
63 | | "negd", "negw", "negx", "negy", "nop", "nsa", "oim", "ora", |
64 | | "oraa", "orab", "orb", "orcc", "ord", "orr", "orx", "ory", |
65 | | "psha", "pshb", "pshc", "pshcw", "pshd", "pshh", "pshs", "pshsw", |
66 | | "pshu", "pshuw", "pshx", "pshy", "pula", "pulb", "pulc", "pulcw", |
67 | | "puld", "pulh", "puls", "pulsw", "pulu", "puluw", "pulx", "puly", |
68 | | "rev", "revw", "rol", "rola", "rolb", "rold", "rolw", "rolx", |
69 | | "roly", "ror", "rora", "rorb", "rord", "rorw", "rorx", "rory", |
70 | | "rsp", "rtc", "rti", "rts", "sba", "sbc", "sbca", "sbcb", |
71 | | "sbcd", "sbcr", "sbed", "sbex", "sbey", "sec", "sei", "sev", |
72 | | "sex", "sexw", "sha", "sla", "slp", "sta", "staa", "stab", |
73 | | "stb", "stbt", "std", "ste", "stf", "stop", "sthx", "stq", |
74 | | "sts", "stu", "stw", "stx", "sty", "sub", "suba", "subb", |
75 | | "subd", "sube", "subf", "subr", "subw", "subx", "suby", "swi", |
76 | | "swi2", "swi3", "sync", "sys", "tab", "tap", "tax", "tba", |
77 | | "tbeq", "tbl", "tbne", "test", "tfm", "tfr", "tim", "tpa", |
78 | | "trap", "tst", "tsta", "tstb", "tstd", "tste", "tstf", "tstw", |
79 | | "tstx", "tsty", "tsx", "tsy", "txa", "txs", "tys", "wai", |
80 | | "wait", "wav", "wavr", "xgdx", "xgdy", |
81 | | }; |
82 | | |
83 | | static const name_map s_group_names[] = { |
84 | | { M680X_GRP_INVALID, "<invalid>" }, |
85 | | { M680X_GRP_JUMP, "jump" }, |
86 | | { M680X_GRP_CALL, "call" }, |
87 | | { M680X_GRP_RET, "return" }, |
88 | | { M680X_GRP_INT, "interrupt" }, |
89 | | { M680X_GRP_IRET, "interrupt_return" }, |
90 | | { M680X_GRP_PRIV, "privileged" }, |
91 | | { M680X_GRP_BRAREL, "branch_relative" }, |
92 | | }; |
93 | | #endif |
94 | | |
95 | | static void printRegName(cs_struct *handle, SStream *OS, unsigned int reg) |
96 | 74.2k | { |
97 | 74.2k | #ifndef CAPSTONE_DIET |
98 | 74.2k | SStream_concat0(OS, handle->reg_name((csh)handle, reg)); |
99 | 74.2k | #endif |
100 | 74.2k | } |
101 | | |
102 | | static void printInstructionName(cs_struct *handle, SStream *OS, |
103 | | unsigned int insn) |
104 | 191k | { |
105 | 191k | #ifndef CAPSTONE_DIET |
106 | 191k | SStream_concat0(OS, handle->insn_name((csh)handle, insn)); |
107 | 191k | #endif |
108 | 191k | } |
109 | | |
110 | | static uint32_t get_unsigned(int32_t value, int byte_size) |
111 | 0 | { |
112 | 0 | switch (byte_size) { |
113 | 0 | case 1: |
114 | 0 | return (uint32_t)(value & 0xff); |
115 | | |
116 | 0 | case 2: |
117 | 0 | return (uint32_t)(value & 0xffff); |
118 | | |
119 | 0 | default: |
120 | 0 | case 4: |
121 | 0 | return (uint32_t)value; |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | static void printIncDec(bool isPost, SStream *O, m680x_info *info, |
126 | | cs_m680x_op *op) |
127 | 102k | { |
128 | 102k | static const char s_inc_dec[][3] = { "--", "-", "", "+", "++" }; |
129 | | |
130 | 102k | if (!op->idx.inc_dec) |
131 | 80.3k | return; |
132 | | |
133 | 22.6k | if ((!isPost && !(op->idx.flags & M680X_IDX_POST_INC_DEC)) || |
134 | 16.4k | (isPost && (op->idx.flags & M680X_IDX_POST_INC_DEC))) { |
135 | 11.3k | const char *prePostfix = ""; |
136 | | |
137 | 11.3k | if (info->cpu_type == M680X_CPU_TYPE_CPU12) |
138 | 3.83k | prePostfix = (op->idx.inc_dec < 0) ? "-" : "+"; |
139 | 7.48k | else if (op->idx.inc_dec >= -2 && (op->idx.inc_dec <= 2)) { |
140 | 5.32k | prePostfix = (char *)s_inc_dec[op->idx.inc_dec + 2]; |
141 | 5.32k | } |
142 | | |
143 | 11.3k | SStream_concat0(O, prePostfix); |
144 | 11.3k | } |
145 | 22.6k | } |
146 | | |
147 | | static void printOperand(MCInst *MI, SStream *O, m680x_info *info, |
148 | | cs_m680x_op *op) |
149 | 177k | { |
150 | 177k | switch (op->type) { |
151 | 18.5k | case M680X_OP_REGISTER: |
152 | 18.5k | printRegName(MI->csh, O, op->reg); |
153 | 18.5k | break; |
154 | | |
155 | 5.39k | case M680X_OP_CONSTANT: |
156 | 5.39k | SStream_concat(O, "%u", op->const_val); |
157 | 5.39k | break; |
158 | | |
159 | 18.7k | case M680X_OP_IMMEDIATE: |
160 | 18.7k | if (MI->csh->imm_unsigned) |
161 | 0 | SStream_concat(O, "#%u", |
162 | 0 | get_unsigned(op->imm, op->size)); |
163 | 18.7k | else |
164 | 18.7k | SStream_concat(O, "#%d", op->imm); |
165 | | |
166 | 18.7k | break; |
167 | | |
168 | 51.4k | case M680X_OP_INDEXED: |
169 | 51.4k | if (op->idx.flags & M680X_IDX_INDIRECT) |
170 | 5.46k | SStream_concat0(O, "["); |
171 | | |
172 | 51.4k | if (op->idx.offset_reg != M680X_REG_INVALID) |
173 | 4.15k | printRegName(MI->csh, O, op->idx.offset_reg); |
174 | 47.3k | else if (op->idx.offset_bits > 0) { |
175 | 30.8k | if (op->idx.base_reg == M680X_REG_PC) |
176 | 3.12k | SStream_concat(O, "$%04x", op->idx.offset_addr); |
177 | 27.7k | else |
178 | 27.7k | SStream_concat(O, "%d", op->idx.offset); |
179 | 30.8k | } else if (op->idx.inc_dec != 0 && |
180 | 10.8k | info->cpu_type == M680X_CPU_TYPE_CPU12) |
181 | 3.83k | SStream_concat(O, "%d", abs(op->idx.inc_dec)); |
182 | | |
183 | 51.4k | if (!(op->idx.flags & M680X_IDX_NO_COMMA)) |
184 | 49.9k | SStream_concat(O, ", "); |
185 | | |
186 | 51.4k | printIncDec(false, O, info, op); |
187 | | |
188 | 51.4k | printRegName(MI->csh, O, op->idx.base_reg); |
189 | | |
190 | 51.4k | if (op->idx.base_reg == M680X_REG_PC && |
191 | 3.53k | (op->idx.offset_bits > 0)) |
192 | 3.12k | SStream_concat(O, "r"); |
193 | | |
194 | 51.4k | printIncDec(true, O, info, op); |
195 | | |
196 | 51.4k | if (op->idx.flags & M680X_IDX_INDIRECT) |
197 | 5.46k | SStream_concat(O, "]"); |
198 | | |
199 | 51.4k | break; |
200 | | |
201 | 18.8k | case M680X_OP_RELATIVE: |
202 | 18.8k | SStream_concat(O, "$%04x", op->rel.address); |
203 | 18.8k | break; |
204 | | |
205 | 39.3k | case M680X_OP_DIRECT: |
206 | 39.3k | SStream_concat(O, "$%02x", op->direct_addr); |
207 | 39.3k | break; |
208 | | |
209 | 25.3k | case M680X_OP_EXTENDED: |
210 | 25.3k | if (op->ext.indirect) |
211 | 233 | SStream_concat(O, "[$%04x]", op->ext.address); |
212 | 25.1k | else { |
213 | 25.1k | if (op->ext.address < 256) { |
214 | 1.26k | SStream_concat(O, ">$%04x", op->ext.address); |
215 | 23.8k | } else { |
216 | 23.8k | SStream_concat(O, "$%04x", op->ext.address); |
217 | 23.8k | } |
218 | 25.1k | } |
219 | | |
220 | 25.3k | break; |
221 | | |
222 | 0 | default: |
223 | 0 | SStream_concat0(O, "<invalid_operand>"); |
224 | 0 | break; |
225 | 177k | } |
226 | 177k | } |
227 | | |
228 | | static const char *getDelimiter(m680x_info *info, cs_m680x *m680x) |
229 | 214k | { |
230 | 214k | bool indexed = false; |
231 | 214k | int count = 0; |
232 | 214k | int i; |
233 | | |
234 | 214k | if (info->insn == M680X_INS_TFM) |
235 | 417 | return ", "; |
236 | | |
237 | 214k | if (m680x->op_count > 1) { |
238 | 303k | for (i = 0; i < m680x->op_count; ++i) { |
239 | 207k | if (m680x->operands[i].type == M680X_OP_INDEXED) |
240 | 34.6k | indexed = true; |
241 | | |
242 | 207k | if (m680x->operands[i].type != M680X_OP_REGISTER) |
243 | 107k | count++; |
244 | 207k | } |
245 | 95.9k | } |
246 | | |
247 | 214k | return (indexed && (count >= 1)) ? "; " : ", "; |
248 | 214k | }; |
249 | | |
250 | | void M680X_printInst(MCInst *MI, SStream *O, void *PrinterInfo) |
251 | 214k | { |
252 | 214k | m680x_info *info = (m680x_info *)PrinterInfo; |
253 | 214k | cs_m680x *m680x = &info->m680x; |
254 | 214k | cs_detail *detail = MI->flat_insn->detail; |
255 | 214k | int suppress_operands = 0; |
256 | 214k | const char *delimiter = getDelimiter(info, m680x); |
257 | 214k | int i; |
258 | | |
259 | 214k | if (detail != NULL) |
260 | 214k | memcpy(&detail->m680x, m680x, sizeof(cs_m680x)); |
261 | | |
262 | 214k | if (info->insn == M680X_INS_INVLD || info->insn == M680X_INS_ILLGL) { |
263 | 23.0k | if (m680x->op_count) |
264 | 23.0k | SStream_concat(O, "fcb $%02x", m680x->operands[0].imm); |
265 | 0 | else |
266 | 0 | SStream_concat0(O, "fcb $<unknown>"); |
267 | | |
268 | 23.0k | return; |
269 | 23.0k | } |
270 | | |
271 | 191k | printInstructionName(MI->csh, O, info->insn); |
272 | 191k | SStream_concat0(O, " "); |
273 | | |
274 | 191k | if ((m680x->flags & M680X_FIRST_OP_IN_MNEM) != 0) |
275 | 101k | suppress_operands++; |
276 | | |
277 | 191k | if ((m680x->flags & M680X_SECOND_OP_IN_MNEM) != 0) |
278 | 2.46k | suppress_operands++; |
279 | | |
280 | 472k | for (i = 0; i < m680x->op_count; ++i) { |
281 | 281k | if (i >= suppress_operands) { |
282 | 177k | printOperand(MI, O, info, &m680x->operands[i]); |
283 | | |
284 | 177k | if ((i + 1) != m680x->op_count) |
285 | 32.5k | SStream_concat0(O, delimiter); |
286 | 177k | } |
287 | 281k | } |
288 | 191k | } |
289 | | |
290 | | const char *M680X_reg_name(csh handle, unsigned int reg) |
291 | 667k | { |
292 | 667k | #ifndef CAPSTONE_DIET |
293 | | |
294 | 667k | if (reg >= ARR_SIZE(s_reg_names)) |
295 | 0 | return NULL; |
296 | | |
297 | 667k | return s_reg_names[(int)reg]; |
298 | | #else |
299 | | return NULL; |
300 | | #endif |
301 | 667k | } |
302 | | |
303 | | const char *M680X_insn_name(csh handle, unsigned int id) |
304 | 406k | { |
305 | 406k | #ifndef CAPSTONE_DIET |
306 | | |
307 | 406k | if (id >= ARR_SIZE(s_instruction_names)) |
308 | 0 | return NULL; |
309 | 406k | else |
310 | 406k | return s_instruction_names[(int)id]; |
311 | | |
312 | | #else |
313 | | return NULL; |
314 | | #endif |
315 | 406k | } |
316 | | |
317 | | const char *M680X_group_name(csh handle, unsigned int id) |
318 | 50.9k | { |
319 | 50.9k | #ifndef CAPSTONE_DIET |
320 | 50.9k | return id2name(s_group_names, ARR_SIZE(s_group_names), id); |
321 | | #else |
322 | | return NULL; |
323 | | #endif |
324 | 50.9k | } |
325 | | |
326 | | cs_err M680X_instprinter_init(cs_struct *ud) |
327 | 1.30k | { |
328 | 1.30k | #ifndef CAPSTONE_DIET |
329 | | |
330 | 1.30k | if (M680X_REG_ENDING != ARR_SIZE(s_reg_names)) { |
331 | 0 | CS_ASSERT(M680X_REG_ENDING == ARR_SIZE(s_reg_names)); |
332 | 0 | return CS_ERR_MODE; |
333 | 0 | } |
334 | | |
335 | 1.30k | if (M680X_INS_ENDING != ARR_SIZE(s_instruction_names)) { |
336 | 0 | CS_ASSERT(M680X_INS_ENDING == ARR_SIZE(s_instruction_names)); |
337 | 0 | return CS_ERR_MODE; |
338 | 0 | } |
339 | | |
340 | 1.30k | if (M680X_GRP_ENDING != ARR_SIZE(s_group_names)) { |
341 | 0 | CS_ASSERT(M680X_GRP_ENDING == ARR_SIZE(s_group_names)); |
342 | 0 | return CS_ERR_MODE; |
343 | 0 | } |
344 | | |
345 | 1.30k | #endif |
346 | | |
347 | 1.30k | return CS_ERR_OK; |
348 | 1.30k | } |
349 | | |
350 | | #endif |