Coverage Report

Created: 2026-07-12 09:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/binutils-gdb/opcodes/mmix-dis.c
Line
Count
Source
1
/* mmix-dis.c -- Disassemble MMIX instructions.
2
   Copyright (C) 2000-2026 Free Software Foundation, Inc.
3
   Written by Hans-Peter Nilsson (hp@bitrange.com)
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 Free
19
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
20
   MA 02110-1301, USA.  */
21
22
#include "sysdep.h"
23
#include <stdio.h>
24
#include "opcode/mmix.h"
25
#include "disassemble.h"
26
#include "libiberty.h"
27
#include "bfd.h"
28
#include "opintl.h"
29
30
#define BAD_CASE(x)           \
31
0
  do                \
32
0
   {               \
33
0
     opcodes_error_handler (_("bad case %d (%s) in %s:%d"),  \
34
0
          x, #x, __FILE__, __LINE__);    \
35
0
     abort ();              \
36
0
   }                \
37
0
 while (0)
38
39
#define FATAL_DEBUG           \
40
0
 do                \
41
0
   {               \
42
0
     opcodes_error_handler (_("internal: non-debugged code " \
43
0
            "(test-case missing): %s:%d"),  \
44
0
          __FILE__, __LINE__);    \
45
0
     abort ();              \
46
0
   }                \
47
0
 while (0)
48
49
#define ROUND_MODE(n)         \
50
2.02k
 ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" :  \
51
1.60k
  (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" :  \
52
483
  _("(unknown)"))
53
54
66.5k
#define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
55
14.1k
#define INSN_BACKWARD_OFFSET_BIT (1 << 24)
56
57
299k
#define MAX_REG_NAME_LEN       256
58
880
#define MAX_SPEC_REG_NAME_LEN  32
59
struct mmix_dis_info
60
 {
61
   const char *reg_name[MAX_REG_NAME_LEN];
62
   const char *spec_reg_name[MAX_SPEC_REG_NAME_LEN];
63
64
   /* Waste a little memory so we don't have to allocate each separately.
65
      We could have an array with static contents for these, but on the
66
      other hand, we don't have to.  */
67
   char basic_reg_name[MAX_REG_NAME_LEN][sizeof ("$255")];
68
 };
69
70
/* Initialize a target-specific array in INFO.  */
71
72
static bool
73
initialize_mmix_dis_info (struct disassemble_info *info)
74
277
{
75
277
  struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
76
277
  long i;
77
78
277
  if (minfop == NULL)
79
0
    return false;
80
81
277
  memset (minfop, 0, sizeof (*minfop));
82
83
  /* Initialize register names from register symbols.  If there's no
84
     register section, then there are no register symbols.  */
85
277
  if ((info->section != NULL && info->section->owner != NULL)
86
241
      || (info->symbols != NULL
87
0
    && info->symbols[0] != NULL
88
0
    && bfd_asymbol_bfd (info->symbols[0]) != NULL))
89
36
    {
90
36
      bfd *abfd = info->section && info->section->owner != NULL
91
36
  ? info->section->owner
92
36
  : bfd_asymbol_bfd (info->symbols[0]);
93
36
      asection *reg_section = bfd_get_section_by_name (abfd, "*REG*");
94
95
36
      if (reg_section != NULL)
96
18
  {
97
    /* The returned symcount *does* include the ending NULL.  */
98
18
    long symsize = bfd_get_symtab_upper_bound (abfd);
99
18
    asymbol **syms = malloc (symsize);
100
18
    long nsyms;
101
102
18
    if (syms == NULL)
103
0
      {
104
0
        FATAL_DEBUG;
105
0
        free (minfop);
106
0
        return false;
107
0
      }
108
18
    nsyms = bfd_canonicalize_symtab (abfd, syms);
109
110
    /* We use the first name for a register.  If this is MMO, then
111
       it's the name with the first sequence number, presumably the
112
       first in the source.  */
113
1.21k
    for (i = 0; i < nsyms && syms[i] != NULL; i++)
114
1.19k
      {
115
1.19k
        if (syms[i]->section == reg_section
116
274
      && syms[i]->value < MAX_REG_NAME_LEN
117
274
      && minfop->reg_name[syms[i]->value] == NULL)
118
53
    minfop->reg_name[syms[i]->value] = syms[i]->name;
119
1.19k
      }
120
18
    free (syms);
121
18
  }
122
36
    }
123
124
  /* Fill in the rest with the canonical names.  */
125
71.1k
  for (i = 0; i < MAX_REG_NAME_LEN; i++)
126
70.9k
    if (minfop->reg_name[i] == NULL)
127
70.8k
      {
128
70.8k
  sprintf (minfop->basic_reg_name[i], "$%ld", i);
129
70.8k
  minfop->reg_name[i] = minfop->basic_reg_name[i];
130
70.8k
      }
131
132
  /* We assume it's actually a one-to-one mapping of number-to-name.  */
133
9.14k
  for (i = 0; mmix_spec_regs[i].name != NULL; i++)
134
8.86k
    minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name;
135
136
277
  info->private_data = (void *) minfop;
137
277
  return true;
138
277
}
139
140
/* A table indexed by the first byte is constructed as we disassemble each
141
   tetrabyte.  The contents is a pointer into mmix_insns reflecting the
142
   first found entry with matching match-bits and lose-bits.  Further
143
   entries are considered one after one until the operand constraints
144
   match or the match-bits and lose-bits do not match.  Normally a
145
   "further entry" will just show that there was no other match.  */
146
147
static const struct mmix_opcode *
148
get_opcode (unsigned long insn)
149
168k
{
150
168k
  static const struct mmix_opcode **opcodes = NULL;
151
168k
  const struct mmix_opcode *opcodep = mmix_opcodes;
152
168k
  unsigned int opcode_part = (insn >> 24) & 255;
153
154
168k
  if (opcodes == NULL)
155
2
    opcodes = xcalloc (256, sizeof (struct mmix_opcode *));
156
157
168k
  opcodep = opcodes[opcode_part];
158
168k
  if (opcodep == NULL
159
36.0k
      || (opcodep->match & insn) != opcodep->match
160
36.0k
      || (opcodep->lose & insn) != 0)
161
132k
    {
162
      /* Search through the table.  */
163
9.85M
      for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++)
164
9.85M
  {
165
    /* FIXME: Break out this into an initialization function.  */
166
9.85M
    if ((opcodep->match & (opcode_part << 24)) == opcode_part
167
2
        && (opcodep->lose & (opcode_part << 24)) == 0)
168
2
      opcodes[opcode_part] = opcodep;
169
170
9.85M
    if ((opcodep->match & insn) == opcodep->match
171
4.24M
        && (opcodep->lose & insn) == 0)
172
126k
      break;
173
9.85M
  }
174
132k
    }
175
176
168k
  if (opcodep->name == NULL)
177
5.74k
    return NULL;
178
179
  /* Check constraints.  If they don't match, loop through the next opcode
180
     entries.  */
181
162k
  do
182
162k
    {
183
162k
      switch (opcodep->operands)
184
162k
  {
185
    /* These have no restraint on what can be in the lower three
186
       bytes.  */
187
15.8k
  case mmix_operands_regs:
188
20.4k
  case mmix_operands_reg_yz:
189
35.3k
  case mmix_operands_regs_z_opt:
190
77.1k
  case mmix_operands_regs_z:
191
78.8k
  case mmix_operands_jmp:
192
79.5k
  case mmix_operands_pushgo:
193
80.2k
  case mmix_operands_pop:
194
81.2k
  case mmix_operands_sync:
195
84.9k
  case mmix_operands_x_regs_z:
196
87.1k
  case mmix_operands_neg:
197
88.0k
  case mmix_operands_pushj:
198
99.6k
  case mmix_operands_regaddr:
199
99.9k
  case mmix_operands_get:
200
100k
  case mmix_operands_set:
201
100k
  case mmix_operands_save:
202
100k
  case mmix_operands_unsave:
203
151k
  case mmix_operands_xyz_opt:
204
151k
    return opcodep;
205
206
    /* For a ROUND_MODE, the middle byte must be 0..4.  */
207
6.87k
  case mmix_operands_roundregs_z:
208
10.4k
  case mmix_operands_roundregs:
209
10.4k
    {
210
10.4k
      int midbyte = (insn >> 8) & 255;
211
212
10.4k
      if (midbyte <= 4)
213
3.71k
        return opcodep;
214
10.4k
    }
215
6.69k
  break;
216
217
6.69k
  case mmix_operands_put:
218
    /* A "PUT".  If it is "immediate", then no restrictions,
219
       otherwise we have to make sure the register number is < 32.  */
220
666
    if ((insn & INSN_IMMEDIATE_BIT)
221
451
        || ((insn >> 16) & 255) < 32)
222
527
      return opcodep;
223
139
    break;
224
225
255
  case mmix_operands_resume:
226
    /* Middle bytes must be zero.  */
227
255
    if ((insn & 0x00ffff00) == 0)
228
255
      return opcodep;
229
0
    break;
230
231
0
  default:
232
0
    BAD_CASE (opcodep->operands);
233
162k
  }
234
235
6.83k
      opcodep++;
236
6.83k
    }
237
162k
  while ((opcodep->match & insn) == opcodep->match
238
0
   && (opcodep->lose & insn) == 0);
239
240
  /* If we got here, we had no match.  */
241
6.83k
  return NULL;
242
162k
}
243
244
static inline const char *
245
get_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
246
227k
{
247
227k
  if (x >= MAX_REG_NAME_LEN)
248
0
    return _("*illegal*");
249
227k
  return minfop->reg_name[x];
250
227k
}
251
252
static inline const char *
253
get_spec_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
254
880
{
255
880
  if (x >= MAX_SPEC_REG_NAME_LEN)
256
124
    return _("*illegal*");
257
756
  return minfop->spec_reg_name[x];
258
880
}
259
260
/* The main disassembly function.  */
261
262
int
263
print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
264
168k
{
265
168k
  unsigned char buffer[4];
266
168k
  unsigned long insn;
267
168k
  unsigned int x, y, z;
268
168k
  const struct mmix_opcode *opcodep;
269
168k
  int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
270
168k
  struct mmix_dis_info *minfop;
271
272
168k
  if (status != 0)
273
178
    {
274
178
      (*info->memory_error_func) (status, memaddr, info);
275
178
      return -1;
276
178
    }
277
278
  /* FIXME: Is -1 suitable?  */
279
168k
  if (info->private_data == NULL
280
277
      && ! initialize_mmix_dis_info (info))
281
0
    return -1;
282
283
168k
  minfop = (struct mmix_dis_info *) info->private_data;
284
168k
  x = buffer[1];
285
168k
  y = buffer[2];
286
168k
  z = buffer[3];
287
288
168k
  insn = bfd_getb32 (buffer);
289
290
168k
  opcodep = get_opcode (insn);
291
292
168k
  if (opcodep == NULL)
293
12.5k
    {
294
12.5k
      (*info->fprintf_func) (info->stream, _("*unknown*"));
295
12.5k
      return 4;
296
12.5k
    }
297
298
155k
  (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
299
300
  /* Present bytes in the order they are laid out in memory.  */
301
155k
  info->display_endian = BFD_ENDIAN_BIG;
302
303
155k
  info->insn_info_valid = 1;
304
155k
  info->bytes_per_chunk = 4;
305
155k
  info->branch_delay_insns = 0;
306
155k
  info->target = 0;
307
155k
  switch (opcodep->type)
308
155k
    {
309
72.9k
    case mmix_type_normal:
310
76.2k
    case mmix_type_memaccess_block:
311
76.2k
      info->insn_type = dis_nonbranch;
312
76.2k
      break;
313
314
2.93k
    case mmix_type_branch:
315
2.93k
      info->insn_type = dis_branch;
316
2.93k
      break;
317
318
11.1k
    case mmix_type_condbranch:
319
11.1k
      info->insn_type = dis_condbranch;
320
11.1k
      break;
321
322
4.29k
    case mmix_type_memaccess_octa:
323
4.29k
      info->insn_type = dis_dref;
324
4.29k
      info->data_size = 8;
325
4.29k
      break;
326
327
3.65k
    case mmix_type_memaccess_tetra:
328
3.65k
      info->insn_type = dis_dref;
329
3.65k
      info->data_size = 4;
330
3.65k
      break;
331
332
1.39k
    case mmix_type_memaccess_wyde:
333
1.39k
      info->insn_type = dis_dref;
334
1.39k
      info->data_size = 2;
335
1.39k
      break;
336
337
5.41k
    case mmix_type_memaccess_byte:
338
5.41k
      info->insn_type = dis_dref;
339
5.41k
      info->data_size = 1;
340
5.41k
      break;
341
342
50.5k
    case mmix_type_jsr:
343
50.5k
      info->insn_type = dis_jsr;
344
50.5k
      break;
345
346
0
    default:
347
0
      BAD_CASE(opcodep->type);
348
155k
    }
349
350
155k
  switch (opcodep->operands)
351
155k
    {
352
15.8k
    case mmix_operands_regs:
353
      /*  All registers: "$X,$Y,$Z".  */
354
15.8k
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
355
15.8k
           get_reg_name (minfop, x),
356
15.8k
           get_reg_name (minfop, y),
357
15.8k
           get_reg_name (minfop, z));
358
15.8k
      break;
359
360
4.53k
    case mmix_operands_reg_yz:
361
      /* Like SETH - "$X,YZ".  */
362
4.53k
      (*info->fprintf_func) (info->stream, "%s,0x%x",
363
4.53k
           get_reg_name (minfop, x), y * 256 + z);
364
4.53k
      break;
365
366
14.9k
    case mmix_operands_regs_z_opt:
367
56.6k
    case mmix_operands_regs_z:
368
57.3k
    case mmix_operands_pushgo:
369
      /* The regular "$X,$Y,$Z|Z".  */
370
57.3k
      if (insn & INSN_IMMEDIATE_BIT)
371
26.7k
  (*info->fprintf_func) (info->stream, "%s,%s,%d",
372
26.7k
             get_reg_name (minfop, x),
373
26.7k
             get_reg_name (minfop, y), z);
374
30.6k
      else
375
30.6k
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
376
30.6k
             get_reg_name (minfop, x),
377
30.6k
             get_reg_name (minfop, y),
378
30.6k
             get_reg_name (minfop, z));
379
57.3k
      break;
380
381
1.68k
    case mmix_operands_jmp:
382
      /* Address; only JMP.  */
383
1.68k
      {
384
1.68k
  bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
385
386
1.68k
  if (insn & INSN_BACKWARD_OFFSET_BIT)
387
816
    offset -= (256 * 65536) * 4;
388
389
1.68k
  info->target = memaddr + offset;
390
1.68k
  (*info->print_address_func) (memaddr + offset, info);
391
1.68k
      }
392
1.68k
      break;
393
394
2.07k
    case mmix_operands_roundregs_z:
395
      /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
396
   "$X,ROUND_MODE,$Z|Z".  */
397
2.07k
      if (y != 0)
398
819
  {
399
819
    if (insn & INSN_IMMEDIATE_BIT)
400
448
      (*info->fprintf_func) (info->stream, "%s,%s,%d",
401
448
           get_reg_name (minfop, x),
402
448
           ROUND_MODE (y), z);
403
371
    else
404
371
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
405
371
           get_reg_name (minfop, x),
406
371
           ROUND_MODE (y),
407
371
           get_reg_name (minfop, z));
408
819
  }
