Commit | Line | Data |
---|---|---|
7b4ae824 | 1 | /* s12z-dis.c -- Freescale S12Z disassembly |
82704155 | 2 | Copyright (C) 2018-2019 Free Software Foundation, Inc. |
7b4ae824 JD |
3 | |
4 | This file is part of the GNU opcodes library. | |
5 | ||
6 | This library is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3, or (at your option) | |
9 | any later version. | |
10 | ||
11 | It is distributed in the hope that it will be useful, but WITHOUT | |
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
14 | License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | |
19 | MA 02110-1301, USA. */ | |
20 | ||
21 | #include "sysdep.h" | |
22 | #include <stdio.h> | |
2d5d5a8f | 23 | #include "bfd_stdint.h" |
7b4ae824 JD |
24 | #include <stdbool.h> |
25 | #include <assert.h> | |
26 | ||
7ba3ba91 | 27 | #include "opcode/s12z.h" |
7b4ae824 JD |
28 | #include "bfd.h" |
29 | #include "dis-asm.h" | |
7b4ae824 | 30 | #include "disassemble.h" |
ef1ad42b | 31 | #include "s12z-opc.h" |
66a66a17 | 32 | #include "opintl.h" |
7b4ae824 | 33 | |
ef1ad42b | 34 | struct mem_read_abstraction |
7b4ae824 | 35 | { |
ef1ad42b JD |
36 | struct mem_read_abstraction_base base; |
37 | bfd_vma memaddr; | |
38 | struct disassemble_info* info; | |
7b4ae824 JD |
39 | }; |
40 | ||
ef1ad42b JD |
41 | static void |
42 | advance (struct mem_read_abstraction_base *b) | |
7b4ae824 | 43 | { |
ef1ad42b JD |
44 | struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b; |
45 | mra->memaddr ++; | |
7b4ae824 JD |
46 | } |
47 | ||
ef1ad42b JD |
48 | static bfd_vma |
49 | posn (struct mem_read_abstraction_base *b) | |
7b4ae824 | 50 | { |
ef1ad42b JD |
51 | struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b; |
52 | return mra->memaddr; | |
7b4ae824 JD |
53 | } |
54 | ||
55 | static int | |
ef1ad42b JD |
56 | abstract_read_memory (struct mem_read_abstraction_base *b, |
57 | int offset, | |
58 | size_t n, bfd_byte *bytes) | |
7b4ae824 | 59 | { |
ef1ad42b | 60 | struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b; |
7b4ae824 | 61 | |
ef1ad42b JD |
62 | int status = |
63 | (*mra->info->read_memory_func) (mra->memaddr + offset, | |
64 | bytes, n, mra->info); | |
7b4ae824 | 65 | |
ef1ad42b | 66 | if (status != 0) |
7b4ae824 | 67 | { |
ef1ad42b JD |
68 | (*mra->info->memory_error_func) (status, mra->memaddr, mra->info); |
69 | return -1; | |
7b4ae824 | 70 | } |
ef1ad42b | 71 | return 0; |
7b4ae824 JD |
72 | } |
73 | ||
ef1ad42b | 74 | /* Start of disassembly file. */ |
7b4ae824 JD |
75 | const struct reg registers[S12Z_N_REGISTERS] = |
76 | { | |
77 | {"d2", 2}, | |
78 | {"d3", 2}, | |
79 | {"d4", 2}, | |
80 | {"d5", 2}, | |
81 | ||
82 | {"d0", 1}, | |
83 | {"d1", 1}, | |
84 | ||
85 | {"d6", 4}, | |
86 | {"d7", 4}, | |
87 | ||
88 | {"x", 3}, | |
89 | {"y", 3}, | |
90 | {"s", 3}, | |
91 | {"p", 3}, | |
92 | {"cch", 1}, | |
93 | {"ccl", 1}, | |
94 | {"ccw", 2} | |
95 | }; | |
96 | ||
ef1ad42b JD |
97 | static const char *mnemonics[] = |
98 | { | |
99 | "!!invalid!!", | |
100 | "psh", | |
101 | "pul", | |
102 | "tbne", "tbeq", "tbpl", "tbmi", "tbgt", "tble", | |
103 | "dbne", "dbeq", "dbpl", "dbmi", "dbgt", "dble", | |
104 | "sex", | |
105 | "exg", | |
106 | "lsl", "lsr", | |
107 | "asl", "asr", | |
108 | "rol", "ror", | |
109 | "bfins", "bfext", | |
110 | ||
111 | "trap", | |
112 | ||
113 | "ld", | |
114 | "st", | |
115 | "cmp", | |
116 | ||
117 | "stop", | |
118 | "wai", | |
119 | "sys", | |
120 | ||
121 | "minu", | |
122 | "mins", | |
123 | "maxu", | |
124 | "maxs", | |
125 | ||
126 | "abs", | |
127 | "adc", | |
128 | "bit", | |
129 | "sbc", | |
130 | "rti", | |
131 | "clb", | |
132 | "eor", | |
133 | ||
134 | "sat", | |
135 | ||
136 | "nop", | |
137 | "bgnd", | |
138 | "brclr", | |
139 | "brset", | |
140 | "rts", | |
141 | "lea", | |
142 | "mov", | |
143 | ||
144 | "bra", | |
145 | "bsr", | |
146 | "bhi", | |
147 | "bls", | |
148 | "bcc", | |
149 | "bcs", | |
150 | "bne", | |
151 | "beq", | |
152 | "bvc", | |
153 | "bvs", | |
154 | "bpl", | |
155 | "bmi", | |
156 | "bge", | |
157 | "blt", | |
158 | "bgt", | |
159 | "ble", | |
160 | "inc", | |
161 | "clr", | |
162 | "dec", | |
163 | ||
164 | "add", | |
165 | "sub", | |
166 | "and", | |
167 | "or", | |
168 | ||
169 | "tfr", | |
170 | "jmp", | |
171 | "jsr", | |
172 | "com", | |
173 | "andcc", | |
174 | "neg", | |
175 | "orcc", | |
176 | "bclr", | |
177 | "bset", | |
178 | "btgl", | |
179 | "swi", | |
180 | ||
181 | "mulu", | |
182 | "divu", | |
183 | "modu", | |
184 | "macu", | |
185 | "qmulu", | |
186 | ||
187 | "muls", | |
188 | "divs", | |
189 | "mods", | |
190 | "macs", | |
191 | "qmuls", | |
192 | ||
193 | NULL | |
194 | }; | |
195 | ||
7b4ae824 | 196 | |
ef1ad42b JD |
197 | static void |
198 | operand_separator (struct disassemble_info *info) | |
7b4ae824 | 199 | { |
ef1ad42b JD |
200 | if ((info->flags & 0x2)) |
201 | (*info->fprintf_func) (info->stream, ","); | |
202 | ||
203 | (*info->fprintf_func) (info->stream, " "); | |
204 | ||
205 | info->flags |= 0x2; | |
7b4ae824 JD |
206 | } |
207 | ||
ef1ad42b JD |
208 | /* Render the symbol name whose value is ADDR + BASE or the adddress itself if |
209 | there is no symbol. If BASE is non zero, then the a PC relative adddress is | |
210 | assumend (ie BASE is the value in the PC. */ | |
7b4ae824 | 211 | static void |
ef1ad42b JD |
212 | decode_possible_symbol (bfd_vma addr, bfd_vma base, |
213 | struct disassemble_info *info, bool relative) | |
7b4ae824 | 214 | { |
ef1ad42b JD |
215 | const char *fmt = relative ? "*%+" BFD_VMA_FMT "d" : "%" BFD_VMA_FMT "d"; |
216 | if (!info->symbol_at_address_func (addr + base, info)) | |
7b4ae824 | 217 | { |
ef1ad42b | 218 | (*info->fprintf_func) (info->stream, fmt, addr); |
7b4ae824 JD |
219 | } |
220 | else | |
221 | { | |
222 | asymbol *sym = NULL; | |
223 | int j; | |
224 | for (j = 0; j < info->symtab_size; ++j) | |
225 | { | |
226 | sym = info->symtab[j]; | |
ef1ad42b | 227 | if (bfd_asymbol_value (sym) == addr + base) |
7b4ae824 JD |
228 | { |
229 | break; | |
230 | } | |
231 | } | |
232 | if (j < info->symtab_size) | |
233 | (*info->fprintf_func) (info->stream, "%s", bfd_asymbol_name (sym)); | |
192c2bfb | 234 | else |
ef1ad42b | 235 | (*info->fprintf_func) (info->stream, fmt, addr); |
7b4ae824 JD |
236 | } |
237 | } | |
238 | ||
7b4ae824 | 239 | |
ef1ad42b | 240 | /* Emit the disassembled text for OPR */ |
7b4ae824 | 241 | static void |
ef1ad42b JD |
242 | opr_emit_disassembly (const struct operand *opr, |
243 | struct disassemble_info *info) | |
7b4ae824 | 244 | { |
7b4ae824 | 245 | operand_separator (info); |
7b4ae824 | 246 | |
ef1ad42b | 247 | switch (opr->cl) |
7b4ae824 | 248 | { |
ef1ad42b JD |
249 | case OPND_CL_IMMEDIATE: |
250 | (*info->fprintf_func) (info->stream, "#%d", | |
251 | ((struct immediate_operand *) opr)->value); | |
252 | break; | |
253 | case OPND_CL_REGISTER: | |
7b4ae824 | 254 | { |
ef1ad42b | 255 | int r = ((struct register_operand*) opr)->reg; |
66a66a17 NC |
256 | |
257 | if (r < 0 || r >= S12Z_N_REGISTERS) | |
258 | (*info->fprintf_func) (info->stream, _("<illegal reg num>")); | |
259 | else | |
260 | (*info->fprintf_func) (info->stream, "%s", registers[r].name); | |
7b4ae824 | 261 | } |
ef1ad42b JD |
262 | break; |
263 | case OPND_CL_REGISTER_ALL16: | |
264 | (*info->fprintf_func) (info->stream, "%s", "ALL16b"); | |
265 | break; | |
266 | case OPND_CL_REGISTER_ALL: | |
267 | (*info->fprintf_func) (info->stream, "%s", "ALL"); | |
268 | break; | |
269 | case OPND_CL_BIT_FIELD: | |
270 | (*info->fprintf_func) (info->stream, "#%d:%d", | |
271 | ((struct bitfield_operand*)opr)->width, | |
272 | ((struct bitfield_operand*)opr)->offset); | |
273 | break; | |
274 | case OPND_CL_SIMPLE_MEMORY: | |
7b4ae824 | 275 | { |
ef1ad42b JD |
276 | struct simple_memory_operand *mo = |
277 | (struct simple_memory_operand *) opr; | |
278 | decode_possible_symbol (mo->addr, mo->base, info, mo->relative); | |
7b4ae824 | 279 | } |
ef1ad42b JD |
280 | break; |
281 | case OPND_CL_MEMORY: | |
7b4ae824 | 282 | { |
ef1ad42b JD |
283 | int used_reg = 0; |
284 | struct memory_operand *mo = (struct memory_operand *) opr; | |
285 | (*info->fprintf_func) (info->stream, "%c", mo->indirect ? '[' : '('); | |
7b4ae824 | 286 | |
39f286cd JD |
287 | const char *fmt; |
288 | assert (mo->mutation == OPND_RM_NONE || mo->n_regs == 1); | |
289 | switch (mo->mutation) | |
290 | { | |
291 | case OPND_RM_PRE_DEC: | |
292 | fmt = "-%s"; | |
293 | break; | |
294 | case OPND_RM_PRE_INC: | |
295 | fmt = "+%s"; | |
296 | break; | |
297 | case OPND_RM_POST_DEC: | |
298 | fmt = "%s-"; | |
299 | break; | |
300 | case OPND_RM_POST_INC: | |
301 | fmt = "%s+"; | |
302 | break; | |
303 | case OPND_RM_NONE: | |
304 | default: | |
305 | if (mo->n_regs < 2) | |
306 | (*info->fprintf_func) (info->stream, (mo->n_regs == 0) ? "%d" : "%d,", mo->base_offset); | |
307 | fmt = "%s"; | |
308 | break; | |
309 | } | |
310 | if (mo->n_regs > 0) | |
66a66a17 NC |
311 | { |
312 | int r = mo->regs[0]; | |
313 | ||
314 | if (r < 0 || r >= S12Z_N_REGISTERS) | |
315 | (*info->fprintf_func) (info->stream, fmt, _("<illegal reg num>")); | |
316 | else | |
317 | (*info->fprintf_func) (info->stream, fmt, registers[r].name); | |
318 | } | |
39f286cd | 319 | used_reg = 1; |
7b4ae824 | 320 | |
ef1ad42b JD |
321 | if (mo->n_regs > used_reg) |
322 | { | |
66a66a17 NC |
323 | int r = mo->regs[used_reg]; |
324 | ||
325 | if (r < 0 || r >= S12Z_N_REGISTERS) | |
326 | (*info->fprintf_func) (info->stream, _("<illegal reg num>")); | |
327 | else | |
328 | (*info->fprintf_func) (info->stream, ",%s", | |
329 | registers[r].name); | |
ef1ad42b | 330 | } |
7b4ae824 | 331 | |
ef1ad42b JD |
332 | (*info->fprintf_func) (info->stream, "%c", |
333 | mo->indirect ? ']' : ')'); | |
7b4ae824 | 334 | } |
ef1ad42b JD |
335 | break; |
336 | }; | |
7b4ae824 JD |
337 | } |
338 | ||
66a66a17 NC |
339 | #define S12Z_N_SIZES 4 |
340 | static const char shift_size_table[S12Z_N_SIZES] = | |
341 | { | |
ef1ad42b JD |
342 | 'b', 'w', 'p', 'l' |
343 | }; | |
7b4ae824 | 344 | |
ef1ad42b JD |
345 | int |
346 | print_insn_s12z (bfd_vma memaddr, struct disassemble_info* info) | |
7b4ae824 | 347 | { |
ef1ad42b | 348 | int o; |
e5a557ac | 349 | enum optr operator = OP_INVALID; |
ef1ad42b JD |
350 | int n_operands = 0; |
351 | ||
352 | /* The longest instruction in S12Z can have 6 operands. | |
353 | (Most have 3 or less. Only PSH and PUL have so many. */ | |
354 | struct operand *operands[6]; | |
355 | ||
356 | struct mem_read_abstraction mra; | |
357 | mra.base.read = (void *) abstract_read_memory ; | |
358 | mra.base.advance = advance ; | |
359 | mra.base.posn = posn; | |
360 | mra.memaddr = memaddr; | |
361 | mra.info = info; | |
362 | ||
363 | short osize = -1; | |
364 | int n_bytes = | |
365 | decode_s12z (&operator, &osize, &n_operands, operands, | |
366 | (struct mem_read_abstraction_base *) &mra); | |
367 | ||
368 | (info->fprintf_func) (info->stream, "%s", mnemonics[(long)operator]); | |
448b8ca8 | 369 | |
ef1ad42b JD |
370 | /* Ship out size sufficies for those instructions which |
371 | need them. */ | |
372 | if (osize == -1) | |
373 | { | |
374 | bool suffix = false; | |
66a66a17 | 375 | |
ef1ad42b JD |
376 | for (o = 0; o < n_operands; ++o) |
377 | { | |
448b8ca8 | 378 | if (operands[o] && operands[o]->osize != -1) |
ef1ad42b JD |
379 | { |
380 | if (!suffix) | |
381 | { | |
382 | (*mra.info->fprintf_func) (mra.info->stream, "%c", '.'); | |
383 | suffix = true; | |
384 | } | |
66a66a17 NC |
385 | |
386 | osize = operands[o]->osize; | |
387 | ||
388 | if (osize < 0 || osize >= S12Z_N_SIZES) | |
389 | (*mra.info->fprintf_func) (mra.info->stream, _("<bad>")); | |
390 | else | |
391 | (*mra.info->fprintf_func) (mra.info->stream, "%c", | |
392 | shift_size_table[osize]); | |
393 | ||
ef1ad42b JD |
394 | } |
395 | } | |
396 | } | |
397 | else | |
398 | { | |
66a66a17 NC |
399 | if (osize < 0 || osize >= S12Z_N_SIZES) |
400 | (*mra.info->fprintf_func) (mra.info->stream, _(".<bad>")); | |
401 | else | |
402 | (*mra.info->fprintf_func) (mra.info->stream, ".%c", | |
403 | shift_size_table[osize]); | |
ef1ad42b | 404 | } |
7b4ae824 | 405 | |
ef1ad42b JD |
406 | /* Ship out the operands. */ |
407 | for (o = 0; o < n_operands; ++o) | |
7b4ae824 | 408 | { |
ef1ad42b JD |
409 | if (operands[o]) |
410 | opr_emit_disassembly (operands[o], mra.info); | |
411 | free (operands[o]); | |
7b4ae824 JD |
412 | } |
413 | ||
ef1ad42b | 414 | return n_bytes; |
7b4ae824 | 415 | } |