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