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