/* asmain.c */ /* * (C) Copyright 1989-1995 * All Rights Reserved * * Alan R. Baldwin * 721 Berkeley St. * Kent, Ohio 44240 * * 29-Oct-97 JLH pass ";!" comments to output file */ #include #include #include #include #include "asm.h" /*)Module asmain.c * * The module asmain.c includes the command argument parser, * the three pass sequencer, and the machine independent * assembler parsing code. * * asmain.c contains the following functions: * VOID main(argc, argv) * VOID asexit() * VOID asmbl() * FILE * afile(fn, ft, wf) * VOID newdot(nap) * VOID phase(ap, a) * VOID usage() * * asmain.c contains the array char *usetxt[] which * references the usage text strings printed by usage(). */ /*)Function VOID main(argc, argv) * * int argc argument count * char * argv array of pointers to argument strings * * The function main() is the entry point to the assembler. * The purpose of main() is to (1) parse the command line * arguments for options and source file specifications and * (2) to process the source files through the 3 pass assembler. * Before each assembler pass various variables are initialized * and source files are rewound to their beginning. During each * assembler pass each assembler-source text line is processed. * After each assembler pass the assembler information is flushed * to any opened output files and the if-else-endif processing * is checked for proper termination. * * The function main() is also responsible for opening all * output files (REL, LST, and SYM), sequencing the global (-g) * and all-global (-a) variable definitions, and dumping the * REL file header information. * * local variables: * char * p pointer to argument string * int c character from argument string * int i argument loop counter * area * ap pointer to area structure * * global variables: * int aflag -a, make all symbols global flag * char afn[] afile() constructed filespec * area * areap pointer to an area structure * int cb[] array of assembler output values * int cbt[] array of assembler relocation types * describing the data in cb[] * int cfile current file handle index * of input assembly files * int * cp pointer to assembler output array cb[] * int * cpt pointer to assembler relocation type * output array cbt[] * char eb[] array of generated error codes * char * ep pointer into error list array eb[] * int fflag -f(f), relocations flagged flag * int flevel IF-ELSE-ENDIF flag will be non * zero for false conditional case * Addr_T fuzz tracks pass to pass changes in the * address of symbols caused by * variable length instruction formats * int gflag -g, make undefined symbols global flag * char ib[] assembler-source text line * int inpfil count of assembler * input files specified * int ifcnd[] array of IF statement condition * values (0 = FALSE) indexed by tlevel * int iflvl[] array of IF-ELSE-ENDIF flevel * values indexed by tlevel * int incfil current file handle index * for include files * char * ip pointer into the assembler-source * text line in ib[] * jmp_buf jump_env compiler dependent structure * used by setjmp() and longjmp() * int lflag -l, generate listing flag * int line current assembler source * line number * int lop current line number on page * int oflag -o, generate relocatable output flag * int jflag -j, generate debug info flag * int page current page number * int pflag enable listing pagination * int pass assembler pass number * int radix current number conversion radix: * 2 (binary), 8 (octal), 10 (decimal), * 16 (hexadecimal) * int sflag -s, generate symbol table flag * char srcfn[][] array of source file names * int srcline[] current source file line * char stb[] Subtitle string buffer * sym * symp pointer to a symbol structure * int tlevel current conditional level * int xflag -x, listing radix flag * FILE * lfp list output file handle * FILE * ofp relocation output file handle * FILE * tfp symbol table output file handle * FILE * sfp[] array of assembler-source file handles * * called functions: * FILE * afile() asmain.c * VOID allglob() assym.c * VOID asexit() asmain.c * VOID diag() assubr.c * VOID err() assubr.c * int fprintf() c-library * int getline() aslex.c * VOID list() aslist.c * VOID lstsym() aslist.c * VOID minit() ___mch.c * VOID newdot() asmain.c * VOID outchk() asout.c * VOID outgsd() asout.c * int rewind() c-library * int setjmp() c-library * VOID symglob() assym.c * VOID syminit() assym.c * VOID usage() asmain.c * * side effects: * Completion of main() completes the assembly process. * REL, LST, and/or SYM files may be generated. */ int fatalErrors=0; char relFile[128]; int main(argc, argv) char *argv[]; { register char *p; register int c, i; struct area *ap; /*fprintf(stdout, "\n");*/ inpfil = -1; pflag = 1; for (i=1; i= 0) usage(); ++p; while ((c = *p++) != 0) switch(c) { case 'a': case 'A': ++aflag; break; case 'c': case 'C': ++cflag; break; case 'g': case 'G': ++gflag; break; case 'j': /* JLH: debug info */ case 'J': ++jflag; ++oflag; /* force object */ break; case 'l': case 'L': ++lflag; break; case 'o': case 'O': ++oflag; break; case 's': case 'S': ++sflag; break; case 'p': case 'P': pflag = 0; break; case 'x': case 'X': xflag = 0; break; case 'q': case 'Q': xflag = 1; break; case 'd': case 'D': xflag = 2; break; case 'f': case 'F': ++fflag; break; default: usage(); } } else { if (++inpfil == MAXFIL) { fprintf(stderr, "too many input files\n"); asexit(1); } sfp[inpfil] = afile(p, "", 0); strcpy(srcfn[inpfil],afn); if (inpfil == 0) { if (lflag) lfp = afile(p, "lst", 1); if (oflag) { ofp = afile(p, "rel", 1); // save the file name if we have to delete it on error strcpy(relFile,afn); } if (sflag) tfp = afile(p, "sym", 1); } } } if (inpfil < 0) usage(); syminit(); for (pass=0; pass<3; ++pass) { if (gflag && pass == 1) symglob(); if (aflag && pass == 1) allglob(); if (oflag && pass == 2) outgsd(); flevel = 0; tlevel = 0; ifcnd[0] = 0; iflvl[0] = 0; radix = 10; srcline[0] = 0; page = 0; stb[0] = 0; lop = NLPP; cfile = 0; incfil = -1; for (i = 0; i <= inpfil; i++) rewind(sfp[i]); ap = areap; while (ap) { ap->a_fuzz = 0; ap->a_size = 0; ap = ap->a_ap; } fuzz = 0; dot.s_addr = 0; dot.s_area = &dca; symp = ˙ minit(); while (getline()) { cp = cb; cpt = cbt; ep = eb; ip = ib; /* JLH: if line begins with ";!", then * pass this comment on to the output file */ if (oflag && (pass == 1) && (ip[0] == ';') && (ip[1] == '!')) { fprintf(ofp, "%s\n", ip ); } if (setjmp(jump_env) == 0) asmbl(); if (pass == 2) { diag(); list(); } } newdot(dot.s_area); /* Flush area info */ if (flevel || tlevel) err('i'); } if (oflag) outchk(HUGE, HUGE); /* Flush */ if (sflag) { lstsym(tfp); } else if (lflag) { lstsym(lfp); } //printf ("aserr: %d\n", aserr); //printf ("fatalErrors: %d\n", fatalErrors); asexit(fatalErrors); return 0; // hush the compiler } /*)Function VOID asexit(i) * * int i exit code * * The function asexit() explicitly closes all open * files and then terminates the program. * * local variables: * int j loop counter * * global variables: * FILE * ifp[] array of include-file file handles * FILE * lfp list output file handle * FILE * ofp relocation output file handle * FILE * tfp symbol table output file handle * FILE * sfp[] array of assembler-source file handles * * functions called: * int fclose() c-library * VOID exit() c-library * * side effects: * All files closed. Program terminates. */ VOID asexit(i) int i; { int j; if (lfp != NULL) fclose(lfp); if (ofp != NULL) fclose(ofp); if (tfp != NULL) fclose(tfp); for (j=0; j= 0) { n = 10*n + d; c = get(); } if (c != '$' || get() != ':') qerr(); tp = symp->s_tsym; if (pass == 0) { while (tp) { if (n == tp->t_num) { tp->t_flg |= S_MDF; break; } tp = tp->t_lnk; } if (tp == NULL) { tp=(struct tsym *) new (sizeof(struct tsym)); tp->t_lnk = symp->s_tsym; tp->t_num = n; tp->t_flg = 0; tp->t_area = dot.s_area; tp->t_addr = dot.s_addr; symp->s_tsym = tp; } } else { while (tp) { if (n == tp->t_num) { break; } tp = tp->t_lnk; } if (tp) { if (pass == 1) { fuzz = tp->t_addr - dot.s_addr; tp->t_area = dot.s_area; tp->t_addr = dot.s_addr; } else { phase(tp->t_area, tp->t_addr); if (tp->t_flg & S_MDF) err('m'); } } else { err('u'); } } lmode = ALIST; goto loop; } /* * If the first character is a letter then assume a lable, * symbol, assembler directive, or assembler mnemonic is * being processed. */ if ((ctype[c] & LETTER) == 0) { if (flevel) { return; } else { qerr(); } } getid(id, c); c = getnb(); /* * If the next character is a : then a label is being processed. * A double :: defines a global label. If this is new label * then create a symbol structure. * pass 0: * Flag multiply defined labels. * pass 1: * Load area, address, and fuzz values * into structure symp. * pass 2: * Check for assembler phase error and * multiply defined error. */ if (c == ':') { if (flevel) return; if ((c = get()) != ':') { unget(c); c = 0; } symp = lookup(id); if (symp == &dot) err('.'); if (pass == 0) if ((symp->s_type != S_NEW) && ((symp->s_flag & S_ASG) == 0)) symp->s_flag |= S_MDF; if (pass != 2) { fuzz = symp->s_addr - dot.s_addr; symp->s_type = S_USER; symp->s_area = dot.s_area; symp->s_addr = dot.s_addr; } else { if (symp->s_flag & S_MDF) err('m'); phase(symp->s_area, symp->s_addr); } if (c) { symp->s_flag |= S_GBL; } lmode = ALIST; goto loop; } /* * If the next character is a = then an equate is being processed. * A double == defines a global equate. If this is new variable * then create a symbol structure. */ if (c == '=') { if (flevel) return; if ((c = get()) != '=') { unget(c); c = 0; } clrexpr(&e1); expr(&e1, 0); sp = lookup(id); if (sp == &dot) { outall(); if (e1.e_flag || e1.e_base.e_ap != dot.s_area) err('.'); } else if (sp->s_type != S_NEW && (sp->s_flag & S_ASG) == 0) { err('m'); } sp->s_type = S_USER; sp->s_area = e1.e_base.e_ap; sp->s_addr = laddr = e1.e_addr; sp->s_flag |= S_ASG; if (c) { sp->s_flag |= S_GBL; } lmode = ELIST; goto loop; } unget(c); lmode = flevel ? SLIST : CLIST; if ((mp = mlookup(id)) == NULL) { if (!flevel) err('o'); return; } /* * If we have gotten this far then we have found an * assembler directive or an assembler mnemonic. * * Check for .if, .else, .endif, and .page directives * which are not controlled by the conditional flags */ switch (mp->m_type) { case S_IF: n = absexpr(); if (tlevel < MAXIF) { ++tlevel; ifcnd[tlevel] = n; iflvl[tlevel] = flevel; if (n == 0) { ++flevel; } } else { err('i'); } lmode = ELIST; laddr = n; return; case S_ELSE: if (ifcnd[tlevel]) { if (++flevel > (iflvl[tlevel]+1)) { err('i'); } } else { if (--flevel < iflvl[tlevel]) { err('i'); } } lmode = SLIST; return; case S_ENDIF: if (tlevel) { flevel = iflvl[tlevel--]; } else { err('i'); } lmode = SLIST; return; case S_PAGE: lop = NLPP; lmode = NLIST; return; default: break; } if (flevel) return; /* * If we are not in a false state for .if/.else then * process the assembler directives here. */ switch (mp->m_type) { case S_EVEN: outall(); laddr = dot.s_addr = (dot.s_addr + 1) & ~1; lmode = ALIST; break; case S_ODD: outall(); laddr = dot.s_addr |= 1; lmode = ALIST; break; case S_BYTE: case S_WORD: do { clrexpr(&e1); expr(&e1, 0); if (mp->m_type == S_BYTE) { outrb(&e1, R_NORM); } else { outrw(&e1, R_NORM); } } while ((c = getnb()) == ','); unget(c); break; case S_ASCII: case S_ASCIZ: if ((d = getnb()) == '\0') qerr(); while ((c = getmap(d)) >= 0) outab(c); if (mp->m_type == S_ASCIZ) outab(0); break; case S_ASCIS: if ((d = getnb()) == '\0') qerr(); c = getmap(d); while (c >= 0) { if ((n = getmap(d)) >= 0) { outab(c); } else { outab(c | 0x80); } c = n; } break; case S_BLK: clrexpr(&e1); expr(&e1, 0); outchk(HUGE,HUGE); dot.s_addr += e1.e_addr*mp->m_valu; lmode = BLIST; break; case S_TITLE: p = tb; if ((c = getnb()) != 0) { do { if (p < &tb[NTITL-1]) *p++ = c; } while ((c = get()) != 0); } *p = 0; unget(c); lmode = SLIST; break; case S_SBTL: p = stb; if ((c = getnb()) != 0) { do { if (p < &stb[NSBTL-1]) *p++ = c; } while ((c = get()) != 0); } *p = 0; unget(c); lmode = SLIST; break; case S_MODUL: getst(id, -1); if (pass == 0) { if (module[0]) { err('m'); } else { strncpy(module, id, NCPS); } } lmode = SLIST; break; case S_GLOBL: do { getid(id, -1); sp = lookup(id); sp->s_flag |= S_GBL; } while ((c = getnb()) == ','); unget(c); lmode = SLIST; break; case S_DAREA: getid(id, -1); uaf = 0; uf = A_CON|A_REL; if ((c = getnb()) == '(') { do { getid(opt, -1); mp = mlookup(opt); if (mp && mp->m_type == S_ATYP) { ++uaf; uf |= mp->m_valu; } else { err('u'); } } while ((c = getnb()) == ','); if (c != ')') qerr(); } else { unget(c); } if ((ap = alookup(id)) != NULL) { if (uaf && uf != ap->a_flag) err('m'); if (ap->a_flag & A_OVR) { ap->a_size = 0; ap->a_fuzz=0; } } else { ap = (struct area *) new (sizeof(struct area)); ap->a_ap = areap; strncpy(ap->a_id, id, NCPS); ap->a_ref = areap->a_ref + 1; ap->a_size = 0; ap->a_fuzz = 0; ap->a_flag = uaf ? uf : (A_CON|A_REL); areap = ap; } newdot(ap); lmode = SLIST; break; case S_ORG: if (dot.s_area->a_flag & A_ABS) { outall(); laddr = dot.s_addr = absexpr(); } else { err('o'); } outall(); lmode = ALIST; break; case S_RADIX: if (more()) { switch (getnb()) { case 'b': case 'B': radix = 2; break; case '@': case 'o': case 'O': case 'q': case 'Q': radix = 8; break; case 'd': case 'D': radix = 10; break; case 'h': case 'H': case 'x': case 'X': radix = 16; break; default: radix = 10; qerr(); break; } } else { radix = 10; } lmode = SLIST; break; case S_INCL: d = getnb(); p = fn; while ((c = get()) != d) { if (p < &fn[PATH_MAX-1]) { *p++ = c; } else { break; } } *p = 0; if (++incfil == MAXINC || (ifp[incfil] = fopen(fn, "r")) == NULL) { --incfil; err('i'); } else { lop = NLPP; incline[incfil] = 0; strcpy(incfn[incfil],fn); } lmode = SLIST; break; case S_FLAT24: if (more()) { getst(id, -1); if (!strcmpi(id, "on")) { /* Quick sanity check: size of * Addr_T must be at least 24 bits. */ if (sizeof(Addr_T) < 3) { warnBanner(); fprintf(stderr, "Cannot enable Flat24 mode: " "host system must have 24 bit " "or greater integers.\n"); } else { flat24Mode = 1; } } else if (!strcmpi(id, "off")) { flat24Mode = 0; } else { qerr(); } } else { qerr(); } lmode = SLIST; #if 0 printf("as8051: ds390 flat mode %sabled.\n", flat24Mode ? "en" : "dis"); #endif break; /* * If not an assembler directive then go to * the machine dependent function which handles * all the assembler mnemonics. */ default: machine(mp); /* if cdb information the generate the line info */ if (cflag && (pass == 1)) DefineCDB_Line(); /* JLH: if -j, generate a line number symbol */ if (jflag && (pass == 1)) { DefineNoICE_Line(); } } goto loop; } /*)Function FILE * afile(fn, ft, wf) * * char * fn file specification string * char * ft file type string * int wf read(0)/write(1) flag * * The function afile() opens a file for reading or writing. * (1) If the file type specification string ft * is not NULL then a file specification is * constructed with the file path\name in fn * and the extension in ft. * (2) If the file type specification string ft * is NULL then the file specification is * constructed from fn. If fn does not have * a file type then the default source file * type dsft is appended to the file specification. * * afile() returns a file handle for the opened file or aborts * the assembler on an open error. * * local variables: * int c character value * FILE * fp filehandle for opened file * char * p1 pointer to filespec string fn * char * p2 pointer to filespec string fb * char * p3 pointer to filetype string ft * * global variables: * char afn[] afile() constructed filespec * char dsft[] default assembler file type string * char afn[] constructed file specification string * * functions called: * VOID asexit() asmain.c * FILE * fopen() c_library * int fprintf() c_library * * side effects: * File is opened for read or write. */ FILE * afile(fn, ft, wf) char *fn; char *ft; int wf; { register char *p2, *p3; register int c; FILE *fp; p2 = afn; p3 = ft; strcpy (afn, fn); p2 = strrchr (afn, FSEPX); // search last '.' if (!p2) p2 = afn + strlen (afn); if (p2 > &afn[PATH_MAX-4]) // truncate filename, if it's too long p2 = &afn[PATH_MAX-4]; *p2++ = FSEPX; // choose a file-extension if (*p3 == 0) { // extension supplied? p3 = strrchr (fn, FSEPX); // no: extension in fn? if (p3) ++p3; else p3 = dsft; // no: default extension } while ((c = *p3++) != 0) { // strncpy if (p2 < &afn[PATH_MAX-1]) *p2++ = c; } *p2++ = 0; if ((fp = fopen(afn, wf?"w":"r")) == NULL) { fprintf(stderr, "%s: cannot %s.\n", afn, wf?"create":"open"); asexit(1); } return (fp); } /*)Function VOID newdot(nap) * * area * nap pointer to the new area structure * * The function newdot(): * (1) copies the current values of fuzz and the last * address into the current area referenced by dot * (2) loads dot with the pointer to the new area and * loads the fuzz and last address parameters * (3) outall() is called to flush any remaining * bufferred code from the old area to the output * * local variables: * area * oap pointer to old area * * global variables: * sym dot defined as sym[0] * Addr_T fuzz tracks pass to pass changes in the * address of symbols caused by * variable length instruction formats * * functions called: * none * * side effects: * Current area saved, new area loaded, buffers flushed. */ VOID newdot(nap) register struct area *nap; { register struct area *oap; oap = dot.s_area; oap->a_fuzz = fuzz; oap->a_size = dot.s_addr; fuzz = nap->a_fuzz; dot.s_area = nap; dot.s_addr = nap->a_size; outall(); } /*)Function VOID phase(ap, a) * * area * ap pointer to area * Addr_T a address in area * * Function phase() compares the area ap and address a * with the current area dot.s_area and address dot.s_addr * to determine if the position of the symbol has changed * between assembler passes. * * local variables: * none * * global varaibles: * sym * dot defined as sym[0] * * functions called: * none * * side effects: * The p error is invoked if the area and/or address * has changed. */ VOID phase(ap, a) struct area *ap; Addr_T a; { if (ap != dot.s_area || a != dot.s_addr) err('p'); } char *usetxt[] = { "Usage: [-dqxjgalopsf] file1 [file2 file3 ...]", " d decimal listing", " q octal listing", " x hex listing (default)", " j add line number and debug information to file", /* JLH */ " g undefined symbols made global", " a all user symbols made global", " l create list output file1[LST]", " o create object output file1[REL]", " s create symbol output file1[SYM]", " p disable listing pagination", " f flag relocatable references by ` in listing file", " ff flag relocatable references by mode in listing file", "", 0 }; /*)Function VOID usage() * * The function usage() outputs to the stderr device the * assembler name and version and a list of valid assembler options. * * local variables: * char ** dp pointer to an array of * text string pointers. * * global variables: * char cpu[] assembler type string * char * usetxt[] array of string pointers * * functions called: * VOID asexit() asmain.c * int fprintf() c_library * * side effects: * program is terminated */ VOID usage() { register char **dp; fprintf(stderr, "\nASxxxx Assembler %s (%s)\n\n", VERSION, cpu); for (dp = usetxt; *dp; dp++) fprintf(stderr, "%s\n", *dp); asexit(1); }