/src/binutils-gdb/opcodes/rl78-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Disassembler code for Renesas RL78. |
2 | | Copyright (C) 2011-2025 Free Software Foundation, Inc. |
3 | | Contributed by Red Hat. |
4 | | Written by DJ Delorie. |
5 | | |
6 | | This file is part of the GNU opcodes library. |
7 | | |
8 | | This library is free software; you can redistribute it and/or modify |
9 | | it under the terms of the GNU General Public License as published by |
10 | | the Free Software Foundation; either version 3, or (at your option) |
11 | | any later version. |
12 | | |
13 | | It is distributed in the hope that it will be useful, but WITHOUT |
14 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
15 | | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
16 | | License for more details. |
17 | | |
18 | | You should have received a copy of the GNU General Public License |
19 | | along with this program; if not, write to the Free Software |
20 | | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
21 | | MA 02110-1301, USA. */ |
22 | | |
23 | | #include "sysdep.h" |
24 | | #include <stdio.h> |
25 | | |
26 | | #include "bfd.h" |
27 | | #include "elf-bfd.h" |
28 | | #include "disassemble.h" |
29 | | #include "opcode/rl78.h" |
30 | | #include "elf/rl78.h" |
31 | | |
32 | | #include <setjmp.h> |
33 | | |
34 | | #define DEBUG_SEMANTICS 0 |
35 | | |
36 | | typedef struct |
37 | | { |
38 | | bfd_vma pc; |
39 | | disassemble_info * dis; |
40 | | } RL78_Data; |
41 | | |
42 | | struct private |
43 | | { |
44 | | OPCODES_SIGJMP_BUF bailout; |
45 | | }; |
46 | | |
47 | | static int |
48 | | rl78_get_byte (void * vdata) |
49 | 5.94M | { |
50 | 5.94M | bfd_byte buf[1]; |
51 | 5.94M | RL78_Data *rl78_data = (RL78_Data *) vdata; |
52 | 5.94M | int status; |
53 | | |
54 | 5.94M | status = rl78_data->dis->read_memory_func (rl78_data->pc, |
55 | 5.94M | buf, |
56 | 5.94M | 1, |
57 | 5.94M | rl78_data->dis); |
58 | 5.94M | if (status != 0) |
59 | 1.34k | { |
60 | 1.34k | struct private *priv = (struct private *) rl78_data->dis->private_data; |
61 | | |
62 | 1.34k | rl78_data->dis->memory_error_func (status, rl78_data->pc, |
63 | 1.34k | rl78_data->dis); |
64 | 1.34k | OPCODES_SIGLONGJMP (priv->bailout, 1); |
65 | 1.34k | } |
66 | | |
67 | 5.94M | rl78_data->pc ++; |
68 | 5.94M | return buf[0]; |
69 | 5.94M | } |
70 | | |
71 | | static char const * |
72 | | register_names[] = |
73 | | { |
74 | | "", |
75 | | "x", "a", "c", "b", "e", "d", "l", "h", |
76 | | "ax", "bc", "de", "hl", |
77 | | "sp", "psw", "cs", "es", "pmc", "mem" |
78 | | }; |
79 | | |
80 | | static char const * |
81 | | condition_names[] = |
82 | | { |
83 | | "t", "f", "c", "nc", "h", "nh", "z", "nz" |
84 | | }; |
85 | | |
86 | | static int |
87 | | indirect_type (int t) |
88 | 13.2k | { |
89 | 13.2k | switch (t) |
90 | 13.2k | { |
91 | 12.4k | case RL78_Operand_Indirect: |
92 | 13.0k | case RL78_Operand_BitIndirect: |
93 | 13.0k | case RL78_Operand_PostInc: |
94 | 13.0k | case RL78_Operand_PreDec: |
95 | 13.0k | return 1; |
96 | 172 | default: |
97 | 172 | return 0; |
98 | 13.2k | } |
99 | 13.2k | } |
100 | | |
101 | | static int |
102 | | print_insn_rl78_common (bfd_vma addr, disassemble_info * dis, RL78_Dis_Isa isa) |
103 | 3.45M | { |
104 | 3.45M | int rv; |
105 | 3.45M | RL78_Data rl78_data; |
106 | 3.45M | RL78_Opcode_Decoded opcode; |
107 | 3.45M | const char * s; |
108 | | #if DEBUG_SEMANTICS |
109 | | static char buf[200]; |
110 | | #endif |
111 | 3.45M | struct private priv; |
112 | | |
113 | 3.45M | dis->private_data = &priv; |
114 | 3.45M | rl78_data.pc = addr; |
115 | 3.45M | rl78_data.dis = dis; |
116 | | |
117 | 3.45M | if (OPCODES_SIGSETJMP (priv.bailout) != 0) |
118 | 1.34k | { |
119 | | /* Error return. */ |
120 | 1.34k | return -1; |
121 | 1.34k | } |
122 | | |
123 | 3.45M | rv = rl78_decode_opcode (addr, &opcode, rl78_get_byte, &rl78_data, isa); |
124 | | |
125 | 3.45M | dis->bytes_per_line = 10; |
126 | | |
127 | 22.7M | #define PR (dis->fprintf_func) |
128 | 22.7M | #define PS (dis->stream) |
129 | 18.8M | #define PC(c) PR (PS, "%c", c) |
130 | | |
131 | 3.45M | s = opcode.syntax; |
132 | | |
133 | | #if DEBUG_SEMANTICS |
134 | | |
135 | | switch (opcode.id) |
136 | | { |
137 | | case RLO_unknown: s = "uknown"; break; |
138 | | case RLO_add: s = "add: %e0%0 += %e1%1"; break; |
139 | | case RLO_addc: s = "addc: %e0%0 += %e1%1 + CY"; break; |
140 | | case RLO_and: s = "and: %e0%0 &= %e1%1"; break; |
141 | | case RLO_branch: s = "branch: pc = %e0%0"; break; |
142 | | case RLO_branch_cond: s = "branch_cond: pc = %e0%0 if %c1 / %e1%1"; break; |
143 | | case RLO_branch_cond_clear: s = "branch_cond_clear: pc = %e0%0 if %c1 / %e1%1, %e1%1 = 0"; break; |
144 | | case RLO_call: s = "call: pc = %e1%0"; break; |
145 | | case RLO_cmp: s = "cmp: %e0%0 - %e1%1"; break; |
146 | | case RLO_mov: s = "mov: %e0%0 = %e1%1"; break; |
147 | | case RLO_or: s = "or: %e0%0 |= %e1%1"; break; |
148 | | case RLO_rol: s = "rol: %e0%0 <<= %e1%1"; break; |
149 | | case RLO_rolc: s = "rol: %e0%0 <<= %e1%1,CY"; break; |
150 | | case RLO_ror: s = "ror: %e0%0 >>= %e1%1"; break; |
151 | | case RLO_rorc: s = "ror: %e0%0 >>= %e1%1,CY"; break; |
152 | | case RLO_sar: s = "sar: %e0%0 >>= %e1%1 signed"; break; |
153 | | case RLO_sel: s = "sel: rb = %1"; break; |
154 | | case RLO_shr: s = "shr: %e0%0 >>= %e1%1 unsigned"; break; |
155 | | case RLO_shl: s = "shl: %e0%0 <<= %e1%1"; break; |
156 | | case RLO_skip: s = "skip: if %c1"; break; |
157 | | case RLO_sub: s = "sub: %e0%0 -= %e1%1"; break; |
158 | | case RLO_subc: s = "subc: %e0%0 -= %e1%1 - CY"; break; |
159 | | case RLO_xch: s = "xch: %e0%0 <-> %e1%1"; break; |
160 | | case RLO_xor: s = "xor: %e0%0 ^= %e1%1"; break; |
161 | | } |
162 | | |
163 | | sprintf(buf, "%s%%W%%f\t\033[32m%s\033[0m", s, opcode.syntax); |
164 | | s = buf; |
165 | | |
166 | | #endif |
167 | | |
168 | 25.6M | for (; *s; s++) |
169 | 22.1M | { |
170 | 22.1M | if (*s != '%') |
171 | 18.2M | { |
172 | 18.2M | PC (*s); |
173 | 18.2M | } |
174 | 3.89M | else |
175 | 3.89M | { |
176 | 3.89M | RL78_Opcode_Operand * oper; |
177 | 3.89M | int do_hex = 0; |
178 | 3.89M | int do_addr = 0; |
179 | 3.89M | int do_es = 0; |
180 | 3.89M | int do_sfr = 0; |
181 | 3.89M | int do_cond = 0; |
182 | 3.89M | int do_bang = 0; |
183 | | |
184 | 5.12M | while (1) |
185 | 5.12M | { |
186 | 5.12M | s ++; |
187 | 5.12M | switch (*s) |
188 | 5.12M | { |
189 | 15.4k | case 'x': |
190 | 15.4k | do_hex = 1; |
191 | 15.4k | break; |
192 | 254k | case '!': |
193 | 254k | do_bang = 1; |
194 | 254k | break; |
195 | 592k | case 'e': |
196 | 592k | do_es = 1; |
197 | 592k | break; |
198 | 260k | case 'a': |
199 | 260k | do_addr = 1; |
200 | 260k | break; |
201 | 103k | case 's': |
202 | 103k | do_sfr = 1; |
203 | 103k | break; |
204 | 1.24k | case 'c': |
205 | 1.24k | do_cond = 1; |
206 | 1.24k | break; |
207 | 3.89M | default: |
208 | 3.89M | goto no_more_modifiers; |
209 | 5.12M | } |
210 | 5.12M | } |
211 | 3.89M | no_more_modifiers:; |
212 | | |
213 | 3.89M | switch (*s) |
214 | 3.89M | { |
215 | 0 | case '%': |
216 | 0 | PC ('%'); |
217 | 0 | break; |
218 | | |
219 | | #if DEBUG_SEMANTICS |
220 | | |
221 | | case 'W': |
222 | | if (opcode.size == RL78_Word) |
223 | | PR (PS, " \033[33mW\033[0m"); |
224 | | break; |
225 | | |
226 | | case 'f': |
227 | | if (opcode.flags) |
228 | | { |
229 | | char *comma = ""; |
230 | | PR (PS, " \033[35m"); |
231 | | |
232 | | if (opcode.flags & RL78_PSW_Z) |
233 | | { PR (PS, "Z"); comma = ","; } |
234 | | if (opcode.flags & RL78_PSW_AC) |
235 | | { PR (PS, "%sAC", comma); comma = ","; } |
236 | | if (opcode.flags & RL78_PSW_CY) |
237 | | { PR (PS, "%sCY", comma); comma = ","; } |
238 | | PR (PS, "\033[0m"); |
239 | | } |
240 | | break; |
241 | | |
242 | | #endif |
243 | | |
244 | 2.23M | case '0': |
245 | 3.89M | case '1': |
246 | 3.89M | oper = *s == '0' ? &opcode.op[0] : &opcode.op[1]; |
247 | 3.89M | if (do_es) |
248 | 592k | { |
249 | 592k | if (oper->use_es && indirect_type (oper->type)) |
250 | 13.0k | PR (PS, "es:"); |
251 | 592k | } |
252 | | |
253 | 3.89M | if (do_bang) |
254 | 254k | { |
255 | | /* If we are going to display SP by name, we must omit the bang. */ |
256 | 254k | if ((oper->type == RL78_Operand_Indirect |
257 | 254k | || oper->type == RL78_Operand_BitIndirect) |
258 | 254k | && oper->reg == RL78_Reg_None |
259 | 254k | && do_sfr |
260 | 254k | && ((oper->addend == 0xffff8 && opcode.size == RL78_Word) |
261 | 11.9k | || (oper->addend == 0x0fff8 && do_es && opcode.size == RL78_Word))) |
262 | 122 | ; |
263 | 254k | else |
264 | 254k | PC ('!'); |
265 | 254k | } |
266 | | |
267 | 3.89M | if (do_cond) |
268 | 1.24k | { |
269 | 1.24k | PR (PS, "%s", condition_names[oper->condition]); |
270 | 1.24k | break; |
271 | 1.24k | } |
272 | | |
273 | 3.89M | switch (oper->type) |
274 | 3.89M | { |
275 | 471k | case RL78_Operand_Immediate: |
276 | 471k | if (do_addr) |
277 | 102k | dis->print_address_func (oper->addend, dis); |
278 | 369k | else if (do_hex |
279 | 369k | || oper->addend > 999 |
280 | 369k | || oper->addend < -999) |
281 | 76.8k | PR (PS, "%#x", oper->addend); |
282 | 292k | else |
283 | 292k | PR (PS, "%d", oper->addend); |
284 | 471k | break; |
285 | | |
286 | 2.36M | case RL78_Operand_Register: |
287 | 2.36M | PR (PS, "%s", register_names[oper->reg]); |
288 | 2.36M | break; |
289 | | |
290 | 21.9k | case RL78_Operand_Bit: |
291 | 21.9k | PR (PS, "%s.%d", register_names[oper->reg], oper->bit_number); |
292 | 21.9k | break; |
293 | | |
294 | 894k | case RL78_Operand_Indirect: |
295 | 1.03M | case RL78_Operand_BitIndirect: |
296 | 1.03M | switch (oper->reg) |
297 | 1.03M | { |
298 | 631k | case RL78_Reg_None: |
299 | 631k | if (oper->addend == 0xffffa && do_sfr && opcode.size == RL78_Byte) |
300 | 415 | PR (PS, "psw"); |
301 | 630k | else if (oper->addend == 0xffff8 && do_sfr && opcode.size == RL78_Word) |
302 | 121 | PR (PS, "sp"); |
303 | 630k | else if (oper->addend == 0x0fff8 && do_sfr && do_es && opcode.size == RL78_Word) |
304 | 122 | PR (PS, "sp"); |
305 | 630k | else if (oper->addend == 0xffff8 && do_sfr && opcode.size == RL78_Byte) |
306 | 396 | PR (PS, "spl"); |
307 | 630k | else if (oper->addend == 0xffff9 && do_sfr && opcode.size == RL78_Byte) |
308 | 340 | PR (PS, "sph"); |
309 | 629k | else if (oper->addend == 0xffffc && do_sfr && opcode.size == RL78_Byte) |
310 | 759 | PR (PS, "cs"); |
311 | 628k | else if (oper->addend == 0xffffd && do_sfr && opcode.size == RL78_Byte) |
312 | 332 | PR (PS, "es"); |
313 | 628k | else if (oper->addend == 0xffffe && do_sfr && opcode.size == RL78_Byte) |
314 | 935 | PR (PS, "pmc"); |
315 | 627k | else if (oper->addend == 0xfffff && do_sfr && opcode.size == RL78_Byte) |
316 | 727 | PR (PS, "mem"); |
317 | 626k | else if (oper->addend >= 0xffe20) |
318 | 409k | PR (PS, "%#x", oper->addend); |
319 | 217k | else |
320 | 217k | { |
321 | 217k | int faddr = oper->addend; |
322 | 217k | if (do_es && ! oper->use_es) |
323 | 198k | faddr += 0xf0000; |
324 | 217k | dis->print_address_func (faddr, dis); |
325 | 217k | } |
326 | 631k | break; |
327 | | |
328 | 23.7k | case RL78_Reg_B: |
329 | 68.6k | case RL78_Reg_C: |
330 | 96.0k | case RL78_Reg_BC: |
331 | 96.0k | PR (PS, "%d[%s]", oper->addend, register_names[oper->reg]); |
332 | 96.0k | break; |
333 | | |
334 | 312k | default: |
335 | 312k | PR (PS, "[%s", register_names[oper->reg]); |
336 | 312k | if (oper->reg2 != RL78_Reg_None) |
337 | 9.28k | PR (PS, "+%s", register_names[oper->reg2]); |
338 | 312k | if (oper->addend || do_addr) |
339 | 158k | PR (PS, "+%d", oper->addend); |
340 | 312k | PC (']'); |
341 | 312k | break; |
342 | | |
343 | 1.03M | } |
344 | 1.03M | if (oper->type == RL78_Operand_BitIndirect) |
345 | 144k | PR (PS, ".%d", oper->bit_number); |
346 | 1.03M | break; |
347 | | |
348 | | #if DEBUG_SEMANTICS |
349 | | /* Shouldn't happen - push and pop don't print |
350 | | [SP] directly. But we *do* use them for |
351 | | semantic debugging. */ |
352 | | case RL78_Operand_PostInc: |
353 | | PR (PS, "[%s++]", register_names[oper->reg]); |
354 | | break; |
355 | | case RL78_Operand_PreDec: |
356 | | PR (PS, "[--%s]", register_names[oper->reg]); |
357 | | break; |
358 | | #endif |
359 | | |
360 | 0 | default: |
361 | | /* If we ever print this, that means the |
362 | | programmer tried to print an operand with a |
363 | | type we don't expect. Print the line and |
364 | | operand number from rl78-decode.opc for |
365 | | them. */ |
366 | 0 | PR (PS, "???%d.%d", opcode.lineno, *s - '0'); |
367 | 0 | break; |
368 | 3.89M | } |
369 | 3.89M | } |
370 | 3.89M | } |
371 | 22.1M | } |
372 | | |
373 | | #if DEBUG_SEMANTICS |
374 | | |
375 | | PR (PS, "\t\033[34m(line %d)\033[0m", opcode.lineno); |
376 | | |
377 | | #endif |
378 | | |
379 | 3.45M | return rv; |
380 | 3.45M | } |
381 | | |
382 | | int |
383 | | print_insn_rl78 (bfd_vma addr, disassemble_info * dis) |
384 | 3.45M | { |
385 | 3.45M | return print_insn_rl78_common (addr, dis, RL78_ISA_DEFAULT); |
386 | 3.45M | } |
387 | | |
388 | | int |
389 | | print_insn_rl78_g10 (bfd_vma addr, disassemble_info * dis) |
390 | 0 | { |
391 | 0 | return print_insn_rl78_common (addr, dis, RL78_ISA_G10); |
392 | 0 | } |
393 | | |
394 | | int |
395 | | print_insn_rl78_g13 (bfd_vma addr, disassemble_info * dis) |
396 | 0 | { |
397 | 0 | return print_insn_rl78_common (addr, dis, RL78_ISA_G13); |
398 | 0 | } |
399 | | |
400 | | int |
401 | | print_insn_rl78_g14 (bfd_vma addr, disassemble_info * dis) |
402 | 0 | { |
403 | 0 | return print_insn_rl78_common (addr, dis, RL78_ISA_G14); |
404 | 0 | } |
405 | | |
406 | | disassembler_ftype |
407 | | rl78_get_disassembler (bfd *abfd) |
408 | 4.64k | { |
409 | 4.64k | int cpu = E_FLAG_RL78_ANY_CPU; |
410 | | |
411 | 4.64k | if (abfd != NULL && bfd_get_flavour (abfd) == bfd_target_elf_flavour) |
412 | 0 | cpu = abfd->tdata.elf_obj_data->elf_header->e_flags & E_FLAG_RL78_CPU_MASK; |
413 | | |
414 | 4.64k | switch (cpu) |
415 | 4.64k | { |
416 | 0 | case E_FLAG_RL78_G10: |
417 | 0 | return print_insn_rl78_g10; |
418 | 0 | case E_FLAG_RL78_G13: |
419 | 0 | return print_insn_rl78_g13; |
420 | 0 | case E_FLAG_RL78_G14: |
421 | 0 | return print_insn_rl78_g14; |
422 | 4.64k | default: |
423 | 4.64k | return print_insn_rl78; |
424 | 4.64k | } |
425 | 4.64k | } |