1/* Disassemble AVR instructions.
2   Copyright (C) 1999-2022 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
30struct 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
43const struct avr_opcodes_s avr_opcodes[] =
44{
45  #include "opcode/avr.h"
46  {NULL, NULL, NULL, 0, 0, 0}
47};
48
49static const char * comment_start = "0x";
50
51static int
52avr_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
322static int
323avrdis_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
341int
342print_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