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