Coverage Report

Created: 2026-09-14 08:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/binutils-gdb/opcodes/avr-dis.c
Line
Count
Source
1
/* Disassemble AVR instructions.
2
   Copyright (C) 1999-2026 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
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
389k
{
65
389k
  int ok = 1;
66
389k
  *sym = 0;
67
68
389k
  switch (constraint)
69
389k
    {
70
      /* Any register operand.  */
71
131k
    case 'r':
72
131k
      if (regs)
73
42.8k
  insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* Source register.  */
74
89.1k
      else
75
89.1k
  insn = (insn & 0x01f0) >> 4; /* Destination register.  */
76
77
131k
      sprintf (buf, "r%d", insn);
78
131k
      *style = dis_style_register;
79
131k
      break;
80
81
84.0k
    case 'd':
82
84.0k
      if (regs)
83
3.11k
  sprintf (buf, "r%d", 16 + (insn & 0xf));
84
80.9k
      else
85
80.9k
  sprintf (buf, "r%d", 16 + ((insn & 0xf0) >> 4));
86
84.0k
      *style = dis_style_register;
87
84.0k
      break;
88
89
1.16k
    case 'w':
90
1.16k
      sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3));
91
1.16k
      *style = dis_style_register;
92
1.16k
      break;
93
94
4.39k
    case 'a':
95
4.39k
      if (regs)
96
2.19k
  sprintf (buf, "r%d", 16 + (insn & 7));
97
2.19k
      else
98
2.19k
  sprintf (buf, "r%d", 16 + ((insn >> 4) & 7));
99
4.39k
      *style = dis_style_register;
100
4.39k
      break;
101
102
7.75k
    case 'v':
103
7.75k
      if (regs)
104
3.87k
  sprintf (buf, "r%d", (insn & 0xf) * 2);
105
3.87k
      else
106
3.87k
  sprintf (buf, "r%d", ((insn & 0xf0) >> 3));
107
7.75k
      *style = dis_style_register;
108
7.75k
      break;
109
110
6.88k
    case 'e':
111
6.88k
      {
112
6.88k
  char *xyz;
113
114
6.88k
  switch (insn & 0x100f)
115
6.88k
    {
116
1.11k
      case 0x0000: xyz = "Z";  break;
117
1.57k
      case 0x1001: xyz = "Z+"; break;
118
857
      case 0x1002: xyz = "-Z"; break;
119
377
      case 0x0008: xyz = "Y";  break;
120
165
      case 0x1009: xyz = "Y+"; break;
121
495
      case 0x100a: xyz = "-Y"; break;
122
211
      case 0x100c: xyz = "X";  break;
123
414
      case 0x100d: xyz = "X+"; break;
124
199
      case 0x100e: xyz = "-X"; break;
125
1.48k
      default: xyz = "??"; ok = 0;
126
6.88k
    }
127
6.88k
  strcpy (buf, xyz);
128
129
6.88k
  if (AVR_UNDEF_P (insn))
130
952
    sprintf (comment, _("undefined"));
131
6.88k
      }
132
0
      *style = dis_style_register;
133
6.88k
      break;
134
135
1.86k
    case 'z':
136
1.86k
      *buf++ = 'Z';
137
138
      /* Check for post-increment. */
139
1.86k
      char *s;
140
30.3k
      for (s = opcode_str; *s; ++s)
141
29.6k
  {
142
29.6k
    if (*s == '+')
143
1.20k
      {
144
1.20k
        if (insn & (1 << (15 - (s - opcode_str))))
145
547
    *buf++ = '+';
146
1.20k
        break;
147
1.20k
      }
148
29.6k
  }
149
150
1.86k
      *buf = '\0';
151
1.86k
      if (AVR_UNDEF_P (insn))
152
98
  sprintf (comment, _("undefined"));
153
1.86k
      *style = dis_style_register;
154
1.86k
      break;
155
156
19.8k
    case 'b':
