]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - opcodes/mmix-dis.c
mmix disassemble memory leak
[thirdparty/binutils-gdb.git] / opcodes / mmix-dis.c
CommitLineData
afd30973 1/* mmix-dis.c -- Disassemble MMIX instructions.
fd67aa11 2 Copyright (C) 2000-2024 Free Software Foundation, Inc.
afd30973
HPN
3 Written by Hans-Peter Nilsson (hp@bitrange.com)
4
9b201bb5 5 This file is part of the GNU opcodes library.
afd30973 6
9b201bb5
NC
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.
afd30973 11
9b201bb5
NC
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.
afd30973 16
47b0e7ad
NC
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. */
afd30973 21
5eb3690e 22#include "sysdep.h"
afd30973 23#include <stdio.h>
afd30973 24#include "opcode/mmix.h"
88c1242d 25#include "disassemble.h"
afd30973
HPN
26#include "libiberty.h"
27#include "bfd.h"
28#include "opintl.h"
29
a6743a54
AM
30#define BAD_CASE(x) \
31 do \
32 { \
33 opcodes_error_handler (_("bad case %d (%s) in %s:%d"), \
34 x, #x, __FILE__, __LINE__); \
35 abort (); \
36 } \
afd30973
HPN
37 while (0)
38
a6743a54
AM
39#define FATAL_DEBUG \
40 do \
41 { \
42 opcodes_error_handler (_("internal: non-debugged code " \
43 "(test-case missing): %s:%d"), \
44 __FILE__, __LINE__); \
45 abort (); \
46 } \
afd30973
HPN
47 while (0)
48
49#define ROUND_MODE(n) \
50 ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" : \
51 (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" : \
52 _("(unknown)"))
53
54#define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24)
55#define INSN_BACKWARD_OFFSET_BIT (1 << 24)
56
1e4b5e7d
NC
57#define MAX_REG_NAME_LEN 256
58#define MAX_SPEC_REG_NAME_LEN 32
afd30973
HPN
59struct mmix_dis_info
60 {
1e4b5e7d
NC
61 const char *reg_name[MAX_REG_NAME_LEN];
62 const char *spec_reg_name[MAX_SPEC_REG_NAME_LEN];
afd30973
HPN
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. */
1e4b5e7d 67 char basic_reg_name[MAX_REG_NAME_LEN][sizeof ("$255")];
afd30973
HPN
68 };
69
afd30973
HPN
70/* Initialize a target-specific array in INFO. */
71
78933a4a 72static bool
47b0e7ad 73initialize_mmix_dis_info (struct disassemble_info *info)
afd30973
HPN
74{
75 struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
91d6fa6a 76 long i;
afd30973
HPN
77
78 if (minfop == NULL)
78933a4a 79 return false;
afd30973
HPN
80
81 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 if ((info->section != NULL && info->section->owner != NULL)
86 || (info->symbols != NULL
87 && info->symbols[0] != NULL
88 && bfd_asymbol_bfd (info->symbols[0]) != NULL))
89 {
90 bfd *abfd = info->section && info->section->owner != NULL
91 ? info->section->owner
92 : bfd_asymbol_bfd (info->symbols[0]);
93 asection *reg_section = bfd_get_section_by_name (abfd, "*REG*");
94
95 if (reg_section != NULL)
96 {
97 /* The returned symcount *does* include the ending NULL. */
98 long symsize = bfd_get_symtab_upper_bound (abfd);
99 asymbol **syms = malloc (symsize);
100 long nsyms;
afd30973
HPN
101
102 if (syms == NULL)
47b0e7ad
NC
103 {
104 FATAL_DEBUG;
afd30973 105 free (minfop);
78933a4a 106 return false;
afd30973
HPN
107 }
108 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 for (i = 0; i < nsyms && syms[i] != NULL; i++)
114 {
115 if (syms[i]->section == reg_section
1e4b5e7d 116 && syms[i]->value < MAX_REG_NAME_LEN
afd30973
HPN
117 && minfop->reg_name[syms[i]->value] == NULL)
118 minfop->reg_name[syms[i]->value] = syms[i]->name;
119 }
a1defbe4 120 free (syms);
afd30973
HPN
121 }
122 }
123
124 /* Fill in the rest with the canonical names. */
1e4b5e7d 125 for (i = 0; i < MAX_REG_NAME_LEN; i++)
afd30973
HPN
126 if (minfop->reg_name[i] == NULL)
127 {
91d6fa6a 128 sprintf (minfop->basic_reg_name[i], "$%ld", i);
afd30973
HPN
129 minfop->reg_name[i] = minfop->basic_reg_name[i];
130 }
131
132 /* We assume it's actually a one-to-one mapping of number-to-name. */
133 for (i = 0; mmix_spec_regs[i].name != NULL; i++)
134 minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name;
135
47b0e7ad 136 info->private_data = (void *) minfop;
78933a4a 137 return true;
afd30973
HPN
138}
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
147static const struct mmix_opcode *
47b0e7ad 148get_opcode (unsigned long insn)
afd30973
HPN
149{
150 static const struct mmix_opcode **opcodes = NULL;
151 const struct mmix_opcode *opcodep = mmix_opcodes;
152 unsigned int opcode_part = (insn >> 24) & 255;
47b0e7ad 153
afd30973
HPN
154 if (opcodes == NULL)
155 opcodes = xcalloc (256, sizeof (struct mmix_opcode *));
156
157 opcodep = opcodes[opcode_part];
158 if (opcodep == NULL
159 || (opcodep->match & insn) != opcodep->match
160 || (opcodep->lose & insn) != 0)
161 {
162 /* Search through the table. */
163 for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++)
164 {
165 /* FIXME: Break out this into an initialization function. */
166 if ((opcodep->match & (opcode_part << 24)) == opcode_part
167 && (opcodep->lose & (opcode_part << 24)) == 0)
168 opcodes[opcode_part] = opcodep;
169
170 if ((opcodep->match & insn) == opcodep->match
171 && (opcodep->lose & insn) == 0)
172 break;
173 }
174 }
175
176 if (opcodep->name == NULL)
177 return NULL;
178
179 /* Check constraints. If they don't match, loop through the next opcode
180 entries. */
181 do
182 {
183 switch (opcodep->operands)
184 {
185 /* These have no restraint on what can be in the lower three
186 bytes. */
187 case mmix_operands_regs:
188 case mmix_operands_reg_yz:
189 case mmix_operands_regs_z_opt:
190 case mmix_operands_regs_z:
191 case mmix_operands_jmp:
192 case mmix_operands_pushgo:
193 case mmix_operands_pop:
194 case mmix_operands_sync:
195 case mmix_operands_x_regs_z:
196 case mmix_operands_neg:
197 case mmix_operands_pushj:
198 case mmix_operands_regaddr:
199 case mmix_operands_get:
200 case mmix_operands_set:
201 case mmix_operands_save:
202 case mmix_operands_unsave:
203 case mmix_operands_xyz_opt:
204 return opcodep;
205
206 /* For a ROUND_MODE, the middle byte must be 0..4. */
207 case mmix_operands_roundregs_z:
208 case mmix_operands_roundregs:
209 {
210 int midbyte = (insn >> 8) & 255;
47b0e7ad 211
afd30973
HPN
212 if (midbyte <= 4)
213 return opcodep;
214 }
215 break;
216
217 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 if ((insn & INSN_IMMEDIATE_BIT)
221 || ((insn >> 16) & 255) < 32)
222 return opcodep;
223 break;
224
225 case mmix_operands_resume:
226 /* Middle bytes must be zero. */
227 if ((insn & 0x00ffff00) == 0)
228 return opcodep;
229 break;
230
231 default:
232 BAD_CASE (opcodep->operands);
233 }
234
235 opcodep++;
236 }
237 while ((opcodep->match & insn) == opcodep->match
238 && (opcodep->lose & insn) == 0);
239
240 /* If we got here, we had no match. */
241 return NULL;
242}
243
1e4b5e7d
NC
244static inline const char *
245get_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
246{
247 if (x >= MAX_REG_NAME_LEN)
248 return _("*illegal*");
249 return minfop->reg_name[x];
250}
251
252static inline const char *
253get_spec_reg_name (const struct mmix_dis_info * minfop, unsigned int x)
254{
255 if (x >= MAX_SPEC_REG_NAME_LEN)
256 return _("*illegal*");
257 return minfop->spec_reg_name[x];
258}
259
afd30973
HPN
260/* The main disassembly function. */
261
262int
47b0e7ad 263print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
afd30973
HPN
264{
265 unsigned char buffer[4];
266 unsigned long insn;
267 unsigned int x, y, z;
268 const struct mmix_opcode *opcodep;
269 int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
270 struct mmix_dis_info *minfop;
271
272 if (status != 0)
273 {
274 (*info->memory_error_func) (status, memaddr, info);
275 return -1;
276 }
277
278 /* FIXME: Is -1 suitable? */
279 if (info->private_data == NULL
280 && ! initialize_mmix_dis_info (info))
281 return -1;
282
283 minfop = (struct mmix_dis_info *) info->private_data;
284 x = buffer[1];
285 y = buffer[2];
286 z = buffer[3];
287
288 insn = bfd_getb32 (buffer);
289
290 opcodep = get_opcode (insn);
291
292 if (opcodep == NULL)
293 {
294 (*info->fprintf_func) (info->stream, _("*unknown*"));
295 return 4;
296 }
297
298 (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
299
300 /* Present bytes in the order they are laid out in memory. */
301 info->display_endian = BFD_ENDIAN_BIG;
302
303 info->insn_info_valid = 1;
304 info->bytes_per_chunk = 4;
305 info->branch_delay_insns = 0;
306 info->target = 0;
307 switch (opcodep->type)
308 {
309 case mmix_type_normal:
310 case mmix_type_memaccess_block:
311 info->insn_type = dis_nonbranch;
312 break;
313
314 case mmix_type_branch:
315 info->insn_type = dis_branch;
316 break;
317
318 case mmix_type_condbranch:
319 info->insn_type = dis_condbranch;
320 break;
321
322 case mmix_type_memaccess_octa:
323 info->insn_type = dis_dref;
324 info->data_size = 8;
325 break;
326
327 case mmix_type_memaccess_tetra:
328 info->insn_type = dis_dref;
329 info->data_size = 4;
330 break;
331
332 case mmix_type_memaccess_wyde:
333 info->insn_type = dis_dref;
334 info->data_size = 2;
335 break;
336
337 case mmix_type_memaccess_byte:
338 info->insn_type = dis_dref;
339 info->data_size = 1;
340 break;
341
342 case mmix_type_jsr:
343 info->insn_type = dis_jsr;
344 break;
345
346 default:
347 BAD_CASE(opcodep->type);
348 }
349
350 switch (opcodep->operands)
351 {
352 case mmix_operands_regs:
353 /* All registers: "$X,$Y,$Z". */
354 (*info->fprintf_func) (info->stream, "%s,%s,%s",
1e4b5e7d
NC
355 get_reg_name (minfop, x),
356 get_reg_name (minfop, y),
357 get_reg_name (minfop, z));
afd30973
HPN
358 break;
359
360 case mmix_operands_reg_yz:
361 /* Like SETH - "$X,YZ". */
362 (*info->fprintf_func) (info->stream, "%s,0x%x",
1e4b5e7d 363 get_reg_name (minfop, x), y * 256 + z);
afd30973
HPN
364 break;
365
366 case mmix_operands_regs_z_opt:
367 case mmix_operands_regs_z:
368 case mmix_operands_pushgo:
369 /* The regular "$X,$Y,$Z|Z". */
370 if (insn & INSN_IMMEDIATE_BIT)
371 (*info->fprintf_func) (info->stream, "%s,%s,%d",
1e4b5e7d
NC
372 get_reg_name (minfop, x),
373 get_reg_name (minfop, y), z);
afd30973
HPN
374 else
375 (*info->fprintf_func) (info->stream, "%s,%s,%s",
1e4b5e7d
NC
376 get_reg_name (minfop, x),
377 get_reg_name (minfop, y),
378 get_reg_name (minfop, z));
afd30973
HPN
379 break;
380
381 case mmix_operands_jmp:
382 /* Address; only JMP. */
383 {
384 bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
385
386 if (insn & INSN_BACKWARD_OFFSET_BIT)
387 offset -= (256 * 65536) * 4;
388
389 info->target = memaddr + offset;
390 (*info->print_address_func) (memaddr + offset, info);
391 }
392 break;
393
394 case mmix_operands_roundregs_z:
395 /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
396 "$X,ROUND_MODE,$Z|Z". */
397 if (y != 0)
398 {
399 if (insn & INSN_IMMEDIATE_BIT)
400 (*info->fprintf_func) (info->stream, "%s,%s,%d",
1e4b5e7d 401 get_reg_name (minfop, x),
afd30973
HPN
402 ROUND_MODE (y), z);
403 else
404 (*info->fprintf_func) (info->stream, "%s,%s,%s",
1e4b5e7d 405 get_reg_name (minfop, x),
afd30973 406 ROUND_MODE (y),
1e4b5e7d 407 get_reg_name (minfop, z));
afd30973
HPN
408 }
409 else
410 {
411 if (insn & INSN_IMMEDIATE_BIT)
412 (*info->fprintf_func) (info->stream, "%s,%d",
1e4b5e7d 413 get_reg_name (minfop, x), z);
afd30973
HPN
414 else
415 (*info->fprintf_func) (info->stream, "%s,%s",
1e4b5e7d
NC
416 get_reg_name (minfop, x),
417 get_reg_name (minfop, z));
afd30973
HPN
418 }
419 break;
420
421 case mmix_operands_pop:
422 /* Like POP - "X,YZ". */
423 (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
424 break;
425
426 case mmix_operands_roundregs:
427 /* Two registers, possibly with rounding: "$X,$Z" or
428 "$X,ROUND_MODE,$Z". */
429 if (y != 0)
430 (*info->fprintf_func) (info->stream, "%s,%s,%s",
1e4b5e7d 431 get_reg_name (minfop, x),
afd30973 432 ROUND_MODE (y),
1e4b5e7d 433 get_reg_name (minfop, z));
afd30973
HPN
434 else
435 (*info->fprintf_func) (info->stream, "%s,%s",
1e4b5e7d
NC
436 get_reg_name (minfop, x),
437 get_reg_name (minfop, z));
afd30973
HPN
438 break;
439
440 case mmix_operands_sync:
441 /* Like SYNC - "XYZ". */
442 (*info->fprintf_func) (info->stream, "%u",
443 x * 65536 + y * 256 + z);
444 break;
445
446 case mmix_operands_x_regs_z:
447 /* Like SYNCD - "X,$Y,$Z|Z". */
448 if (insn & INSN_IMMEDIATE_BIT)
449 (*info->fprintf_func) (info->stream, "%d,%s,%d",
1e4b5e7d 450 x, get_reg_name (minfop, y), z);
afd30973
HPN
451 else
452 (*info->fprintf_func) (info->stream, "%d,%s,%s",
1e4b5e7d
NC
453 x, get_reg_name (minfop, y),
454 get_reg_name (minfop, z));
afd30973
HPN
455 break;
456
457 case mmix_operands_neg:
458 /* Like NEG and NEGU - "$X,Y,$Z|Z". */
459 if (insn & INSN_IMMEDIATE_BIT)
460 (*info->fprintf_func) (info->stream, "%s,%d,%d",
1e4b5e7d 461 get_reg_name (minfop, x), y, z);
afd30973
HPN
462 else
463 (*info->fprintf_func) (info->stream, "%s,%d,%s",
1e4b5e7d
NC
464 get_reg_name (minfop, x), y,
465 get_reg_name (minfop, z));
afd30973
HPN
466 break;
467
468 case mmix_operands_pushj:
469 case mmix_operands_regaddr:
470 /* Like GETA or branches - "$X,Address". */
471 {
472 bfd_signed_vma offset = (y * 256 + z) * 4;
473
474 if (insn & INSN_BACKWARD_OFFSET_BIT)
475 offset -= 65536 * 4;
476
477 info->target = memaddr + offset;
478
1e4b5e7d 479 (*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x));
afd30973
HPN
480 (*info->print_address_func) (memaddr + offset, info);
481 }
482 break;
483
484 case mmix_operands_get:
485 /* GET - "X,spec_reg". */
486 (*info->fprintf_func) (info->stream, "%s,%s",
1e4b5e7d
NC
487 get_reg_name (minfop, x),
488 get_spec_reg_name (minfop, z));
afd30973
HPN
489 break;
490
491 case mmix_operands_put:
492 /* PUT - "spec_reg,$Z|Z". */
493 if (insn & INSN_IMMEDIATE_BIT)
494 (*info->fprintf_func) (info->stream, "%s,%d",
1e4b5e7d 495 get_spec_reg_name (minfop, x), z);
afd30973
HPN
496 else
497 (*info->fprintf_func) (info->stream, "%s,%s",
1e4b5e7d
NC
498 get_spec_reg_name (minfop, x),
499 get_reg_name (minfop, z));
afd30973
HPN
500 break;
501
502 case mmix_operands_set:
503 /* Two registers, "$X,$Y". */
504 (*info->fprintf_func) (info->stream, "%s,%s",
1e4b5e7d
NC
505 get_reg_name (minfop, x),
506 get_reg_name (minfop, y));
afd30973
HPN
507 break;
508
509 case mmix_operands_save:
510 /* SAVE - "$X,0". */
511 (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
512 break;
513
514 case mmix_operands_unsave:
515 /* UNSAVE - "0,$Z". */
516 (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
517 break;
518
519 case mmix_operands_xyz_opt:
520 /* Like SWYM or TRAP - "X,Y,Z". */
521 (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
522 break;
523
524 case mmix_operands_resume:
525 /* Just "Z", like RESUME. */
526 (*info->fprintf_func) (info->stream, "%d", z);
527 break;
528
529 default:
530 (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
531 opcodep->operands);
532 break;
533 }
534
535 return 4;
536}