2011-05-24 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
[platform/upstream/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   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 }