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