Coverage Report

Created: 2026-04-04 08:16

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
627
 ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" :  \
51
469
  (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" :  \
52
186
  _("(unknown)"))
53
54
38.7k
#define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
55
8.88k
#define INSN_BACKWARD_OFFSET_BIT (1 << 24)
56
57
183k
#define MAX_REG_NAME_LEN       256
58
399
#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
194
{
75
194
  struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
76
194
  long i;
77
78
194
  if (minfop == NULL)
79
0
    return false;
80
81
194
  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
194
  if ((info->section != NULL && info->section->owner != NULL)
86
175
      || (info->symbols != NULL
87
0
    && info->symbols[0] != NULL
88
0
    && bfd_asymbol_bfd (info->symbols[0]) != NULL))
89
19
    {
90
19
      bfd *abfd = info->section && info->section->owner != NULL
91
19
  ? info->section->owner
92
19
  : bfd_asymbol_bfd (info->symbols[0]);
93
19
      asection *reg_section = bfd_get_section_by_name (abfd, "*REG*");
94
95
19
      if (reg_section != NULL)
96
16
  {
97
    /* The returned symcount *does* include the ending NULL.  */
98
16
    long symsize = bfd_get_symtab_upper_bound (abfd);
99
16
    asymbol **syms = malloc (symsize);
100
16
    long nsyms;
101
102
16
    if (syms == NULL)
103
0
      {
104
0
        FATAL_DEBUG;
105
0
        free (minfop);
106
0
        return false;
107
0
      }
108
16
    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.22k
    for (i = 0; i < nsyms && syms[i] != NULL; i++)
114
1.20k
      {
115
1.20k
        if (syms[i]->section == reg_section
116
260
      && syms[i]->value < MAX_REG_NAME_LEN
117
260
      && minfop->reg_name[syms[i]->value] == NULL)
118
49
    minfop->reg_name[syms[i]->value] = syms[i]->name;
119
1.20k
      }
120
16
    free (syms);
121
16
  }
122
19
    }
123
124
  /* Fill in the rest with the canonical names.  */
125
49.8k
  for (i = 0; i < MAX_REG_NAME_LEN; i++)
126
49.6k
    if (minfop->reg_name[i] == NULL)
127
49.6k
      {
128
49.6k
  sprintf (minfop->basic_reg_name[i], "$%ld", i);
129
49.6k
  minfop->reg_name[i] = minfop->basic_reg_name[i];
130
49.6k
      }
131
132
  /* We assume it's actually a one-to-one mapping of number-to-name.  */
133
6.40k
  for (i = 0; mmix_spec_regs[i].name != NULL; i++)
134
6.20k
    minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name;
135
136
194
  info->private_data = (void *) minfop;
137
194
  return true;
138
194
}
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
98.7k
{
150
98.7k
  static const struct mmix_opcode **opcodes = NULL;
151
98.7k
  const struct mmix_opcode *opcodep = mmix_opcodes;
152
98.7k
  unsigned int opcode_part = (insn >> 24) & 255;
153
154
98.7k
  if (opcodes == NULL)
155
2
    opcodes = xcalloc (256, sizeof (struct mmix_opcode *));
156
157
98.7k
  opcodep = opcodes[opcode_part];
158
98.7k
  if (opcodep == NULL
159
21.6k
      || (opcodep->match & insn) != opcodep->match
160
21.6k
      || (opcodep->lose & insn) != 0)
161
77.0k
    {
162
      /* Search through the table.  */
163
5.81M
      for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++)
164
5.81M
  {
165
    /* FIXME: Break out this into an initialization function.  */
166
5.81M
    if ((opcodep->match & (opcode_part << 24)) == opcode_part
167
2
        && (opcodep->lose & (opcode_part << 24)) == 0)
168
2
      opcodes[opcode_part] = opcodep;
169
170
5.81M
    if ((opcodep->match & insn) == opcodep->match
171
2.57M
        && (opcodep->lose & insn) == 0)
172
73.9k
      break;
173
5.81M
  }
174
77.0k
    }
175
176
98.7k
  if (opcodep->name == NULL)
177
3.15k
    return NULL;
178
179
  /* Check constraints.  If they don't match, loop through the next opcode
180
     entries.  */
181
95.5k
  do
