Coverage Report

Created: 2026-03-10 08:46

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