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