182
95.5k
    {
183
95.5k
      switch (opcodep->operands)
184
95.5k
  {
185
    /* These have no restraint on what can be in the lower three
186
       bytes.  */
187
9.14k
  case mmix_operands_regs:
188
11.9k
  case mmix_operands_reg_yz:
189
21.3k
  case mmix_operands_regs_z_opt:
190
44.9k
  case mmix_operands_regs_z:
191
45.5k
  case mmix_operands_jmp:
192
46.2k
  case mmix_operands_pushgo:
193
46.5k
  case mmix_operands_pop:
194
47.2k
  case mmix_operands_sync:
195
49.1k
  case mmix_operands_x_regs_z:
196
50.6k
  case mmix_operands_neg:
197
51.5k
  case mmix_operands_pushj:
198
58.9k
  case mmix_operands_regaddr:
199
59.0k
  case mmix_operands_get:
200
59.2k
  case mmix_operands_set:
201
59.5k
  case mmix_operands_save:
202
59.5k
  case mmix_operands_unsave:
203
90.2k
  case mmix_operands_xyz_opt:
204
90.2k
    return opcodep;
205
206
    /* For a ROUND_MODE, the middle byte must be 0..4.  */
207
3.40k
  case mmix_operands_roundregs_z:
208
4.97k
  case mmix_operands_roundregs:
209
4.97k
    {
210
4.97k
      int midbyte = (insn >> 8) & 255;
211
212
4.97k
      if (midbyte <= 4)
213
1.85k
        return opcodep;
214
4.97k
    }
215
3.12k
  break;
216
217
3.12k
  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
284
    if ((insn & INSN_IMMEDIATE_BIT)
221
106
        || ((insn >> 16) & 255) < 32)
222
259
      return opcodep;
223
25
    break;
224
225
91
  case mmix_operands_resume:
226
    /* Middle bytes must be zero.  */
227
91
    if ((insn & 0x00ffff00) == 0)
228
91
      return opcodep;
229
0
    break;
230
231
0
  default:
232
0
    BAD_CASE (opcodep->operands);
233
95.5k
  }
234
235
3.14k
      opcodep++;
236
3.14k
    }
237
95.5k
  while ((opcodep->match & insn) == opcodep->match
238
0
   && (opcodep->lose & insn) == 0);
239
240
  /* If we got here, we had no match.  */
241
3.14k
  return NULL;
