1 /*+M************************************************************************* 2 * Adaptec AIC7770/AIC7870 sequencer code assembler. 3 * 4 * Copyright (c) 1994 John Aycock 5 * The University of Calgary Department of Computer Science. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions, and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of Calgary 19 * Department of Computer Science and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * Comments are started by `#' and continue to the end of the line; lines 37 * may be of the form: 38 * <label>* 39 * <label>* <undef-sym> = <value> 40 * <label>* <opcode> <operand>* 41 * 42 * A <label> is an <undef-sym> ending in a colon. Spaces, tabs, and commas 43 * are token separators. 44 * 45 *-M*************************************************************************/ 46 static char id[] = "$Id: aic7xxx_asm.c,v 1.8 1995/04/15 21:45:56 gibbs Exp $"; 47 #include <ctype.h> 48 #include <stdio.h> 49 #include <string.h> 50 #include <stdlib.h> 51 #include <unistd.h> 52 53 #define MEMORY 448 54 #define MAXLINE 1024 55 #define MAXTOKEN 32 56 #define ADOTOUT "a.out" 57 #define NOVALUE -1 58 59 /* 60 * AIC-7770/AIC-7870 register definitions 61 */ 62 #define R_SINDEX 0x65 63 #define R_ALLONES 0x69 64 #define R_ALLZEROS 0x6a 65 #define R_NONE 0x6a 66 67 int debug; 68 int lineno, LC; 69 char *filename; 70 FILE *ifp, *ofp; 71 unsigned char M[MEMORY][4]; 72 73 void 74 error(char *s) 75 { 76 fprintf(stderr, "%s: %s at line %d\n", filename, s, lineno); 77 exit(EXIT_FAILURE); 78 } 79 80 void * 81 Malloc(size_t size) 82 { 83 void *p = malloc(size); 84 if (!p) 85 error("out of memory"); 86 return(p); 87 } 88 89 void * 90 Realloc(void *ptr, size_t size) 91 { 92 void *p = realloc(ptr, size); 93 if (!p) 94 error("out of memory"); 95 return(p); 96 } 97 98 char * 99 Strdup(char *s) 100 { 101 char *p = (char *)Malloc(strlen(s) + 1); 102 strcpy(p, s); 103 return(p); 104 } 105 106 typedef struct sym_t { 107 struct sym_t *next; /* MUST BE FIRST */ 108 char *name; 109 int value; 110 int npatch; 111 int *patch; 112 } sym_t; 113 114 sym_t *head; 115 116 void 117 define(char *name, int value) 118 { 119 sym_t *p, *q; 120 121 for (p = head, q = (sym_t *)&head; p; p = p->next) { 122 if (!strcmp(p->name, name)) 123 error("redefined symbol"); 124 q = p; 125 } 126 127 p = q->next = (sym_t *)Malloc(sizeof(sym_t)); 128 p->next = NULL; 129 p->name = Strdup(name); 130 p->value = value; 131 p->npatch = 0; 132 p->patch = NULL; 133 134 if (debug) { 135 fprintf(stderr, "\"%s\" ", p->name); 136 if (p->value != NOVALUE) 137 fprintf(stderr, "defined as 0x%x\n", p->value); 138 else 139 fprintf(stderr, "undefined\n"); 140 } 141 } 142 143 sym_t * 144 lookup(char *name) 145 { 146 sym_t *p; 147 148 for (p = head; p; p = p->next) 149 if (!strcmp(p->name, name)) 150 return(p); 151 return(NULL); 152 } 153 154 void 155 patch(sym_t *p, int location) 156 { 157 p->npatch += 1; 158 p->patch = (int *)Realloc(p->patch, p->npatch * sizeof(int *)); 159 160 p->patch[p->npatch - 1] = location; 161 } 162 163 void backpatch(void) 164 { 165 int i; 166 sym_t *p; 167 168 for (p = head; p; p = p->next) { 169 170 if (p->value == NOVALUE) { 171 fprintf(stderr, 172 "%s: undefined symbol \"%s\"\n", 173 filename, p->name); 174 exit(EXIT_FAILURE); 175 } 176 177 if (p->npatch) { 178 if (debug) 179 fprintf(stderr, 180 "\"%s\" (0x%x) patched at", 181 p->name, p->value); 182 183 for (i = 0; i < p->npatch; i++) { 184 M[p->patch[i]][0] &= ~1; 185 M[p->patch[i]][0] |= ((p->value >> 8) & 1); 186 M[p->patch[i]][1] = p->value & 0xff; 187 188 if (debug) 189 fprintf(stderr, " 0x%x", p->patch[i]); 190 } 191 192 if (debug) 193 fputc('\n', stderr); 194 } 195 } 196 } 197 198 /* 199 * Output words in byte-reversed order (least significant first) 200 * since the sequencer RAM is loaded that way. 201 */ 202 void 203 output(FILE *fp) 204 { 205 int i; 206 207 for (i = 0; i < LC; i++) 208 fprintf(fp, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n", 209 M[i][3], 210 M[i][2], 211 M[i][1], 212 M[i][0]); 213 printf("%d out of %d instructions used.\n", LC, MEMORY); 214 } 215 216 char ** 217 getl(int *n) 218 { 219 int i; 220 char *p, *quote; 221 static char buf[MAXLINE]; 222 static char *a[MAXTOKEN]; 223 224 i = 0; 225 226 while (fgets(buf, sizeof(buf), ifp)) { 227 228 lineno += 1; 229 230 if (buf[strlen(buf)-1] != '\n') 231 error("line too long"); 232 233 p = strchr(buf, '#'); 234 if (p) 235 *p = '\0'; 236 p = buf; 237 rescan: 238 quote = strchr(p, '\"'); 239 if (quote) 240 *quote = '\0'; 241 for (p = strtok(p, ", \t\n"); p; p = strtok(NULL, ", \t\n")) 242 if (i < MAXTOKEN-1) 243 a[i++] = p; 244 else 245 error("too many tokens"); 246 if (quote) { 247 quote++; 248 p = strchr(quote, '\"'); 249 if (!p) 250 error("unterminated string constant"); 251 else if (i < MAXTOKEN-1) { 252 a[i++] = quote; 253 *p = '\0'; 254 p++; 255 } 256 else 257 error("too many tokens"); 258 goto rescan; 259 } 260 if (i) { 261 *n = i; 262 return(a); 263 } 264 } 265 return(NULL); 266 } 267 268 #define A 0x8000 /* `A'ccumulator ok */ 269 #define I 0x4000 /* use as immediate value */ 270 #define SL 0x2000 /* shift left */ 271 #define SR 0x1000 /* shift right */ 272 #define RL 0x0800 /* rotate left */ 273 #define RR 0x0400 /* rotate right */ 274 #define LO 0x8000 /* lookup: ori-{jmp,jc,jnc,call} */ 275 #define LA 0x4000 /* lookup: and-{jz,jnz} */ 276 #define LX 0x2000 /* lookup: xor-{je,jne} */ 277 #define NA -1 /* not applicable */ 278 279 struct { 280 char *name; 281 int n; /* number of operands, including opcode */ 282 unsigned int op; /* immediate or L?|pos_from_0 */ 283 unsigned int dest; /* NA, pos_from_0, or I|immediate */ 284 unsigned int src; /* NA, pos_from_0, or I|immediate */ 285 unsigned int imm; /* pos_from_0, A|pos_from_0, or I|immediate */ 286 unsigned int addr; /* NA or pos_from_0 */ 287 int fmt; /* instruction format - 1, 2, or 3 */ 288 } instr[] = { 289 /* 290 * N OP DEST SRC IMM ADDR FMT 291 */ 292 { "mov", 3, 1, 1, 2, I|0xff, NA, 1 }, 293 { "mov", 4, LO|2, NA, 1, I|0, 3, 3 }, 294 { "mvi", 3, 0, 1, I|R_ALLZEROS, A|2, NA, 1 }, 295 { "mvi", 4, LO|2, NA, I|R_ALLZEROS, 1, 3, 3 }, 296 { "not", 2, 2, 1, 1, I|0xff, NA, 1 }, 297 { "and", 3, 1, 1, 1, A|2, NA, 1 }, 298 { "and", 4, 1, 1, 3, A|2, NA, 1 }, 299 { "or", 3, 0, 1, 1, A|2, NA, 1 }, 300 { "or", 4, 0, 1, 3, A|2, NA, 1 }, 301 { "or", 5, LO|3, NA, 1, 2, 4, 3 }, 302 { "xor", 3, 2, 1, 1, A|2, NA, 1 }, 303 { "xor", 4, 2, 1, 3, A|2, NA, 1 }, 304 { "nop", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1 }, 305 { "inc", 2, 3, 1, 1, I|1, NA, 1 }, 306 { "inc", 3, 3, 1, 2, I|1, NA, 1 }, 307 { "dec", 2, 3, 1, 1, I|0xff, NA, 1 }, 308 { "dec", 3, 3, 1, 2, I|0xff, NA, 1 }, 309 { "jmp", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 }, 310 { "jc", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 }, 311 { "jnc", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 }, 312 { "call", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 }, 313 { "test", 5, LA|3, NA, 1, A|2, 4, 3 }, 314 { "cmp", 5, LX|3, NA, 1, A|2, 4, 3 }, 315 { "ret", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1 }, 316 { "ret", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1 }, 317 { "clc", 1, 3, I|R_NONE, I|R_ALLZEROS, I|1, NA, 1 }, 318 { "clc", 4, 3, 2, I|R_ALLZEROS, A|3, NA, 1 }, 319 { "stc", 2, 3, 1, I|R_ALLONES, I|1, NA, 1 }, 320 { "add", 3, 3, 1, 1, A|2, NA, 1 }, 321 { "add", 4, 3, 1, 3, A|2, NA, 1 }, 322 { "adc", 3, 4, 1, 1, A|2, NA, 1 }, 323 { "adc", 4, 4, 1, 3, A|2, NA, 1 }, 324 { "shl", 3, 5, 1, 1, SL|2, NA, 2 }, 325 { "shl", 4, 5, 1, 2, SL|3, NA, 2 }, 326 { "shr", 3, 5, 1, 1, SR|2, NA, 2 }, 327 { "shr", 4, 5, 1, 2, SR|3, NA, 2 }, 328 { "rol", 3, 5, 1, 1, RL|2, NA, 2 }, 329 { "rol", 4, 5, 1, 2, RL|3, NA, 2 }, 330 { "ror", 3, 5, 1, 1, RR|2, NA, 2 }, 331 { "ror", 4, 5, 1, 2, RR|3, NA, 2 }, 332 /* 333 * Extensions (note also that mvi allows A) 334 */ 335 { "clr", 2, 1, 1, I|R_ALLZEROS, I|0xff, NA, 1 }, 336 { 0, 0, 0, 0, 0, 0, 0, 0 } 337 }; 338 339 int 340 eval_operand(char **a, int spec) 341 { 342 int i; 343 unsigned int want = spec & (LO|LA|LX); 344 345 static struct { 346 unsigned int what; 347 char *name; 348 int value; 349 } jmptab[] = { 350 { LO, "jmp", 8 }, 351 { LO, "jc", 9 }, 352 { LO, "jnc", 10 }, 353 { LO, "call", 11 }, 354 { LA, "jz", 15 }, 355 { LA, "jnz", 13 }, 356 { LX, "je", 14 }, 357 { LX, "jne", 12 }, 358 }; 359 360 spec &= ~(LO|LA|LX); 361 362 for (i = 0; i < sizeof(jmptab)/sizeof(jmptab[0]); i++) 363 if (jmptab[i].what == want && 364 !strcmp(jmptab[i].name, a[spec])) 365 { 366 return(jmptab[i].value); 367 } 368 369 if (want) 370 error("invalid jump"); 371 372 return(spec); /* "case 0" - no flags set */ 373 } 374 375 int 376 eval_sdi(char **a, int spec) 377 { 378 sym_t *p; 379 unsigned val; 380 381 if (spec == NA) 382 return(NA); 383 384 switch (spec & (A|I|SL|SR|RL|RR)) { 385 case SL: 386 case SR: 387 case RL: 388 case RR: 389 if (isdigit(*a[spec &~ (SL|SR|RL|RR)])) 390 val = strtol(a[spec &~ (SL|SR|RL|RR)], NULL, 0); 391 else { 392 p = lookup(a[spec &~ (SL|SR|RL|RR)]); 393 if (!p) 394 error("undefined symbol used"); 395 val = p->value; 396 } 397 398 switch (spec & (SL|SR|RL|RR)) { /* blech */ 399 case SL: 400 if (val > 7) 401 return(0xf0); 402 return(((val % 8) << 4) | 403 (val % 8)); 404 case SR: 405 if (val > 7) 406 return(0xf0); 407 return(((val % 8) << 4) | 408 (1 << 3) | 409 ((8 - (val % 8)) % 8)); 410 case RL: 411 return(val % 8); 412 case RR: 413 return((8 - (val % 8)) % 8); 414 } 415 case I: 416 return(spec &~ I); 417 case A: 418 /* 419 * An immediate field of zero selects 420 * the accumulator. Vigorously object 421 * if zero is given otherwise - it's 422 * most likely an error. 423 */ 424 spec &= ~A; 425 if (!strcmp("A", a[spec])) 426 return(0); 427 if (isdigit(*a[spec]) && 428 strtol(a[spec], NULL, 0) == 0) 429 { 430 error("immediate value of zero selects accumulator"); 431 } 432 /* falls through */ 433 case 0: 434 if (isdigit(*a[spec])) 435 return(strtol(a[spec], NULL, 0)); 436 p = lookup(a[spec]); 437 if (p) 438 return(p->value); 439 error("undefined symbol used"); 440 } 441 442 return(NA); /* shut the compiler up */ 443 } 444 445 int 446 eval_addr(char **a, int spec) 447 { 448 sym_t *p; 449 450 if (spec == NA) 451 return(NA); 452 if (isdigit(*a[spec])) 453 return(strtol(a[spec], NULL, 0)); 454 455 p = lookup(a[spec]); 456 457 if (p) { 458 if (p->value != NOVALUE) 459 return(p->value); 460 patch(p, LC); 461 } else { 462 define(a[spec], NOVALUE); 463 p = lookup(a[spec]); 464 patch(p, LC); 465 } 466 467 return(NA); /* will be patched in later */ 468 } 469 470 int 471 crack(char **a, int n) 472 { 473 int i; 474 int I_imm, I_addr; 475 int I_op, I_dest, I_src, I_ret; 476 477 /* 478 * Check for "ret" at the end of the line; remove 479 * it unless it's "ret" alone - we still want to 480 * look it up in the table. 481 */ 482 I_ret = (strcmp(a[n-1], "ret") ? 0 : !0); 483 if (I_ret && n > 1) 484 n -= 1; 485 486 for (i = 0; instr[i].name; i++) { 487 /* 488 * Look for match in table given constraints, 489 * currently just the name and the number of 490 * operands. 491 */ 492 if (!strcmp(instr[i].name, *a) && instr[i].n == n) 493 break; 494 } 495 if (!instr[i].name) 496 error("unknown opcode or wrong number of operands"); 497 498 I_op = eval_operand(a, instr[i].op); 499 I_src = eval_sdi(a, instr[i].src); 500 I_imm = eval_sdi(a, instr[i].imm); 501 I_dest = eval_sdi(a, instr[i].dest); 502 I_addr = eval_addr(a, instr[i].addr); 503 504 if( LC >= MEMORY ) 505 error("Memory exhausted!\n"); 506 507 switch (instr[i].fmt) { 508 case 1: 509 case 2: 510 M[LC][0] = (I_op << 1) | I_ret; 511 M[LC][1] = I_dest; 512 M[LC][2] = I_src; 513 M[LC][3] = I_imm; 514 break; 515 case 3: 516 if (I_ret) 517 error("illegal use of \"ret\""); 518 M[LC][0] = (I_op << 1) | ((I_addr >> 8) & 1); 519 M[LC][1] = I_addr & 0xff; 520 M[LC][2] = I_src; 521 M[LC][3] = I_imm; 522 break; 523 } 524 525 return (1); /* no two-byte instructions yet */ 526 } 527 528 #undef SL 529 #undef SR 530 #undef RL 531 #undef RR 532 #undef LX 533 #undef LA 534 #undef LO 535 #undef I 536 #undef A 537 538 void 539 assemble(void) 540 { 541 int n; 542 char **a; 543 sym_t *p; 544 545 while ((a = getl(&n))) { 546 547 while (a[0][strlen(*a)-1] == ':') { 548 a[0][strlen(*a)-1] = '\0'; 549 p = lookup(*a); 550 if (p) 551 p->value = LC; 552 else 553 define(*a, LC); 554 a += 1; 555 n -= 1; 556 } 557 558 if (!n) /* line was all labels */ 559 continue; 560 561 if (n == 3 && !strcmp("VERSION", *a)) 562 fprintf(ofp, "#define %s \"%s\"\n", a[1], a[2]); 563 else { 564 if (n == 3 && !strcmp("=", a[1])) 565 define(*a, strtol(a[2], NULL, 0)); 566 else 567 LC += crack(a, n); 568 } 569 } 570 571 backpatch(); 572 output(ofp); 573 574 if (debug) 575 output(stderr); 576 } 577 578 int 579 main(int argc, char **argv) 580 { 581 int c; 582 583 while ((c = getopt(argc, argv, "dho:vD")) != EOF) { 584 switch (c) { 585 case 'd': 586 debug = !0; 587 break; 588 case 'D': 589 { 590 char *p; 591 if ((p = strchr(optarg, '=')) != NULL) { 592 *p = '\0'; 593 define(optarg, strtol(p + 1, NULL, 0)); 594 } 595 else 596 define(optarg, 1); 597 break; 598 } 599 case 'o': 600 ofp = fopen(optarg, "w"); 601 if (!ofp) { 602 perror(optarg); 603 exit(EXIT_FAILURE); 604 } 605 break; 606 case 'h': 607 printf("usage: %s [-d] [-Dname] [-ooutput] input\n", 608 *argv); 609 exit(EXIT_SUCCESS); 610 break; 611 case 'v': 612 printf("%s\n", id); 613 exit(EXIT_SUCCESS); 614 break; 615 default: 616 exit(EXIT_FAILURE); 617 break; 618 } 619 } 620 621 if (argc - optind != 1) { 622 fprintf(stderr, "%s: must have one input file\n", *argv); 623 exit(EXIT_FAILURE); 624 } 625 filename = argv[optind]; 626 627 ifp = fopen(filename, "r"); 628 if (!ifp) { 629 perror(filename); 630 exit(EXIT_FAILURE); 631 } 632 633 if (!ofp) { 634 ofp = fopen(ADOTOUT, "w"); 635 if (!ofp) { 636 perror(ADOTOUT); 637 exit(EXIT_FAILURE); 638 } 639 } 640 641 assemble(); 642 exit(EXIT_SUCCESS); 643 } 644