Coverage Report

Created: 2023-08-28 06:31

/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
372
 ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" :  \
51
234
  (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" :  \
52
51
  _("(unknown)"))
53
54
47.0k
#define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
55
12.3k
#define INSN_BACKWARD_OFFSET_BIT (1 << 24)
56
57
179k
#define MAX_REG_NAME_LEN       256
58
187
#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
120
{
75
120
  struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
76
120
  long i;
77
78
120
  if (minfop == NULL)
79
0
    return false;
80
81
120
  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
120
  if ((info->section != NULL && info->section->owner != NULL)
86
120
      || (info->symbols != NULL
87
120
    && info->symbols[0] != NULL
88
120
    && 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
30.8k
  for (i = 0; i < MAX_REG_NAME_LEN; i++)
125
30.7k
    if (minfop->reg_name[i] == NULL)
126
30.7k
      {
127
30.7k
  sprintf (minfop->basic_reg_name[i], "$%ld", i);
128
30.7k
  minfop->reg_name[i] = minfop->basic_reg_name[i];
129
30.7k
      }
130
131
  /* We assume it's actually a one-to-one mapping of number-to-name.  */
132
3.96k
  for (i = 0; mmix_spec_regs[i].name != NULL; i++)
133
3.84k
    minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name;
134
135
120
  info->private_data = (void *) minfop;
136
120
  return true;
137
120
}
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
110k
{
149
110k
  static const struct mmix_opcode **opcodes = NULL;
150
110k
  const struct mmix_opcode *opcodep = mmix_opcodes;
151
110k
  unsigned int opcode_part = (insn >> 24) & 255;
152
153
110k
  if (opcodes == NULL)
154
1
    opcodes = xcalloc (256, sizeof (struct mmix_opcode *));
155
156
110k
  opcodep = opcodes[opcode_part];
157
110k
  if (opcodep == NULL
158
110k
      || (opcodep->match & insn) != opcodep->match
159
110k
      || (opcodep->lose & insn) != 0)
160
88.9k
    {
161
      /* Search through the table.  */
162
7.00M
      for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++)
163
7.00M
  {
164
    /* FIXME: Break out this into an initialization function.  */
165
7.00M
    if ((opcodep->match & (opcode_part << 24)) == opcode_part
166
7.00M
        && (opcodep->lose & (opcode_part << 24)) == 0)
167
1
      opcodes[opcode_part] = opcodep;
168
169
7.00M
    if ((opcodep->match & insn) == opcodep->match
170
7.00M
        && (opcodep->lose & insn) == 0)
171
86.5k
      break;
172
7.00M
  }
173
88.9k
    }
174
175
110k
  if (opcodep->name == NULL)
176
2.37k
    return NULL;
177
178
  /* Check constraints.  If they don't match, loop through the next opcode
179
     entries.  */
180
107k
  do
181
107k
    {
182
107k
      switch (opcodep->operands)
183
107k
  {
184
    /* These have no restraint on what can be in the lower three
185
       bytes.  */
186
6.23k
  case mmix_operands_regs:
187
10.1k
  case mmix_operands_reg_yz:
188
22.1k
  case mmix_operands_regs_z_opt:
189
51.8k
  case mmix_operands_regs_z:
190
52.8k
  case mmix_operands_jmp:
191
53.3k
  case mmix_operands_pushgo:
192
54.1k
  case mmix_operands_pop:
193
55.0k
  case mmix_operands_sync:
194
57.2k
  case mmix_operands_x_regs_z:
195
58.7k
  case mmix_operands_neg:
196
60.0k
  case mmix_operands_pushj:
197
70.0k
  case mmix_operands_regaddr:
198
70.1k
  case mmix_operands_get:
199
70.2k
  case mmix_operands_set:
200
70.2k
  case mmix_operands_save:
201
70.2k
  case mmix_operands_unsave:
202
101k
  case mmix_operands_xyz_opt:
203
101k
    return opcodep;
204
205
    /* For a ROUND_MODE, the middle byte must be 0..4.  */
206
3.76k
  case mmix_operands_roundregs_z:
207
5.61k
  case mmix_operands_roundregs:
208
5.61k
    {
209
5.61k
      int midbyte = (insn >> 8) & 255;
210
211
5.61k
      if (midbyte <= 4)
212
1.45k
        return opcodep;
213
5.61k
    }
214
4.16k
  break;
215
216
4.16k
  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
126
    if ((insn & INSN_IMMEDIATE_BIT)
220
126
        || ((insn >> 16) & 255) < 32)
221
93
      return opcodep;
222
33
    break;
223
224
33
  case mmix_operands_resume:
225
    /* Middle bytes must be zero.  */
226
18
    if ((insn & 0x00ffff00) == 0)
227
18
      return opcodep;
228
0
    break;
229
230
0
  default:
231
0
    BAD_CASE (opcodep->operands);
232
107k
  }
233
234
4.19k
      opcodep++;
235
4.19k
    }
236
107k
  while ((opcodep->match & insn) == opcodep->match
237
4.19k
   && (opcodep->lose & insn) == 0);
238
239
  /* If we got here, we had no match.  */
240
4.19k
  return NULL;
241
107k
}
242
243
static inline const char *
244
get_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
245
148k
{
246
148k
  if (x >= MAX_REG_NAME_LEN)
247
0
    return _("*illegal*");
248
148k
  return minfop->reg_name[x];
249
148k
}
250
251
static inline const char *
252
get_spec_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
253
187
{
254
187
  if (x >= MAX_SPEC_REG_NAME_LEN)
255
12
    return _("*illegal*");
256
175
  return minfop->spec_reg_name[x];
257
187
}
258
259
/* The main disassembly function.  */
260
261
int
262
print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
263
110k
{
264
110k
  unsigned char buffer[4];
265
110k
  unsigned long insn;
266
110k
  unsigned int x, y, z;
267
110k
  const struct mmix_opcode *opcodep;
268
110k
  int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
269
110k
  struct mmix_dis_info *minfop;
270
271
110k
  if (status != 0)
272
89
    {
273
89
      (*info->memory_error_func) (status, memaddr, info);
274
89
      return -1;
275
89
    }
276
277
  /* FIXME: Is -1 suitable?  */
278
110k
  if (info->private_data == NULL
279
110k
      && ! initialize_mmix_dis_info (info))
280
0
    return -1;
281
282
110k
  minfop = (struct mmix_dis_info *) info->private_data;
283
110k
  x = buffer[1];
284
110k
  y = buffer[2];
285
110k
  z = buffer[3];
286
287
110k
  insn = bfd_getb32 (buffer);
288
289
110k
  opcodep = get_opcode (insn);
290
291
110k
  if (opcodep == NULL)
292
6.56k
    {
293
6.56k
      (*info->fprintf_func) (info->stream, _("*unknown*"));
294
6.56k
      return 4;
295
6.56k
    }
296
297
103k
  (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
298
299
  /* Present bytes in the order they are laid out in memory.  */
300
103k
  info->display_endian = BFD_ENDIAN_BIG;
301
302
103k
  info->insn_info_valid = 1;
303
103k
  info->bytes_per_chunk = 4;
304
103k
  info->branch_delay_insns = 0;
305
103k
  info->target = 0;
306
103k
  switch (opcodep->type)
307
103k
    {
308
46.2k
    case mmix_type_normal:
309
48.2k
    case mmix_type_memaccess_block:
310
48.2k
      info->insn_type = dis_nonbranch;
311
48.2k
      break;
312
313
2.22k
    case mmix_type_branch:
314
2.22k
      info->insn_type = dis_branch;
315
2.22k
      break;
316
317
9.00k
    case mmix_type_condbranch:
318
9.00k
      info->insn_type = dis_condbranch;
319
9.00k
      break;
320
321
2.92k
    case mmix_type_memaccess_octa:
322
2.92k
      info->insn_type = dis_dref;
323
2.92k
      info->data_size = 8;
324
2.92k
      break;
325
326
2.84k
    case mmix_type_memaccess_tetra:
327
2.84k
      info->insn_type = dis_dref;
328
2.84k
      info->data_size = 4;
329
2.84k
      break;
330
331
2.09k
    case mmix_type_memaccess_wyde:
332
2.09k
      info->insn_type = dis_dref;
333
2.09k
      info->data_size = 2;
334
2.09k
      break;
335
336
3.29k
    case mmix_type_memaccess_byte:
337
3.29k
      info->insn_type = dis_dref;
338
3.29k
      info->data_size = 1;
339
3.29k
      break;
340
341
32.8k
    case mmix_type_jsr:
342
32.8k
      info->insn_type = dis_jsr;
343
32.8k
      break;
344
345
0
    default:
346
0
      BAD_CASE(opcodep->type);
347
103k
    }
348
349
103k
  switch (opcodep->operands)
350
103k
    {
351
6.23k
    case mmix_operands_regs:
352
      /*  All registers: "$X,$Y,$Z".  */
353
6.23k
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
354
6.23k
           get_reg_name (minfop, x),
355
6.23k
           get_reg_name (minfop, y),
356
6.23k
           get_reg_name (minfop, z));
357
6.23k
      break;
358
359
3.93k
    case mmix_operands_reg_yz:
360
      /* Like SETH - "$X,YZ".  */
361
3.93k
      (*info->fprintf_func) (info->stream, "%s,0x%x",
362
3.93k
           get_reg_name (minfop, x), y * 256 + z);
363
3.93k
      break;
364
365
12.0k
    case mmix_operands_regs_z_opt:
366
41.7k
    case mmix_operands_regs_z:
367
42.1k
    case mmix_operands_pushgo:
368
      /* The regular "$X,$Y,$Z|Z".  */
369
42.1k
      if (insn & INSN_IMMEDIATE_BIT)
370
19.8k
  (*info->fprintf_func) (info->stream, "%s,%s,%d",
371
19.8k
             get_reg_name (minfop, x),
372
19.8k
             get_reg_name (minfop, y), z);
373
22.3k
      else
374
22.3k
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
375
22.3k
             get_reg_name (minfop, x),
376
22.3k
             get_reg_name (minfop, y),
377
22.3k
             get_reg_name (minfop, z));
378
42.1k
      break;
379
380
962
    case mmix_operands_jmp:
381
      /* Address; only JMP.  */
382
962
      {
383
962
  bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
384
385
962
  if (insn & INSN_BACKWARD_OFFSET_BIT)
386
312
    offset -= (256 * 65536) * 4;
387
388
962
  info->target = memaddr + offset;
389
962
  (*info->print_address_func) (memaddr + offset, info);
390
962
      }
391
962
      break;
392
393
994
    case mmix_operands_roundregs_z:
394
      /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
395
   "$X,ROUND_MODE,$Z|Z".  */
396
994
      if (y != 0)
397
265
  {
398
265
    if (insn & INSN_IMMEDIATE_BIT)
399
134
      (*info->fprintf_func) (info->stream, "%s,%s,%d",
400
134
           get_reg_name (minfop, x),
401
134
           ROUND_MODE (y), z);
402
131
    else
403
131
      (*info->fprintf_func) (info->stream, "%s,%s,%s",
404
131
           get_reg_name (minfop, x),
405
131
           ROUND_MODE (y),
406
131
           get_reg_name (minfop, z));
407
265
  }
408
729
      else
409
729
  {
410
729
    if (insn & INSN_IMMEDIATE_BIT)
411
523
      (*info->fprintf_func) (info->stream, "%s,%d",
412
523
           get_reg_name (minfop, x), z);
413
206
    else
414
206
      (*info->fprintf_func) (info->stream, "%s,%s",
415
206
           get_reg_name (minfop, x),
416
206
           get_reg_name (minfop, z));
417
729
  }
418
994
      break;
419
420
797
    case mmix_operands_pop:
421
      /* Like POP - "X,YZ".  */
422
797
      (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
423
797
      break;
424
425
460
    case mmix_operands_roundregs:
426
      /* Two registers, possibly with rounding: "$X,$Z" or
427
   "$X,ROUND_MODE,$Z".  */
428
460
      if (y != 0)
429
107
  (*info->fprintf_func) (info->stream, "%s,%s,%s",
430
107
             get_reg_name (minfop, x),
431
107
             ROUND_MODE (y),
432
107
             get_reg_name (minfop, z));
433
353
      else
434
353
  (*info->fprintf_func) (info->stream, "%s,%s",
435
353
             get_reg_name (minfop, x),
436
353
             get_reg_name (minfop, z));
437
460
      break;
438
439
982
    case mmix_operands_sync:
440
  /* Like SYNC - "XYZ".  */
441
982
      (*info->fprintf_func) (info->stream, "%u",
442
982
           x * 65536 + y * 256 + z);
443
982
      break;
444
445
2.18k
    case mmix_operands_x_regs_z:
446
      /* Like SYNCD - "X,$Y,$Z|Z".  */
447
2.18k
      if (insn & INSN_IMMEDIATE_BIT)
448
823
  (*info->fprintf_func) (info->stream, "%d,%s,%d",
449
823
             x, get_reg_name (minfop, y), z);
450
1.36k
      else
451
1.36k
  (*info->fprintf_func) (info->stream, "%d,%s,%s",
452
1.36k
             x, get_reg_name (minfop, y),
453
1.36k
             get_reg_name (minfop, z));
454
2.18k
      break;
455
456
1.43k
    case mmix_operands_neg:
457
      /* Like NEG and NEGU - "$X,Y,$Z|Z".  */
458
1.43k
      if (insn & INSN_IMMEDIATE_BIT)
459
695
  (*info->fprintf_func) (info->stream, "%s,%d,%d",
460
695
             get_reg_name (minfop, x), y, z);
461
740
      else
462
740
  (*info->fprintf_func) (info->stream, "%s,%d,%s",
463
740
             get_reg_name (minfop, x), y,
464
740
             get_reg_name (minfop, z));
465
1.43k
      break;
466
467
1.34k
    case mmix_operands_pushj:
468
11.3k
    case mmix_operands_regaddr:
469
      /* Like GETA or branches - "$X,Address".  */
470
11.3k
      {
471
11.3k
  bfd_signed_vma offset = (y * 256 + z) * 4;
472
473
11.3k
  if (insn & INSN_BACKWARD_OFFSET_BIT)
474
6.18k
    offset -= 65536 * 4;
475
476
11.3k
  info->target = memaddr + offset;
477
478
11.3k
  (*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x));
479
11.3k
  (*info->print_address_func) (memaddr + offset, info);
480
11.3k
      }
481
11.3k
      break;
482
483
94
    case mmix_operands_get:
484
      /* GET - "X,spec_reg".  */
485
94
      (*info->fprintf_func) (info->stream, "%s,%s",
486
94
           get_reg_name (minfop, x),
487
94
           get_spec_reg_name (minfop, z));
488
94
      break;
489
490
93
    case mmix_operands_put:
491
      /* PUT - "spec_reg,$Z|Z".  */
492
93
      if (insn & INSN_IMMEDIATE_BIT)
493
18
  (*info->fprintf_func) (info->stream, "%s,%d",
494
18
             get_spec_reg_name (minfop, x), z);
495
75
      else
496
75
  (*info->fprintf_func) (info->stream, "%s,%s",
497
75
             get_spec_reg_name (minfop, x),
498
75
             get_reg_name (minfop, z));
499
93
      break;
500
501
22
    case mmix_operands_set:
502
      /*  Two registers, "$X,$Y".  */
503
22
      (*info->fprintf_func) (info->stream, "%s,%s",
504
22
           get_reg_name (minfop, x),
505
22
           get_reg_name (minfop, y));
506
22
      break;
507
508
13
    case mmix_operands_save:
509
      /* SAVE - "$X,0".  */
510
13
      (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
511
13
      break;
512
513
16
    case mmix_operands_unsave:
514
      /* UNSAVE - "0,$Z".  */
515
16
      (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
516
16
      break;
517
518
31.7k
    case mmix_operands_xyz_opt:
519
      /* Like SWYM or TRAP - "X,Y,Z".  */
520
31.7k
      (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
521
31.7k
      break;
522
523
18
    case mmix_operands_resume:
524
      /* Just "Z", like RESUME.  */
525
18
      (*info->fprintf_func) (info->stream, "%d", z);
526
18
      break;
527
528
0
    default:
529
0
      (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
530
0
           opcodep->operands);
531
0
      break;
532
103k
    }
533
534
103k
  return 4;
535
103k
}