242
95.5k
}
243
244
static inline const char *
245
get_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
246
132k
{
247
132k
  if (x >= MAX_REG_NAME_LEN)
248
0
    return _("*illegal*");
249
132k
  return minfop->reg_name[x];
250
132k
}
251
252
static inline const char *
253
get_spec_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
254
399
{
255
399
  if (x >= MAX_SPEC_REG_NAME_LEN)
256
64
    return _("*illegal*");
257
335
  return minfop->spec_reg_name[x];
258
399
}
259
260
/* The main disassembly function.  */
261
262
int
263
print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
264
98.8k
{
265
98.8k
  unsigned char buffer[4];
266
98.8k
  unsigned long insn;
267
98.8k
  unsigned int x, y, z;
268
98.8k
  const struct mmix_opcode *opcodep;
269
98.8k
  int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
270
98.8k
  struct mmix_dis_info *minfop;
271
272
98.8k
  if (status != 0)
273
126
    {
274
126
      (*info->memory_error_func) (status, memaddr, info);
275
126
      return -1;
276
126
    }
277
278
  /* FIXME: Is -1 suitable?  */
279
98.7k
  if (info->private_data == NULL
280
194
      && ! initialize_mmix_dis_info (info))
281
0
    return -1;
282
283
98.7k
  minfop = (struct mmix_dis_info *) info->private_data;
284
98.7k
  x = buffer[1];
285
98.7k
  y = buffer[2];
286
98.7k
  z = buffer[3];
287
288
98.7k
  insn = bfd_getb32 (buffer);
289
290
98.7k
  opcodep = get_opcode (insn);
291
292
98.7k
  if (opcodep == NULL)
293
6.29k
    {
294
6.29k
      (*info->fprintf_func) (info->stream, _("*unknown*"));
295
6.29k
      return 4;
296
6.29k
    }
297
298
92.4k
  (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
299
300
  /* Present bytes in the order they are laid out in memory.  */
301
92.4k
  info->display_endian = BFD_ENDIAN_BIG;
302
303
92.4k
  info->insn_info_valid = 1;
304
92.4k
  info->bytes_per_chunk = 4;
305
92.4k
  info->branch_delay_insns = 0;
306
92.4k
  info->target = 0;
307
92.4k
  switch (opcodep->type)
308
92.4k
    {
309
41.8k
    case mmix_type_normal:
310
43.5k
    case mmix_type_memaccess_block:
311
43.5k
      info->insn_type = dis_nonbranch;
312
43.5k
      break;
313
314
1.29k
    case mmix_type_branch:
315
1.29k
      info->insn_type = dis_branch;
316
1.29k
      break;
317
318
7.19k
    case mmix_type_condbranch:
319
7.19k
      info->insn_type = dis_condbranch;
320
7.19k
      break;
321
322
2.66k
    case mmix_type_memaccess_octa:
323
2.66k
      info->insn_type = dis_dref;
324
2.66k
      info->data_size = 8;
325
2.66k
      break;
326
327
2.57k
    case mmix_type_memaccess_tetra:
328
2.57k
      info->insn_type = dis_dref;
329
2.57k
      info->data_size = 4;
330
2.57k
      break;
331
332
1.13k
    case mmix_type_memaccess_wyde:
333
1.13k
      info->insn_type = dis_dref;
334
1.13k
      info->data_size = 2;
335
1.13k
      break;
336
337
2.70k
    case mmix_type_memaccess_byte:
338
2.70k
      info->insn_type = dis_dref;
339
2.70k
      info->data_size = 1;
340
2.70k
      break;
341
342
31.2k
    case mmix_type_jsr:
343
31.2k
      info->insn_type = dis_jsr;
344
31.2k
      break;
345
346
0
    default:
347
0
      BAD_CASE(opcodep->type);
348
92.4k
    }
349
350
92.4k
  switch (opcodep->operands)
351
92.4k
    {
352
9.14k
    case mmix_operands_regs:
353
      /*  All registers: "$X,$Y,$Z".  */
354
9.14k
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
355
9.14k
           get_reg_name (minfop, x),
356
9.14k
           get_reg_name (minfop, y),
357
9.14k
           get_reg_name (minfop, z));
358
9.14k
      break;
359
360
2.84k
    case mmix_operands_reg_yz:
361
      /* Like SETH - "$X,YZ".  */
362
2.84k
      (*info->fprintf_func) (info->stream, "%s,0x%x",
363
2.84k
           get_reg_name (minfop, x), y * 256 + z);
364
2.84k
      break;
365
366
9.34k
    case mmix_operands_regs_z_opt:
367
32.9k
    case mmix_operands_regs_z:
368
33.6k
    case mmix_operands_pushgo:
369
      /* The regular "$X,$Y,$Z|Z".  */
370
33.6k
      if (insn & INSN_IMMEDIATE_BIT)
371
16.1k
  (*info->fprintf_func) (info->stream, "%s,%s,%d",
372
16.1k
             get_reg_name (minfop, x),
373
16.1k
             get_reg_name (minfop, y), z);
374
17.4k
      else
375
17.4k
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
376
17.4k
             get_reg_name (minfop, x),
377
17.4k
             get_reg_name (minfop, y),
378
17.4k
             get_reg_name (minfop, z));
379
33.6k
      break;
380
381
624
    case mmix_operands_jmp:
382
      /* Address; only JMP.  */
383
624
      {
384
624
  bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
385
386
624
  if (insn & INSN_BACKWARD_OFFSET_BIT)
387
334
    offset -= (256 * 65536) * 4;
388
389
624
  info->target = memaddr + offset;
390
624
  (*info->print_address_func) (memaddr + offset, info);
391
624
      }
392
624
      break;
393
394
1.18k
    case mmix_operands_roundregs_z:
395
      /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
396
   "$X,ROUND_MODE,$Z|Z".  */
397
1.18k
      if (y != 0)
398
299
  {
399
299
    if (insn & INSN_IMMEDIATE_BIT)
400
104
      (*info->fprintf_func) (info->stream, "%s,%s,%d",
401
104
           get_reg_name (minfop, x),
402
104
           ROUND_MODE (y), z);
403
195
    else
404
195
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
405
195
           get_reg_name (minfop, x),
406
195
           ROUND_MODE (y),
407
195
           get_reg_name (minfop, z));
408
299
  }
409
888
      else
410
888
  {
411
888
    if (insn & INSN_IMMEDIATE_BIT)
412
650
      (*info->fprintf_func) (info->stream, "%s,%d",
413
650
           get_reg_name (minfop, x), z);
414
238
    else
415
238
      (*info->fprintf_func) (info->stream, "%s,%s",
416
238
           get_reg_name (minfop, x),
417
238
           get_reg_name (minfop, z));
418
888
  }
419
1.18k
      break;
420
421
360
    case mmix_operands_pop:
422
      /* Like POP - "X,YZ".  */