409
1.25k
      else
410
1.25k
  {
411
1.25k
    if (insn & INSN_IMMEDIATE_BIT)
412
769
      (*info->fprintf_func) (info->stream, "%s,%d",
413
769
           get_reg_name (minfop, x), z);
414
487
    else
415
487
      (*info->fprintf_func) (info->stream, "%s,%s",
416
487
           get_reg_name (minfop, x),
417
487
           get_reg_name (minfop, z));
418
1.25k
  }
419
2.07k
      break;
420
421
735
    case mmix_operands_pop:
422
      /* Like POP - "X,YZ".  */
423
735
      (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
424
735
      break;
425
426
1.64k
    case mmix_operands_roundregs:
427
      /* Two registers, possibly with rounding: "$X,$Z" or
428
   "$X,ROUND_MODE,$Z".  */
429
1.64k
      if (y != 0)
430
1.20k
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
431
1.20k
             get_reg_name (minfop, x),
432
1.20k
             ROUND_MODE (y),
433
1.20k
             get_reg_name (minfop, z));
434
433
      else
435
433
  (*info->fprintf_func) (info->stream, "%s,%s",
436
433
             get_reg_name (minfop, x),
437
433
             get_reg_name (minfop, z));
438
1.64k
      break;
439
440
1.01k
    case mmix_operands_sync:
