Coverage Report

Created: 2023-06-29 07:13

/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-2023 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
801
 ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" :  \
51
640
  (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" :  \
52
121
  _("(unknown)"))
53
54
65.1k
#define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
55
15.4k
#define INSN_BACKWARD_OFFSET_BIT (1 << 24)
56
57
240k
#define MAX_REG_NAME_LEN       256
58
478
#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
136
{
75
136
  struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
76
136
  long i;
77
78
136
  if (minfop == NULL)
79
0
    return false;
80
81
136
  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
136
  if ((info->section != NULL && info->section->owner != NULL)
86
136
      || (info->symbols != NULL
87
136
    && info->symbols[0] != NULL
88
136
    && bfd_asymbol_bfd (info->symbols[0]) != NULL))
89
0
    {
90
0
      bfd *abfd = info->section && info->section->owner != NULL
91
0
  ? info->section->owner
92
0
  : bfd_asymbol_bfd (info->symbols[0]);
93
0
      asection *reg_section = bfd_get_section_by_name (abfd, "*REG*");
94
95
0
      if (reg_section != NULL)
96
0
  {
97
    /* The returned symcount *does* include the ending NULL.  */
98
0
    long symsize = bfd_get_symtab_upper_bound (abfd);
99
0
    asymbol **syms = malloc (symsize);
100
0
    long nsyms;
101
102
0
    if (syms == NULL)
103
0
      {
104
0
        FATAL_DEBUG;
105
0
        free (minfop);
106
0
        return false;
107
0
      }
108
0
    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
0
    for (i = 0; i < nsyms && syms[i] != NULL; i++)
114
0
      {
115
0
        if (syms[i]->section == reg_section
116
0
      && syms[i]->value < MAX_REG_NAME_LEN
117
0
      && minfop->reg_name[syms[i]->value] == NULL)
118
0
    minfop->reg_name[syms[i]->value] = syms[i]->name;
119
0
      }
120
0
  }
121
0
    }
122
123
  /* Fill in the rest with the canonical names.  */
124
34.9k
  for (i = 0; i < MAX_REG_NAME_LEN; i++)
125
34.8k
    if (minfop->reg_name[i] == NULL)
126
34.8k
      {
127
34.8k
  sprintf (minfop->basic_reg_name[i], "$%ld", i);
128
34.8k
  minfop->reg_name[i] = minfop->basic_reg_name[i];
129
34.8k
      }
130
131
  /* We assume it's actually a one-to-one mapping of number-to-name.  */
132
4.48k
  for (i = 0; mmix_spec_regs[i].name != NULL; i++)
133
4.35k
    minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name;
134
135
136
  info->private_data = (void *) minfop;
136
136
  return true;
137
136
}
138
139
/* A table indexed by the first byte is constructed as we disassemble each
140
   tetrabyte.  The contents is a pointer into mmix_insns reflecting the
141
   first found entry with matching match-bits and lose-bits.  Further
142
   entries are considered one after one until the operand constraints
143
   match or the match-bits and lose-bits do not match.  Normally a
144
   "further entry" will just show that there was no other match.  */
145
146
static const struct mmix_opcode *
147
get_opcode (unsigned long insn)
148
149k
{
149
149k
  static const struct mmix_opcode **opcodes = NULL;
150
149k
  const struct mmix_opcode *opcodep = mmix_opcodes;
151
149k
  unsigned int opcode_part = (insn >> 24) & 255;
152
153
149k
  if (opcodes == NULL)
154
1
    opcodes = xcalloc (256, sizeof (struct mmix_opcode *));
155
156
149k
  opcodep = opcodes[opcode_part];
157
149k
  if (opcodep == NULL
158
149k
      || (opcodep->match & insn) != opcodep->match
159
149k
      || (opcodep->lose & insn) != 0)
160
122k
    {
161
      /* Search through the table.  */
162
9.60M
      for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++)
163
9.59M
  {
164
    /* FIXME: Break out this into an initialization function.  */
165
9.59M
    if ((opcodep->match & (opcode_part << 24)) == opcode_part
166
9.59M
        && (opcodep->lose & (opcode_part << 24)) == 0)
167
1
      opcodes[opcode_part] = opcodep;
168
169
9.59M
    if ((opcodep->match & insn) == opcodep->match
170
9.59M
        && (opcodep->lose & insn) == 0)
171
119k
      break;
172
9.59M
  }
173
122k
    }
174
175
149k
  if (opcodep->name == NULL)
176
3.78k
    return NULL;
177
178
  /* Check constraints.  If they don't match, loop through the next opcode
179
     entries.  */
180
145k
  do
