xref: /netbsd-src/external/gpl3/binutils/dist/opcodes/avr-dis.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1 /* Disassemble AVR instructions.
2    Copyright (C) 1999-2024 Free Software Foundation, Inc.
3 
4    Contributed by Denis Chertykov <denisc@overta.ru>
5 
6    This file is part of libopcodes.
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 <assert.h>
25 #include "disassemble.h"
26 #include "opintl.h"
27 #include "libiberty.h"
28 #include <stdint.h>
29 
30 struct avr_opcodes_s
31 {
32   char *name;
33   char *constraints;
34   char *opcode;
35   int insn_size;		/* In words.  */
36   int isa;
37   unsigned int bin_opcode;
38 };
39 
40 #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
41 {#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
42 
43 const struct avr_opcodes_s avr_opcodes[] =
44 {
45   #include "opcode/avr.h"
46   {NULL, NULL, NULL, 0, 0, 0}
47 };
48 
49 static const char * comment_start = "0x";
50 
51 static int
avr_operand(unsigned int insn,unsigned int insn2,unsigned int pc,int constraint,char * opcode_str,char * buf,char * comment,enum disassembler_style * style,int regs,int * sym,bfd_vma * sym_addr,disassemble_info * info)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)
64 {
65   int ok = 1;
66   *sym = 0;
67 
68   switch (constraint)
69     {
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.  */
76 
77       sprintf (buf, "r%d", insn);
78       *style = dis_style_register;
79       break;
80 
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;
88 
89     case 'w':
90       sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3));
91       *style = dis_style_register;
92       break;
93 
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;
101 
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;
109 
110     case 'e':
111       {
112 	char *xyz;
113 
114 	switch (insn & 0x100f)
115 	  {
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;
126 	  }
127 	strcpy (buf, xyz);
128 
129 	if (AVR_UNDEF_P (insn))
130 	  sprintf (comment, _("undefined"));
131       }
132       *style = dis_style_register;
133       break;
134 
135     case 'z':
136       *buf++ = 'Z';
137 
138       /* Check for post-increment. */
139       char *s;
140       for (s = opcode_str; *s; ++s)
141         {
142           if (*s == '+')
143             {
144 	      if (insn & (1 << (15 - (s - opcode_str))))
145 		*buf++ = '+';
146               break;
147             }
148         }
149 
150       *buf = '\0';
151       if (AVR_UNDEF_P (insn))
152 	sprintf (comment, _("undefined"));
153       *style = dis_style_register;
154       break;
155 
156     case 'b':
157       {
158 	unsigned int x;
159 
160 	x = (insn & 7);
161 	x |= (insn >> 7) & (3 << 3);
162 	x |= (insn >> 8) & (1 << 5);
163 
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;
171       }
172       break;
173 
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;
187 
188     case 'L':
189       {
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;
199       }
200       break;
201 
202     case 'l':
203       {
204 	int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2;
205 
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;
214       }
215       break;
216 
217     case 'i':
218       {
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;
225       }
226       break;
227 
228     case 'j':
229       {
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;
239       }
240       break;
241 
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;
247 
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;
255 
256     case 'K':
257       {
258 	unsigned int x;
259 
260 	x = (insn & 0xf) | ((insn >> 2) & 0x30);
261 	sprintf (buf, "0x%02x", x);
262 	sprintf (comment, "%d", x);
263 	*style = dis_style_immediate;
264       }
265       break;
266 
267     case 's':
268       sprintf (buf, "%d", insn & 7);
269       *style = dis_style_immediate;
270       break;
271 
272     case 'S':
273       sprintf (buf, "%d", (insn >> 4) & 7);
274       *style = dis_style_immediate;
275       break;
276 
277     case 'P':
278       {
279 	unsigned int x;
280 
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;
286       }
287       break;
288 
289     case 'p':
290       {
291 	unsigned int x;
292 
293 	x = (insn >> 3) & 0x1f;
294 	sprintf (buf, "0x%02x", x);
295 	sprintf (comment, "%d", x);
296 	*style = dis_style_address;
297       }
298       break;
299 
300     case 'E':
301       sprintf (buf, "%d", (insn >> 4) & 15);
302       *style = dis_style_immediate;
303       break;
304 
305     case '?':
306       *buf = '\0';
307       break;
308 
309     default:
310       sprintf (buf, "??");
311       /* xgettext:c-format */
312       opcodes_error_handler (_("unknown constraint `%c'"), constraint);
313       ok = 0;
314     }
315 
316     return ok;
317 }
318 
319 /* Read the opcode from ADDR.  Return 0 in success and save opcode
320    in *INSN, otherwise, return -1.  */
321 
322 static int
avrdis_opcode(bfd_vma addr,disassemble_info * info,uint16_t * insn)323 avrdis_opcode (bfd_vma addr, disassemble_info *info, uint16_t *insn)
324 {
325   bfd_byte buffer[2];
326   int status;
327 
328   status = info->read_memory_func (addr, buffer, 2, info);
329 
330   if (status == 0)
331     {
332       *insn = bfd_getl16 (buffer);
333       return 0;
334     }
335 
336   info->memory_error_func (status, addr, info);
337   return -1;
338 }
339 
340 
341 int
print_insn_avr(bfd_vma addr,disassemble_info * info)342 print_insn_avr (bfd_vma addr, disassemble_info *info)
343 {
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;
357 
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;
365 
366   if (!initialized)
367     {
368       unsigned int nopcodes;
369 
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 = " ";
375 
376       nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s);
377 
378       avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int));
379 
380       for (opcode = avr_opcodes, maskptr = avr_bin_masks;
381 	   opcode->name;
382 	   opcode++, maskptr++)
383 	{
384 	  char * s;
385 	  unsigned int bin = 0;
386 	  unsigned int mask = 0;
387 
388 	  for (s = opcode->opcode; *s; ++s)
389 	    {
390 	      bin <<= 1;
391 	      mask <<= 1;
392 	      bin |= (*s == '1');
393 	      mask |= (*s == '1' || *s == '0');
394 	    }
395 	  assert (s - opcode->opcode == 16);
396 	  assert (opcode->bin_opcode == bin);
397 	  *maskptr = mask;
398 	}
399 
400       initialized = 1;
401     }
402 
403   if (avrdis_opcode (addr, info, &insn)  != 0)
404     return -1;
405 
406   for (opcode = avr_opcodes, maskptr = avr_bin_masks;
407        opcode->name;
408        opcode++, maskptr++)
409     {
410       if ((opcode->isa == AVR_ISA_TINY) && (info->mach != bfd_mach_avrtiny))
411         continue;
412       if ((insn & *maskptr) == opcode->bin_opcode)
413         break;
414     }
415 
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).  */
418 
419   if (AVR_DISP0_P (insn))
420     opcode++;
421 
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;
428 
429   if (opcode->name)
430     {
431       char *constraints = opcode->constraints;
432       char *opcode_str = opcode->opcode;
433 
434       insn2 = 0;
435       ok = 1;
436 
437       if (opcode->insn_size > 1)
438 	{
439 	  if (avrdis_opcode (addr + 2, info, &insn2) != 0)
440 	    return -1;
441 	  cmd_len = 4;
442 	}
443 
444       if (*constraints && *constraints != '?')
445 	{
446 	  int regs = REGISTER_P (*constraints);
447 
448 	  ok = avr_operand (insn, insn2, addr, *constraints, opcode_str, op1,
449 			    comment1, &style_op1, 0, &sym_op1, &sym_addr1,
450 			    info);
451 
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);
457 	}
458     }
459 
460   if (!ok)
461     {
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;
467     }
468 
469   (*prin) (stream, ok ? dis_style_mnemonic : dis_style_assembler_directive,
470 	   "%s", ok ? opcode->name : ".word");
471 
472   if (*op1)
473     (*prin) (stream, style_op1, "\t%s", op1);
474 
475   if (*op2)
476     {
477       (*prin) (stream, dis_style_text, ", ");
478       (*prin) (stream, style_op2, "%s", op2);
479     }
480 
481   if (*comment1)
482     (*prin) (stream, dis_style_comment_start, "\t; %s", comment1);
483 
484   if (sym_op1)
485     info->print_address_func (sym_addr1, info);
486 
487   if (*comment2)
488     (*prin) (stream, dis_style_comment_start, " %s", comment2);
489 
490   if (sym_op2)
491     info->print_address_func (sym_addr2, info);
492 
493   return cmd_len;
494 }
495