423
360
      (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
424
360
      break;
425
426
668
    case mmix_operands_roundregs:
427
      /* Two registers, possibly with rounding: "$X,$Z" or
428
   "$X,ROUND_MODE,$Z".  */
429
668
      if (y != 0)
430
328
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
431
328
             get_reg_name (minfop, x),
432
328
             ROUND_MODE (y),
433
328
             get_reg_name (minfop, z));
434
340
      else
435
340
  (*info->fprintf_func) (info->stream, "%s,%s",
436
340
             get_reg_name (minfop, x),
437
340
             get_reg_name (minfop, z));
438
668
      break;
439
440
689
    case mmix_operands_sync:
441
  /* Like SYNC - "XYZ".  */
442
689
      (*info->fprintf_func) (info->stream, "%u",
443
689
           x * 65536 + y * 256 + z);
444
689
      break;
445
446
1.84k
    case mmix_operands_x_regs_z:
447
      /* Like SYNCD - "X,$Y,$Z|Z".  */
448
1.84k
      if (insn & INSN_IMMEDIATE_BIT)
449
773
  (*info->fprintf_func) (info->stream, "%d,%s,%d",
450
773
             x, get_reg_name (minfop, y), z);
451
1.06k
      else
452
1.06k
  (*info->fprintf_func) (info->stream, "%d,%s,%s",
453
1.06k
             x, get_reg_name (minfop, y),
454
1.06k
             get_reg_name (minfop, z));
455
1.84k
      break;
456
457
1.56k
    case mmix_operands_neg:
458
      /* Like NEG and NEGU - "$X,Y,$Z|Z".  */
459
1.56k
      if (insn & INSN_IMMEDIATE_BIT)
460
605
  (*info->fprintf_func) (info->stream, "%s,%d,%d",
461
605
             get_reg_name (minfop, x), y, z);
462
960
      else
463
960
  (*info->fprintf_func) (info->stream, "%s,%d,%s",
464
960
             get_reg_name (minfop, x), y,
465
960
             get_reg_name (minfop, z));
466
1.56k
      break;
467
468
890
    case mmix_operands_pushj:
469
8.26k
    case mmix_operands_regaddr:
470
      /* Like GETA or branches - "$X,Address".  */
471
8.26k
      {
472
8.26k
  bfd_signed_vma offset = (y * 256 + z) * 4;
473
474
8.26k
  if (insn & INSN_BACKWARD_OFFSET_BIT)
475
4.38k
    offset -= 65536 * 4;
476
477
8.26k
  info->target = memaddr + offset;
478
479
8.26k
  (*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x));
480
8.26k
  (*info->print_address_func) (memaddr + offset, info);
481
8.26k
      }
482
8.26k
      break;
483
484
140
    case mmix_operands_get:
485
      /* GET - "X,spec_reg".  */
486
140
      (*info->fprintf_func) (info->stream, "%s,%s",
487
140
           get_reg_name (minfop, x),
488
140
           get_spec_reg_name (minfop, z));
489
140
      break;
490
491
259
    case mmix_operands_put:
492
      /* PUT - "spec_reg,$Z|Z".  */
493
259
      if (insn & INSN_IMMEDIATE_BIT)
494
178
  (*info->fprintf_func) (info->stream, "%s,%d",
495
178
             get_spec_reg_name (minfop, x), z);
496
81
      else
497
81
  (*info->fprintf_func) (info->stream, "%s,%s",
498
81
             get_spec_reg_name (minfop, x),
499
81
             get_reg_name (minfop, z));
500
259
      break;
501
502
142
    case mmix_operands_set:
503
      /*  Two registers, "$X,$Y".  */
504
142
      (*info->fprintf_func) (info->stream, "%s,%s",
505
142
           get_reg_name (minfop, x),
506
142
           get_reg_name (minfop, y));
507
142
      break;
508
509
302
    case mmix_operands_save:
510
      /* SAVE - "$X,0".  */
511
302
      (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
512
302
      break;
513
514
12
    case mmix_operands_unsave:
515
      /* UNSAVE - "0,$Z".  */
516
12
      (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
517
12
      break;
518
519
30.6k
    case mmix_operands_xyz_opt:
520
      /* Like SWYM or TRAP - "X,Y,Z".  */
521
30.6k
      (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
522
30.6k
      break;
523
524
91
    case mmix_operands_resume:
525
      /* Just "Z", like RESUME.  */
526
91
      (*info->fprintf_func) (info->stream, "%d", z);
527
91
      break;
528
529
0
    default:
530
0
      (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
531
0
           opcodep->operands);
532
0
      break;
533
92.4k
    }
534
535
92.4k
  return 4;
536
92.4k
}