Coverage Report

Created: 2026-10-02 09:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/binutils-gdb/opcodes/pru-dis.c
Line
Count
Source
1
/* TI PRU disassemble routines
2
   Copyright (C) 2014-2026 Free Software Foundation, Inc.
3
   Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
4
5
   This file is part of the GNU opcodes library.
6
7
   This library is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3, or (at your option)
10
   any later version.
11
12
   It is distributed in the hope that it will be useful, but WITHOUT
13
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15
   License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this file; see the file COPYING.  If not, write to the
19
   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
20
   MA 02110-1301, USA.  */
21
22
#include "sysdep.h"
23
#include "disassemble.h"
24
#include "opcode/pru.h"
25
#include "libiberty.h"
26
#include <string.h>
27
#include <assert.h>
28
29
/* No symbol table is available when this code runs out in an embedded
30
   system as when it is used for disassembler support in a monitor.  */
31
#if !defined (EMBEDDED_ENV)
32
#define SYMTAB_AVAILABLE 1
33
#include "elf-bfd.h"
34
#include "elf/pru.h"
35
#endif
36
37
/* Length of PRU instruction in bytes.  */
38
235k
#define INSNLEN 4
39
40
/* Return a pointer to an pru_opcode struct for a given instruction
41
   opcode, or NULL if there is an error.  */
42
const struct pru_opcode *
43
pru_find_opcode (unsigned long opcode)
44
58.9k
{
45
58.9k
  const struct pru_opcode *p;
46
58.9k
  const struct pru_opcode *op = NULL;
47
58.9k
  const struct pru_opcode *pseudo_op = NULL;
48
49
3.35M
  for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++)
50
3.30M
    {
51
3.30M
      if ((p->mask & opcode) == p->match)
52
51.1k
  {
53
51.1k
    if ((p->pinfo & PRU_INSN_MACRO) == PRU_INSN_MACRO)
54
270
      pseudo_op = p;
55
50.8k
    else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32)
56
1.07k
      /* ignore - should be caught with regular patterns */;
57
49.8k
    else
58
49.8k
      op = p;
59
51.1k
  }
60
3.30M
    }
61
62
58.9k
  return pseudo_op ? pseudo_op : op;
63
58.9k
}
64
65
/* There are 32 regular registers, each with 8 possible subfield selectors.  */
66
#define NUMREGNAMES (32 * 8)
67
68
static void
69
pru_print_insn_arg_indreg (unsigned int r, unsigned int sel,
70
         unsigned int mode,
71
         disassemble_info *info)
72
92.9k
{
73
92.9k
  const char *fmtstr;
74
92.9k
  unsigned int i = r * RSEL_NUM_ITEMS + sel;
75
92.9k
  assert (i < (unsigned int)pru_num_regs);
76
92.9k
  assert (i < NUMREGNAMES);
77
78
92.9k
  switch (mode)
79
92.9k
    {
80
92.2k
    case MVI_OP_MODE_DIRECT:   fmtstr = "%s"; break;
81
313
    case MVI_OP_MODE_INDIRECT:   fmtstr = "*%s"; break;
82
280
    case MVI_OP_MODE_INDIRECT_POSTINC: fmtstr = "*%s++"; break;
83
128
    case MVI_OP_MODE_INDIRECT_PREDEC: fmtstr = "*--%s"; break;
84
0
    default:        fmtstr = "<invalid>%s"; break;
85
92.9k
    }
86
92.9k
  (*info->fprintf_func) (info->stream, fmtstr, pru_regs[i].name);
87
92.9k
}
88
89
static void
90
pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
91
      disassemble_info *info)
92
92.0k
{
93
92.0k
  pru_print_insn_arg_indreg (r, sel, MVI_OP_MODE_DIRECT, info);
94
92.0k
}
95
96
/* The function pru_print_insn_arg uses the character pointed
97
   to by ARGPTR to determine how it print the next token or separator
98
   character in the arguments to an instruction.  */
99
static int
100
pru_print_insn_arg (const char *argptr,
101
          unsigned long opcode, bfd_vma address,
102
          disassemble_info *info)
