1596Sbill /* Copyright (c) 1980 Regents of the University of California */ 2*679Shenry static char sccsid[] = "@(#)asmain.c 4.8 08/20/80"; 3596Sbill #include <stdio.h> 4596Sbill #include <ctype.h> 5596Sbill #include <signal.h> 6596Sbill 7596Sbill #include "as.h" 8596Sbill #include "assyms.h" 9596Sbill #include "asexpr.h" 10596Sbill #include "asscan.h" 11596Sbill 12596Sbill #ifdef UNIX 13*679Shenry #define unix_lang_name "VAX/UNIX Assembler V08/20/80 4.8" 14596Sbill #endif 15596Sbill 16596Sbill #ifdef VMS 17631Shenry #define vms_lang_name "VAX/VMS C Assembler V1.00" 18596Sbill #endif VMS 19596Sbill 20596Sbill /* 21596Sbill * variables to manage reading the assembly source files 22596Sbill */ 23596Sbill char *dotsname; /*the current file name; managed by the parser*/ 24596Sbill int lineno; /*current line number; managed by the parser*/ 25643Sbill char **innames; /*names of the files being assembled*/ 26596Sbill int ninfiles; /*how many interesting files there are*/ 27596Sbill /* 28596Sbill * Flags settable from the argv process argument list 29596Sbill */ 30596Sbill int silent = 0; /*don't complain about any errors*/ 31596Sbill int savelabels = 0; /*write the labels to the a.out file*/ 32596Sbill int d124 = 4; /*default allocate 4 bytes for unknown pointers*/ 33596Sbill int anyerrs = 0; /*no errors yet*/ 34596Sbill int orgwarn = 0; /*Bad origins*/ 35596Sbill int passno = 1; /* current pass*/ 36636Shenry int jxxxJUMP = 0; /* in jxxxes that branch too far, use jmp instead of brw */ 37638Sbill int readonlydata = 0; /* initialzed data -> text space */ 38596Sbill 39596Sbill #ifdef DEBUG 40596Sbill int debug = 0; 41596Sbill int toktrace = 0; 42596Sbill #endif 43596Sbill 44596Sbill int useVM = /*put the temp file in virtual memory*/ 45596Sbill #ifdef VMS 46596Sbill 1; /*VMS has virtual memory (duh)*/ 47596Sbill #endif VMS 48596Sbill #ifdef UNIX 49596Sbill 0; 50596Sbill #endif 51596Sbill 52596Sbill char *endcore; /*where to get more symbol space*/ 53596Sbill 54596Sbill /* 55596Sbill * Managers of the a.out file. 56596Sbill */ 57596Sbill struct exec hdr; 58638Sbill #define MAGIC 0407 59596Sbill u_long tsize; /* total text size */ 60596Sbill u_long dsize; /* total data size */ 61596Sbill u_long datbase; /* base of the data segment */ 62596Sbill u_long trsize; /* total text relocation size */ 63596Sbill u_long drsize; /* total data relocation size */ 64596Sbill 65596Sbill /* 66596Sbill * Information about the current segment is accumulated in 67596Sbill * usedot; the most important information stored is the 68596Sbill * accumulated size of each of the text and data segments 69596Sbill * 70596Sbill * dotp points to the correct usedot expression for the current segment 71596Sbill */ 72596Sbill struct exp usedot[NLOC+NLOC]; /* info about all segments */ 73596Sbill struct exp *dotp; /* data/text location pointer */ 74596Sbill /* 75596Sbill * The inter pass temporary file is opened and closed by stdio, but 76596Sbill * is written to using direct read/write, as the temporary file 77596Sbill * is composed of buffers exactly BUFSIZ long. 78596Sbill */ 79596Sbill FILE *tmpfil; /* interpass communication file */ 80596Sbill /* 81596Sbill * a.out is created during the second pass. 82596Sbill * It is opened by stdio, but is filled with the parallel 83596Sbill * block I/O library 84596Sbill */ 85596Sbill char *outfile = "a.out"; 86596Sbill FILE *a_out_file; 87596Sbill off_t a_out_off; /* cumulative offsets for segments */ 88596Sbill /* 89596Sbill * The logical files containing the assembled data for each of 90596Sbill * the text and data segments are 91596Sbill * managed by the parallel block I/O library. 92596Sbill * a.out is logically opened in many places at once to 93596Sbill * receive the assembled data from the various segments as 94596Sbill * it all trickles in, but is physically opened only once 95596Sbill * to minimize file overhead. 96596Sbill */ 97596Sbill BFILE *usefile[NLOC+NLOC]; /* text/data files */ 98596Sbill BFILE *txtfil; /* current text/data file */ 99596Sbill /* 100596Sbill * Relocation information is accumulated seperately for each 101596Sbill * segment. This is required by the old loader (from BTL), 102596Sbill * but not by the new loader (Bill Joy). 103596Sbill * 104596Sbill * However, the size of the relocation information can not be computed 105596Sbill * during or after the 1st pass because the ''absoluteness' of values 106596Sbill * is unknown until all locally declared symbols have been seen. 107596Sbill * Thus, the size of the relocation information is only 108596Sbill * known after the second pass is finished. 109596Sbill * This obviates the use of the block I/O 110596Sbill * library, which requires knowing the exact offsets in a.out. 111596Sbill * 112596Sbill * So, we save the relocation information internally (we don't 113596Sbill * go to internal files to minimize overhead). 114596Sbill * 115596Sbill * Empirically, we studied 259 files composing the system, 116596Sbill * two compilers and a compiler generator: (all of which have 117596Sbill * fairly large source files) 118596Sbill * 119596Sbill * Number of files = 259 120596Sbill * Number of non zero text reloc files: 233 121596Sbill * Number of non zero data reloc files: 53 122596Sbill * Average text relocation = 889 123596Sbill * Average data relocation = 346 124596Sbill * Number of files > BUFSIZ text relocation = 71 125596Sbill * Number of files > BUFSIZ data relocation = 6 126596Sbill * 127596Sbill * For compiled C code, there is usually one text segment and two 128596Sbill * data segments; we see that allocating our own buffers and 129596Sbill * doing our internal handling of relocation information will, 130596Sbill * on the average, not use more memory than taken up by the buffers 131596Sbill * allocated for doing file I/O in parallel to a number of file. 132596Sbill * 133596Sbill * If we are assembling with the -V option, we 134596Sbill * use the left over token buffers from the 2nd pass, 135596Sbill * otherwise, we create our own. 136596Sbill * 137596Sbill * When the 2nd pass is complete, closeoutrel flushes the token 138596Sbill * buffers out to a BFILE. 139596Sbill * 140596Sbill * The internals to relbufdesc are known only in assyms.c 141596Sbill * 142596Sbill * outrel constructs the relocation information. 143596Sbill * closeoutrel flushes the relocation information to relfil. 144596Sbill */ 145596Sbill struct relbufdesc *rusefile[NLOC+NLOC]; 146596Sbill struct relbufdesc *relfil; /* un concatnated relocation info */ 147596Sbill BFILE *relocfile; /* concatnated relocation info */ 148596Sbill /* 149596Sbill * Once the relocation information has been written, 150596Sbill * we can write out the symbol table using the Block I/O 151596Sbill * mechanisms, as we once again know the offsets into 152596Sbill * the a.out file. 153596Sbill * 154596Sbill * We use relfil to output the symbol table information. 155596Sbill */ 156596Sbill 157596Sbill char *tmpdirprefix = 158596Sbill #ifdef UNIX 159596Sbill "/tmp/"; 160596Sbill #else VMS 161596Sbill "/usr/tmp/"; 162596Sbill #endif 163596Sbill 164596Sbill #define TMP_SUFFIX "asXXXXXX" 165596Sbill char tmpn1[TNAMESIZE]; 166596Sbill 167596Sbill int delexit(); 168596Sbill 169596Sbill main(argc, argv) 170596Sbill int argc; 171596Sbill char **argv; 172596Sbill { 173596Sbill 174596Sbill tmpn1[0] = 0; 175596Sbill endcore = (char *)sbrk(0); 176596Sbill 177596Sbill argprocess(argc, argv); /* process argument lists */ 178596Sbill if (anyerrs) exit(1); 179596Sbill 180596Sbill initialize(); 181596Sbill zeroorigins(); /* set origins to zero */ 182596Sbill zerolocals(); /* fix local label counters */ 183596Sbill 184596Sbill i_pass1(); /* open temp files, etc */ 185596Sbill pass1(); /* first pass through .s files */ 186596Sbill testlocals(); /* check for undefined locals */ 187596Sbill if (anyerrs) delexit(); 188596Sbill 189596Sbill pass1_5(); /* resolve jxxx */ 190596Sbill if (anyerrs) delexit(); 191596Sbill 192596Sbill open_a_out(); /* open a.out */ 193*679Shenry roundsegments(); /* round segments to FW */ 194596Sbill build_hdr(); /* build initial header, and output */ 195596Sbill 196596Sbill i_pass2(); /* reopen temporary file, etc */ 197596Sbill pass2(); /* second pass through the virtual .s */ 198596Sbill if (anyerrs) delexit(); 199596Sbill 200*679Shenry fillsegments(); /* fill segments with 0 to FW */ 201596Sbill reloc_syms(); /* dump relocation and symbol table */ 202596Sbill 203596Sbill delete(); /* remove tmp file */ 204596Sbill bflush(); /* close off block I/O view of a.out */ 205596Sbill fix_a_out(); /* add in text and data reloc counts */ 206596Sbill 207596Sbill if (anyerrs == 0 && orgwarn) 208596Sbill yyerror("Caution: absolute origins.\n"); 209596Sbill exit(anyerrs != 0); 210596Sbill } /*end of UNIX main*/ 211596Sbill 212596Sbill argprocess(argc, argv) 213596Sbill int argc; 214596Sbill char *argv[]; 215596Sbill { 216596Sbill register char *cp; 217596Sbill 218596Sbill ninfiles = 0; 219596Sbill silent = 0; 220596Sbill #ifdef DEBUG 221596Sbill debug = 0; 222596Sbill #endif 223643Sbill innames = (char **)ClearCalloc(argc+1, sizeof (innames[0])); 224596Sbill dotsname = "<argv error>"; 225596Sbill while (argc > 1) { 226643Sbill if (argv[1][0] != '-') 227643Sbill innames[ninfiles++] = argv[1]; 228643Sbill else { 229596Sbill cp = argv[1] + 1; 230596Sbill /* 231596Sbill * We can throw away single minus signs, so 232596Sbill * that make scripts for the PDP 11 assembler work 233596Sbill * on this assembler too 234596Sbill */ 235596Sbill while (*cp){ 236596Sbill switch(*cp++){ 237596Sbill default: 238596Sbill yyerror("Unknown flag: %c", *--cp); 239596Sbill cp++; 240596Sbill break; 241596Sbill case 'd': 242596Sbill d124 = *cp++ - '0'; 243596Sbill if ( (d124 != 1) && (d124 != 2) && 244596Sbill (d124 != 4)){ 245596Sbill yyerror("-d[124] only"); 246596Sbill exit(1); 247596Sbill } 248596Sbill break; 249596Sbill case 'o': 250596Sbill if (argc < 3){ 251596Sbill yyerror("-o what???"); 252596Sbill exit(1); 253596Sbill } 254596Sbill outfile = argv[2]; 255596Sbill bumpone: 256596Sbill argc -= 2; 257596Sbill argv += 2; 258596Sbill goto nextarg; 259596Sbill 260596Sbill case 't': 261596Sbill if (argc < 3){ 262596Sbill yyerror("-t what???"); 263596Sbill exit(1); 264596Sbill } 265596Sbill tmpdirprefix = argv[2]; 266596Sbill goto bumpone; 267596Sbill 268596Sbill case 'V': 269596Sbill useVM = 1; 270596Sbill break; 271596Sbill case 'W': 272596Sbill silent = 1; 273596Sbill break; 274596Sbill case 'L': 275596Sbill savelabels = 1; 276596Sbill break; 277636Shenry case 'J': 278636Shenry jxxxJUMP = 1; 279636Shenry break; 280596Sbill #ifdef DEBUG 281596Sbill case 'D': 282596Sbill debug = 1; 283596Sbill break; 284596Sbill case 'T': 285596Sbill toktrace = 1; 286596Sbill break; 287596Sbill #endif 288638Sbill case 'R': 289638Sbill readonlydata = 1; 290638Sbill break; 291596Sbill } /*end of the switch*/ 292596Sbill } /*end of pulling out all arguments*/ 293596Sbill } /*end of a flag argument*/ 294596Sbill --argc; ++argv; 295596Sbill nextarg:; 296596Sbill } 297643Sbill /* innames[ninfiles] = 0; */ 298596Sbill } 299596Sbill 300596Sbill initialize() 301596Sbill { 302596Sbill if (signal(SIGINT, SIG_IGN) != SIG_IGN) 303596Sbill signal(SIGINT, delexit); 304596Sbill /* 305596Sbill * Install symbols in the table 306596Sbill */ 307596Sbill symtabinit(); 308596Sbill syminstall(); 309596Sbill /* 310596Sbill * Build the expression parser accelerator token sets 311596Sbill */ 312596Sbill buildtokensets(); 313596Sbill } 314596Sbill 315596Sbill zeroorigins() 316596Sbill { 317596Sbill register int locindex; 318596Sbill /* 319596Sbill * Mark usedot: the first NLOC slots are for named text segments, 320596Sbill * the next for named data segments. 321596Sbill */ 322596Sbill for (locindex = 0; locindex < NLOC; locindex++){ 323631Shenry usedot[locindex].e_xtype = XTEXT; 324631Shenry usedot[NLOC + locindex].e_xtype = XDATA; 325631Shenry usedot[locindex].e_xvalue = 0; 326631Shenry usedot[NLOC + locindex].e_xvalue = 0; 327631Shenry usedot[locindex].e_yvalue = 0; 328631Shenry usedot[NLOC + locindex].e_yvalue = 0; 329596Sbill } 330596Sbill } 331596Sbill 332596Sbill zerolocals() 333596Sbill { 334596Sbill register int i; 335596Sbill 336596Sbill for (i = 0; i <= 9; i++) { 337596Sbill lgensym[i] = 1; 338596Sbill genref[i] = 0; 339596Sbill } 340596Sbill } 341596Sbill 342596Sbill i_pass1() 343596Sbill { 344596Sbill if (useVM == 0){ 345596Sbill strcat(tmpn1, tmpdirprefix); 346644Shenry if (tmpdirprefix[strlen(tmpdirprefix)-1] != '/') 347644Shenry strcat(tmpn1, "/"); 348596Sbill strcat(tmpn1, TMP_SUFFIX); 349596Sbill mktemp(tmpn1); 350596Sbill tmpfil = fopen(tmpn1, "w"); 351596Sbill if (tmpfil==NULL) { 352596Sbill yyerror("Bad pass 1 temporary file for writing %s", tmpn1); 353596Sbill delexit(); 354596Sbill } 355596Sbill } 356596Sbill 357596Sbill inittmpfile(); 358636Shenry initijxxx(); 359596Sbill } 360596Sbill 361596Sbill pass1() 362596Sbill { 363596Sbill register int i; 364596Sbill 365596Sbill passno = 1; 366596Sbill dotp = &usedot[0]; 367596Sbill txtfil = (BFILE *)0; 368596Sbill relfil = (struct relbufdesc *)0; 369596Sbill 370596Sbill if (ninfiles == 0){ /*take the input from stdin directly*/ 371596Sbill lineno = 1; 372596Sbill dotsname = "<stdin>"; 373596Sbill 374596Sbill yyparse(); 375596Sbill } else { /*we have the names tanked*/ 376596Sbill for (i = 0; i < ninfiles; i++){ 377596Sbill new_dot_s(innames[i]); 378596Sbill if (freopen(innames[i], "r", stdin) == NULL) { 379596Sbill yyerror( "Can't open source file %s\n", 380596Sbill innames[i]); 381596Sbill exit(2); 382596Sbill } 383596Sbill /* stdio is NOT used to read the input characters */ 384596Sbill /* we use read directly, into our own buffers */ 385596Sbill yyparse(); 386596Sbill } 387596Sbill } 388596Sbill 389596Sbill closetmpfile(); /*kick out the last buffered intermediate text*/ 390596Sbill } 391596Sbill 392596Sbill testlocals() 393596Sbill { 394596Sbill register int i; 395596Sbill for (i = 0; i <= 9; i++) { 396596Sbill if (genref[i]) 397596Sbill yyerror("Reference to undefined local label %df", i); 398596Sbill lgensym[i] = 1; 399596Sbill genref[i] = 0; 400596Sbill } 401596Sbill } 402596Sbill 403596Sbill pass1_5() 404596Sbill { 405596Sbill sortsymtab(); 406596Sbill #ifdef DEBUG 407596Sbill if (debug) dumpsymtab(); 408596Sbill #endif 409596Sbill jxxxfix(); 410596Sbill #ifdef DEBUG 411596Sbill if (debug) dumpsymtab(); 412596Sbill #endif 413596Sbill } 414596Sbill 415596Sbill open_a_out() 416596Sbill { 417596Sbill /* 418596Sbill * Open up the a.out file now, and get set to build 419596Sbill * up offsets into it for all of the various text,data 420596Sbill * text relocation and data relocation segments. 421596Sbill */ 422596Sbill a_out_file = fopen(outfile, "w"); 423596Sbill if (a_out_file == NULL) { 424596Sbill yyerror("Cannot create %s", outfile); 425596Sbill delexit(); 426596Sbill } 427596Sbill biofd = a_out_file->_file; 428596Sbill a_out_off = 0; 429596Sbill } 430596Sbill 431596Sbill roundsegments() 432596Sbill { 433596Sbill register int locindex; 434596Sbill register long v; 435596Sbill /* 436596Sbill * round and assign text segment origins 437596Sbill * the exec header always goes in usefile[0] 438596Sbill */ 439596Sbill tsize = 0; 440596Sbill for (locindex=0; locindex<NLOC; locindex++) { 441*679Shenry v = round(usedot[locindex].e_xvalue, FW); 442631Shenry usedot[locindex].e_xvalue = tsize; 443596Sbill if ((locindex == 0) || (v != 0) ){ 444596Sbill usefile[locindex] = (BFILE *)Calloc(1, sizeof(BFILE)); 445596Sbill bopen(usefile[locindex], a_out_off); 446596Sbill if (locindex == 0) 447596Sbill a_out_off = sizeof (struct exec); 448596Sbill } else { 449596Sbill usefile[locindex] = (BFILE *)-1; 450596Sbill } 451596Sbill tsize += v; 452596Sbill a_out_off += v; 453596Sbill } 454596Sbill /* 455596Sbill * Round and assign data segment origins. 456596Sbill */ 457*679Shenry datbase = round(tsize, FW); 458596Sbill for (locindex=0; locindex<NLOC; locindex++) { 459*679Shenry v = round(usedot[NLOC+locindex].e_xvalue, FW); 460631Shenry usedot[NLOC+locindex].e_xvalue = datbase + dsize; 461596Sbill if (v != 0){ 462596Sbill usefile[NLOC + locindex] = (BFILE *)Calloc(1,sizeof(BFILE)); 463596Sbill bopen(usefile[NLOC + locindex], a_out_off); 464596Sbill } else { 465596Sbill usefile[NLOC + locindex] = (BFILE *)-1; 466596Sbill } 467596Sbill dsize += v; 468596Sbill a_out_off += v; 469596Sbill } 470596Sbill /* 471596Sbill * Assign final values to symbols 472596Sbill */ 473596Sbill hdr.a_bss = dsize; 474596Sbill freezesymtab(); /* this touches hdr.a_bss */ 475596Sbill stabfix(); 476596Sbill /* 477596Sbill * Set up the relocation information "files" to 478596Sbill * be zero; outrel takes care of the rest 479596Sbill */ 480596Sbill for (locindex = 0; locindex < NLOC + NLOC; locindex++){ 481596Sbill rusefile[locindex] = (struct relbufdesc *)0; 482596Sbill } 483596Sbill } 484596Sbill 485596Sbill build_hdr() 486596Sbill { 487596Sbill /* 488596Sbill * Except for the text and data relocation sizes, 489596Sbill * calculate the final values for the header 490596Sbill * 491596Sbill * Write out the initial copy; we to come 492596Sbill * back later and patch up a_trsize and a_drsize, 493596Sbill * and overwrite this first version of the header. 494596Sbill */ 495596Sbill hdr.a_magic = MAGIC; 496596Sbill hdr.a_text = tsize; 497596Sbill hdr.a_data = dsize; 498596Sbill hdr.a_bss -= dsize; 499596Sbill hdr.a_syms = sizesymtab(); /* Does not include string pool length */ 500596Sbill hdr.a_entry = 0; 501596Sbill hdr.a_trsize = 0; 502596Sbill hdr.a_drsize = 0; 503596Sbill 504596Sbill bwrite((char *)&hdr, sizeof(hdr), usefile[0]); 505596Sbill } 506596Sbill 507596Sbill i_pass2() 508596Sbill { 509596Sbill if (useVM == 0) { 510596Sbill fclose(tmpfil); 511596Sbill tmpfil = fopen(tmpn1, "r"); 512596Sbill if (tmpfil==NULL) { 513596Sbill yyerror("Bad pass 2 temporary file for reading %s", tmpn1); 514596Sbill delexit(); 515596Sbill } 516596Sbill } 517596Sbill } 518596Sbill 519596Sbill pass2() 520596Sbill { 521596Sbill #ifdef DEBUG 522596Sbill if (debug) 523596Sbill printf("\n\n\n\t\tPASS 2\n\n\n\n"); 524596Sbill #endif DEBUG 525596Sbill passno = 2; 526596Sbill lineno = 1; 527596Sbill dotp = &usedot[0]; 528596Sbill txtfil = usefile[0]; /* already opened (always!) */ 529596Sbill relfil = 0; /* outrel takes care of the rest */ 530596Sbill initoutrel(); 531596Sbill 532596Sbill inittmpfile(); 533596Sbill 534596Sbill yyparse(); 535596Sbill 536596Sbill closetmpfile(); 537596Sbill } 538596Sbill 539596Sbill fillsegments() 540596Sbill { 541596Sbill int locindex; 542596Sbill /* 543*679Shenry * Round text and data segments to FW by appending zeros 544596Sbill */ 545596Sbill for (locindex = 0; locindex < NLOC + NLOC; locindex++) { 546596Sbill if (usefile[locindex]) { 547596Sbill txtfil = usefile[locindex]; 548596Sbill dotp = &usedot[locindex]; 549*679Shenry while (usedot[locindex].e_xvalue & FW) 550596Sbill outb(0); 551596Sbill } 552596Sbill } 553596Sbill } 554596Sbill 555596Sbill reloc_syms() 556596Sbill { 557596Sbill u_long closerelfil(); 558596Sbill /* 559596Sbill * Move the relocation information to a.out 560596Sbill * a_out_off is the offset so far: 561596Sbill * exec + text segments + data segments 562596Sbill */ 563596Sbill relocfile = (BFILE *)Calloc(1,sizeof(BFILE)); 564596Sbill bopen(relocfile, a_out_off); 565596Sbill a_out_off += closeoutrel(relocfile); 566596Sbill 567596Sbill hdr.a_trsize = trsize; 568596Sbill hdr.a_drsize = drsize; 569638Sbill if (readonlydata) { 570638Sbill hdr.a_text += hdr.a_data; 571638Sbill hdr.a_data = 0; 572638Sbill hdr.a_trsize += hdr.a_drsize; 573638Sbill hdr.a_drsize = 0; 574638Sbill } 575596Sbill /* 576596Sbill * Output the symbol table 577596Sbill * and if FLEXNAMES is set, the string pool 578596Sbill */ 579596Sbill symwrite(relocfile); 580596Sbill } 581596Sbill 582596Sbill fix_a_out() 583596Sbill { 584596Sbill if (lseek(a_out_file->_file, 0L, 0) < 0) 585596Sbill yyerror("Reposition for header rewrite fails"); 586596Sbill if (write(a_out_file->_file, (char *)&hdr, sizeof (struct exec)) < 0) 587596Sbill yyerror("Rewrite of header fails"); 588596Sbill } 589596Sbill 590596Sbill delexit() 591596Sbill { 592596Sbill delete(); 593596Sbill if (passno == 2){ 594596Sbill unlink(outfile); 595596Sbill } 596596Sbill exit(1); 597596Sbill } 598596Sbill 599596Sbill delete() 600596Sbill { 601596Sbill if (useVM == 0 || tmpn1[0]) 602596Sbill unlink(tmpn1); 603596Sbill } 604596Sbill 605596Sbill sawabort() 606596Sbill { 607596Sbill char *fillinbuffer(); 608596Sbill while (fillinbuffer() != (char *)0) 609596Sbill continue; 610596Sbill delete(); 611596Sbill exit(1); /*although the previous pass will also exit non zero*/ 612596Sbill } 613596Sbill 614596Sbill panic(fmt, a1, a2, a3, a4) 615596Sbill char *fmt; 616596Sbill /*VARARGS 1*/ 617596Sbill { 618596Sbill yyerror("Assembler panic: bad internal data structure."); 619596Sbill yyerror(fmt, a1, a2, a3, a4); 620596Sbill delete(); 621596Sbill abort(); 622596Sbill } 623