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