103
240k
{
104
240k
  long offs = 0;
105
240k
  unsigned long i = 0;
106
240k
  unsigned long io = 0;
107
108
240k
  switch (*argptr)
109
240k
    {
110
95.7k
    case ',':
111
95.7k
      (*info->fprintf_func) (info->stream, "%c ", *argptr);
112
95.7k
      break;
113
19.5k
    case 'd':
114
19.5k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
115
19.5k
            GET_INSN_FIELD (RDSEL, opcode),
116
19.5k
            info);
117
19.5k
      break;
118
11.6k
    case 'D':
119
      /* The first 4 values for RDB and RSEL are the same, so we
120
   can reuse some code.  */
121
11.6k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode),
122
11.6k
            GET_INSN_FIELD (RDB, opcode),
123
11.6k
            info);
124
11.6k
      break;
125
29.1k
    case 's':
126
29.1k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
127
29.1k
            GET_INSN_FIELD (RS1SEL, opcode),
128
29.1k
            info);
129
29.1k
      break;
130
7.76k
    case 'S':
131
7.76k
      pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode),
132
7.76k
            RSEL_31_0,
133
7.76k
            info);
134
7.76k
      break;
135
40.1k
    case 'b':
136
40.1k
      io = GET_INSN_FIELD (IO, opcode);
137
138
40.1k
      if (io)
139
18.0k
  {
140
18.0k
    i = GET_INSN_FIELD (IMM8, opcode);
141
18.0k
    (*info->fprintf_func) (info->stream, "%ld", i);
142
18.0k
  }
143
22.1k
      else
144
22.1k
  {
145
22.1k
  pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
146
22.1k
        GET_INSN_FIELD (RS2SEL, opcode),
147
22.1k
        info);
148
22.1k
  }
149
40.1k
      break;
150
1.60k
    case 'B':
151
1.60k
      io = GET_INSN_FIELD (IO, opcode);
152
153
1.60k
      if (io)
154
1.03k
  {
155
1.03k
    i = GET_INSN_FIELD (IMM8, opcode) + 1;
156
1.03k
    (*info->fprintf_func) (info->stream, "%ld", i);
157
1.03k
  }
158
573
      else
159
573
  {
160
573
  pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
161
573
        GET_INSN_FIELD (RS2SEL, opcode),
162
573
        info);
163
573
  }
164
1.60k
      break;
165
1.72k
    case 'j':
166
1.72k
      io = GET_INSN_FIELD (IO, opcode);
167
168
1.72k
      if (io)
169
503
  {
170
    /* For the sake of pretty-printing, dump text addresses with
171
       their "virtual" offset that we use for distinguishing
172
       PMEM vs DMEM. This is needed for printing the correct text
173
       labels.  */
174
503
    bfd_vma text_offset = address & ~0x3fffff;
175
503
    i = GET_INSN_FIELD (IMM16, opcode) * 4;
176
503
    (*info->print_address_func) (i + text_offset, info);
177
503
  }
178
1.22k
      else
179
1.22k
  {
180
1.22k
    pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode),
181
1.22k
        GET_INSN_FIELD (RS2SEL, opcode),
182
1.22k
        info);
183
1.22k
  }
184
1.72k
      break;
185
1.07k
    case 'W':
186
1.07k
      i = GET_INSN_FIELD (IMM16, opcode);
187
1.07k
      (*info->fprintf_func) (info->stream, "%ld", i);
188
1.07k
      break;
189
11.7k
    case 'o':
190
11.7k
      offs = GET_BROFF_SIGNED (opcode) * 4;
191
11.7k
      (*info->print_address_func) (address + offs, info);
192
11.7k
      break;
193
1.60k
    case 'O':
194
1.60k
      offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4;
195
1.60k
      (*info->print_address_func) (address + offs, info);
196
1.60k
      break;
197
11.1k
    case 'l':
198
11.1k
      i = GET_BURSTLEN (opcode);
199
11.1k
      if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
200
7.79k
  (*info->fprintf_func) (info->stream, "%ld", i + 1);
201
3.32k
      else
202
3.32k
  {
203
3.32k
    i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
204
3.32k
    (*info->fprintf_func) (info->stream, "r0.b%ld", i);
205
3.32k
  }
206
11.1k
      break;
207
471
    case 'm':
208
471
      pru_print_insn_arg_indreg (GET_INSN_FIELD (RD, opcode),
209
471
         GET_INSN_FIELD (RDSEL, opcode),
210
471
         GET_INSN_FIELD (MVI_RD_MODE, opcode),
211
471
         info);
212
471
      break;
213
471
    case 'M':
