Automatic date update in version.in
[binutils-gdb.git] / opcodes / avr-dis.c
blobf4ce1b22578bea488b631d75c666d10bf6d9839e
1 /* Disassemble AVR instructions.
2 Copyright (C) 1999-2024 Free Software Foundation, Inc.
4 Contributed by Denis Chertykov <denisc@overta.ru>
6 This file is part of libopcodes.
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.
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.
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. */
23 #include "sysdep.h"
24 #include <assert.h>
25 #include "disassemble.h"
26 #include "opintl.h"
27 #include "libiberty.h"
28 #include <stdint.h>
30 struct avr_opcodes_s
32 char *name;
33 char *constraints;
34 char *opcode;
35 int insn_size; /* In words. */
36 int isa;
37 unsigned int bin_opcode;
40 #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
41 {#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
43 const struct avr_opcodes_s avr_opcodes[] =
45 #include "opcode/avr.h"
46 {NULL, NULL, NULL, 0, 0, 0}
49 static const char * comment_start = "0x";
51 static int
52 avr_operand (unsigned int insn,
53 unsigned int insn2,
54 unsigned int pc,
55 int constraint,
56 char * opcode_str,
57 char * buf,
58 char * comment,
59 enum disassembler_style * style,
60 int regs,
61 int * sym,
62 bfd_vma * sym_addr,
63 disassemble_info * info)
65 int ok = 1;
66 *sym = 0;
68 switch (constraint)
70 /* Any register operand. */
71 case 'r':
72 if (regs)
73 insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* Source register. */
74 else
75 insn = (insn & 0x01f0) >> 4; /* Destination register. */
77 sprintf (buf, "r%d", insn);
78 *style = dis_style_register;
79 break;
81 case 'd':
82 if (regs)
83 sprintf (buf, "r%d", 16 + (insn & 0xf));
84 else
85 sprintf (buf, "r%d", 16 + ((insn & 0xf0) >> 4));
86 *style = dis_style_register;
87 break;
89 case 'w':
90 sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3));
91 *style = dis_style_register;
92 break;
94 case 'a':
95 if (regs)
96 sprintf (buf, "r%d", 16 + (insn & 7));
97 else
98 sprintf (buf, "r%d", 16 + ((insn >> 4) & 7));
99 *style = dis_style_register;
100 break;
102 case 'v':
103 if (regs)
104 sprintf (buf, "r%d", (insn & 0xf) * 2);
105 else
106 sprintf (buf, "r%d", ((insn & 0xf0) >> 3));
107 *style = dis_style_register;
108 break;
110 case 'e':
112 char *xyz;
114 switch (insn & 0x100f)
116 case 0x0000: xyz = "Z"; break;
117 case 0x1001: xyz = "Z+"; break;
118 case 0x1002: xyz = "-Z"; break;
119 case 0x0008: xyz = "Y"; break;
120 case 0x1009: xyz = "Y+"; break;
121 case 0x100a: xyz = "-Y"; break;
122 case 0x100c: xyz = "X"; break;
123 case 0x100d: xyz = "X+"; break;
124 case 0x100e: xyz = "-X"; break;
125 default: xyz = "??"; ok = 0;
127 strcpy (buf, xyz);
129 if (AVR_UNDEF_P (insn))
130 sprintf (comment, _("undefined"));
132 *style = dis_style_register;
133 break;
135 case 'z':
136 *buf++ = 'Z';
138 /* Check for post-increment. */
139 char *s;
140 for (s = opcode_str; *s; ++s)
142 if (*s == '+')
144 if (insn & (1 << (15 - (s - opcode_str))))
145 *buf++ = '+';
146 break;
150 *buf = '\0';
151 if (AVR_UNDEF_P (insn))
152 sprintf (comment, _("undefined"));
153 *style = dis_style_register;
154 break;
156 case 'b':
158 unsigned int x;
160 x = (insn & 7);
161 x |= (insn >> 7) & (3 << 3);
162 x |= (insn >> 8) & (1 << 5);
164 if (insn & 0x8)
165 *buf++ = 'Y';
166 else
167 *buf++ = 'Z';
168 sprintf (buf, "+%d", x);
169 sprintf (comment, "0x%02x", x);
170 *style = dis_style_register;
172 break;
174 case 'h':
175 *sym = 1;
176 *sym_addr = ((((insn & 1) | ((insn & 0x1f0) >> 3)) << 16) | insn2) * 2;
177 /* See PR binutils/2454. Ideally we would like to display the hex
178 value of the address only once, but this would mean recoding
179 objdump_print_address() which would affect many targets. */
180 sprintf (buf, "%#lx", (unsigned long) *sym_addr);
181 strcpy (comment, comment_start);
182 info->insn_info_valid = 1;
183 info->insn_type = dis_jsr;
184 info->target = *sym_addr;
185 *style = dis_style_address;
186 break;
188 case 'L':
190 int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2;
191 sprintf (buf, ".%+-8d", rel_addr);
192 *sym = 1;
193 *sym_addr = pc + 2 + rel_addr;
194 strcpy (comment, comment_start);
195 info->insn_info_valid = 1;
196 info->insn_type = dis_branch;
197 info->target = *sym_addr;
198 *style = dis_style_address_offset;
200 break;
202 case 'l':
204 int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2;
206 sprintf (buf, ".%+-8d", rel_addr);
207 *sym = 1;
208 *sym_addr = pc + 2 + rel_addr;
209 strcpy (comment, comment_start);
210 info->insn_info_valid = 1;
211 info->insn_type = dis_condbranch;
212 info->target = *sym_addr;
213 *style = dis_style_address_offset;
215 break;
217 case 'i':
219 unsigned int val = insn2 | 0x800000;
220 *sym = 1;
221 *sym_addr = val;
222 sprintf (buf, "0x%04X", insn2);
223 strcpy (comment, comment_start);
224 *style = dis_style_immediate;
226 break;
228 case 'j':
230 unsigned int val = ((insn & 0xf) | ((insn & 0x600) >> 5)
231 | ((insn & 0x100) >> 2));
232 if ((insn & 0x100) == 0)
233 val |= 0x80;
234 *sym = 1;
235 *sym_addr = val | 0x800000;
236 sprintf (buf, "0x%02x", val);
237 strcpy (comment, comment_start);
238 *style = dis_style_immediate;
240 break;
242 case 'M':
243 sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf));
244 sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf));
245 *style = dis_style_immediate;
246 break;
248 case 'n':
249 sprintf (buf, "??");
250 /* xgettext:c-format */
251 opcodes_error_handler (_("internal disassembler error"));
252 ok = 0;
253 *style = dis_style_immediate;
254 break;
256 case 'K':
258 unsigned int x;
260 x = (insn & 0xf) | ((insn >> 2) & 0x30);
261 sprintf (buf, "0x%02x", x);
262 sprintf (comment, "%d", x);
263 *style = dis_style_immediate;
265 break;
267 case 's':
268 sprintf (buf, "%d", insn & 7);
269 *style = dis_style_immediate;
270 break;
272 case 'S':
273 sprintf (buf, "%d", (insn >> 4) & 7);
274 *style = dis_style_immediate;
275 break;
277 case 'P':
279 unsigned int x;
281 x = (insn & 0xf);
282 x |= (insn >> 5) & 0x30;
283 sprintf (buf, "0x%02x", x);
284 sprintf (comment, "%d", x);
285 *style = dis_style_address;
287 break;
289 case 'p':
291 unsigned int x;
293 x = (insn >> 3) & 0x1f;
294 sprintf (buf, "0x%02x", x);
295 sprintf (comment, "%d", x);
296 *style = dis_style_address;
298 break;
300 case 'E':
301 sprintf (buf, "%d", (insn >> 4) & 15);
302 *style = dis_style_immediate;
303 break;
305 case '?':
306 *buf = '\0';
307 break;
309 default:
310 sprintf (buf, "??");
311 /* xgettext:c-format */
312 opcodes_error_handler (_("unknown constraint `%c'"), constraint);
313 ok = 0;
316 return ok;
319 /* Read the opcode from ADDR. Return 0 in success and save opcode
320 in *INSN, otherwise, return -1. */
322 static int
323 avrdis_opcode (bfd_vma addr, disassemble_info *info, uint16_t *insn)
325 bfd_byte buffer[2];
326 int status;
328 status = info->read_memory_func (addr, buffer, 2, info);
330 if (status == 0)
332 *insn = bfd_getl16 (buffer);
333 return 0;
336 info->memory_error_func (status, addr, info);
337 return -1;
342 print_insn_avr (bfd_vma addr, disassemble_info *info)
344 uint16_t insn, insn2;
345 const struct avr_opcodes_s *opcode;
346 static unsigned int *maskptr;
347 void *stream = info->stream;
348 fprintf_styled_ftype prin = info->fprintf_styled_func;
349 static unsigned int *avr_bin_masks;
350 static int initialized;
351 int cmd_len = 2;
352 int ok = 0;
353 char op1[20], op2[20], comment1[40], comment2[40];
354 enum disassembler_style style_op1, style_op2;
355 int sym_op1 = 0, sym_op2 = 0;
356 bfd_vma sym_addr1, sym_addr2;
358 /* Clear instruction information field. */
359 info->insn_info_valid = 0;
360 info->branch_delay_insns = 0;
361 info->data_size = 0;
362 info->insn_type = dis_noninsn;
363 info->target = 0;
364 info->target2 = 0;
366 if (!initialized)
368 unsigned int nopcodes;
370 /* PR 4045: Try to avoid duplicating the 0x prefix that
371 objdump_print_addr() will put on addresses when there
372 is no symbol table available. */
373 if (info->symtab_size == 0)
374 comment_start = " ";
376 nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s);
378 avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int));
380 for (opcode = avr_opcodes, maskptr = avr_bin_masks;
381 opcode->name;
382 opcode++, maskptr++)
384 char * s;
385 unsigned int bin = 0;
386 unsigned int mask = 0;
388 for (s = opcode->opcode; *s; ++s)
390 bin <<= 1;
391 mask <<= 1;
392 bin |= (*s == '1');
393 mask |= (*s == '1' || *s == '0');
395 assert (s - opcode->opcode == 16);
396 assert (opcode->bin_opcode == bin);
397 *maskptr = mask;
400 initialized = 1;
403 if (avrdis_opcode (addr, info, &insn) != 0)
404 return -1;
406 for (opcode = avr_opcodes, maskptr = avr_bin_masks;
407 opcode->name;
408 opcode++, maskptr++)
410 if ((opcode->isa == AVR_ISA_TINY) && (info->mach != bfd_mach_avrtiny))
411 continue;
412 if ((insn & *maskptr) == opcode->bin_opcode)
413 break;
416 /* Special case: disassemble `ldd r,b+0' as `ld r,b', and
417 `std b+0,r' as `st b,r' (next entry in the table). */
419 if (AVR_DISP0_P (insn))
420 opcode++;
422 op1[0] = 0;
423 op2[0] = 0;
424 comment1[0] = 0;
425 comment2[0] = 0;
426 style_op1 = dis_style_text;
427 style_op2 = dis_style_text;
429 if (opcode->name)
431 char *constraints = opcode->constraints;
432 char *opcode_str = opcode->opcode;
434 insn2 = 0;
435 ok = 1;
437 if (opcode->insn_size > 1)
439 if (avrdis_opcode (addr + 2, info, &insn2) != 0)
440 return -1;
441 cmd_len = 4;
444 if (*constraints && *constraints != '?')
446 int regs = REGISTER_P (*constraints);
448 ok = avr_operand (insn, insn2, addr, *constraints, opcode_str, op1,
449 comment1, &style_op1, 0, &sym_op1, &sym_addr1,
450 info);
452 if (ok && *(++constraints) == ',')
453 ok = avr_operand (insn, insn2, addr, *(++constraints), opcode_str,
454 op2, *comment1 ? comment2 : comment1,
455 &style_op2, regs, &sym_op2, &sym_addr2,
456 info);
460 if (!ok)
462 /* Unknown opcode, or invalid combination of operands. */
463 sprintf (op1, "0x%04x", insn);
464 op2[0] = 0;
465 sprintf (comment1, "????");
466 comment2[0] = 0;
469 (*prin) (stream, ok ? dis_style_mnemonic : dis_style_assembler_directive,
470 "%s", ok ? opcode->name : ".word");
472 if (*op1)
473 (*prin) (stream, style_op1, "\t%s", op1);
475 if (*op2)
477 (*prin) (stream, dis_style_text, ", ");
478 (*prin) (stream, style_op2, "%s", op2);
481 if (*comment1)
482 (*prin) (stream, dis_style_comment_start, "\t; %s", comment1);
484 if (sym_op1)
485 info->print_address_func (sym_addr1, info);
487 if (*comment2)
488 (*prin) (stream, dis_style_comment_start, " %s", comment2);
490 if (sym_op2)
491 info->print_address_func (sym_addr2, info);
493 return cmd_len;