Coverage Report

Created: 2025-07-08 11:15

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