214
471
      pru_print_insn_arg_indreg (GET_INSN_FIELD (RS1, opcode),
215
471
         GET_INSN_FIELD (RS1SEL, opcode),
216
471
         GET_INSN_FIELD (MVI_RS1_MODE, opcode),
217
471
         info);
218
471
      break;
219
496
    case 'n':
220
496
      i = GET_INSN_FIELD (XFR_LENGTH, opcode);
221
496
      if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
222
397
  (*info->fprintf_func) (info->stream, "%ld", i + 1);
223
99
      else
224
99
  {
225
99
    i -= LSSBBO_BYTECOUNT_R0_BITS7_0;
226
99
    (*info->fprintf_func) (info->stream, "r0.b%ld", i);
227
99
  }
228
496
      break;
229
3.34k
    case 'c':
230
3.34k
      i = GET_INSN_FIELD (CB, opcode);
231
3.34k
      (*info->fprintf_func) (info->stream, "%ld", i);
232
3.34k
      break;
233
779
    case 't':
234
779
      i = GET_INSN_FIELD (TSKMGR_MODE, opcode);
235
779
      (*info->fprintf_func) (info->stream, "%ld", i);
236
779
      break;
237
1.61k
    case 'w':
238
1.61k
      i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
239
1.61k
      (*info->fprintf_func) (info->stream, "%ld", i);
240
1.61k
      break;
241
490
    case 'x':
242
490
      i = GET_INSN_FIELD (XFR_WBA, opcode);
243
490
      (*info->fprintf_func) (info->stream, "%ld", i);
244
490
      break;
245
0
    default:
246
0
      (*info->fprintf_func) (info->stream, "unknown");
247
0
      break;
248
240k
    }
249
240k
  return 0;
250
240k
}
251
252
/* pru_disassemble does all the work of disassembling a PRU
253
   instruction opcode.  */
254
static int
255
pru_disassemble (bfd_vma address, unsigned long opcode,
256
       disassemble_info *info)
257
58.9k
{
258
58.9k
  const struct pru_opcode *op;
259
260
58.9k
  info->bytes_per_line = INSNLEN;
261
58.9k
  info->bytes_per_chunk = INSNLEN;
262
58.9k
  info->display_endian = info->endian;
263
58.9k
  info->insn_info_valid = 1;
264
58.9k
  info->branch_delay_insns = 0;
265
58.9k
  info->data_size = 0;
266
58.9k
  info->insn_type = dis_nonbranch;
267
58.9k
  info->target = 0;
268
58.9k
  info->target2 = 0;
269
270
  /* Find the major opcode and use this to disassemble
271
     the instruction and its arguments.  */
272
58.9k
  op = pru_find_opcode (opcode);
273
274
58.9k
  if (op != NULL)
275
49.8k
    {
276
49.8k
      (*info->fprintf_func) (info->stream, "%s", op->name);
277
278
49.8k
      const char *argstr = op->args;
279
49.8k
      if (argstr != NULL && *argstr != '\0')
280
49.0k
  {
281
49.0k
    (*info->fprintf_func) (info->stream, "\t");
282
289k
    while (*argstr != '\0')
283
240k
      {
284
240k
        pru_print_insn_arg (argstr, opcode, address, info);
285
240k
        ++argstr;
286
240k
      }
287
49.0k
  }
288
49.8k
    }
289
9.11k
  else
290
9.11k
    {
291
      /* Handle undefined instructions.  */
292
9.11k
      info->insn_type = dis_noninsn;
293
9.11k
      (*info->fprintf_func) (info->stream, "0x%lx", opcode);
294
9.11k
    }
295
  /* Tell the caller how far to advance the program counter.  */
296
58.9k
  return INSNLEN;
297
58.9k
}
298
299
300
/* print_insn_pru is the main disassemble function for PRU.  */
301
int
302
print_insn_pru (bfd_vma address, disassemble_info *info)
303
59.0k
{
304
59.0k
  bfd_byte buffer[INSNLEN];
305
59.0k
  int status;
306
307
59.0k
  status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
308
59.0k
  if (status == 0)
309
58.9k
    {
310
58.9k
      unsigned long insn;
311
58.9k
      insn = (unsigned long) bfd_getl32 (buffer);
312
58.9k
      status = pru_disassemble (address, insn, info);
313
58.9k
    }
314
123
  else
315
123
    {
316
123
      (*info->memory_error_func) (status, address, info);
317
123
      status = -1;
318
123
    }
319
59.0k
  return status;
320
59.0k
}