Coverage Report

Created: 2026-10-02 09:53

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