Coverage Report

Created: 2026-05-11 07:54

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