Coverage Report

Created: 2023-08-28 06:30

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