1 /* 2 * Aic7xxx SCSI host adapter firmware asssembler 3 * 4 * Copyright (c) 1997, 1998 Justin T. Gibbs. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions, and the following disclaimer, 12 * without modification, immediately at the beginning of the file. 13 * 2. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 #include <sys/types.h> 31 #include <sys/mman.h> 32 33 #include <ctype.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <sysexits.h> 38 #include <unistd.h> 39 40 #include "aicasm.h" 41 #include "aicasm_symbol.h" 42 #include "sequencer.h" 43 44 typedef struct patch { 45 STAILQ_ENTRY(patch) links; 46 int patch_func; 47 u_int begin; 48 u_int skip_instr; 49 u_int skip_patch; 50 } patch_t; 51 52 STAILQ_HEAD(patch_list, patch) patches; 53 54 static void usage(void); 55 static void back_patch(void); 56 static void output_code(FILE *ofile); 57 static void output_listing(FILE *listfile, char *ifilename); 58 static void dump_scope(scope_t *scope); 59 static void emit_patch(scope_t *scope, int patch); 60 static int check_patch(patch_t **start_patch, int start_instr, 61 int *skip_addr, int *func_vals); 62 63 struct path_list search_path; 64 int includes_search_curdir; 65 char *appname; 66 FILE *ofile; 67 char *ofilename; 68 char *regfilename; 69 FILE *regfile; 70 char *listfilename; 71 FILE *listfile; 72 73 static STAILQ_HEAD(,instruction) seq_program; 74 struct scope_list scope_stack; 75 symlist_t patch_functions; 76 77 #if DEBUG 78 extern int yy_flex_debug; 79 extern int yydebug; 80 #endif 81 extern FILE *yyin; 82 extern int yyparse __P((void)); 83 84 int 85 main(argc, argv) 86 int argc; 87 char *argv[]; 88 { 89 extern char *optarg; 90 extern int optind; 91 int ch; 92 int retval; 93 char *inputfilename; 94 scope_t *sentinal; 95 96 STAILQ_INIT(&patches); 97 SLIST_INIT(&search_path); 98 STAILQ_INIT(&seq_program); 99 SLIST_INIT(&scope_stack); 100 101 /* Set Sentinal scope node */ 102 sentinal = scope_alloc(); 103 sentinal->type = SCOPE_ROOT; 104 105 includes_search_curdir = 1; 106 appname = *argv; 107 regfile = NULL; 108 listfile = NULL; 109 #if DEBUG 110 yy_flex_debug = 0; 111 yydebug = 0; 112 #endif 113 while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) { 114 switch(ch) { 115 case 'd': 116 #if DEBUG 117 if (strcmp(optarg, "s") == 0) { 118 yy_flex_debug = 1; 119 } else if (strcmp(optarg, "p") == 0) { 120 yydebug = 1; 121 } else { 122 fprintf(stderr, "%s: -d Requires either an " 123 "'s' or 'p' argument\n", appname); 124 usage(); 125 } 126 #else 127 stop("-d: Assembler not built with debugging " 128 "information", EX_SOFTWARE); 129 #endif 130 break; 131 case 'l': 132 /* Create a program listing */ 133 if ((listfile = fopen(optarg, "w")) == NULL) { 134 perror(optarg); 135 stop(NULL, EX_CANTCREAT); 136 } 137 listfilename = optarg; 138 break; 139 case 'n': 140 /* Don't complain about the -nostdinc directrive */ 141 if (strcmp(optarg, "ostdinc")) { 142 fprintf(stderr, "%s: Unknown option -%c%s\n", 143 appname, ch, optarg); 144 usage(); 145 /* NOTREACHED */ 146 } 147 break; 148 case 'o': 149 if ((ofile = fopen(optarg, "w")) == NULL) { 150 perror(optarg); 151 stop(NULL, EX_CANTCREAT); 152 } 153 ofilename = optarg; 154 break; 155 case 'r': 156 if ((regfile = fopen(optarg, "w")) == NULL) { 157 perror(optarg); 158 stop(NULL, EX_CANTCREAT); 159 } 160 regfilename = optarg; 161 break; 162 case 'I': 163 { 164 path_entry_t include_dir; 165 166 if (strcmp(optarg, "-") == 0) { 167 if (includes_search_curdir == 0) { 168 fprintf(stderr, "%s: Warning - '-I-' " 169 "specified multiple " 170 "times\n", appname); 171 } 172 includes_search_curdir = 0; 173 for (include_dir = search_path.slh_first; 174 include_dir != NULL; 175 include_dir = include_dir->links.sle_next) 176 /* 177 * All entries before a '-I-' only 178 * apply to includes specified with 179 * quotes instead of "<>". 180 */ 181 include_dir->quoted_includes_only = 1; 182 } else { 183 include_dir = 184 (path_entry_t)malloc(sizeof(*include_dir)); 185 if (include_dir == NULL) { 186 perror(optarg); 187 stop(NULL, EX_OSERR); 188 } 189 include_dir->directory = strdup(optarg); 190 if (include_dir->directory == NULL) { 191 perror(optarg); 192 stop(NULL, EX_OSERR); 193 } 194 include_dir->quoted_includes_only = 0; 195 SLIST_INSERT_HEAD(&search_path, include_dir, 196 links); 197 } 198 break; 199 } 200 case '?': 201 default: 202 usage(); 203 /* NOTREACHED */ 204 } 205 } 206 argc -= optind; 207 argv += optind; 208 209 if (argc != 1) { 210 fprintf(stderr, "%s: No input file specifiled\n", appname); 211 usage(); 212 /* NOTREACHED */ 213 } 214 215 symtable_open(); 216 inputfilename = *argv; 217 include_file(*argv, SOURCE_FILE); 218 retval = yyparse(); 219 if (retval == 0) { 220 if (SLIST_FIRST(&scope_stack) == NULL 221 || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) { 222 stop("Unterminated conditional expression", 223 EX_DATAERR); 224 /* NOTREACHED */ 225 } 226 227 /* Process outmost scope */ 228 process_scope(SLIST_FIRST(&scope_stack)); 229 /* 230 * Decend the tree of scopes and insert/emit 231 * patches as appropriate. We perform a depth first 232 * tranversal, recursively handling each scope. 233 */ 234 /* start at the root scope */ 235 dump_scope(SLIST_FIRST(&scope_stack)); 236 237 /* Patch up forward jump addresses */ 238 back_patch(); 239 240 if (ofile != NULL) 241 output_code(ofile); 242 if (regfile != NULL) { 243 symtable_dump(regfile); 244 } 245 if (listfile != NULL) 246 output_listing(listfile, inputfilename); 247 } 248 249 stop(NULL, 0); 250 /* NOTREACHED */ 251 return (0); 252 } 253 254 static void 255 usage() 256 { 257 258 (void)fprintf(stderr, 259 "usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file] 260 [-r register_output_file] [-l program_list_file] 261 input_file\n", 262 appname); 263 exit(EX_USAGE); 264 } 265 266 static void 267 back_patch() 268 { 269 struct instruction *cur_instr; 270 271 for(cur_instr = seq_program.stqh_first; 272 cur_instr != NULL; 273 cur_instr = cur_instr->links.stqe_next) { 274 if (cur_instr->patch_label != NULL) { 275 struct ins_format3 *f3_instr; 276 u_int address; 277 278 if (cur_instr->patch_label->type != LABEL) { 279 char buf[255]; 280 281 snprintf(buf, sizeof(buf), 282 "Undefined label %s", 283 cur_instr->patch_label->name); 284 stop(buf, EX_DATAERR); 285 /* NOTREACHED */ 286 } 287 f3_instr = &cur_instr->format.format3; 288 address = f3_instr->address; 289 address += cur_instr->patch_label->info.linfo->address; 290 f3_instr->address = address; 291 } 292 } 293 } 294 295 static void 296 output_code(ofile) 297 FILE *ofile; 298 { 299 struct instruction *cur_instr; 300 patch_t *cur_patch; 301 symbol_node_t *cur_node; 302 int instrcount; 303 304 instrcount = 0; 305 fprintf(ofile, 306 "/* 307 * DO NOT EDIT - This file is automatically generated. 308 */\n"); 309 310 fprintf(ofile, "static u_int8_t seqprog[] = {\n"); 311 for(cur_instr = seq_program.stqh_first; 312 cur_instr != NULL; 313 cur_instr = cur_instr->links.stqe_next) { 314 315 fprintf(ofile, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n", 316 cur_instr->format.bytes[0], 317 cur_instr->format.bytes[1], 318 cur_instr->format.bytes[2], 319 cur_instr->format.bytes[3]); 320 instrcount++; 321 } 322 fprintf(ofile, "};\n\n"); 323 324 /* 325 * Output patch information. Patch functions first. 326 */ 327 for(cur_node = SLIST_FIRST(&patch_functions); 328 cur_node != NULL; 329 cur_node = SLIST_NEXT(cur_node,links)) { 330 fprintf(ofile, 331 "static int ahc_patch%d_func(struct ahc_softc *ahc); 332 333 static int 334 ahc_patch%d_func(struct ahc_softc *ahc) 335 { 336 return (%s); 337 }\n\n", 338 cur_node->symbol->info.condinfo->func_num, 339 cur_node->symbol->info.condinfo->func_num, 340 cur_node->symbol->name); 341 } 342 343 fprintf(ofile, 344 "typedef int patch_func_t __P((struct ahc_softc *)); 345 struct patch { 346 patch_func_t *patch_func; 347 u_int32_t begin :10, 348 skip_instr :10, 349 skip_patch :12; 350 } patches[] = {\n"); 351 352 for(cur_patch = STAILQ_FIRST(&patches); 353 cur_patch != NULL; 354 cur_patch = STAILQ_NEXT(cur_patch,links)) { 355 fprintf(ofile, "\t{ ahc_patch%d_func, %d, %d, %d },\n", 356 cur_patch->patch_func, cur_patch->begin, 357 cur_patch->skip_instr, cur_patch->skip_patch); 358 } 359 360 fprintf(ofile, "\n};\n"); 361 362 fprintf(stderr, "%s: %d instructions used\n", appname, instrcount); 363 } 364 365 static void 366 dump_scope(scope_t *scope) 367 { 368 scope_t *cur_scope; 369 370 /* 371 * Emit the first patch for this scope 372 */ 373 emit_patch(scope, 0); 374 375 /* 376 * Dump each scope within this one. 377 */ 378 cur_scope = TAILQ_FIRST(&scope->inner_scope); 379 380 while (cur_scope != NULL) { 381 382 dump_scope(cur_scope); 383 384 cur_scope = TAILQ_NEXT(cur_scope, scope_links); 385 } 386 387 /* 388 * Emit the second, closing, patch for this scope 389 */ 390 emit_patch(scope, 1); 391 } 392 393 void 394 emit_patch(scope_t *scope, int patch) 395 { 396 patch_info_t *pinfo; 397 patch_t *new_patch; 398 399 pinfo = &scope->patches[patch]; 400 401 if (pinfo->skip_instr == 0) 402 /* No-Op patch */ 403 return; 404 405 new_patch = (patch_t *)malloc(sizeof(*new_patch)); 406 407 if (new_patch == NULL) 408 stop("Could not malloc patch structure", EX_OSERR); 409 410 memset(new_patch, 0, sizeof(*new_patch)); 411 412 if (patch == 0) { 413 new_patch->patch_func = scope->func_num; 414 new_patch->begin = scope->begin_addr; 415 } else { 416 new_patch->patch_func = 0; 417 new_patch->begin = scope->end_addr; 418 } 419 new_patch->skip_instr = pinfo->skip_instr; 420 new_patch->skip_patch = pinfo->skip_patch; 421 STAILQ_INSERT_TAIL(&patches, new_patch, links); 422 } 423 424 void 425 output_listing(FILE *listfile, char *ifilename) 426 { 427 char buf[1024]; 428 FILE *ifile; 429 struct instruction *cur_instr; 430 patch_t *cur_patch; 431 symbol_node_t *cur_func; 432 int *func_values; 433 int instrcount; 434 int instrptr; 435 int line; 436 int func_count; 437 int skip_addr; 438 439 instrcount = 0; 440 instrptr = 0; 441 line = 1; 442 skip_addr = 0; 443 if ((ifile = fopen(ifilename, "r")) == NULL) { 444 perror(ifilename); 445 stop(NULL, EX_DATAERR); 446 } 447 448 /* 449 * Determine which options to apply to this listing. 450 */ 451 for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions); 452 cur_func != NULL; 453 cur_func = SLIST_NEXT(cur_func, links)) 454 func_count++; 455 456 if (func_count != 0) { 457 func_values = (int *)malloc(func_count * sizeof(int)); 458 459 if (func_values == NULL) 460 stop("Could not malloc", EX_OSERR); 461 462 func_values[0] = 0; /* FALSE func */ 463 func_count--; 464 465 /* 466 * Ask the user to fill in the return values for 467 * the rest of the functions. 468 */ 469 470 471 for (cur_func = SLIST_FIRST(&patch_functions); 472 cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL; 473 cur_func = SLIST_NEXT(cur_func, links), func_count--) { 474 int input; 475 476 fprintf(stdout, "\n(%s)\n", cur_func->symbol->name); 477 fprintf(stdout, 478 "Enter the return value for " 479 "this expression[T/F]:"); 480 481 while (1) { 482 483 input = getchar(); 484 input = toupper(input); 485 486 if (input == 'T') { 487 func_values[func_count] = 1; 488 break; 489 } else if (input == 'F') { 490 func_values[func_count] = 0; 491 break; 492 } 493 } 494 if (isatty(fileno(stdin)) == 0) 495 putchar(input); 496 } 497 fprintf(stdout, "\nThanks!\n"); 498 } 499 500 /* Now output the listing */ 501 cur_patch = STAILQ_FIRST(&patches); 502 for(cur_instr = STAILQ_FIRST(&seq_program); 503 cur_instr != NULL; 504 cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) { 505 506 if (check_patch(&cur_patch, instrcount, 507 &skip_addr, func_values) == 0) { 508 /* Don't count this instruction as it is in a patch 509 * that was removed. 510 */ 511 continue; 512 } 513 514 while (line < cur_instr->srcline) { 515 fgets(buf, sizeof(buf), ifile); 516 fprintf(listfile, "\t\t%s", buf); 517 line++; 518 } 519 fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr, 520 cur_instr->format.bytes[0], 521 cur_instr->format.bytes[1], 522 cur_instr->format.bytes[2], 523 cur_instr->format.bytes[3]); 524 fgets(buf, sizeof(buf), ifile); 525 fprintf(listfile, "\t%s", buf); 526 line++; 527 instrptr++; 528 } 529 /* Dump the remainder of the file */ 530 while(fgets(buf, sizeof(buf), ifile) != NULL) 531 fprintf(listfile, "\t\t%s", buf); 532 533 fclose(ifile); 534 } 535 536 static int 537 check_patch(patch_t **start_patch, int start_instr, 538 int *skip_addr, int *func_vals) 539 { 540 patch_t *cur_patch; 541 542 cur_patch = *start_patch; 543 544 while (cur_patch != NULL && start_instr == cur_patch->begin) { 545 if (func_vals[cur_patch->patch_func] == 0) { 546 int skip; 547 548 /* Start rejecting code */ 549 *skip_addr = start_instr + cur_patch->skip_instr; 550 for (skip = cur_patch->skip_patch; 551 skip > 0 && cur_patch != NULL; 552 skip--) 553 cur_patch = STAILQ_NEXT(cur_patch, links); 554 } else { 555 /* Accepted this patch. Advance to the next 556 * one and wait for our intruction pointer to 557 * hit this point. 558 */ 559 cur_patch = STAILQ_NEXT(cur_patch, links); 560 } 561 } 562 563 *start_patch = cur_patch; 564 if (start_instr < *skip_addr) 565 /* Still skipping */ 566 return (0); 567 568 return (1); 569 } 570 571 /* 572 * Print out error information if appropriate, and clean up before 573 * terminating the program. 574 */ 575 void 576 stop(string, err_code) 577 const char *string; 578 int err_code; 579 { 580 if (string != NULL) { 581 fprintf(stderr, "%s: ", appname); 582 if (yyfilename != NULL) { 583 fprintf(stderr, "Stopped at file %s, line %d - ", 584 yyfilename, yylineno); 585 } 586 fprintf(stderr, "%s\n", string); 587 } 588 589 if (ofile != NULL) { 590 fclose(ofile); 591 if (err_code != 0) { 592 fprintf(stderr, "%s: Removing %s due to error\n", 593 appname, ofilename); 594 unlink(ofilename); 595 } 596 } 597 598 if (regfile != NULL) { 599 fclose(regfile); 600 if (err_code != 0) { 601 fprintf(stderr, "%s: Removing %s due to error\n", 602 appname, regfilename); 603 unlink(regfilename); 604 } 605 } 606 607 if (listfile != NULL) { 608 fclose(listfile); 609 if (err_code != 0) { 610 fprintf(stderr, "%s: Removing %s due to error\n", 611 appname, listfilename); 612 unlink(listfilename); 613 } 614 } 615 616 symlist_free(&patch_functions); 617 symtable_close(); 618 619 exit(err_code); 620 } 621 622 struct instruction * 623 seq_alloc() 624 { 625 struct instruction *new_instr; 626 627 new_instr = (struct instruction *)malloc(sizeof(struct instruction)); 628 if (new_instr == NULL) 629 stop("Unable to malloc instruction object", EX_SOFTWARE); 630 memset(new_instr, 0, sizeof(*new_instr)); 631 STAILQ_INSERT_TAIL(&seq_program, new_instr, links); 632 new_instr->srcline = yylineno; 633 return new_instr; 634 } 635 636 scope_t * 637 scope_alloc() 638 { 639 scope_t *new_scope; 640 641 new_scope = (scope_t *)malloc(sizeof(scope_t)); 642 if (new_scope == NULL) 643 stop("Unable to malloc scope object", EX_SOFTWARE); 644 memset(new_scope, 0, sizeof(*new_scope)); 645 TAILQ_INIT(&new_scope->inner_scope); 646 647 if (SLIST_FIRST(&scope_stack) != NULL) { 648 TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope, 649 new_scope, scope_links); 650 } 651 /* This patch is now the current scope */ 652 SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links); 653 return new_scope; 654 } 655 656 void 657 process_scope(scope_t *scope) 658 { 659 /* 660 * We are "leaving" this scope. We should now have 661 * enough information to process the lists of scopes 662 * we encapsulate. 663 */ 664 scope_t *cur_scope; 665 u_int skip_patch_count; 666 u_int skip_instr_count; 667 668 cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq); 669 skip_patch_count = 0; 670 skip_instr_count = 0; 671 while (cur_scope != NULL) { 672 u_int patch0_patch_skip; 673 674 patch0_patch_skip = 0; 675 switch (cur_scope->type) { 676 case SCOPE_IF: 677 case SCOPE_ELSE_IF: 678 if (skip_instr_count != 0) { 679 /* Create a tail patch */ 680 patch0_patch_skip++; 681 cur_scope->patches[1].skip_patch = 682 skip_patch_count + 1; 683 cur_scope->patches[1].skip_instr = 684 skip_instr_count; 685 } 686 687 /* Count Head patch */ 688 patch0_patch_skip++; 689 690 /* Count any patches contained in our inner scope */ 691 patch0_patch_skip += cur_scope->inner_scope_patches; 692 693 cur_scope->patches[0].skip_patch = patch0_patch_skip; 694 cur_scope->patches[0].skip_instr = 695 cur_scope->end_addr - cur_scope->begin_addr; 696 697 skip_instr_count += cur_scope->patches[0].skip_instr; 698 699 skip_patch_count += patch0_patch_skip; 700 if (cur_scope->type == SCOPE_IF) { 701 scope->inner_scope_patches += skip_patch_count; 702 skip_patch_count = 0; 703 skip_instr_count = 0; 704 } 705 break; 706 case SCOPE_ELSE: 707 /* Count any patches contained in our innter scope */ 708 skip_patch_count += cur_scope->inner_scope_patches; 709 710 skip_instr_count += cur_scope->end_addr 711 - cur_scope->begin_addr; 712 break; 713 case SCOPE_ROOT: 714 stop("Unexpected scope type encountered", EX_SOFTWARE); 715 /* NOTREACHED */ 716 } 717 718 cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links); 719 } 720 } 721