update copyright dates
[external/binutils.git] / opcodes / mmix-dis.c
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   int 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           long i;
102
103           if (syms == NULL)
104             {
105               FATAL_DEBUG;
106               free (minfop);
107               return FALSE;
108             }
109           nsyms = bfd_canonicalize_symtab (abfd, syms);
110
111           /* We use the first name for a register.  If this is MMO, then
112              it's the name with the first sequence number, presumably the
113              first in the source.  */
114           for (i = 0; i < nsyms && syms[i] != NULL; i++)
115             {
116               if (syms[i]->section == reg_section
117                   && syms[i]->value < 256
118                   && minfop->reg_name[syms[i]->value] == NULL)
119                 minfop->reg_name[syms[i]->value] = syms[i]->name;
120             }
121         }
122     }
123
124   /* Fill in the rest with the canonical names.  */
125   for (i = 0; i < 256; i++)
126     if (minfop->reg_name[i] == NULL)
127       {
128         sprintf (minfop->basic_reg_name[i], "$%d", 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 /* The main disassembly function.  */
245
246 int
247 print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info)
248 {
249   unsigned char buffer[4];
250   unsigned long insn;
251   unsigned int x, y, z;
252   const struct mmix_opcode *opcodep;
253   int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
254   struct mmix_dis_info *minfop;
255
256   if (status != 0)
257     {
258       (*info->memory_error_func) (status, memaddr, info);
259       return -1;
260     }
261
262   /* FIXME: Is -1 suitable?  */
263   if (info->private_data == NULL
264       && ! initialize_mmix_dis_info (info))
265     return -1;
266
267   minfop = (struct mmix_dis_info *) info->private_data;
268   x = buffer[1];
269   y = buffer[2];
270   z = buffer[3];
271
272   insn = bfd_getb32 (buffer);
273
274   opcodep = get_opcode (insn);
275
276   if (opcodep == NULL)
277     {
278       (*info->fprintf_func) (info->stream, _("*unknown*"));
279       return 4;
280     }
281
282   (*info->fprintf_func) (info->stream, "%s ", opcodep->name);
283
284   /* Present bytes in the order they are laid out in memory.  */
285   info->display_endian = BFD_ENDIAN_BIG;
286
287   info->insn_info_valid = 1;
288   info->bytes_per_chunk = 4;
289   info->branch_delay_insns = 0;
290   info->target = 0;
291   switch (opcodep->type)
292     {
293     case mmix_type_normal:
294     case mmix_type_memaccess_block:
295       info->insn_type = dis_nonbranch;
296       break;
297
298     case mmix_type_branch:
299       info->insn_type = dis_branch;
300       break;
301
302     case mmix_type_condbranch:
303       info->insn_type = dis_condbranch;
304       break;
305
306     case mmix_type_memaccess_octa:
307       info->insn_type = dis_dref;
308       info->data_size = 8;
309       break;
310
311     case mmix_type_memaccess_tetra:
312       info->insn_type = dis_dref;
313       info->data_size = 4;
314       break;
315
316     case mmix_type_memaccess_wyde:
317       info->insn_type = dis_dref;
318       info->data_size = 2;
319       break;
320
321     case mmix_type_memaccess_byte:
322       info->insn_type = dis_dref;
323       info->data_size = 1;
324       break;
325
326     case mmix_type_jsr:
327       info->insn_type = dis_jsr;
328       break;
329
330     default:
331       BAD_CASE(opcodep->type);
332     }
333
334   switch (opcodep->operands)
335     {
336     case mmix_operands_regs:
337       /*  All registers: "$X,$Y,$Z".  */
338       (*info->fprintf_func) (info->stream, "%s,%s,%s",
339                              minfop->reg_name[x],
340                              minfop->reg_name[y],
341                              minfop->reg_name[z]);
342       break;
343
344     case mmix_operands_reg_yz:
345       /* Like SETH - "$X,YZ".  */
346       (*info->fprintf_func) (info->stream, "%s,0x%x",
347                              minfop->reg_name[x], y * 256 + z);
348       break;
349
350     case mmix_operands_regs_z_opt:
351     case mmix_operands_regs_z:
352     case mmix_operands_pushgo:
353       /* The regular "$X,$Y,$Z|Z".  */
354       if (insn & INSN_IMMEDIATE_BIT)
355         (*info->fprintf_func) (info->stream, "%s,%s,%d",
356                                minfop->reg_name[x], minfop->reg_name[y], z);
357       else
358         (*info->fprintf_func) (info->stream, "%s,%s,%s",
359                                minfop->reg_name[x],
360                                minfop->reg_name[y],
361                                minfop->reg_name[z]);
362       break;
363
364     case mmix_operands_jmp:
365       /* Address; only JMP.  */
366       {
367         bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4;
368
369         if (insn & INSN_BACKWARD_OFFSET_BIT)
370           offset -= (256 * 65536) * 4;
371
372         info->target = memaddr + offset;
373         (*info->print_address_func) (memaddr + offset, info);
374       }
375       break;
376
377     case mmix_operands_roundregs_z:
378       /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z"
379          "$X,ROUND_MODE,$Z|Z".  */
380       if (y != 0)
381         {
382           if (insn & INSN_IMMEDIATE_BIT)
383             (*info->fprintf_func) (info->stream, "%s,%s,%d",
384                                    minfop->reg_name[x],
385                                    ROUND_MODE (y), z);
386           else
387             (*info->fprintf_func) (info->stream, "%s,%s,%s",
388                                    minfop->reg_name[x],
389                                    ROUND_MODE (y),
390                                    minfop->reg_name[z]);
391         }
392       else
393         {
394           if (insn & INSN_IMMEDIATE_BIT)
395             (*info->fprintf_func) (info->stream, "%s,%d",
396                                    minfop->reg_name[x], z);
397           else
398             (*info->fprintf_func) (info->stream, "%s,%s",
399                                    minfop->reg_name[x],
400                                    minfop->reg_name[z]);
401         }
402       break;
403
404     case mmix_operands_pop:
405       /* Like POP - "X,YZ".  */
406       (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z);
407       break;
408
409     case mmix_operands_roundregs:
410       /* Two registers, possibly with rounding: "$X,$Z" or
411          "$X,ROUND_MODE,$Z".  */
412       if (y != 0)
413         (*info->fprintf_func) (info->stream, "%s,%s,%s",
414                                minfop->reg_name[x],
415                                ROUND_MODE (y),
416                                minfop->reg_name[z]);
417       else
418         (*info->fprintf_func) (info->stream, "%s,%s",
419                                minfop->reg_name[x],
420                                minfop->reg_name[z]);
421       break;
422
423     case mmix_operands_sync:
424         /* Like SYNC - "XYZ".  */
425       (*info->fprintf_func) (info->stream, "%u",
426                              x * 65536 + y * 256 + z);
427       break;
428
429     case mmix_operands_x_regs_z:
430       /* Like SYNCD - "X,$Y,$Z|Z".  */
431       if (insn & INSN_IMMEDIATE_BIT)
432         (*info->fprintf_func) (info->stream, "%d,%s,%d",
433                                x, minfop->reg_name[y], z);
434       else
435         (*info->fprintf_func) (info->stream, "%d,%s,%s",
436                                x, minfop->reg_name[y],
437                                minfop->reg_name[z]);
438       break;
439
440     case mmix_operands_neg:
441       /* Like NEG and NEGU - "$X,Y,$Z|Z".  */
442       if (insn & INSN_IMMEDIATE_BIT)
443         (*info->fprintf_func) (info->stream, "%s,%d,%d",
444                                minfop->reg_name[x], y, z);
445       else
446         (*info->fprintf_func) (info->stream, "%s,%d,%s",
447                                minfop->reg_name[x], y,
448                                minfop->reg_name[z]);
449       break;
450
451     case mmix_operands_pushj:
452     case mmix_operands_regaddr:
453       /* Like GETA or branches - "$X,Address".  */
454       {
455         bfd_signed_vma offset = (y * 256 + z) * 4;
456
457         if (insn & INSN_BACKWARD_OFFSET_BIT)
458           offset -= 65536 * 4;
459
460         info->target = memaddr + offset;
461
462         (*info->fprintf_func) (info->stream, "%s,", minfop->reg_name[x]);
463         (*info->print_address_func) (memaddr + offset, info);
464       }
465       break;
466
467     case mmix_operands_get:
468       /* GET - "X,spec_reg".  */
469       (*info->fprintf_func) (info->stream, "%s,%s",
470                              minfop->reg_name[x],
471                              minfop->spec_reg_name[z]);
472       break;
473
474     case mmix_operands_put:
475       /* PUT - "spec_reg,$Z|Z".  */
476       if (insn & INSN_IMMEDIATE_BIT)
477         (*info->fprintf_func) (info->stream, "%s,%d",
478                                minfop->spec_reg_name[x], z);
479       else
480         (*info->fprintf_func) (info->stream, "%s,%s",
481                                minfop->spec_reg_name[x],
482                                minfop->reg_name[z]);
483       break;
484
485     case mmix_operands_set:
486       /*  Two registers, "$X,$Y".  */
487       (*info->fprintf_func) (info->stream, "%s,%s",
488                              minfop->reg_name[x],
489                              minfop->reg_name[y]);
490       break;
491
492     case mmix_operands_save:
493       /* SAVE - "$X,0".  */
494       (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]);
495       break;
496
497     case mmix_operands_unsave:
498       /* UNSAVE - "0,$Z".  */
499       (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]);
500       break;
501
502     case mmix_operands_xyz_opt:
503       /* Like SWYM or TRAP - "X,Y,Z".  */
504       (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z);
505       break;
506
507     case mmix_operands_resume:
508       /* Just "Z", like RESUME.  */
509       (*info->fprintf_func) (info->stream, "%d", z);
510       break;
511
512     default:
513       (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"),
514                              opcodep->operands);
515       break;
516     }
517
518   return 4;
519 }