181
145k
    {
182
145k
      switch (opcodep->operands)
183
145k
  {
184
    /* These have no restraint on what can be in the lower three
185
       bytes.  */
186
8.57k
  case mmix_operands_regs:
187
13.7k
  case mmix_operands_reg_yz:
188
30.8k
  case mmix_operands_regs_z_opt:
189
70.9k
  case mmix_operands_regs_z:
190
72.0k
  case mmix_operands_jmp:
191
72.4k
  case mmix_operands_pushgo:
192
73.4k
  case mmix_operands_pop:
193
74.7k
  case mmix_operands_sync:
194
77.4k
  case mmix_operands_x_regs_z:
195
79.9k
  case mmix_operands_neg:
196
81.7k
  case mmix_operands_pushj:
197
94.3k
  case mmix_operands_regaddr:
198
94.4k
  case mmix_operands_get:
199
94.5k
  case mmix_operands_set:
200
94.5k
  case mmix_operands_save:
201
94.5k
  case mmix_operands_unsave:
202
135k
  case mmix_operands_xyz_opt:
203
135k
    return opcodep;
204
205
    /* For a ROUND_MODE, the middle byte must be 0..4.  */
206
6.75k
  case mmix_operands_roundregs_z:
207
10.1k
  case mmix_operands_roundregs:
208
10.1k
    {
209
10.1k
      int midbyte = (insn >> 8) & 255;
210
211
10.1k
      if (midbyte <= 4)
212
2.44k
        return opcodep;
213
10.1k
    }
214
7.67k
  break;
215
216
7.67k
  case mmix_operands_put:
217
    /* A "PUT".  If it is "immediate", then no restrictions,
218
       otherwise we have to make sure the register number is < 32.  */
219
328
    if ((insn & INSN_IMMEDIATE_BIT)
220
328
        || ((insn >> 16) & 255) < 32)
221
308
      return opcodep;
222
20
    break;
223
224
20
  case mmix_operands_resume:
225
    /* Middle bytes must be zero.  */
226
3
    if ((insn & 0x00ffff00) == 0)
227
3
      return opcodep;
228
0
    break;
229
230
0
  default:
231
0
    BAD_CASE (opcodep->operands);
232
145k
  }
233
234
7.69k
      opcodep++;
235
7.69k
    }
236
145k
  while ((opcodep->match & insn) == opcodep->match
237
7.69k
   && (opcodep->lose & insn) == 0);
238
239
  /* If we got here, we had no match.  */
240
7.69k
  return NULL;
