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