Coverage Report

Created: 2025-06-24 06:45

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