441
  /* Like SYNC - "XYZ".  */
442
1.01k
      (*info->fprintf_func) (info->stream, "%u",
443
1.01k
           x * 65536 + y * 256 + z);
444
1.01k
      break;
445
446
3.70k
    case mmix_operands_x_regs_z:
447
      /* Like SYNCD - "X,$Y,$Z|Z".  */
448
3.70k
      if (insn & INSN_IMMEDIATE_BIT)
449
1.10k
  (*info->fprintf_func) (info->stream, "%d,%s,%d",
450
1.10k
             x, get_reg_name (minfop, y), z);
451
2.59k
      else
452
2.59k
  (*info->fprintf_func) (info->stream, "%d,%s,%s",
453
2.59k
             x, get_reg_name (minfop, y),
454
2.59k
             get_reg_name (minfop, z));
455
3.70k
      break;
456
457
2.22k
    case mmix_operands_neg:
458
      /* Like NEG and NEGU - "$X,Y,$Z|Z".  */
459
2.22k
      if (insn & INSN_IMMEDIATE_BIT)
460
1.12k
  (*info->fprintf_func) (info->stream, "%s,%d,%d",
461
1.12k
             get_reg_name (minfop, x), y, z);
462
1.09k
      else
463
1.09k
  (*info->fprintf_func) (info->stream, "%s,%d,%s",
464
1.09k
             get_reg_name (minfop, x), y,
465
1.09k
             get_reg_name (minfop, z));
