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