xref: /netbsd-src/external/gpl3/gdb/dist/opcodes/mmix-dis.c (revision 4adfa041e14dc071f6d5e464e5b88c9721db4b02)
1 /* mmix-dis.c -- Disassemble MMIX instructions.
2    Copyright (C) 2000-2024 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   do								\
32    {								\
33      opcodes_error_handler (_("bad case %d (%s) in %s:%d"),	\
34 			    x, #x, __FILE__, __LINE__);		\
35      abort ();							\
36    }								\
37  while (0)
38 
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    }								\
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 
57 #define MAX_REG_NAME_LEN       256
58 #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 {
75   struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info));
76   long i;
77 
78   if (minfop == NULL)
79     return false;
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;
101 
102 	  if (syms == NULL)
103 	    {
104 	      FATAL_DEBUG;
105 	      free (minfop);
106 	      return false;
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
116 		  && syms[i]->value < MAX_REG_NAME_LEN
117 		  && minfop->reg_name[syms[i]->value] == NULL)
118 		minfop->reg_name[syms[i]->value] = syms[i]->name;
119 	    }
120 	  free (syms);
121 	}
122     }
123 
124   /* Fill in the rest with the canonical names.  */
125   for (i = 0; i < MAX_REG_NAME_LEN; i++)
126     if (minfop->reg_name[i] == NULL)
127       {
128 	sprintf (minfop->basic_reg_name[i], "$%ld", i);
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 
136   info->private_data = (void *) minfop;
137   return true;
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 
147 static const struct mmix_opcode *
148 get_opcode (unsigned long insn)
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;
153 
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;
211 
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 
244 static inline const char *
245 get_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 
252 static inline const char *
253 get_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 
260 /* The main disassembly function.  */
261 
262 int
263 print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
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",
355 			     get_reg_name (minfop, x),
356 			     get_reg_name (minfop, y),
357 			     get_reg_name (minfop, z));
358       break;
359 
360     case mmix_operands_reg_yz:
361       /* Like SETH - "$X,YZ".  */
362       (*info->fprintf_func) (info->stream, "%s,0x%x",
363 			     get_reg_name (minfop, x), y * 256 + z);
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",
372 			       get_reg_name (minfop, x),
373 			       get_reg_name (minfop, y), z);
374       else
375 	(*info->fprintf_func) (info->stream, "%s,%s,%s",
376 			       get_reg_name (minfop, x),
377 			       get_reg_name (minfop, y),
378 			       get_reg_name (minfop, z));
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",
401 				   get_reg_name (minfop, x),
402 				   ROUND_MODE (y), z);
403 	  else
404 	    (*info->fprintf_func) (info->stream, "%s,%s,%s",
405 				   get_reg_name (minfop, x),
406 				   ROUND_MODE (y),
407 				   get_reg_name (minfop, z));
408 	}
409       else
410 	{
411 	  if (insn & INSN_IMMEDIATE_BIT)
412 	    (*info->fprintf_func) (info->stream, "%s,%d",
413 				   get_reg_name (minfop, x), z);
414 	  else
415 	    (*info->fprintf_func) (info->stream, "%s,%s",
416 				   get_reg_name (minfop, x),
417 				   get_reg_name (minfop, z));
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",
431 			       get_reg_name (minfop, x),
432 			       ROUND_MODE (y),
433 			       get_reg_name (minfop, z));
434       else
435 	(*info->fprintf_func) (info->stream, "%s,%s",
436 			       get_reg_name (minfop, x),
437 			       get_reg_name (minfop, z));
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",
450 			       x, get_reg_name (minfop, y), z);
451       else
452 	(*info->fprintf_func) (info->stream, "%d,%s,%s",
453 			       x, get_reg_name (minfop, y),
454 			       get_reg_name (minfop, z));
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",
461 			       get_reg_name (minfop, x), y, z);
462       else
463 	(*info->fprintf_func) (info->stream, "%s,%d,%s",
464 			       get_reg_name (minfop, x), y,
465 			       get_reg_name (minfop, z));
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 
479 	(*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x));
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",
487 			     get_reg_name (minfop, x),
488 			     get_spec_reg_name (minfop, z));
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",
495 			       get_spec_reg_name (minfop, x), z);
496       else
497 	(*info->fprintf_func) (info->stream, "%s,%s",
498 			       get_spec_reg_name (minfop, x),
499 			       get_reg_name (minfop, z));
500       break;
501 
502     case mmix_operands_set:
503       /*  Two registers, "$X,$Y".  */
504       (*info->fprintf_func) (info->stream, "%s,%s",
505 			     get_reg_name (minfop, x),
506 			     get_reg_name (minfop, y));
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 }
537