]> rtime.felk.cvut.cz Git - fpga/lx-cpu1/binutils-tumbl.git/blob - opcodes/microblaze-dis.c
MBTumbl: Fix imm5 compilation bug (missing macro) and nuke delay bit jumps
[fpga/lx-cpu1/binutils-tumbl.git] / opcodes / microblaze-dis.c
1 /* Disassemble Xilinx microblaze instructions.
2
3    Copyright 2009, 2012 Free Software Foundation, Inc.
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
19    Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
20    MA 02110-1301, USA.  */
21
22
23 #include "sysdep.h"
24 #define STATIC_TABLE
25 #define DEFINE_TABLE
26
27 #include "dis-asm.h"
28 #include <strings.h>
29 #include "microblaze-opc.h"
30 #include "microblaze-dis.h"
31
32 #define get_field_rd(instr)        get_field (instr, RD_MASK, RD_LOW)
33 #define get_field_r1(instr)        get_field (instr, RA_MASK, RA_LOW)
34 #define get_field_r2(instr)        get_field (instr, RB_MASK, RB_LOW)
35 #define get_int_field_imm(instr)   ((instr & IMM_MASK) >> IMM_LOW)
36 #define get_int_field_imm5(instr)  ((instr & IMM5_MASK) >> IMM_LOW)
37 #define get_int_field_r1(instr)    ((instr & RA_MASK) >> RA_LOW)
38
39
40
41 static char *
42 get_field (long instr, long mask, unsigned short low)
43 {
44   char tmpstr[25];
45
46   sprintf (tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low));
47   return (strdup (tmpstr));
48 }
49
50 static char *
51 get_field_imm (long instr)
52 {
53   char tmpstr[25];
54
55   sprintf (tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
56   return (strdup (tmpstr));
57 }
58
59 static char *
60 get_field_imm5 (long instr)
61 {
62   char tmpstr[25];
63
64   sprintf (tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
65   return (strdup (tmpstr));
66 }
67
68 #ifndef ARCH_mbtumbl
69
70 static char *
71 get_field_rfsl (long instr)
72 {
73   char tmpstr[25];
74
75   sprintf (tmpstr, "%s%d", fsl_register_prefix,
76            (short)((instr & RFSL_MASK) >> IMM_LOW));
77   return (strdup (tmpstr));
78 }
79
80 static char *
81 get_field_imm15 (long instr)
82 {
83   char tmpstr[25];
84
85   sprintf (tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
86   return (strdup (tmpstr));
87 }
88
89 #endif
90
91 static char *
92 get_field_special (long instr, struct op_code_struct * op)
93 {
94   char tmpstr[25];
95   char spr[6];
96
97   switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
98     {
99     case REG_MSR_MASK :
100       strcpy (spr, "msr");
101       break;
102     case REG_PC_MASK :
103       strcpy (spr, "pc");
104       break;
105 #ifndef ARCH_mbtumbl
106     case REG_EAR_MASK :
107       strcpy (spr, "ear");
108       break;
109     case REG_ESR_MASK :
110       strcpy (spr, "esr");
111       break;
112     case REG_FSR_MASK :
113       strcpy (spr, "fsr");
114       break;
115     case REG_BTR_MASK :
116       strcpy (spr, "btr");
117       break;
118     case REG_EDR_MASK :
119       strcpy (spr, "edr");
120       break;
121     case REG_PID_MASK :
122       strcpy (spr, "pid");
123       break;
124     case REG_ZPR_MASK :
125       strcpy (spr, "zpr");
126       break;
127     case REG_TLBX_MASK :
128       strcpy (spr, "tlbx");
129       break;
130     case REG_TLBLO_MASK :
131       strcpy (spr, "tlblo");
132       break;
133     case REG_TLBHI_MASK :
134       strcpy (spr, "tlbhi");
135       break;
136     case REG_TLBSX_MASK :
137       strcpy (spr, "tlbsx");
138       break;
139     default :
140       if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
141           == REG_PVR_MASK)
142         {
143           sprintf (tmpstr, "%spvr%d", register_prefix,
144                    (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
145                                     ^ op->immval_mask) ^ REG_PVR_MASK);
146           return (strdup (tmpstr));
147         }
148       else
149         strcpy (spr, "pc");
150       break;
151 #else
152     default :
153         strcpy (spr, "pc");
154       break;
155     }
156 #endif
157
158    sprintf (tmpstr, "%s%s", register_prefix, spr);
159    return (strdup (tmpstr));
160 }
161
162 static unsigned long
163 read_insn_microblaze (bfd_vma memaddr,
164                       struct disassemble_info *info,
165                       struct op_code_struct **opr)
166 {
167   unsigned char       ibytes[4];
168   int                 status;
169   struct op_code_struct * op;
170   unsigned long inst;
171
172   status = info->read_memory_func (memaddr, ibytes, 4, info);
173
174   if (status != 0)
175     {
176       info->memory_error_func (status, memaddr, info);
177       return 0;
178     }
179
180   if (info->endian == BFD_ENDIAN_BIG)
181     inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
182   else if (info->endian == BFD_ENDIAN_LITTLE)
183     inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
184   else
185     abort ();
186
187   /* Just a linear search of the table.  */
188   for (op = opcodes; op->name != 0; op ++)
189     if (op->bit_sequence == (inst & op->opcode_mask))
190       break;
191
192   *opr = op;
193   return inst;
194 }
195
196
197 int
198 print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
199 {
200   fprintf_ftype       print_func = info->fprintf_func;
201   void *              stream = info->stream;
202   unsigned long       inst, prev_inst;
203   struct op_code_struct * op, *pop;
204   int                 immval = 0;
205   bfd_boolean         immfound = FALSE;
206   static bfd_vma      prev_insn_addr = -1; /* Init the prev insn addr.  */
207   static int          prev_insn_vma = -1;  /* Init the prev insn vma.  */
208   int                 curr_insn_vma = info->buffer_vma;
209
210   info->bytes_per_chunk = 4;
211
212   inst = read_insn_microblaze (memaddr, info, &op);
213   if (inst == 0)
214     return -1;
215
216   if (prev_insn_vma == curr_insn_vma)
217     {
218       if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
219         {
220           prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
221           if (prev_inst == 0)
222             return -1;
223           if (pop->instr == imm)
224             {
225               immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
226               immfound = TRUE;
227             }
228           else
229             {
230               immval = 0;
231               immfound = FALSE;
232             }
233         }
234     }
235
236   /* Make curr insn as prev insn.  */
237   prev_insn_addr = memaddr;
238   prev_insn_vma = curr_insn_vma;
239
240   if (op->name == NULL)
241     print_func (stream, ".short 0x%04x", (unsigned int) inst);
242   else
243     {
244       print_func (stream, "%s", op->name);
245
246       switch (op->inst_type)
247         {
248         case INST_TYPE_RD_R1_R2:
249           print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
250                    get_field_r1(inst), get_field_r2 (inst));
251           break;
252         case INST_TYPE_RD_R1_IMM:
253           print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
254                    get_field_r1(inst), get_field_imm (inst));
255           if (info->print_address_func && get_int_field_r1 (inst) == 0
256               && info->symbol_at_address_func)
257             {
258               if (immfound)
259                 immval |= (get_int_field_imm (inst) & 0x0000ffff);
260               else
261                 {
262                   immval = get_int_field_imm (inst);
263                   if (immval & 0x8000)
264                     immval |= 0xFFFF0000;
265                 }
266               if (immval > 0 && info->symbol_at_address_func (immval, info))
267                 {
268                   print_func (stream, "\t// ");
269                   info->print_address_func (immval, info);
270                 }
271             }
272           break;
273         case INST_TYPE_RD_R1_IMM5:
274           print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
275                    get_field_r1(inst), get_field_imm5 (inst));
276           break;
277 #ifndef ARCH_mbtumbl
278         case INST_TYPE_RD_RFSL:
279           print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_rfsl (inst));
280           break;
281         case INST_TYPE_R1_RFSL:
282           print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_rfsl (inst));
283           break;
284 #endif
285         case INST_TYPE_RD_SPECIAL:
286           print_func (stream, "\t%s, %s", get_field_rd (inst),
287                    get_field_special (inst, op));
288           break;
289         case INST_TYPE_SPECIAL_R1:
290           print_func (stream, "\t%s, %s", get_field_special (inst, op),
291                    get_field_r1(inst));
292           break;
293         case INST_TYPE_RD_R1:
294           print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r1 (inst));
295           break;
296         case INST_TYPE_R1_R2:
297           print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
298           break;
299         case INST_TYPE_R1_IMM:
300           print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_imm (inst));
301           /* The non-pc relative instructions are returns, which shouldn't
302              have a label printed.  */
303           if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
304               && info->symbol_at_address_func)
305             {
306               if (immfound)
307                 immval |= (get_int_field_imm (inst) & 0x0000ffff);
308               else
309                 {
310                   immval = get_int_field_imm (inst);
311                   if (immval & 0x8000)
312                     immval |= 0xFFFF0000;
313                 }
314               immval += memaddr;
315               if (immval > 0 && info->symbol_at_address_func (immval, info))
316                 {
317                   print_func (stream, "\t// ");
318                   info->print_address_func (immval, info);
319                 }
320               else
321                 {
322                   print_func (stream, "\t\t// ");
323                   print_func (stream, "%x", immval);
324                 }
325             }
326           break;
327         case INST_TYPE_RD_IMM:
328           print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm (inst));
329           if (info->print_address_func && info->symbol_at_address_func)
330             {
331             if (immfound)
332               immval |= (get_int_field_imm (inst) & 0x0000ffff);
333             else
334               {
335                 immval = get_int_field_imm (inst);
336                 if (immval & 0x8000)
337                   immval |= 0xFFFF0000;
338               }
339             if (op->inst_offset_type == INST_PC_OFFSET)
340               immval += (int) memaddr;
341             if (info->symbol_at_address_func (immval, info))
342               {
343                 print_func (stream, "\t// ");
344                 info->print_address_func (immval, info);
345               }
346             }
347           break;
348         case INST_TYPE_IMM:
349         case INST_TYPE_IMM5:
350           print_func (stream, "\t%s", get_field_imm (inst));
351           if (info->print_address_func && info->symbol_at_address_func
352               && op->instr != imm)
353             {
354               if ((immfound) && (op->inst_type != INST_TYPE_IMM5))
355                 immval |= (get_int_field_imm (inst) & 0x0000ffff);
356               else
357                 {
358           if (op->inst_type == INST_TYPE_IMM5)
359             immval = get_int_field_imm5 (inst);
360           else
361           {
362                   immval = get_int_field_imm (inst);
363                   if (immval & 0x8000)
364                           immval |= 0xFFFF0000;
365                 }
366     }
367               if (op->inst_offset_type == INST_PC_OFFSET)
368                 immval += (int) memaddr;
369               if (immval > 0 && info->symbol_at_address_func (immval, info))
370                 {
371                   print_func (stream, "\t// ");
372                   info->print_address_func (immval, info);
373                 }
374               else if (op->inst_offset_type == INST_PC_OFFSET)
375                 {
376                   print_func (stream, "\t\t// ");
377                   print_func (stream, "%x", immval);
378                 }
379             }
380           break;
381         case INST_TYPE_RD_R2:
382           print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
383           break;
384         case INST_TYPE_R2:
385           print_func (stream, "\t%s", get_field_r2 (inst));
386           break;
387         case INST_TYPE_R1:
388           print_func (stream, "\t%s", get_field_r1 (inst));
389           break;
390         case INST_TYPE_RD_R1_SPECIAL:
391           print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
392           break;
393 #ifndef ARCH_mbtumbl
394         case INST_TYPE_RD_IMM15:
395           print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm15 (inst));
396           break;
397         /* For tuqula instruction */
398         case INST_TYPE_RD:
399           print_func (stream, "\t%s", get_field_rd (inst));
400           break;
401         case INST_TYPE_RFSL:
402           print_func (stream, "\t%s", get_field_rfsl (inst));
403           break;
404 #endif
405         default:
406           /* If the disassembler lags the instruction set.  */
407           print_func (stream, "\tundecoded operands, inst is 0x%04x", (unsigned int) inst);
408           break;
409         }
410     }
411
412   /* Say how many bytes we consumed.  */
413   return 4;
414 }
415
416 enum microblaze_instr
417 get_insn_microblaze (long inst,
418                      bfd_boolean *isunsignedimm,
419                      enum microblaze_instr_type *insn_type,
420                      short *delay_slots)
421 {
422   struct op_code_struct * op;
423   *isunsignedimm = FALSE;
424
425   /* Just a linear search of the table.  */
426   for (op = opcodes; op->name != 0; op ++)
427     if (op->bit_sequence == (inst & op->opcode_mask))
428       break;
429
430   if (op->name == 0)
431     return invalid_inst;
432   else
433     {
434       *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
435       *insn_type = op->instr_type;
436       *delay_slots = op->delay_slots;
437       return op->instr;
438     }
439 }
440
441 enum microblaze_instr
442 microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed)
443 {
444   enum microblaze_instr op;
445   bfd_boolean t1;
446   enum microblaze_instr_type t2;
447   short t3;
448
449   op = get_insn_microblaze (insn, &t1, &t2, &t3);
450   *rd = (insn & RD_MASK) >> RD_LOW;
451   *ra = (insn & RA_MASK) >> RA_LOW;
452   *rb = (insn & RB_MASK) >> RB_LOW;
453   t3 = (insn & IMM_MASK) >> IMM_LOW;
454   *immed = (int) t3;
455   return (op);
456 }
457
458 unsigned long
459 microblaze_get_target_address (long inst, bfd_boolean immfound, int immval,
460                                long pcval, long r1val, long r2val,
461                                bfd_boolean *targetvalid,
462                                bfd_boolean *unconditionalbranch)
463 {
464   struct op_code_struct * op;
465   long targetaddr = 0;
466
467   *unconditionalbranch = FALSE;
468   /* Just a linear search of the table.  */
469   for (op = opcodes; op->name != 0; op ++)
470     if (op->bit_sequence == (inst & op->opcode_mask))
471       break;
472
473   if (op->name == 0)
474     {
475       *targetvalid = FALSE;
476     }
477   else if (op->instr_type == branch_inst)
478     {
479       switch (op->inst_type)
480         {
481         case INST_TYPE_R2:
482           *unconditionalbranch = TRUE;
483         /* Fall through.  */
484         case INST_TYPE_RD_R2:
485         case INST_TYPE_R1_R2:
486           targetaddr = r2val;
487           *targetvalid = TRUE;
488           if (op->inst_offset_type == INST_PC_OFFSET)
489             targetaddr += pcval;
490           break;
491         case INST_TYPE_IMM:
492           *unconditionalbranch = TRUE;
493         /* Fall through.  */
494         case INST_TYPE_RD_IMM:
495         case INST_TYPE_R1_IMM:
496           if (immfound)
497             {
498               targetaddr = (immval << 16) & 0xffff0000;
499               targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
500             }
501           else
502             {
503               targetaddr = get_int_field_imm (inst);
504               if (targetaddr & 0x8000)
505                 targetaddr |= 0xFFFF0000;
506             }
507           if (op->inst_offset_type == INST_PC_OFFSET)
508             targetaddr += pcval;
509           *targetvalid = TRUE;
510           break;
511         default:
512           *targetvalid = FALSE;
513           break;
514         }
515     }
516   else if (op->instr_type == return_inst)
517     {
518       if (immfound)
519         {
520           targetaddr = (immval << 16) & 0xffff0000;
521           targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
522         }
523       else
524         {
525           targetaddr = get_int_field_imm (inst);
526           if (targetaddr & 0x8000)
527             targetaddr |= 0xFFFF0000;
528         }
529       targetaddr += r1val;
530       *targetvalid = TRUE;
531     }
532   else
533     *targetvalid = FALSE;
534   return targetaddr;
535 }