Commit | Line | Data |
---|---|---|
99c513f6 | 1 | /* Disassembler code for Renesas RL78. |
b3adc24a | 2 | Copyright (C) 2011-2020 Free Software Foundation, Inc. |
99c513f6 DD |
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 | ||
5eb3690e | 23 | #include "sysdep.h" |
99c513f6 DD |
24 | #include <stdio.h> |
25 | ||
26 | #include "bfd.h" | |
0952813b | 27 | #include "elf-bfd.h" |
88c1242d | 28 | #include "disassemble.h" |
99c513f6 | 29 | #include "opcode/rl78.h" |
0952813b | 30 | #include "elf/rl78.h" |
99c513f6 | 31 | |
3a0b8f7d YQ |
32 | #include <setjmp.h> |
33 | ||
99c513f6 DD |
34 | #define DEBUG_SEMANTICS 0 |
35 | ||
36 | typedef struct | |
37 | { | |
38 | bfd_vma pc; | |
39 | disassemble_info * dis; | |
40 | } RL78_Data; | |
41 | ||
3a0b8f7d YQ |
42 | struct private |
43 | { | |
44 | OPCODES_SIGJMP_BUF bailout; | |
45 | }; | |
46 | ||
99c513f6 DD |
47 | static int |
48 | rl78_get_byte (void * vdata) | |
49 | { | |
50 | bfd_byte buf[1]; | |
51 | RL78_Data *rl78_data = (RL78_Data *) vdata; | |
3a0b8f7d YQ |
52 | int status; |
53 | ||
54 | status = rl78_data->dis->read_memory_func (rl78_data->pc, | |
55 | buf, | |
56 | 1, | |
57 | rl78_data->dis); | |
58 | if (status != 0) | |
59 | { | |
60 | struct private *priv = (struct private *) rl78_data->dis->private_data; | |
99c513f6 | 61 | |
3a0b8f7d YQ |
62 | rl78_data->dis->memory_error_func (status, rl78_data->pc, |
63 | rl78_data->dis); | |
64 | OPCODES_SIGLONGJMP (priv->bailout, 1); | |
65 | } | |
99c513f6 DD |
66 | |
67 | rl78_data->pc ++; | |
68 | return buf[0]; | |
69 | } | |
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 | { | |
89 | switch (t) | |
90 | { | |
91 | case RL78_Operand_Indirect: | |
92 | case RL78_Operand_BitIndirect: | |
93 | case RL78_Operand_PostInc: | |
94 | case RL78_Operand_PreDec: | |
95 | return 1; | |
96 | default: | |
97 | return 0; | |
98 | } | |
99 | } | |
100 | ||
0952813b DD |
101 | static int |
102 | print_insn_rl78_common (bfd_vma addr, disassemble_info * dis, RL78_Dis_Isa isa) | |
99c513f6 DD |
103 | { |
104 | int rv; | |
105 | RL78_Data rl78_data; | |
106 | RL78_Opcode_Decoded opcode; | |
107 | const char * s; | |
108 | #if DEBUG_SEMANTICS | |
109 | static char buf[200]; | |
110 | #endif | |
3a0b8f7d | 111 | struct private priv; |
99c513f6 | 112 | |
3a0b8f7d | 113 | dis->private_data = (PTR) &priv; |
99c513f6 DD |
114 | rl78_data.pc = addr; |
115 | rl78_data.dis = dis; | |
116 | ||
3a0b8f7d YQ |
117 | if (OPCODES_SIGSETJMP (priv.bailout) != 0) |
118 | { | |
119 | /* Error return. */ | |
120 | return -1; | |
121 | } | |
122 | ||
0952813b | 123 | rv = rl78_decode_opcode (addr, &opcode, rl78_get_byte, &rl78_data, isa); |
99c513f6 DD |
124 | |
125 | dis->bytes_per_line = 10; | |
126 | ||
127 | #define PR (dis->fprintf_func) | |
128 | #define PS (dis->stream) | |
129 | #define PC(c) PR (PS, "%c", c) | |
130 | ||
131 | 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 | for (; *s; s++) | |
169 | { | |
170 | if (*s != '%') | |
171 | { | |
172 | PC (*s); | |
173 | } | |
174 | else | |
175 | { | |
176 | RL78_Opcode_Operand * oper; | |
177 | int do_hex = 0; | |
178 | int do_addr = 0; | |
179 | int do_es = 0; | |
180 | int do_sfr = 0; | |
181 | int do_cond = 0; | |
182 | int do_bang = 0; | |
183 | ||
3d557b4c | 184 | while (1) |
99c513f6 | 185 | { |
3d557b4c DD |
186 | s ++; |
187 | switch (*s) | |
188 | { | |
189 | case 'x': | |
190 | do_hex = 1; | |
191 | break; | |
192 | case '!': | |
193 | do_bang = 1; | |
194 | break; | |
195 | case 'e': | |
196 | do_es = 1; | |
197 | break; | |
198 | case 'a': | |
199 | do_addr = 1; | |
200 | break; | |
201 | case 's': | |
202 | do_sfr = 1; | |
203 | break; | |
204 | case 'c': | |
205 | do_cond = 1; | |
206 | break; | |
207 | default: | |
208 | goto no_more_modifiers; | |
209 | } | |
99c513f6 | 210 | } |
3d557b4c | 211 | no_more_modifiers:; |
99c513f6 DD |
212 | |
213 | switch (*s) | |
214 | { | |
215 | case '%': | |
216 | PC ('%'); | |
217 | 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"); | |
43e65147 | 231 | |
99c513f6 DD |
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 | case '0': | |
245 | case '1': | |
731df338 | 246 | oper = *s == '0' ? &opcode.op[0] : &opcode.op[1]; |
3d557b4c DD |
247 | if (do_es) |
248 | { | |
249 | if (oper->use_es && indirect_type (oper->type)) | |
250 | PR (PS, "es:"); | |
251 | } | |
252 | ||
253 | if (do_bang) | |
4d82fe66 NC |
254 | { |
255 | /* If we are going to display SP by name, we must omit the bang. */ | |
020efce5 NC |
256 | if ((oper->type == RL78_Operand_Indirect |
257 | || oper->type == RL78_Operand_BitIndirect) | |
4d82fe66 NC |
258 | && oper->reg == RL78_Reg_None |
259 | && do_sfr | |
260 | && ((oper->addend == 0xffff8 && opcode.size == RL78_Word) | |
261 | || (oper->addend == 0x0fff8 && do_es && opcode.size == RL78_Word))) | |
262 | ; | |
263 | else | |
264 | PC ('!'); | |
265 | } | |
3d557b4c DD |
266 | |
267 | if (do_cond) | |
268 | { | |
269 | PR (PS, "%s", condition_names[oper->condition]); | |
270 | break; | |
271 | } | |
272 | ||
273 | switch (oper->type) | |
274 | { | |
275 | case RL78_Operand_Immediate: | |
276 | if (do_addr) | |
277 | dis->print_address_func (oper->addend, dis); | |
278 | else if (do_hex | |
279 | || oper->addend > 999 | |
280 | || oper->addend < -999) | |
281 | PR (PS, "%#x", oper->addend); | |
282 | else | |
283 | PR (PS, "%d", oper->addend); | |
284 | break; | |
285 | ||
286 | case RL78_Operand_Register: | |
287 | PR (PS, "%s", register_names[oper->reg]); | |
288 | break; | |
289 | ||
290 | case RL78_Operand_Bit: | |
291 | PR (PS, "%s.%d", register_names[oper->reg], oper->bit_number); | |
292 | break; | |
293 | ||
294 | case RL78_Operand_Indirect: | |
295 | case RL78_Operand_BitIndirect: | |
296 | switch (oper->reg) | |
99c513f6 | 297 | { |
3d557b4c DD |
298 | case RL78_Reg_None: |
299 | if (oper->addend == 0xffffa && do_sfr && opcode.size == RL78_Byte) | |
300 | PR (PS, "psw"); | |
301 | else if (oper->addend == 0xffff8 && do_sfr && opcode.size == RL78_Word) | |
302 | PR (PS, "sp"); | |
4d82fe66 NC |
303 | else if (oper->addend == 0x0fff8 && do_sfr && do_es && opcode.size == RL78_Word) |
304 | PR (PS, "sp"); | |
46662804 VK |
305 | else if (oper->addend == 0xffff8 && do_sfr && opcode.size == RL78_Byte) |
306 | PR (PS, "spl"); | |
307 | else if (oper->addend == 0xffff9 && do_sfr && opcode.size == RL78_Byte) | |
308 | PR (PS, "sph"); | |
309 | else if (oper->addend == 0xffffc && do_sfr && opcode.size == RL78_Byte) | |
310 | PR (PS, "cs"); | |
311 | else if (oper->addend == 0xffffd && do_sfr && opcode.size == RL78_Byte) | |
312 | PR (PS, "es"); | |
313 | else if (oper->addend == 0xffffe && do_sfr && opcode.size == RL78_Byte) | |
314 | PR (PS, "pmc"); | |
315 | else if (oper->addend == 0xfffff && do_sfr && opcode.size == RL78_Byte) | |
316 | PR (PS, "mem"); | |
3d557b4c | 317 | else if (oper->addend >= 0xffe20) |
99c513f6 DD |
318 | PR (PS, "%#x", oper->addend); |
319 | else | |
3d557b4c DD |
320 | { |
321 | int faddr = oper->addend; | |
322 | if (do_es && ! oper->use_es) | |
323 | faddr += 0xf0000; | |
324 | dis->print_address_func (faddr, dis); | |
325 | } | |
99c513f6 DD |
326 | break; |
327 | ||
3d557b4c DD |
328 | case RL78_Reg_B: |
329 | case RL78_Reg_C: | |
330 | case RL78_Reg_BC: | |
331 | PR (PS, "%d[%s]", oper->addend, register_names[oper->reg]); | |
99c513f6 DD |
332 | break; |
333 | ||
3d557b4c DD |
334 | default: |
335 | PR (PS, "[%s", register_names[oper->reg]); | |
336 | if (oper->reg2 != RL78_Reg_None) | |
337 | PR (PS, "+%s", register_names[oper->reg2]); | |
90092e73 | 338 | if (oper->addend || do_addr) |
3d557b4c DD |
339 | PR (PS, "+%d", oper->addend); |
340 | PC (']'); | |
99c513f6 | 341 | break; |
43e65147 | 342 | |
3d557b4c DD |
343 | } |
344 | if (oper->type == RL78_Operand_BitIndirect) | |
345 | PR (PS, ".%d", oper->bit_number); | |
346 | break; | |
99c513f6 DD |
347 | |
348 | #if DEBUG_SEMANTICS | |
3d557b4c DD |
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; | |
99c513f6 DD |
358 | #endif |
359 | ||
3d557b4c DD |
360 | 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 | PR (PS, "???%d.%d", opcode.lineno, *s - '0'); | |
367 | break; | |
368 | } | |
99c513f6 DD |
369 | } |
370 | } | |
371 | } | |
372 | ||
373 | #if DEBUG_SEMANTICS | |
374 | ||
375 | PR (PS, "\t\033[34m(line %d)\033[0m", opcode.lineno); | |
376 | ||
377 | #endif | |
378 | ||
379 | return rv; | |
380 | } | |
0952813b DD |
381 | |
382 | int | |
383 | print_insn_rl78 (bfd_vma addr, disassemble_info * dis) | |
384 | { | |
385 | return print_insn_rl78_common (addr, dis, RL78_ISA_DEFAULT); | |
386 | } | |
387 | ||
388 | int | |
389 | print_insn_rl78_g10 (bfd_vma addr, disassemble_info * dis) | |
390 | { | |
391 | return print_insn_rl78_common (addr, dis, RL78_ISA_G10); | |
392 | } | |
393 | ||
394 | int | |
395 | print_insn_rl78_g13 (bfd_vma addr, disassemble_info * dis) | |
396 | { | |
397 | return print_insn_rl78_common (addr, dis, RL78_ISA_G13); | |
398 | } | |
399 | ||
400 | int | |
401 | print_insn_rl78_g14 (bfd_vma addr, disassemble_info * dis) | |
402 | { | |
403 | return print_insn_rl78_common (addr, dis, RL78_ISA_G14); | |
404 | } | |
405 | ||
406 | disassembler_ftype | |
407 | rl78_get_disassembler (bfd *abfd) | |
408 | { | |
ab20fa4a YQ |
409 | int cpu = E_FLAG_RL78_ANY_CPU; |
410 | ||
411 | if (abfd != NULL) | |
412 | cpu = abfd->tdata.elf_obj_data->elf_header->e_flags & E_FLAG_RL78_CPU_MASK; | |
413 | ||
0952813b DD |
414 | switch (cpu) |
415 | { | |
416 | case E_FLAG_RL78_G10: | |
417 | return print_insn_rl78_g10; | |
418 | case E_FLAG_RL78_G13: | |
419 | return print_insn_rl78_g13; | |
420 | case E_FLAG_RL78_G14: | |
421 | return print_insn_rl78_g14; | |
422 | default: | |
423 | return print_insn_rl78; | |
424 | } | |
425 | } |