1 /* Disassemble Xilinx microblaze instructions. 2 3 Copyright (C) 2009-2020 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 * 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_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 * 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 * 113 get_field_special (struct string_buf *buf, long instr, 114 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 185 read_insn_microblaze (bfd_vma memaddr, 186 struct disassemble_info *info, 187 struct op_code_struct **opr) 188 { 189 unsigned char ibytes[4]; 190 int status; 191 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 = 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 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 struct op_code_struct * op, *pop; 228 int immval = 0; 229 bfd_boolean 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 450 get_insn_microblaze (long inst, 451 bfd_boolean *isunsignedimm, 452 enum microblaze_instr_type *insn_type, 453 short *delay_slots) 454 { 455 struct op_code_struct * op; 456 *isunsignedimm = FALSE; 457 458 /* Just a linear search of the table. */ 459 for (op = 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 475 microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed) 476 { 477 enum microblaze_instr op; 478 bfd_boolean 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 492 microblaze_get_target_address (long inst, bfd_boolean immfound, int immval, 493 long pcval, long r1val, long r2val, 494 bfd_boolean *targetvalid, 495 bfd_boolean *unconditionalbranch) 496 { 497 struct op_code_struct * op; 498 long targetaddr = 0; 499 500 *unconditionalbranch = FALSE; 501 /* Just a linear search of the table. */ 502 for (op = 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