241
145k
}
242
243
static inline const char *
244
get_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
245
205k
{
246
205k
  if (x >= MAX_REG_NAME_LEN)
247
0
    return _("*illegal*");
248
205k
  return minfop->reg_name[x];
249
205k
}
250
251
static inline const char *
252
get_spec_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
253
478
{
254
478
  if (x >= MAX_SPEC_REG_NAME_LEN)
255
127
    return _("*illegal*");
256
351
  return minfop->spec_reg_name[x];
257
478
}
258
259
/* The main disassembly function.  */
260
261
int
262
print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
263
149k
{
264
149k
  unsigned char buffer[4];
265
149k
  unsigned long insn;
266
149k
  unsigned int x, y, z;
267
149k
  const struct mmix_opcode *opcodep;
268
149k
  int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
269
149k
  struct mmix_dis_info *minfop;
270
271
149k
  if (status != 0)
272
97
    {
273
97
      (*info->memory_error_func) (status, memaddr, info);
274
97
      return -1;
275
97
    }
276
277
  /* FIXME: Is -1 suitable?  */
278
149k
  if (info->private_data == NULL
279
149k
      && ! initialize_mmix_dis_info (info))
280
0
    return -1;
281
282
149k
  minfop = (struct mmix_dis_info *) info->private_data;
283
149k
  x = buffer[1];
284
149k
  y = buffer[2];
285
149k
  z = buffer[3];
286
287
149k
  insn = bfd_getb32 (buffer);
288
289
149k
  opcodep = get_opcode (insn);
290
291
149k
  if (opcodep == NULL)
292
11.4k
    {
293
11.4k
      (*info->fprintf_func) (info->stream, _("*unknown*"));
294
11.4k
      return 4;
295
11.4k
    }
296
297
138k
  (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
298
299
  /* Present bytes in the order they are laid out in memory.  */
300
138k
  info->display_endian = BFD_ENDIAN_BIG;
301
302
138k
  info->insn_info_valid = 1;
303
138k
  info->bytes_per_chunk = 4;
304
138k
  info->branch_delay_insns = 0;
305
138k
  info->target = 0;
306
138k
  switch (opcodep->type)
307
138k
    {
308
63.6k
    case mmix_type_normal:
309
68.2k
    case mmix_type_memaccess_block:
310
68.2k
      info->insn_type = dis_nonbranch;
311
68.2k
      break;
312
313
2.56k
    case mmix_type_branch:
314
2.56k
      info->insn_type = dis_branch;
315
2.56k
      break;
316
317
11.0k
    case mmix_type_condbranch:
318
11.0k
      info->insn_type = dis_condbranch;
319
11.0k
      break;
320
321
4.00k
    case mmix_type_memaccess_octa:
322
4.00k
      info->insn_type = dis_dref;
323
4.00k
      info->data_size = 8;
324
4.00k
      break;
325
326
4.38k
    case mmix_type_memaccess_tetra:
327
4.38k
      info->insn_type = dis_dref;
328
4.38k
      info->data_size = 4;
329
4.38k
      break;
330
331
1.84k
    case mmix_type_memaccess_wyde:
332
1.84k
      info->insn_type = dis_dref;
333
1.84k
      info->data_size = 2;
334
1.84k
      break;
335
336
4.03k
    case mmix_type_memaccess_byte:
337
4.03k
      info->insn_type = dis_dref;
338
4.03k
      info->data_size = 1;
339
4.03k
      break;
340
341
42.0k
    case mmix_type_jsr:
342
42.0k
      info->insn_type = dis_jsr;
343
42.0k
      break;
344
345
0
    default:
346
0
      BAD_CASE(opcodep->type);
347
138k
    }
348
349
138k
  switch (opcodep->operands)
350
138k
    {
351
8.57k
    case mmix_operands_regs:
352
      /*  All registers: "$X,$Y,$Z".  */
353
8.57k
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
354
8.57k
           get_reg_name (minfop, x),
355
8.57k
           get_reg_name (minfop, y),
356
8.57k
           get_reg_name (minfop, z));
357
8.57k
      break;
358
359
5.14k
    case mmix_operands_reg_yz:
360
      /* Like SETH - "$X,YZ".  */
361
5.14k
      (*info->fprintf_func) (info->stream, "%s,0x%x",
362
5.14k
           get_reg_name (minfop, x), y * 256 + z);
363
5.14k
      break;
364
365
17.1k
    case mmix_operands_regs_z_opt:
366
57.2k
    case mmix_operands_regs_z:
367
57.6k
    case mmix_operands_pushgo:
368
      /* The regular "$X,$Y,$Z|Z".  */
369
57.6k
      if (insn & INSN_IMMEDIATE_BIT)
370
24.8k
  (*info->fprintf_func) (info->stream, "%s,%s,%d",
371
24.8k
             get_reg_name (minfop, x),
372
24.8k
             get_reg_name (minfop, y), z);
373
32.7k
      else
374
32.7k
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
375
32.7k
             get_reg_name (minfop, x),
376
32.7k
             get_reg_name (minfop, y),
377
32.7k
             get_reg_name (minfop, z));
378
57.6k
      break;
379
380
1.05k
    case mmix_operands_jmp:
381
      /* Address; only JMP.  */
382
1.05k
      {
383
1.05k
  bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
384
385
1.05k
  if (insn & INSN_BACKWARD_OFFSET_BIT)
386
266
    offset -= (256 * 65536) * 4;
387
388
1.05k
  info->target = memaddr + offset;
389
1.05k
  (*info->print_address_func) (memaddr + offset, info);
390
1.05k
      }
391
1.05k
      break;
392
393
1.63k
    case mmix_operands_roundregs_z:
394
      /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
395
   "$X,ROUND_MODE,$Z|Z".  */
396
1.63k
      if (y != 0)
397
538
  {
398
538
    if (insn & INSN_IMMEDIATE_BIT)
399
217
      (*info->fprintf_func) (info->stream, "%s,%s,%d",
400
217
           get_reg_name (minfop, x),
401
217
           ROUND_MODE (y), z);
402
321
    else
403
321
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
404
321
           get_reg_name (minfop, x),
405
321
           ROUND_MODE (y),
406
321
           get_reg_name (minfop, z));
407
538
  }
408
1.09k
      else
409
1.09k
  {
410
1.09k
    if (insn & INSN_IMMEDIATE_BIT)
411
563
      (*info->fprintf_func) (info->stream, "%s,%d",
412
563
           get_reg_name (minfop, x), z);
413
534
    else
414
534
      (*info->fprintf_func) (info->stream, "%s,%s",
415
534
           get_reg_name (minfop, x),
416
534
           get_reg_name (minfop, z));
417
1.09k
  }
418
1.63k
      break;
419
420
991
    case mmix_operands_pop:
421
      /* Like POP - "X,YZ".  */
