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