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