422
991
      (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
423
991
      break;
424
425
807
    case mmix_operands_roundregs:
426
      /* Two registers, possibly with rounding: "$X,$Z" or
427
   "$X,ROUND_MODE,$Z".  */
428
807
      if (y != 0)
429
263
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
430
263
             get_reg_name (minfop, x),
431
263
             ROUND_MODE (y),
432
263
             get_reg_name (minfop, z));
433
544
      else
434
544
  (*info->fprintf_func) (info->stream, "%s,%s",
435
544
             get_reg_name (minfop, x),
436
544
             get_reg_name (minfop, z));
437
807
      break;
438
439
1.30k
    case mmix_operands_sync:
440
  /* Like SYNC - "XYZ".  */
441
1.30k
      (*info->fprintf_func) (info->stream, "%u",
442
1.30k
           x * 65536 + y * 256 + z);
443
1.30k
      break;
444
445
2.70k
    case mmix_operands_x_regs_z:
446
      /* Like SYNCD - "X,$Y,$Z|Z".  */
447
2.70k
      if (insn & INSN_IMMEDIATE_BIT)
448
1.25k
  (*info->fprintf_func) (info->stream, "%d,%s,%d",
449
1.25k
             x, get_reg_name (minfop, y), z);
450
1.45k
      else
451
1.45k
  (*info->fprintf_func) (info->stream, "%d,%s,%s",
452
1.45k
             x, get_reg_name (minfop, y),
453
1.45k
             get_reg_name (minfop, z));
454
2.70k
      break;
455
456
2.46k
    case mmix_operands_neg:
457
      /* Like NEG and NEGU - "$X,Y,$Z|Z".  */
458
2.46k
      if (insn & INSN_IMMEDIATE_BIT)
459
1.09k
  (*info->fprintf_func) (info->stream, "%s,%d,%d",
460
1.09k
             get_reg_name (minfop, x), y, z);
461
1.36k
      else
462
1.36k
  (*info->fprintf_func) (info->stream, "%s,%d,%s",
463
1.36k
             get_reg_name (minfop, x), y,
464
1.36k
             get_reg_name (minfop, z));
465
2.46k
      break;
466
467
1.88k
    case mmix_operands_pushj:
468
14.4k
    case mmix_operands_regaddr:
469
      /* Like GETA or branches - "$X,Address".  */
470
14.4k
      {
471
14.4k
  bfd_signed_vma offset = (y * 256 + z) * 4;
472
473
14.4k
  if (insn & INSN_BACKWARD_OFFSET_BIT)
474
6.55k
    offset -= 65536 * 4;
475
476
14.4k
  info->target = memaddr + offset;
477
478
14.4k
  (*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x));
479
14.4k
  (*info->print_address_func) (memaddr + offset, info);
480
14.4k
      }
481
14.4k
      break;
482
483
170
    case mmix_operands_get:
484
      /* GET - "X,spec_reg".  */
485
170
      (*info->fprintf_func) (info->stream, "%s,%s",
486
170
           get_reg_name (minfop, x),
487
170
           get_spec_reg_name (minfop, z));
488
170
      break;
489
490
308
    case mmix_operands_put:
491
      /* PUT - "spec_reg,$Z|Z".  */
492
308
      if (insn & INSN_IMMEDIATE_BIT)
493
235
  (*info->fprintf_func) (info->stream, "%s,%d",
494
235
             get_spec_reg_name (minfop, x), z);
495
73
      else
496
73
  (*info->fprintf_func) (info->stream, "%s,%s",
497
73
             get_spec_reg_name (minfop, x),
498
73
             get_reg_name (minfop, z));
499
308
      break;
500
501
43
    case mmix_operands_set:
502
      /*  Two registers, "$X,$Y".  */
503
43
      (*info->fprintf_func) (info->stream, "%s,%s",
504
43
           get_reg_name (minfop, x),
505
43
           get_reg_name (minfop, y));
506
43
      break;
507
508
16
    case mmix_operands_save:
509
      /* SAVE - "$X,0".  */
510
16
      (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
511
16
      break;
512
513
38
    case mmix_operands_unsave:
514
      /* UNSAVE - "0,$Z".  */
515
38
      (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
516
38
      break;
517
518
40.7k
    case mmix_operands_xyz_opt:
519
      /* Like SWYM or TRAP - "X,Y,Z".  */
520
40.7k
      (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
521
40.7k
      break;
522
523
3
    case mmix_operands_resume:
524
      /* Just "Z", like RESUME.  */
525
3
      (*info->fprintf_func) (info->stream, "%d", z);
526
3
      break;
527
528
0
    default:
529
0
      (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
530
0
           opcodep->operands);
531
0
      break;
532
138k
    }
533
534
138k
  return 4;
535
138k
}