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