1 /* IP2K opcode support. -*- C -*- 2 Copyright 2002, 2005, 2011 Free Software Foundation, Inc. 3 4 Contributed by Red Hat Inc; 5 6 This file is part of the GNU Binutils. 7 8 This program 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 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public 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 /* 24 Each section is delimited with start and end markers. 25 26 <arch>-opc.h additions use: "-- opc.h" 27 <arch>-opc.c additions use: "-- opc.c" 28 <arch>-asm.c additions use: "-- asm.c" 29 <arch>-dis.c additions use: "-- dis.c" 30 <arch>-ibd.h additions use: "-- ibd.h". */ 31 32 /* -- opc.h */ 34 35 /* Check applicability of instructions against machines. */ 36 #define CGEN_VALIDATE_INSN_SUPPORTED 37 38 /* Allows reason codes to be output when assembler errors occur. */ 39 #define CGEN_VERBOSE_ASSEMBLER_ERRORS 40 41 /* Override disassembly hashing - there are variable bits in the top 42 byte of these instructions. */ 43 #define CGEN_DIS_HASH_SIZE 8 44 #define CGEN_DIS_HASH(buf, value) \ 45 (((* (unsigned char*) (buf)) >> 5) % CGEN_DIS_HASH_SIZE) 46 47 #define CGEN_ASM_HASH_SIZE 127 48 #define CGEN_ASM_HASH(insn) ip2k_asm_hash (insn) 49 50 extern unsigned int ip2k_asm_hash (const char *); 51 extern int ip2k_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *); 52 53 /* -- opc.c */ 55 56 #include "safe-ctype.h" 57 58 /* A better hash function for instruction mnemonics. */ 59 unsigned int 60 ip2k_asm_hash (const char* insn) 61 { 62 unsigned int hash; 63 const char* m = insn; 64 65 for (hash = 0; *m && ! ISSPACE (*m); m++) 66 hash = (hash * 23) ^ (0x1F & TOLOWER (*m)); 67 68 /* printf ("%s %d\n", insn, (hash % CGEN_ASM_HASH_SIZE)); */ 69 70 return hash % CGEN_ASM_HASH_SIZE; 71 } 72 73 74 /* Special check to ensure that instruction exists for given machine. */ 75 76 int 77 ip2k_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn) 78 { 79 int machs = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_MACH); 80 81 /* No mach attribute? Assume it's supported for all machs. */ 82 if (machs == 0) 83 return 1; 84 85 return (machs & cd->machs) != 0; 86 } 87 88 89 /* -- asm.c */ 91 92 static const char * 93 parse_fr (CGEN_CPU_DESC cd, 94 const char **strp, 95 int opindex, 96 unsigned long *valuep) 97 { 98 const char *errmsg; 99 const char *old_strp; 100 char *afteroffset; 101 enum cgen_parse_operand_result result_type; 102 bfd_vma value; 103 extern CGEN_KEYWORD ip2k_cgen_opval_register_names; 104 bfd_vma tempvalue; 105 106 old_strp = *strp; 107 afteroffset = NULL; 108 109 /* Check here to see if you're about to try parsing a w as the first arg 110 and return an error if you are. */ 111 if ((strncmp (*strp, "w", 1) == 0) || (strncmp (*strp, "W", 1) == 0)) 112 { 113 (*strp)++; 114 115 if ((strncmp (*strp, ",", 1) == 0) || ISSPACE (**strp)) 116 { 117 /* We've been passed a w. Return with an error message so that 118 cgen will try the next parsing option. */ 119 errmsg = _("W keyword invalid in FR operand slot."); 120 return errmsg; 121 } 122 *strp = old_strp; 123 } 124 125 /* Attempt parse as register keyword. */ 126 errmsg = cgen_parse_keyword (cd, strp, & ip2k_cgen_opval_register_names, 127 (long *) valuep); 128 if (*strp != NULL 129 && errmsg == NULL) 130 return errmsg; 131 132 /* Attempt to parse for "(IP)". */ 133 afteroffset = strstr (*strp, "(IP)"); 134 135 if (afteroffset == NULL) 136 /* Make sure it's not in lower case. */ 137 afteroffset = strstr (*strp, "(ip)"); 138 139 if (afteroffset != NULL) 140 { 141 if (afteroffset != *strp) 142 { 143 /* Invalid offset present. */ 144 errmsg = _("offset(IP) is not a valid form"); 145 return errmsg; 146 } 147 else 148 { 149 *strp += 4; 150 *valuep = 0; 151 errmsg = NULL; 152 return errmsg; 153 } 154 } 155 156 /* Attempt to parse for DP. ex: mov w, offset(DP) 157 mov offset(DP),w */ 158 159 /* Try parsing it as an address and see what comes back. */ 160 afteroffset = strstr (*strp, "(DP)"); 161 162 if (afteroffset == NULL) 163 /* Maybe it's in lower case. */ 164 afteroffset = strstr (*strp, "(dp)"); 165 166 if (afteroffset != NULL) 167 { 168 if (afteroffset == *strp) 169 { 170 /* No offset present. Use 0 by default. */ 171 tempvalue = 0; 172 errmsg = NULL; 173 } 174 else 175 errmsg = cgen_parse_address (cd, strp, opindex, 176 BFD_RELOC_IP2K_FR_OFFSET, 177 & result_type, & tempvalue); 178 179 if (errmsg == NULL) 180 { 181 if (tempvalue <= 127) 182 { 183 /* Value is ok. Fix up the first 2 bits and return. */ 184 *valuep = 0x0100 | tempvalue; 185 *strp += 4; /* Skip over the (DP) in *strp. */ 186 return errmsg; 187 } 188 else 189 { 190 /* Found something there in front of (DP) but it's out 191 of range. */ 192 errmsg = _("(DP) offset out of range."); 193 return errmsg; 194 } 195 } 196 } 197 198 199 /* Attempt to parse for SP. ex: mov w, offset(SP) 200 mov offset(SP), w. */ 201 afteroffset = strstr (*strp, "(SP)"); 202 203 if (afteroffset == NULL) 204 /* Maybe it's in lower case. */ 205 afteroffset = strstr (*strp, "(sp)"); 206 207 if (afteroffset != NULL) 208 { 209 if (afteroffset == *strp) 210 { 211 /* No offset present. Use 0 by default. */ 212 tempvalue = 0; 213 errmsg = NULL; 214 } 215 else 216 errmsg = cgen_parse_address (cd, strp, opindex, 217 BFD_RELOC_IP2K_FR_OFFSET, 218 & result_type, & tempvalue); 219 220 if (errmsg == NULL) 221 { 222 if (tempvalue <= 127) 223 { 224 /* Value is ok. Fix up the first 2 bits and return. */ 225 *valuep = 0x0180 | tempvalue; 226 *strp += 4; /* Skip over the (SP) in *strp. */ 227 return errmsg; 228 } 229 else 230 { 231 /* Found something there in front of (SP) but it's out 232 of range. */ 233 errmsg = _("(SP) offset out of range."); 234 return errmsg; 235 } 236 } 237 } 238 239 /* Attempt to parse as an address. */ 240 *strp = old_strp; 241 errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_IP2K_FR9, 242 & result_type, & value); 243 if (errmsg == NULL) 244 { 245 *valuep = value; 246 247 /* If a parenthesis is found, warn about invalid form. */ 248 if (**strp == '(') 249 errmsg = _("illegal use of parentheses"); 250 251 /* If a numeric value is specified, ensure that it is between 252 1 and 255. */ 253 else if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) 254 { 255 if (value < 0x1 || value > 0xff) 256 errmsg = _("operand out of range (not between 1 and 255)"); 257 } 258 } 259 return errmsg; 260 } 261 262 static const char * 263 parse_addr16 (CGEN_CPU_DESC cd, 264 const char **strp, 265 int opindex, 266 unsigned long *valuep) 267 { 268 const char *errmsg; 269 enum cgen_parse_operand_result result_type; 270 bfd_reloc_code_real_type code = BFD_RELOC_NONE; 271 bfd_vma value; 272 273 if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16H) 274 code = BFD_RELOC_IP2K_HI8DATA; 275 else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16L) 276 code = BFD_RELOC_IP2K_LO8DATA; 277 else 278 { 279 /* Something is very wrong. opindex has to be one of the above. */ 280 errmsg = _("parse_addr16: invalid opindex."); 281 return errmsg; 282 } 283 284 errmsg = cgen_parse_address (cd, strp, opindex, code, 285 & result_type, & value); 286 if (errmsg == NULL) 287 { 288 /* We either have a relocation or a number now. */ 289 if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) 290 { 291 /* We got a number back. */ 292 if (code == BFD_RELOC_IP2K_HI8DATA) 293 value >>= 8; 294 else 295 /* code = BFD_RELOC_IP2K_LOW8DATA. */ 296 value &= 0x00FF; 297 } 298 *valuep = value; 299 } 300 301 return errmsg; 302 } 303 304 static const char * 305 parse_addr16_cjp (CGEN_CPU_DESC cd, 306 const char **strp, 307 int opindex, 308 unsigned long *valuep) 309 { 310 const char *errmsg; 311 enum cgen_parse_operand_result result_type; 312 bfd_reloc_code_real_type code = BFD_RELOC_NONE; 313 bfd_vma value; 314 315 if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP) 316 code = BFD_RELOC_IP2K_ADDR16CJP; 317 else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P) 318 code = BFD_RELOC_IP2K_PAGE3; 319 320 errmsg = cgen_parse_address (cd, strp, opindex, code, 321 & result_type, & value); 322 if (errmsg == NULL) 323 { 324 if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) 325 { 326 if ((value & 0x1) == 0) /* If the address is even .... */ 327 { 328 if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP) 329 *valuep = (value >> 1) & 0x1FFF; /* Should mask be 1FFF? */ 330 else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P) 331 *valuep = (value >> 14) & 0x7; 332 } 333 else 334 errmsg = _("Byte address required. - must be even."); 335 } 336 else if (result_type == CGEN_PARSE_OPERAND_RESULT_QUEUED) 337 { 338 /* This will happen for things like (s2-s1) where s2 and s1 339 are labels. */ 340 *valuep = value; 341 } 342 else 343 errmsg = _("cgen_parse_address returned a symbol. Literal required."); 344 } 345 return errmsg; 346 } 347 348 static const char * 349 parse_lit8 (CGEN_CPU_DESC cd, 350 const char **strp, 351 int opindex, 352 long *valuep) 353 { 354 const char *errmsg; 355 enum cgen_parse_operand_result result_type; 356 bfd_reloc_code_real_type code = BFD_RELOC_NONE; 357 bfd_vma value; 358 359 /* Parse %OP relocating operators. */ 360 if (strncmp (*strp, "%bank", 5) == 0) 361 { 362 *strp += 5; 363 code = BFD_RELOC_IP2K_BANK; 364 } 365 else if (strncmp (*strp, "%lo8data", 8) == 0) 366 { 367 *strp += 8; 368 code = BFD_RELOC_IP2K_LO8DATA; 369 } 370 else if (strncmp (*strp, "%hi8data", 8) == 0) 371 { 372 *strp += 8; 373 code = BFD_RELOC_IP2K_HI8DATA; 374 } 375 else if (strncmp (*strp, "%ex8data", 8) == 0) 376 { 377 *strp += 8; 378 code = BFD_RELOC_IP2K_EX8DATA; 379 } 380 else if (strncmp (*strp, "%lo8insn", 8) == 0) 381 { 382 *strp += 8; 383 code = BFD_RELOC_IP2K_LO8INSN; 384 } 385 else if (strncmp (*strp, "%hi8insn", 8) == 0) 386 { 387 *strp += 8; 388 code = BFD_RELOC_IP2K_HI8INSN; 389 } 390 391 /* Parse %op operand. */ 392 if (code != BFD_RELOC_NONE) 393 { 394 errmsg = cgen_parse_address (cd, strp, opindex, code, 395 & result_type, & value); 396 if ((errmsg == NULL) && 397 (result_type != CGEN_PARSE_OPERAND_RESULT_QUEUED)) 398 errmsg = _("percent-operator operand is not a symbol"); 399 400 *valuep = value; 401 } 402 /* Parse as a number. */ 403 else 404 { 405 errmsg = cgen_parse_signed_integer (cd, strp, opindex, valuep); 406 407 /* Truncate to eight bits to accept both signed and unsigned input. */ 408 if (errmsg == NULL) 409 *valuep &= 0xFF; 410 } 411 412 return errmsg; 413 } 414 415 static const char * 416 parse_bit3 (CGEN_CPU_DESC cd, 417 const char **strp, 418 int opindex, 419 unsigned long *valuep) 420 { 421 const char *errmsg; 422 char mode = 0; 423 long count = 0; 424 unsigned long value; 425 426 if (strncmp (*strp, "%bit", 4) == 0) 427 { 428 *strp += 4; 429 mode = 1; 430 } 431 else if (strncmp (*strp, "%msbbit", 7) == 0) 432 { 433 *strp += 7; 434 mode = 1; 435 } 436 else if (strncmp (*strp, "%lsbbit", 7) == 0) 437 { 438 *strp += 7; 439 mode = 2; 440 } 441 442 errmsg = cgen_parse_unsigned_integer (cd, strp, opindex, valuep); 443 if (errmsg) 444 return errmsg; 445 446 if (mode) 447 { 448 value = * valuep; 449 if (value == 0) 450 { 451 errmsg = _("Attempt to find bit index of 0"); 452 return errmsg; 453 } 454 455 if (mode == 1) 456 { 457 count = 31; 458 while ((value & 0x80000000) == 0) 459 { 460 count--; 461 value <<= 1; 462 } 463 } 464 else if (mode == 2) 465 { 466 count = 0; 467 while ((value & 0x00000001) == 0) 468 { 469 count++; 470 value >>= 1; 471 } 472 } 473 474 *valuep = count; 475 } 476 477 return errmsg; 478 } 479 480 /* -- dis.c */ 481 482 static void 483 print_fr (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 484 void * dis_info, 485 long value, 486 unsigned int attrs ATTRIBUTE_UNUSED, 487 bfd_vma pc ATTRIBUTE_UNUSED, 488 int length ATTRIBUTE_UNUSED) 489 { 490 disassemble_info *info = (disassemble_info *) dis_info; 491 const CGEN_KEYWORD_ENTRY *ke; 492 extern CGEN_KEYWORD ip2k_cgen_opval_register_names; 493 long offsettest; 494 long offsetvalue; 495 496 if (value == 0) /* This is (IP). */ 497 { 498 (*info->fprintf_func) (info->stream, "%s", "(IP)"); 499 return; 500 } 501 502 offsettest = value >> 7; 503 offsetvalue = value & 0x7F; 504 505 /* Check to see if first two bits are 10 -> (DP). */ 506 if (offsettest == 2) 507 { 508 if (offsetvalue == 0) 509 (*info->fprintf_func) (info->stream, "%s","(DP)"); 510 else 511 (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue, "(DP)"); 512 return; 513 } 514 515 /* Check to see if first two bits are 11 -> (SP). */ 516 if (offsettest == 3) 517 { 518 if (offsetvalue == 0) 519 (*info->fprintf_func) (info->stream, "%s", "(SP)"); 520 else 521 (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue,"(SP)"); 522 return; 523 } 524 525 /* Attempt to print as a register keyword. */ 526 ke = cgen_keyword_lookup_value (& ip2k_cgen_opval_register_names, value); 527 528 if (ke != NULL) 529 (*info->fprintf_func) (info->stream, "%s", ke->name); 530 else 531 /* Print as an address literal. */ 532 (*info->fprintf_func) (info->stream, "$%02lx", value); 533 } 534 535 static void 536 print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 537 void * dis_info, 538 long value, 539 unsigned int attrs ATTRIBUTE_UNUSED, 540 bfd_vma pc ATTRIBUTE_UNUSED, 541 int length ATTRIBUTE_UNUSED) 542 { 543 disassemble_info *info = (disassemble_info *) dis_info; 544 545 (*info->fprintf_func) (info->stream, "$%lx", value); 546 } 547 548 static void 549 print_dollarhex8 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 550 void * dis_info, 551 long value, 552 unsigned int attrs ATTRIBUTE_UNUSED, 553 bfd_vma pc ATTRIBUTE_UNUSED, 554 int length ATTRIBUTE_UNUSED) 555 { 556 disassemble_info *info = (disassemble_info *) dis_info; 557 558 (*info->fprintf_func) (info->stream, "$%02lx", value); 559 } 560 561 static void 562 print_dollarhex_addr16h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 563 void * dis_info, 564 long value, 565 unsigned int attrs ATTRIBUTE_UNUSED, 566 bfd_vma pc ATTRIBUTE_UNUSED, 567 int length ATTRIBUTE_UNUSED) 568 { 569 disassemble_info *info = (disassemble_info *) dis_info; 570 571 /* This is a loadh instruction. Shift the value to the left 572 by 8 bits so that disassembled code will reassemble properly. */ 573 value = ((value << 8) & 0xFF00); 574 575 (*info->fprintf_func) (info->stream, "$%04lx", value); 576 } 577 578 static void 579 print_dollarhex_addr16l (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 580 void * dis_info, 581 long value, 582 unsigned int attrs ATTRIBUTE_UNUSED, 583 bfd_vma pc ATTRIBUTE_UNUSED, 584 int length ATTRIBUTE_UNUSED) 585 { 586 disassemble_info *info = (disassemble_info *) dis_info; 587 588 (*info->fprintf_func) (info->stream, "$%04lx", value); 589 } 590 591 static void 592 print_dollarhex_p (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 593 void * dis_info, 594 long value, 595 unsigned int attrs ATTRIBUTE_UNUSED, 596 bfd_vma pc ATTRIBUTE_UNUSED, 597 int length ATTRIBUTE_UNUSED) 598 { 599 disassemble_info *info = (disassemble_info *) dis_info; 600 601 value = ((value << 14) & 0x1C000); 602 ;value = (value & 0x1FFFF); 603 (*info->fprintf_func) (info->stream, "$%05lx", value); 604 } 605 606 static void 607 print_dollarhex_cj (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 608 void * dis_info, 609 long value, 610 unsigned int attrs ATTRIBUTE_UNUSED, 611 bfd_vma pc ATTRIBUTE_UNUSED, 612 int length ATTRIBUTE_UNUSED) 613 { 614 disassemble_info *info = (disassemble_info *) dis_info; 615 616 value = ((value << 1) & 0x1FFFF); 617 (*info->fprintf_func) (info->stream, "$%05lx", value); 618 } 619 620 static void 621 print_decimal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 622 void * dis_info, 623 long value, 624 unsigned int attrs ATTRIBUTE_UNUSED, 625 bfd_vma pc ATTRIBUTE_UNUSED, 626 int length ATTRIBUTE_UNUSED) 627 { 628 disassemble_info *info = (disassemble_info *) dis_info; 629 630 (*info->fprintf_func) (info->stream, "%ld", value); 631 } 632 633 634 635 /* -- */ 636 637