157
19.8k
      {
158
19.8k
  unsigned int x;
159
160
19.8k
  x = (insn & 7);
161
19.8k
  x |= (insn >> 7) & (3 << 3);
162
19.8k
  x |= (insn >> 8) & (1 << 5);
163
164
19.8k
  if (insn & 0x8)
165
8.71k
    *buf++ = 'Y';
166
11.1k
  else
167
11.1k
    *buf++ = 'Z';
168
19.8k
  sprintf (buf, "+%d", x);
169
19.8k
  sprintf (comment, "0x%02x", x);
170
19.8k
  *style = dis_style_register;
171
19.8k
      }
172
19.8k
      break;
173
174
281
    case 'h':
175
281
      *sym = 1;
176
281
      *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
281
      sprintf (buf, "%#lx", (unsigned long) *sym_addr);
181
281
      strcpy (comment, comment_start);
182
281
      info->insn_info_valid = 1;
183
281
      info->insn_type = dis_jsr;
184
281
      info->target = *sym_addr;
185
281
      *style = dis_style_address;
186
281
      break;
187
188
21.5k
    case 'L':
189
21.5k
      {
190
21.5k
  int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2;
191
21.5k
  sprintf (buf, ".%+-8d", rel_addr);
192
21.5k
  *sym = 1;
193
21.5k
  *sym_addr = pc + 2 + rel_addr;
194
21.5k
  strcpy (comment, comment_start);
195
21.5k
  info->insn_info_valid = 1;
196
21.5k
  info->insn_type = dis_branch;
197
21.5k
  info->target = *sym_addr;
198
21.5k
  *style = dis_style_address_offset;
199
21.5k
      }
200
21.5k
      break;
201
202
6.36k
    case 'l':
203
6.36k
      {
204
6.36k
  int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2;
205
206
6.36k
  sprintf (buf, ".%+-8d", rel_addr);
207
6.36k
  *sym = 1;
208
6.36k
  *sym_addr = pc + 2 + rel_addr;
209
6.36k
  strcpy (comment, comment_start);
210
6.36k
  info->insn_info_valid = 1;
211
6.36k
  info->insn_type = dis_condbranch;
212
6.36k
  info->target = *sym_addr;
213
6.36k
  *style = dis_style_address_offset;
214
6.36k
      }
215
6.36k
      break;
216
217
939
    case 'i':
218
939
      {
219
939
  unsigned int val = insn2 | 0x800000;
220
939
  *sym = 1;
221
939
  *sym_addr = val;
222
939
  sprintf (buf, "0x%04X", insn2);
223
939
  strcpy (comment, comment_start);
224
939
  *style = dis_style_immediate;
225
939
      }
226
939
      break;
227
228
1.13k
    case 'j':
229
1.13k
      {
230
1.13k
  unsigned int val = ((insn & 0xf) | ((insn & 0x600) >> 5)
231
1.13k
           | ((insn & 0x100) >> 2));
232
1.13k
  if ((insn & 0x100) == 0)
233
593
    val |= 0x80;
234
1.13k
  *sym = 1;
235
1.13k
  *sym_addr = val | 0x800000;
236
1.13k
  sprintf (buf, "0x%02x", val);
237
1.13k
  strcpy (comment, comment_start);
238
1.13k
  *style = dis_style_immediate;
239
1.13k
      }
240
1.13k
      break;
241
242
76.7k
    case 'M':
243
76.7k
      sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf));
244
76.7k
      sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf));
245
76.7k
      *style = dis_style_immediate;
246
76.7k
      break;
247
248
0
    case 'n':
249
0
      sprintf (buf, "??");
250
      /* xgettext:c-format */
251
0
      opcodes_error_handler (_("internal disassembler error"));
252
0
      ok = 0;
253
0
      *style = dis_style_immediate;
254
0
      break;
255
256
1.16k
    case 'K':
257
1.16k
      {
258
1.16k
  unsigned int x;
259
260
1.16k
  x = (insn & 0xf) | ((insn >> 2) & 0x30);
261
1.16k
  sprintf (buf, "0x%02x", x);
262
1.16k
  sprintf (comment, "%d", x);
263
1.16k
  *style = dis_style_immediate;
264
1.16k
      }
265
1.16k
      break;
266
267
8.82k
    case 's':
