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