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