268
8.82k
      sprintf (buf, "%d", insn & 7);
269
8.82k
      *style = dis_style_immediate;
270
8.82k
      break;
271
272
0
    case 'S':
273
0
      sprintf (buf, "%d", (insn >> 4) & 7);
274
0
      *style = dis_style_immediate;
275
0
      break;
276
277
11.3k
    case 'P':
278
11.3k
      {
279
11.3k
  unsigned int x;
280
281
11.3k
  x = (insn & 0xf);
282
11.3k
  x |= (insn >> 5) & 0x30;
283
11.3k
  sprintf (buf, "0x%02x", x);
284
11.3k
  sprintf (comment, "%d", x);
285
11.3k
  *style = dis_style_address;
286
11.3k
      }
287
11.3k
      break;
288
289
2.92k
    case 'p':
290
2.92k
      {
291
2.92k
  unsigned int x;
292
293
2.92k
  x = (insn >> 3) & 0x1f;
294
2.92k
  sprintf (buf, "0x%02x", x);
295
2.92k
  sprintf (comment, "%d", x);
296
2.92k
  *style = dis_style_address;
297
2.92k
      }
298
2.92k
      break;
299
300
248
    case 'E':
301
248
      sprintf (buf, "%d", (insn >> 4) & 15);
302
248
      *style = dis_style_immediate;
303
248
      break;
304
305
0
    case '?':
306
0
      *buf = '\0';
307
0
      break;
308
309
0
    default:
310
0
      sprintf (buf, "??");
311
      /* xgettext:c-format */
312
0
      opcodes_error_handler (_("unknown constraint `%c'"), constraint);
313
0
      ok = 0;
314
389k
    }
315
316
389k
    return ok;
317
389k
}
318
319
/* Read the opcode from ADDR.  Return 0 in success and save opcode
320
   in *INSN, otherwise, return -1.  */
321
322
static int
323
avrdis_opcode (bfd_vma addr, disassemble_info *info, uint16_t *insn)
324
299k
{
325
299k
  bfd_byte buffer[2];
326
299k
  int status;
327
328
299k
  status = info->read_memory_func (addr, buffer, 2, info);
329
330
299k
  if (status == 0)
331
299k
    {
332
299k
      *insn = bfd_getl16 (buffer);
333
299k
      return 0;
334
299k
    }
335
336
203
  info->memory_error_func (status, addr, info);
337
203
  return -1;
338
299k
}
339
340
341
int
342
print_insn_avr (bfd_vma addr, disassemble_info *info)
343
298k
{
344
298k
  uint16_t insn, insn2;
345
298k
  const struct avr_opcodes_s *opcode;
346
298k
  static unsigned int *maskptr;
347
298k
  void *stream = info->stream;
348
298k
  fprintf_styled_ftype prin = info->fprintf_styled_func;
349
298k
  static unsigned int *avr_bin_masks;
350
298k
  static int initialized;
351
298k
  int cmd_len = 2;
352
298k
  int ok = 0;
353
298k
  char op1[20], op2[20], comment1[40], comment2[40];
354
298k
  enum disassembler_style style_op1, style_op2;
355
298k
  int sym_op1 = 0, sym_op2 = 0;
356
298k
  bfd_vma sym_addr1, sym_addr2;
357
358
  /* Clear instruction information field.  */
359
298k
  info->insn_info_valid = 0;
360
298k
  info->branch_delay_insns = 0;
361
298k
  info->data_size = 0;
362
298k
  info->insn_type = dis_noninsn;
363
298k
  info->target = 0;
364
298k
  info->target2 = 0;
365
366
298k
  if (!initialized)
367
2
    {
368
2
      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
2
      if (info->symtab_size == 0)
374
2
  comment_start = " ";
375
376
2
      nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s);
377
378
2
      avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int));
379
380
2
      for (opcode = avr_opcodes, maskptr = avr_bin_masks;
381
252
     opcode->name;
382
250
     opcode++, maskptr++)