466
2.22k
      break;
467
468
924
    case mmix_operands_pushj:
469
12.4k
    case mmix_operands_regaddr:
470
      /* Like GETA or branches - "$X,Address".  */
471
12.4k
      {
472
12.4k
  bfd_signed_vma offset = (y * 256 + z) * 4;
473
474
12.4k
  if (insn & INSN_BACKWARD_OFFSET_BIT)
475
5.79k
    offset -= 65536 * 4;
476
477
12.4k
  info->target = memaddr + offset;
478
479
12.4k
  (*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x));
480
12.4k
  (*info->print_address_func) (memaddr + offset, info);
481
12.4k
      }
482
12.4k
      break;
483
484
353
    case mmix_operands_get:
485
      /* GET - "X,spec_reg".  */
486
353
      (*info->fprintf_func) (info->stream, "%s,%s",
487
353
           get_reg_name (minfop, x),
488
353
           get_spec_reg_name (minfop, z));
489
353
      break;
490
491
527
    case mmix_operands_put:
492
      /* PUT - "spec_reg,$Z|Z".  */
493
527
      if (insn & INSN_IMMEDIATE_BIT)
494
215
  (*info->fprintf_func) (info->stream, "%s,%d",
495
215
             get_spec_reg_name (minfop, x), z);
