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 * 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 |= 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 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) & 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