383
250
  {
384
250
    char * s;
385
250
    unsigned int bin = 0;
386
250
    unsigned int mask = 0;
387
388
4.25k
    for (s = opcode->opcode; *s; ++s)
389
4.00k
      {
390
4.00k
        bin <<= 1;
391
4.00k
        mask <<= 1;
392
4.00k
        bin |= (*s == '1');
393
4.00k
        mask |= (*s == '1' || *s == '0');
394
4.00k
      }
395
250
    assert (s - opcode->opcode == 16);
396
250
    assert (opcode->bin_opcode == bin);
397
250
    *maskptr = mask;
398
250
  }
399
400
2
      initialized = 1;
401
2
    }
402
403
298k
  if (avrdis_opcode (addr, info, &insn)  != 0)
404
201
    return -1;
405
406
297k
  for (opcode = avr_opcodes, maskptr = avr_bin_masks;
407
21.5M
       opcode->name;
408
21.2M
       opcode++, maskptr++)
409
21.5M
    {
410
21.5M
      if ((opcode->isa == AVR_ISA_TINY) && (info->mach != bfd_mach_avrtiny))
411
146k
        continue;
412
21.3M
      if ((insn & *maskptr) == opcode->bin_opcode)
413
246k
        break;
414
21.3M
    }
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
297k
  if (AVR_DISP0_P (insn))
420
1.49k
    opcode++;
421
422
297k
  op1[0] = 0;
423
297k
  op2[0] = 0;
424
297k
  comment1[0] = 0;
425
297k
  comment2[0] = 0;
426
297k
  style_op1 = dis_style_text;
427
297k
  style_op2 = dis_style_text;
428
429
297k
  if (opcode->name)
430
246k
    {
431
246k
      char *constraints = opcode->constraints;
432
246k
      char *opcode_str = opcode->opcode;
433
434
246k
      insn2 = 0;
435
246k
      ok = 1;
436
437
246k
      if (opcode->insn_size > 1)
438
1.22k
  {
439
1.22k
    if (avrdis_opcode (addr + 2, info, &insn2) != 0)
440
2
      return -1;
441
1.22k
    cmd_len = 4;
442
1.22k
  }
443
444
246k
      if (*constraints && *constraints != '?')
445
209k
  {
446
209k
    int regs = REGISTER_P (*constraints);
447
448
209k
    ok = avr_operand (insn, insn2, addr, *constraints, opcode_str, op1,
449
209k
          comment1, &style_op1, 0, &sym_op1, &sym_addr1,
450
209k
          info);
451
452
209k
    if (ok && *(++constraints) == ',')
453
179k
      ok = avr_operand (insn, insn2, addr, *(++constraints), opcode_str,
454
179k
            op2, *comment1 ? comment2 : comment1,
455
179k
            &style_op2, regs, &sym_op2, &sym_addr2,
456
179k
            info);
457
209k
  }
458
246k
    }
459
460
297k
  if (!ok)
461
53.0k
    {
462
      /* Unknown opcode, or invalid combination of operands.  */
463
53.0k
      sprintf (op1, "0x%04x", insn);
464
53.0k
      op2[0] = 0;
465
53.0k
      sprintf (comment1, "????");
466
53.0k
      comment2[0] = 0;
467
53.0k
    }
468
469
297k
  (*prin) (stream, ok ? dis_style_mnemonic : dis_style_assembler_directive,
470
297k
     "%s", ok ? opcode->name : ".word");
471
  
472
297k
  if (*op1)
473
261k
    (*prin) (stream, style_op1, "\t%s", op1);
474
475
297k
  if (*op2)
476
179k
    {
477
179k
      (*prin) (stream, dis_style_text, ", ");
478
179k
      (*prin) (stream, style_op2, "%s", op2);
479
179k
    }
480
481
297k
  if (*comment1)
482
196k
    (*prin) (stream, dis_style_comment_start, "\t; %s", comment1);
483
484
297k
  if (sym_op1)
485
28.9k
    info->print_address_func (sym_addr1, info);
486
487
297k
  if (*comment2)
488
0
    (*prin) (stream, dis_style_comment_start, " %s", comment2);
489
490
297k
  if (sym_op2)
491
1.30k
    info->print_address_func (sym_addr2, info);
492
493
297k
  return cmd_len;
494
297k
}