/src/binutils-gdb/opcodes/rx-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Disassembler code for Renesas RX. |
2 | | Copyright (C) 2008-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 "dis-asm.h" |
28 | | #include "opcode/rx.h" |
29 | | #include "libiberty.h" |
30 | | #include "opintl.h" |
31 | | |
32 | | #include <setjmp.h> |
33 | | |
34 | | typedef struct |
35 | | { |
36 | | bfd_vma pc; |
37 | | disassemble_info * dis; |
38 | | } RX_Data; |
39 | | |
40 | | struct private |
41 | | { |
42 | | OPCODES_SIGJMP_BUF bailout; |
43 | | }; |
44 | | |
45 | | static int |
46 | | rx_get_byte (void * vdata) |
47 | 91.0M | { |
48 | 91.0M | bfd_byte buf[1]; |
49 | 91.0M | RX_Data *rx_data = (RX_Data *) vdata; |
50 | 91.0M | int status; |
51 | | |
52 | 91.0M | status = rx_data->dis->read_memory_func (rx_data->pc, |
53 | 91.0M | buf, |
54 | 91.0M | 1, |
55 | 91.0M | rx_data->dis); |
56 | 91.0M | if (status != 0) |
57 | 8.64k | { |
58 | 8.64k | struct private *priv = (struct private *) rx_data->dis->private_data; |
59 | | |
60 | 8.64k | rx_data->dis->memory_error_func (status, rx_data->pc, |
61 | 8.64k | rx_data->dis); |
62 | 8.64k | OPCODES_SIGLONGJMP (priv->bailout, 1); |
63 | 8.64k | } |
64 | | |
65 | 91.0M | rx_data->pc ++; |
66 | 91.0M | return buf[0]; |
67 | 91.0M | } |
68 | | |
69 | | static char const * size_names[RX_MAX_SIZE] = |
70 | | { |
71 | | "", ".b", ".ub", ".b", ".w", ".uw", ".w", ".a", ".l", "", "<error>" |
72 | | }; |
73 | | |
74 | | static char const * opsize_names[RX_MAX_SIZE] = |
75 | | { |
76 | | "", ".b", ".b", ".b", ".w", ".w", ".w", ".a", ".l", ".d", "<error>" |
77 | | }; |
78 | | |
79 | | static char const * register_names[] = |
80 | | { |
81 | | /* General registers. */ |
82 | | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
83 | | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", |
84 | | /* Control registers. */ |
85 | | "psw", "pc", "usp", "fpsw", NULL, NULL, NULL, NULL, |
86 | | "bpsw", "bpc", "isp", "fintv", "intb", "extb", NULL, NULL, |
87 | | "a0", "a1", NULL, NULL, NULL, NULL, NULL, NULL, |
88 | | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL |
89 | | }; |
90 | | |
91 | | static char const * condition_names[] = |
92 | | { |
93 | | /* Condition codes. */ |
94 | | "eq", "ne", "c", "nc", "gtu", "leu", "pz", "n", |
95 | | "ge", "lt", "gt", "le", "o", "no", "<invalid>", "<invalid>" |
96 | | }; |
97 | | |
98 | | static const char * flag_names[] = |
99 | | { |
100 | | "c", "z", "s", "o", "", "", "", "", |
101 | | "", "", "", "", "", "", "", "", |
102 | | "i", "u", "", "", "", "", "", "", |
103 | | "", "", "", "", "", "", "", "" |
104 | | }; |
105 | | |
106 | | static const char * double_register_names[] = |
107 | | { |
108 | | "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7", |
109 | | "dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14", "dr15" |
110 | | }; |
111 | | |
112 | | static const char * double_register_high_names[] = |
113 | | { |
114 | | "drh0", "drh1", "drh2", "drh3", "drh4", "drh5", "drh6", "drh7", |
115 | | "drh8", "drh9", "drh10", "drh11", "drh12", "drh13", "drh14", "drh15" |
116 | | }; |
117 | | |
118 | | static const char * double_register_low_names[] = |
119 | | { |
120 | | "drl0", "drl1", "drl2", "drl3", "drl4", "drl5", "drl6", "drl7", |
121 | | "drl8", "drl9", "drl10", "drl11", "drl12", "drl13", "drl14", "drl15" |
122 | | }; |
123 | | |
124 | | static const char * double_control_register_names[] = |
125 | | { |
126 | | "dpsw", "dcmr", "decnt", "depc" |
127 | | }; |
128 | | |
129 | | static const char * double_condition_names[] = |
130 | | { |
131 | | "", "un", "eq", "", "lt", "", "le" |
132 | | }; |
133 | | |
134 | | static inline const char * |
135 | | get_register_name (unsigned int reg) |
136 | 35.3M | { |
137 | 35.3M | if (reg < ARRAY_SIZE (register_names)) |
138 | 35.3M | return register_names[reg]; |
139 | 0 | return _("<invalid register number>"); |
140 | 35.3M | } |
141 | | |
142 | | static inline const char * |
143 | | get_condition_name (unsigned int cond) |
144 | 5.55M | { |
145 | 5.55M | if (cond < ARRAY_SIZE (condition_names)) |
146 | 5.55M | return condition_names[cond]; |
147 | 0 | return _("<invalid condition code>"); |
148 | 5.55M | } |
149 | | |
150 | | static inline const char * |
151 | | get_flag_name (unsigned int flag) |
152 | 16.0k | { |
153 | 16.0k | if (flag < ARRAY_SIZE (flag_names)) |
154 | 16.0k | return flag_names[flag]; |
155 | 0 | return _("<invalid flag>"); |
156 | 16.0k | } |
157 | | |
158 | | static inline const char * |
159 | | get_double_register_name (unsigned int reg) |
160 | 102k | { |
161 | 102k | if (reg < ARRAY_SIZE (double_register_names)) |
162 | 101k | return double_register_names[reg]; |
163 | 273 | return _("<invalid register number>"); |
164 | 102k | } |
165 | | |
166 | | static inline const char * |
167 | | get_double_register_high_name (unsigned int reg) |
168 | 11.0k | { |
169 | 11.0k | if (reg < ARRAY_SIZE (double_register_high_names)) |
170 | 11.0k | return double_register_high_names[reg]; |
171 | 0 | return _("<invalid register number>"); |
172 | 11.0k | } |
173 | | |
174 | | static inline const char * |
175 | | get_double_register_low_name (unsigned int reg) |
176 | 8.68k | { |
177 | 8.68k | if (reg < ARRAY_SIZE (double_register_low_names)) |
178 | 8.68k | return double_register_low_names[reg]; |
179 | 0 | return _("<invalid register number>"); |
180 | 8.68k | } |
181 | | |
182 | | static inline const char * |
183 | | get_double_control_register_name (unsigned int reg) |
184 | 10.2k | { |
185 | 10.2k | if (reg < ARRAY_SIZE (double_control_register_names)) |
186 | 2.66k | return double_control_register_names[reg]; |
187 | 7.57k | return _("<invalid register number>"); |
188 | 10.2k | } |
189 | | |
190 | | static inline const char * |
191 | | get_double_condition_name (unsigned int cond) |
192 | 3.27k | { |
193 | 3.27k | if (cond < ARRAY_SIZE (double_condition_names)) |
194 | 1.91k | return double_condition_names[cond]; |
195 | 1.36k | return _("<invalid condition code>"); |
196 | 3.27k | } |
197 | | |
198 | | static inline const char * |
199 | | get_opsize_name (unsigned int opsize) |
200 | 11.3M | { |
201 | 11.3M | if (opsize < ARRAY_SIZE (opsize_names)) |
202 | 11.3M | return opsize_names[opsize]; |
203 | 0 | return _("<invalid opsize>"); |
204 | 11.3M | } |
205 | | |
206 | | static inline const char * |
207 | | get_size_name (unsigned int size) |
208 | 2.76M | { |
209 | 2.76M | if (size < ARRAY_SIZE (size_names)) |
210 | 2.76M | return size_names[size]; |
211 | 0 | return _("<invalid size>"); |
212 | 2.76M | } |
213 | | |
214 | | |
215 | | int |
216 | | print_insn_rx (bfd_vma addr, disassemble_info * dis) |
217 | 49.4M | { |
218 | 49.4M | int rv; |
219 | 49.4M | RX_Data rx_data; |
220 | 49.4M | RX_Opcode_Decoded opcode; |
221 | 49.4M | const char * s; |
222 | 49.4M | struct private priv; |
223 | | |
224 | 49.4M | dis->private_data = &priv; |
225 | 49.4M | rx_data.pc = addr; |
226 | 49.4M | rx_data.dis = dis; |
227 | | |
228 | 49.4M | if (OPCODES_SIGSETJMP (priv.bailout) != 0) |
229 | 8.64k | { |
230 | | /* Error return. */ |
231 | 8.64k | return -1; |
232 | 8.64k | } |
233 | | |
234 | 49.4M | rv = rx_decode_opcode (addr, &opcode, rx_get_byte, &rx_data); |
235 | | |
236 | 49.4M | dis->bytes_per_line = 10; |
237 | | |
238 | 320M | #define PR (dis->fprintf_func) |
239 | 320M | #define PS (dis->stream) |
240 | 259M | #define PC(c) PR (PS, "%c", c) |
241 | | |
242 | | /* Detect illegal instructions. */ |
243 | 49.4M | if (opcode.op[0].size == RX_Bad_Size |
244 | 49.4M | || register_names [opcode.op[0].reg] == NULL |
245 | 49.4M | || register_names [opcode.op[1].reg] == NULL |
246 | 49.4M | || register_names [opcode.op[2].reg] == NULL) |
247 | 224k | { |
248 | 224k | bfd_byte buf[10]; |
249 | 224k | int i; |
250 | | |
251 | 224k | PR (PS, ".byte "); |
252 | 224k | rx_data.dis->read_memory_func (rx_data.pc - rv, buf, rv, rx_data.dis); |
253 | | |
254 | 1.13M | for (i = 0 ; i < rv; i++) |
255 | 908k | PR (PS, "0x%02x ", buf[i]); |
256 | 224k | return rv; |
257 | 224k | } |
258 | | |
259 | 377M | for (s = opcode.syntax; *s; s++) |
260 | 328M | { |
261 | 328M | if (*s != '%') |
262 | 259M | { |
263 | 259M | PC (*s); |
264 | 259M | } |
265 | 69.5M | else |
266 | 69.5M | { |
267 | 69.5M | RX_Opcode_Operand * oper; |
268 | 69.5M | int do_size = 0; |
269 | 69.5M | int do_hex = 0; |
270 | 69.5M | int do_addr = 0; |
271 | | |
272 | 69.5M | s ++; |
273 | | |
274 | 69.5M | if (*s == 'S') |
275 | 3.78M | { |
276 | 3.78M | do_size = 1; |
277 | 3.78M | s++; |
278 | 3.78M | } |
279 | 69.5M | if (*s == 'x') |
280 | 0 | { |
281 | 0 | do_hex = 1; |
282 | 0 | s++; |
283 | 0 | } |
284 | 69.5M | if (*s == 'a') |
285 | 8.13M | { |
286 | 8.13M | do_addr = 1; |
287 | 8.13M | s++; |
288 | 8.13M | } |
289 | | |
290 | 69.5M | switch (*s) |
291 | 69.5M | { |
292 | 0 | case '%': |
293 | 0 | PC ('%'); |
294 | 0 | break; |
295 | | |
296 | 11.3M | case 's': |
297 | 11.3M | PR (PS, "%s", get_opsize_name (opcode.size)); |
298 | 11.3M | break; |
299 | | |
300 | 1.05k | case 'b': |
301 | 1.05k | s ++; |
302 | 1.05k | if (*s == 'f') |
303 | 1.05k | { |
304 | 1.05k | int imm = opcode.op[2].addend; |
305 | 1.05k | int slsb, dlsb, width; |
306 | | |
307 | 1.05k | dlsb = (imm >> 5) & 0x1f; |
308 | 1.05k | slsb = (imm & 0x1f); |
309 | 1.05k | slsb = (slsb >= 0x10?(slsb ^ 0x1f) + 1:slsb); |
310 | 1.05k | slsb = dlsb - slsb; |
311 | 1.05k | slsb = (slsb < 0?-slsb:slsb); |
312 | 1.05k | width = ((imm >> 10) & 0x1f) - dlsb; |
313 | 1.05k | PR (PS, "#%d, #%d, #%d, %s, %s", |
314 | 1.05k | slsb, dlsb, width, |
315 | 1.05k | get_register_name (opcode.op[1].reg), |
316 | 1.05k | get_register_name (opcode.op[0].reg)); |
317 | 1.05k | } |
318 | 1.05k | break; |
319 | 25.7M | case '0': |
320 | 52.2M | case '1': |
321 | 58.1M | case '2': |
322 | 58.1M | oper = opcode.op + (*s - '0'); |
323 | 58.1M | if (do_size) |
324 | 3.78M | { |
325 | 3.78M | if (oper->type == RX_Operand_Indirect || oper->type == RX_Operand_Zero_Indirect) |
326 | 2.76M | PR (PS, "%s", get_size_name (oper->size)); |
327 | 3.78M | } |
328 | 54.4M | else |
329 | 54.4M | switch (oper->type) |
330 | 54.4M | { |
331 | 13.3M | case RX_Operand_Immediate: |
332 | 13.3M | if (do_addr) |
333 | 8.13M | dis->print_address_func (oper->addend, dis); |
334 | 5.19M | else if (do_hex |
335 | 5.19M | || oper->addend > 999 |
336 | 5.19M | || oper->addend < -999) |
337 | 779k | PR (PS, "%#x", oper->addend); |
338 | 4.41M | else |
339 | 4.41M | PR (PS, "%d", oper->addend); |
340 | 13.3M | break; |
341 | 19.7M | case RX_Operand_Register: |
342 | 19.8M | case RX_Operand_TwoReg: |
343 | 19.8M | PR (PS, "%s", get_register_name (oper->reg)); |
344 | 19.8M | break; |
345 | 11.8M | case RX_Operand_Indirect: |
346 | 11.8M | PR (PS, "%d[%s]", oper->addend, get_register_name (oper->reg)); |
347 | 11.8M | break; |
348 | 3.69M | case RX_Operand_Zero_Indirect: |
349 | 3.69M | PR (PS, "[%s]", get_register_name (oper->reg)); |
350 | 3.69M | break; |
351 | 5.35k | case RX_Operand_Postinc: |
352 | 5.35k | PR (PS, "[%s+]", get_register_name (oper->reg)); |
353 | 5.35k | break; |
354 | 7.24k | case RX_Operand_Predec: |
355 | 7.24k | PR (PS, "[-%s]", get_register_name (oper->reg)); |
356 | 7.24k | break; |
357 | 5.55M | case RX_Operand_Condition: |
358 | 5.55M | PR (PS, "%s", get_condition_name (oper->reg)); |
359 | 5.55M | break; |
360 | 16.0k | case RX_Operand_Flag: |
361 | 16.0k | PR (PS, "%s", get_flag_name (oper->reg)); |
362 | 16.0k | break; |
363 | 102k | case RX_Operand_DoubleReg: |
364 | 102k | PR (PS, "%s", get_double_register_name (oper->reg)); |
365 | 102k | break; |
366 | 11.0k | case RX_Operand_DoubleRegH: |
367 | 11.0k | PR (PS, "%s", get_double_register_high_name (oper->reg)); |
368 | 11.0k | break; |
369 | 8.68k | case RX_Operand_DoubleRegL: |
370 | 8.68k | PR (PS, "%s", get_double_register_low_name (oper->reg)); |
371 | 8.68k | break; |
372 | 10.2k | case RX_Operand_DoubleCReg: |
373 | 10.2k | PR (PS, "%s", get_double_control_register_name (oper->reg)); |
374 | 10.2k | break; |
375 | 3.27k | case RX_Operand_DoubleCond: |
376 | 3.27k | PR (PS, "%s", get_double_condition_name (oper->reg)); |
377 | 3.27k | break; |
378 | 0 | default: |
379 | 0 | PR (PS, "[???]"); |
380 | 0 | break; |
381 | 54.4M | } |
382 | 69.5M | } |
383 | 69.5M | } |
384 | 328M | } |
385 | | |
386 | 49.2M | return rv; |
387 | 49.2M | } |