Home | History | Annotate | Download | only in opcodes
      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 *
     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_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 *
     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 *
     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 *
     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
    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
    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
    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
    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
    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