1 /* Disassembler code for CRX. 2 Copyright (C) 2004-2024 Free Software Foundation, Inc. 3 Contributed by Tomer Levi, NSC, Israel. 4 Written by Tomer Levi. 5 6 This file is part of the GNU opcodes library. 7 8 This library is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3, or (at your option) 11 any later version. 12 13 It is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 16 License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 21 MA 02110-1301, USA. */ 22 23 #include "sysdep.h" 24 #include "disassemble.h" 25 #include "opcode/crx.h" 26 27 /* String to print when opcode was not matched. */ 28 #define ILLEGAL "illegal" 29 /* Escape to 16-bit immediate. */ 30 #define ESCAPE_16_BIT 0xE 31 32 /* Extract 'n_bits' from 'a' starting from offset 'offs'. */ 33 #define EXTRACT(a, offs, n_bits) \ 34 (((a) >> (offs)) & ((2ull << (n_bits - 1)) - 1)) 35 36 /* Set Bit Mask - a mask to set all bits starting from offset 'offs'. */ 37 #define SBM(offs) ((-1u << (offs)) & 0xffffffff) 38 39 typedef unsigned long dwordU; 40 typedef unsigned short wordU; 41 42 typedef struct 43 { 44 dwordU val; 45 int nbits; 46 } parameter; 47 48 /* Structure to hold valid 'cinv' instruction options. */ 49 50 typedef struct 51 { 52 /* Cinv printed string. */ 53 char *str; 54 /* Value corresponding to the string. */ 55 unsigned int value; 56 } 57 cinv_entry; 58 59 /* CRX 'cinv' options. */ 60 static const cinv_entry crx_cinvs[] = 61 { 62 {"[i]", 2}, {"[i,u]", 3}, {"[d]", 4}, {"[d,u]", 5}, 63 {"[d,i]", 6}, {"[d,i,u]", 7}, {"[b]", 8}, 64 {"[b,i]", 10}, {"[b,i,u]", 11}, {"[b,d]", 12}, 65 {"[b,d,u]", 13}, {"[b,d,i]", 14}, {"[b,d,i,u]", 15} 66 }; 67 68 /* Enum to distinguish different registers argument types. */ 69 typedef enum REG_ARG_TYPE 70 { 71 /* General purpose register (r<N>). */ 72 REG_ARG = 0, 73 /* User register (u<N>). */ 74 USER_REG_ARG, 75 /* CO-Processor register (c<N>). */ 76 COP_ARG, 77 /* CO-Processor special register (cs<N>). */ 78 COPS_ARG 79 } 80 REG_ARG_TYPE; 81 82 /* Number of valid 'cinv' instruction options. */ 83 static int NUMCINVS = ((sizeof crx_cinvs)/(sizeof crx_cinvs[0])); 84 /* Current opcode table entry we're disassembling. */ 85 static const inst *instruction; 86 /* Current instruction we're disassembling. */ 87 static ins currInsn; 88 /* The current instruction is read into 3 consecutive words. */ 89 static wordU words[3]; 90 /* Contains all words in appropriate order. */ 91 static ULONGLONG allWords; 92 /* Holds the current processed argument number. */ 93 static int processing_argument_number; 94 /* Nonzero means a CST4 instruction. */ 95 static int cst4flag; 96 /* Nonzero means the instruction's original size is 97 incremented (escape sequence is used). */ 98 static int size_changed; 99 100 101 /* Retrieve the number of operands for the current assembled instruction. */ 102 103 static int 104 get_number_of_operands (void) 105 { 106 int i; 107 108 for (i = 0; i < MAX_OPERANDS && instruction->operands[i].op_type; i++) 109 ; 110 111 return i; 112 } 113 114 /* Return the bit size for a given operand. */ 115 116 static int 117 getbits (operand_type op) 118 { 119 if (op < MAX_OPRD) 120 return crx_optab[op].bit_size; 121 else 122 return 0; 123 } 124 125 /* Return the argument type of a given operand. */ 126 127 static argtype 128 getargtype (operand_type op) 129 { 130 if (op < MAX_OPRD) 131 return crx_optab[op].arg_type; 132 else 133 return nullargs; 134 } 135 136 /* Given the trap index in dispatch table, return its name. 137 This routine is used when disassembling the 'excp' instruction. */ 138 139 static char * 140 gettrapstring (unsigned int trap_index) 141 { 142 const trap_entry *trap; 143 144 for (trap = crx_traps; trap < crx_traps + NUMTRAPS; trap++) 145 if (trap->entry == trap_index) 146 return trap->name; 147 148 return ILLEGAL; 149 } 150 151 /* Given a 'cinv' instruction constant operand, return its corresponding string. 152 This routine is used when disassembling the 'cinv' instruction. */ 153 154 static char * 155 getcinvstring (unsigned int num) 156 { 157 const cinv_entry *cinv; 158 159 for (cinv = crx_cinvs; cinv < (crx_cinvs + NUMCINVS); cinv++) 160 if (cinv->value == num) 161 return cinv->str; 162 163 return ILLEGAL; 164 } 165 166 /* Given a register enum value, retrieve its name. */ 167 168 static char * 169 getregname (reg r) 170 { 171 const reg_entry * regentry = &crx_regtab[r]; 172 173 if (regentry->type != CRX_R_REGTYPE) 174 return ILLEGAL; 175 else 176 return regentry->name; 177 } 178 179 /* Given a coprocessor register enum value, retrieve its name. */ 180 181 static char * 182 getcopregname (copreg r, reg_type type) 183 { 184 const reg_entry * regentry; 185 186 if (type == CRX_C_REGTYPE) 187 regentry = &crx_copregtab[r]; 188 else if (type == CRX_CS_REGTYPE) 189 regentry = &crx_copregtab[r+(cs0-c0)]; 190 else 191 return ILLEGAL; 192 193 return regentry->name; 194 } 195 196 197 /* Getting a processor register name. */ 198 199 static char * 200 getprocregname (int reg_index) 201 { 202 const reg_entry *r; 203 204 for (r = crx_regtab; r < crx_regtab + NUMREGS; r++) 205 if (r->image == reg_index) 206 return r->name; 207 208 return "ILLEGAL REGISTER"; 209 } 210 211 /* Get the power of two for a given integer. */ 212 213 static int 214 powerof2 (int x) 215 { 216 int product, i; 217 218 for (i = 0, product = 1; i < x; i++) 219 product *= 2; 220 221 return product; 222 } 223 224 /* Transform a register bit mask to a register list. */ 225 226 static void 227 getregliststring (int mask, char *string, enum REG_ARG_TYPE core_cop) 228 { 229 char temp_string[16]; 230 int i; 231 232 string[0] = '{'; 233 string[1] = '\0'; 234 235 236 /* A zero mask means HI/LO registers. */ 237 if (mask == 0) 238 { 239 if (core_cop == USER_REG_ARG) 240 strcat (string, "ulo,uhi"); 241 else 242 strcat (string, "lo,hi"); 243 } 244 else 245 { 246 for (i = 0; i < 16; i++) 247 { 248 if (mask & 0x1) 249 { 250 switch (core_cop) 251 { 252 case REG_ARG: 253 sprintf (temp_string, "r%d", i); 254 break; 255 case USER_REG_ARG: 256 sprintf (temp_string, "u%d", i); 257 break; 258 case COP_ARG: 259 sprintf (temp_string, "c%d", i); 260 break; 261 case COPS_ARG: 262 sprintf (temp_string, "cs%d", i); 263 break; 264 default: 265 break; 266 } 267 strcat (string, temp_string); 268 if (mask & 0xfffe) 269 strcat (string, ","); 270 } 271 mask >>= 1; 272 } 273 } 274 275 strcat (string, "}"); 276 } 277 278 /* START and END are relating 'allWords' struct, which is 48 bits size. 279 280 START|--------|END 281 +---------+---------+---------+---------+ 282 | | V | A | L | 283 +---------+---------+---------+---------+ 284 0 16 32 48 285 words [0] [1] [2] */ 286 287 static parameter 288 makelongparameter (ULONGLONG val, int start, int end) 289 { 290 parameter p; 291 292 p.val = (dwordU) EXTRACT(val, 48 - end, end - start); 293 p.nbits = end - start; 294 return p; 295 } 296 297 /* Build a mask of the instruction's 'constant' opcode, 298 based on the instruction's printing flags. */ 299 300 static unsigned int 301 build_mask (void) 302 { 303 unsigned int print_flags; 304 unsigned int mask; 305 306 print_flags = instruction->flags & FMT_CRX; 307 switch (print_flags) 308 { 309 case FMT_1: 310 mask = 0xF0F00000; 311 break; 312 case FMT_2: 313 mask = 0xFFF0FF00; 314 break; 315 case FMT_3: 316 mask = 0xFFF00F00; 317 break; 318 case FMT_4: 319 mask = 0xFFF0F000; 320 break; 321 case FMT_5: 322 mask = 0xFFF0FFF0; 323 break; 324 default: 325 mask = SBM(instruction->match_bits); 326 break; 327 } 328 329 return mask; 330 } 331 332 /* Search for a matching opcode. Return 1 for success, 0 for failure. */ 333 334 static int 335 match_opcode (void) 336 { 337 unsigned int mask; 338 339 /* The instruction 'constant' opcode doewsn't exceed 32 bits. */ 340 unsigned int doubleWord = words[1] + ((unsigned) words[0] << 16); 341 342 /* Start searching from end of instruction table. */ 343 instruction = &crx_instruction[NUMOPCODES - 2]; 344 345 /* Loop over instruction table until a full match is found. */ 346 while (instruction >= crx_instruction) 347 { 348 mask = build_mask (); 349 if ((doubleWord & mask) == BIN(instruction->match, instruction->match_bits)) 350 return 1; 351 else 352 instruction--; 353 } 354 return 0; 355 } 356 357 /* Set the proper parameter value for different type of arguments. */ 358 359 static void 360 make_argument (argument * a, int start_bits) 361 { 362 int inst_bit_size, total_size; 363 parameter p; 364 365 if ((instruction->size == 3) && a->size >= 16) 366 inst_bit_size = 48; 367 else 368 inst_bit_size = 32; 369 370 switch (a->type) 371 { 372 case arg_copr: 373 case arg_copsr: 374 p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), 375 inst_bit_size - start_bits); 376 a->cr = p.val; 377 break; 378 379 case arg_r: 380 p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), 381 inst_bit_size - start_bits); 382 a->r = p.val; 383 break; 384 385 case arg_ic: 386 p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), 387 inst_bit_size - start_bits); 388 389 if ((p.nbits == 4) && cst4flag) 390 { 391 if (IS_INSN_TYPE (CMPBR_INS) && (p.val == ESCAPE_16_BIT)) 392 { 393 /* A special case, where the value is actually stored 394 in the last 4 bits. */ 395 p = makelongparameter (allWords, 44, 48); 396 /* The size of the instruction should be incremented. */ 397 size_changed = 1; 398 } 399 400 if (p.val == 6) 401 p.val = -1; 402 else if (p.val == 13) 403 p.val = 48; 404 else if (p.val == 5) 405 p.val = -4; 406 else if (p.val == 10) 407 p.val = 32; 408 else if (p.val == 11) 409 p.val = 20; 410 else if (p.val == 9) 411 p.val = 16; 412 } 413 414 a->constant = p.val; 415 break; 416 417 case arg_idxr: 418 a->scale = 0; 419 total_size = a->size + 10; /* sizeof(rbase + ridx + scl2) = 10. */ 420 p = makelongparameter (allWords, inst_bit_size - total_size, 421 inst_bit_size - (total_size - 4)); 422 a->r = p.val; 423 p = makelongparameter (allWords, inst_bit_size - (total_size - 4), 424 inst_bit_size - (total_size - 8)); 425 a->i_r = p.val; 426 p = makelongparameter (allWords, inst_bit_size - (total_size - 8), 427 inst_bit_size - (total_size - 10)); 428 a->scale = p.val; 429 p = makelongparameter (allWords, inst_bit_size - (total_size - 10), 430 inst_bit_size); 431 a->constant = p.val; 432 break; 433 434 case arg_rbase: 435 p = makelongparameter (allWords, inst_bit_size - (start_bits + 4), 436 inst_bit_size - start_bits); 437 a->r = p.val; 438 break; 439 440 case arg_cr: 441 if (a->size <= 8) 442 { 443 p = makelongparameter (allWords, inst_bit_size - (start_bits + 4), 444 inst_bit_size - start_bits); 445 a->r = p.val; 446 /* Case for opc4 r dispu rbase. */ 447 p = makelongparameter (allWords, inst_bit_size - (start_bits + 8), 448 inst_bit_size - (start_bits + 4)); 449 } 450 else 451 { 452 /* The 'rbase' start_bits is always relative to a 32-bit data type. */ 453 p = makelongparameter (allWords, 32 - (start_bits + 4), 454 32 - start_bits); 455 a->r = p.val; 456 p = makelongparameter (allWords, 32 - start_bits, 457 inst_bit_size); 458 } 459 if ((p.nbits == 4) && cst4flag) 460 { 461 if (instruction->flags & DISPUW4) 462 p.val *= 2; 463 else if (instruction->flags & DISPUD4) 464 p.val *= 4; 465 } 466 a->constant = p.val; 467 break; 468 469 case arg_c: 470 p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size), 471 inst_bit_size - start_bits); 472 a->constant = p.val; 473 break; 474 default: 475 break; 476 } 477 } 478 479 /* Print a single argument. */ 480 481 static void 482 print_arg (argument *a, bfd_vma memaddr, struct disassemble_info *info) 483 { 484 ULONGLONG longdisp, mask; 485 int sign_flag = 0; 486 int relative = 0; 487 bfd_vma number; 488 int op_index = 0; 489 char string[200]; 490 void *stream = info->stream; 491 fprintf_ftype func = info->fprintf_func; 492 493 switch (a->type) 494 { 495 case arg_copr: 496 func (stream, "%s", getcopregname (a->cr, CRX_C_REGTYPE)); 497 break; 498 499 case arg_copsr: 500 func (stream, "%s", getcopregname (a->cr, CRX_CS_REGTYPE)); 501 break; 502 503 case arg_r: 504 if (IS_INSN_MNEMONIC ("mtpr") || IS_INSN_MNEMONIC ("mfpr")) 505 func (stream, "%s", getprocregname (a->r)); 506 else 507 func (stream, "%s", getregname (a->r)); 508 break; 509 510 case arg_ic: 511 if (IS_INSN_MNEMONIC ("excp")) 512 func (stream, "%s", gettrapstring (a->constant)); 513 514 else if (IS_INSN_MNEMONIC ("cinv")) 515 func (stream, "%s", getcinvstring (a->constant)); 516 517 else if (INST_HAS_REG_LIST) 518 { 519 REG_ARG_TYPE reg_arg_type = IS_INSN_TYPE (COP_REG_INS) ? 520 COP_ARG : IS_INSN_TYPE (COPS_REG_INS) ? 521 COPS_ARG : (instruction->flags & USER_REG) ? 522 USER_REG_ARG : REG_ARG; 523 524 if ((reg_arg_type == COP_ARG) || (reg_arg_type == COPS_ARG)) 525 { 526 /* Check for proper argument number. */ 527 if (processing_argument_number == 2) 528 { 529 getregliststring (a->constant, string, reg_arg_type); 530 func (stream, "%s", string); 531 } 532 else 533 func (stream, "$0x%lx", a->constant & 0xffffffff); 534 } 535 else 536 { 537 getregliststring (a->constant, string, reg_arg_type); 538 func (stream, "%s", string); 539 } 540 } 541 else 542 func (stream, "$0x%lx", a->constant & 0xffffffff); 543 break; 544 545 case arg_idxr: 546 func (stream, "0x%lx(%s,%s,%d)", a->constant & 0xffffffff, 547 getregname (a->r), getregname (a->i_r), powerof2 (a->scale)); 548 break; 549 550 case arg_rbase: 551 func (stream, "(%s)", getregname (a->r)); 552 break; 553 554 case arg_cr: 555 func (stream, "0x%lx(%s)", a->constant & 0xffffffff, getregname (a->r)); 556 557 if (IS_INSN_TYPE (LD_STOR_INS_INC)) 558 func (stream, "+"); 559 break; 560 561 case arg_c: 562 /* Removed the *2 part as because implicit zeros are no more required. 563 Have to fix this as this needs a bit of extension in terms of branchins. 564 Have to add support for cmp and branch instructions. */ 565 if (IS_INSN_TYPE (BRANCH_INS) || IS_INSN_MNEMONIC ("bal") 566 || IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (DCR_BRANCH_INS) 567 || IS_INSN_TYPE (COP_BRANCH_INS)) 568 { 569 relative = 1; 570 longdisp = a->constant; 571 longdisp <<= 1; 572 573 switch (a->size) 574 { 575 case 8: 576 case 16: 577 case 24: 578 case 32: 579 mask = ((LONGLONG) 1 << a->size) - 1; 580 if (longdisp & ((ULONGLONG) 1 << a->size)) 581 { 582 sign_flag = 1; 583 longdisp = ~(longdisp) + 1; 584 } 585 a->constant = (unsigned long int) (longdisp & mask); 586 break; 587 default: 588 func (stream, 589 "Wrong offset used in branch/bal instruction"); 590 break; 591 } 592 593 } 594 /* For branch Neq instruction it is 2*offset + 2. */ 595 else if (IS_INSN_TYPE (BRANCH_NEQ_INS)) 596 a->constant = 2 * a->constant + 2; 597 else if (IS_INSN_TYPE (LD_STOR_INS_INC) 598 || IS_INSN_TYPE (LD_STOR_INS) 599 || IS_INSN_TYPE (STOR_IMM_INS) 600 || IS_INSN_TYPE (CSTBIT_INS)) 601 { 602 op_index = instruction->flags & REVERSE_MATCH ? 0 : 1; 603 if (instruction->operands[op_index].op_type == abs16) 604 a->constant |= 0xFFFF0000; 605 } 606 func (stream, "%s", "0x"); 607 number = (relative ? memaddr : 0) 608 + (sign_flag ? -a->constant : a->constant); 609 (*info->print_address_func) (number, info); 610 break; 611 default: 612 break; 613 } 614 } 615 616 /* Print all the arguments of CURRINSN instruction. */ 617 618 static void 619 print_arguments (ins *currentInsn, bfd_vma memaddr, struct disassemble_info *info) 620 { 621 int i; 622 623 for (i = 0; i < currentInsn->nargs; i++) 624 { 625 processing_argument_number = i; 626 627 print_arg (¤tInsn->arg[i], memaddr, info); 628 629 if (i != currentInsn->nargs - 1) 630 info->fprintf_func (info->stream, ", "); 631 } 632 } 633 634 /* Build the instruction's arguments. */ 635 636 static void 637 make_instruction (void) 638 { 639 int i; 640 unsigned int shift; 641 642 for (i = 0; i < currInsn.nargs; i++) 643 { 644 argument a; 645 646 memset (&a, 0, sizeof (a)); 647 a.type = getargtype (instruction->operands[i].op_type); 648 if (instruction->operands[i].op_type == cst4 649 || instruction->operands[i].op_type == rbase_dispu4) 650 cst4flag = 1; 651 a.size = getbits (instruction->operands[i].op_type); 652 shift = instruction->operands[i].shift; 653 654 make_argument (&a, shift); 655 currInsn.arg[i] = a; 656 } 657 658 /* Calculate instruction size (in bytes). */ 659 currInsn.size = instruction->size + (size_changed ? 1 : 0); 660 /* Now in bits. */ 661 currInsn.size *= 2; 662 } 663 664 /* Retrieve a single word from a given memory address. */ 665 666 static wordU 667 get_word_at_PC (bfd_vma memaddr, struct disassemble_info *info) 668 { 669 bfd_byte buffer[4]; 670 int status; 671 wordU insn = 0; 672 673 status = info->read_memory_func (memaddr, buffer, 2, info); 674 675 if (status == 0) 676 insn = (wordU) bfd_getl16 (buffer); 677 678 return insn; 679 } 680 681 /* Retrieve multiple words (3) from a given memory address. */ 682 683 static void 684 get_words_at_PC (bfd_vma memaddr, struct disassemble_info *info) 685 { 686 int i; 687 bfd_vma mem; 688 689 for (i = 0, mem = memaddr; i < 3; i++, mem += 2) 690 words[i] = get_word_at_PC (mem, info); 691 692 allWords = 693 ((ULONGLONG) words[0] << 32) + ((unsigned long) words[1] << 16) + words[2]; 694 } 695 696 /* Prints the instruction by calling print_arguments after proper matching. */ 697 698 int 699 print_insn_crx (bfd_vma memaddr, struct disassemble_info *info) 700 { 701 int is_decoded; /* Nonzero means instruction has a match. */ 702 703 /* Initialize global variables. */ 704 cst4flag = 0; 705 size_changed = 0; 706 707 /* Retrieve the encoding from current memory location. */ 708 get_words_at_PC (memaddr, info); 709 /* Find a matching opcode in table. */ 710 is_decoded = match_opcode (); 711 /* If found, print the instruction's mnemonic and arguments. */ 712 if (is_decoded > 0 && (words[0] != 0 || words[1] != 0)) 713 { 714 info->fprintf_func (info->stream, "%s", instruction->mnemonic); 715 if ((currInsn.nargs = get_number_of_operands ()) != 0) 716 info->fprintf_func (info->stream, "\t"); 717 make_instruction (); 718 print_arguments (&currInsn, memaddr, info); 719 return currInsn.size; 720 } 721 722 /* No match found. */ 723 info->fprintf_func (info->stream,"%s ",ILLEGAL); 724 return 2; 725 } 726