Home | History | Annotate | Line # | Download | only in opcodes
      1 /* Disassemble Xilinx microblaze instructions.
      2 
      3    Copyright (C) 2009-2025 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 *
     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 *
     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 *
     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 *
     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 *
     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 *
     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 *
    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 *
    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 *
    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
    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
    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 |= (~0xFFFF);
    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 |= (~0xFFFF);
    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 |= (~0xFFFF);
    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 |= (~0xFFFF);
    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
    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
    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
    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) & (~0xffff);
    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 |= (~0xFFFF);
    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) & (~0xffff);
    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 |= (~0xFFFF);
    584 	}
    585       targetaddr += r1val;
    586       *targetvalid = true;
    587     }
    588   else
    589     *targetvalid = false;
    590   return targetaddr;
    591 }
    592