496
312
      else
497
312
  (*info->fprintf_func) (info->stream, "%s,%s",
498
312
             get_spec_reg_name (minfop, x),
499
312
             get_reg_name (minfop, z));
500
527
      break;
501
502
331
    case mmix_operands_set:
503
      /*  Two registers, "$X,$Y".  */
504
331
      (*info->fprintf_func) (info->stream, "%s,%s",
505
331
           get_reg_name (minfop, x),
506
331
           get_reg_name (minfop, y));
507
331
      break;
508
509
623
    case mmix_operands_save:
510
      /* SAVE - "$X,0".  */
511
623
      (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
512
623
      break;
513
514
56
    case mmix_operands_unsave:
515
      /* UNSAVE - "0,$Z".  */
516
56
      (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
517
56
      break;
518
519
50.1k
    case mmix_operands_xyz_opt:
520
      /* Like SWYM or TRAP - "X,Y,Z".  */
521
50.1k
      (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
522
50.1k
      break;
523
524
255
    case mmix_operands_resume:
525
      /* Just "Z", like RESUME.  */
526
255
      (*info->fprintf_func) (info->stream, "%d", z);
527
255
      break;
528
529
0
    default:
530
0
      (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
531
0
           opcodep->operands);
532
0
      break;
533
155k
    }
534
535
155k
  return 4;
536
155k
}