#include #include #include "ass00.h" #include "assex.h" #include "assci.h" #include "asscm.h" #include "assrl.h" /* * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * See the copyright notice in the ACK home directory, in the file "Copyright". * */ char oflag; static int memflg; /* Forward declarations. */ static siz_t* getsizes(char *); static void getcore(void); static void argument(char *); static void flags(char *); static void skipentry(void); static void enmd_pro(void); static void enmd_glo(void); static void archive(void); static void finish_up(void); static void check_def(void); static void c_print(void); static void c_dprint(char *, char*); /* External definitions */ void pass_3(void); void pass_4(void); void pass_5(void); /* ** Main routine of EM1-assembler/loader */ int main(int argc, char **argv) { /* * Usage: ass [-[d][p][m][u][U]] [-s(s/m/l/x)] [ [file] [flag] ] ... * The d flag can be repeated several times, resulting in more * debugging information. */ char workspace[6000]; register char *cp; register int argno; progname = argv[0]; for (cp = argv[0]; *cp;) if (*cp++ == '/') progname = cp; for (argno = 1; argno < argc; argno++) { if (argv[argno][0] == '-' && LC(argv[argno][1]) == 's') { oursize = getsizes(&argv[argno][2]); break; } } /* A piece of the interpreter's stack frame is used as free area initially */ freearea((area_t) workspace, (unsigned) sizeof workspace); getcore(); init_files(); init_vars(); while (--argc) argument(*++argv); finish_up(); exit(nerrors != 0); } static void getcore(void) { register siz_t *p; siz_t bytes; register unsigned n; register char *base; /* * xglobs[] should be located in front of mglobs[], see upd_reloc() */ p = oursize; n = 0; n += (bytes.n_glab = p->n_glab * (sizeof *xglobs)); n += (bytes.n_mlab = p->n_mlab * (sizeof *mglobs)); n += (bytes.n_mproc = p->n_mproc * (sizeof *mprocs)); n += (bytes.n_xproc = p->n_xproc * (sizeof *xprocs)); n += (bytes.n_proc = p->n_proc * (sizeof *proctab)); base = getarea(n); memset(base, 0, n); xglobs = gbp_cast base; base += bytes.n_glab; mglobs = gbp_cast base; base += bytes.n_mlab; mprocs = prp_cast base; base += bytes.n_mproc; xprocs = prp_cast base; base += bytes.n_xproc; proctab = ptp_cast base; base += bytes.n_proc; } static siz_t* getsizes(char *str) { /* * accepts -ss (small), -sm (medium), -sl (large), -sx (extra large) */ switch (LC(*str)) { default: error("bad size option %s", str); case 's': return &sizes[0]; break; case 'm': return &sizes[1]; break; case 'l': return &sizes[2]; break; case 'x': return &sizes[3]; break; } } /* * This routine decides what to do with each argument. * It recognises flags and modules. * Furthermore, it knows a library when it sees it and * call archive() to split it apart. */ static void argument(char *arg) { register int w; if (oflag) { eout = arg; oflag = 0; return; } if (*arg == '-') { flags(arg); return; } curfile = arg; /* for error messages etc. */ if ((ifile = fopen(arg, "r")) == NULL) { error("can't open %s", arg); return; } inpoff = 2; if ((w = getu16()) == sp_magic) read_compact(); else if (w == ARMAG || w == AALMAG) { archmode = TRUE; archive(); archmode = FALSE; } else error("%s: bad format", arg); if (fclose(ifile) == EOF) { } } /* ** process flag arguments */ static void flags(char *arg) { register char *argp; register int on; argp = arg; while (*++argp) { switch (LC(*argp)) { case 'd': d_flag++; break; case 'r': r_flag++; break; case 's': return; /* s-flag is already scanned */ #ifdef MEMUSE case 'm': memflg++; break; #endif case 'p': ++procflag; break; #ifdef DUMP case 'u': ++c_flag; break; #endif case 'o': ++oflag; break; case 'w': ++wflag; break; #ifdef JOHAN case 'j': ++jflag; break; #endif case 'U': ++Uflag; break; case '-': case '+': on = (*argp == '+'); while (*++argp) switch (LC(*argp)) { case 't': if (on) intflags |= 01; else intflags &= ~01; break; case 'p': if (on) intflags |= 02; else intflags &= ~02; break; case 'f': if (on) intflags |= 04; else intflags &= ~04; break; case 'c': if (on) intflags |= 010; else intflags &= ~010; case 'e': if (on) intflags |= 040; else intflags &= ~040; break; default: error("bad interpreter option %s", argp); } --argp; break; default: error("bad flag %s", argp); break; } } } void do_proc(void) { /* One procedure has been read and will be processed. * * NOTE: The numbers of the passes, 1 3 4 and 5, are a remainder * of ancient times. */ dump(1); if (memflg > 2) memuse(); pass_3(); dump(3); pass_4(); dump(4); pass_5(); if (memflg > 2) memuse(); endproc(); if (memflg > 1) memuse(); } static void archive(void) { register int i; register char *p; /* * Read a library. * The format of the libary used is that of a UNIX/V7(PDP)-archive. * * NOTE: If it was allowed for an archive to contain * obligatory modules as well as optionals, * it would not be possible to speed up things a bit * by stopping when all references are resolved. * This is the only reason. */ for (;;) { if (unresolved == 0) { /* no use for this library anymore */ return; } p = chp_cast &archhdr; if ((i = fgetc(ifile)) == EOF) { return; } *p++ = i; for (i = 1; i < sizeof archhdr.ar_name; i++) *p++ = get8(); for (i = 0; i < 8; i++) get8(); archhdr.ar_size = ((long) get16() << 16); archhdr.ar_size += getu16(); inpoff = 0; libeof = archhdr.ar_size; /* * UNIX archiveheader is read now, now process the contents * of it. Note that recursive archives are not implemented. * * The variable libeof is used by get8() to check * whether or not we try to pass the library-boundary. */ if (getu16() == sp_magic) { read_compact(); } else error("bad archive entry"); skipentry(); libeof = 0; } /* up to the next entry */ } static void skipentry(void) { /* * for some reason the rest of this library entry needs to be * skipped. Do that now. */ while (inpoff < libeof) get8(); if (odd(libeof)) /* archive entries are evensized */ if (fgetc(ifile) == EOF) /* except maybe the last one */ ; } void init_vars(void) { /* * A small collection of variables is initialized. * This occurs only for those that couldn't be initialized * at compile-time. */ } void init_files(void) { /* * The temporary files on which text and data are kept * during assembly are set up here. */ /* * The function tmpfil() returns a file-descriptor * of a file that is valid for reading and writing. * It has the nice property of generating truly unique names. */ tfile = fopen(tmpfil(), "w+"); dfile = fopen(tmpfil(), "w+"); rtfile = fopen(tmpfil(), "w+"); rdfile = fopen(tmpfil(), "w+"); } void initproc(void) { /* * Called at the start of assembly of every procedure. */ stat_t *prevstate; prevstate = pst_cast getarea(sizeof pstate); *prevstate = pstate; pstate.s_prevstat = prevstate; pstate.s_curpro = prp_cast 0; pstate.s_fline = lnp_cast 0; pstate.s_fdata = l_data; pstate.s_locl = (locl_t (*)[]) getarea( LOCLABSIZE * sizeof((*(pstate.s_locl))[0])); memset(chp_cast pstate.s_locl, 0, LOCLABSIZE * (unsigned) sizeof((*(pstate.s_locl))[0])); if (memflg > 2) memuse(); } void endproc(void) { /* Throw the contents of the line and local label table away */ register line_t *lnp1; register locl_t *lbhead, *lbp, *lbp_next; register int kind; register stat_t *prevstate; while ((lnp1 = pstate.s_fline) != NULL) { pstate.s_fline = lnp1->l_next; kind = lnp1->type1; if (kind > VALLOW) kind = VALLOW; freearea((area_t) lnp1, (unsigned) linesize[kind]); } prevstate = pstate.s_prevstat; if (prevstate != pst_cast 0) { for (lbhead = *pstate.s_locl; lbhead < &(*pstate.s_locl)[LOCLABSIZE]; lbhead++) { for (lbp = lbhead->l_chain; lbp != lbp_cast 0; lbp = lbp_next) { lbp_next = lbp->l_chain; freearea((area_t) lbp, (unsigned) sizeof *lbp); } } freearea((area_t) (*pstate.s_locl), LOCLABSIZE * (sizeof((*pstate.s_locl)[0]))); pstate = *prevstate; freearea((area_t) prevstate, (unsigned) sizeof *prevstate); } } void init_module(void) { /* * Called at the start of every module. */ holbase = 0; line_num = 1; mod_sizes = 0; } void end_module(void) { /* * Finish a module. * Work to be done is mainly forgetting of local names, * and remembering of those that will live during assembly. */ align(wordsize); set_mode(DATA_NUL); dump(100); enmd_pro(); enmd_glo(); if (memflg) memuse(); } static void enmd_pro(void) { register proc_t *p, *limit; /* * Check that all local procedures have been defined, * and forget them immediately thereafter. */ limit = &mprocs[oursize->n_mproc]; for (p = mprocs; p < limit; p++) { if (p->p_name == 0) continue; if ((p->p_status & DEF) == 0) error("undefined local procedure '%s'", p->p_name); } memset(chp_cast mprocs, 0, (limit - mprocs) * (unsigned ) sizeof *mprocs); /* Clobber all flags indicating that external procedures * were used in this module. */ limit = &xprocs[oursize->n_xproc]; for (p = xprocs; p < limit; p++) { p->p_status &= ~EXT; } } static void enmd_glo(void) { register glob_t *mg, *xg, *limit; /* * Tougher then enmd_pro(). * Check all the symbols used in this module that are * not to be forgotten immediately. * A difficulty arises here: * In the tables textreloc[] and datareloc[] * pointers are used to identify the symbols concerned. * These pointers point into mglobs[]. * Since at the end of assembly only the value of xglobs[] * is defined, these pointers have to be changed. * upd_reloc() takes care of this. */ limit = &mglobs[oursize->n_mlab]; for (mg = mglobs; mg < limit; mg++) { if (mg->g_name == 0) continue; if ((mg->g_status & (EXT | DEF)) == 0) error("undefined local symbol '%s'", glostring(mg)); if ((mg->g_status & EXT) == 0) continue; xg = xglolookup(mg->g_name, ENTERING); switch (xg->g_status & (EXT | DEF)) { case 0: /* new symbol */ if ((mg->g_status & DEF) == 0) ++unresolved; break; case EXT: /* already used but not defined */ if (mg->g_status & DEF) { --unresolved; } break; } xg->g_status |= mg->g_status; if (mg->g_status & DEF) xg->g_val.g_addr = mg->g_val.g_addr; else mg->g_val.g_gp = xg; /* used by upd_reloc */ } /* up to the next symbol */ upd_reloc(); memset(chp_cast mglobs, 0, (limit - mglobs) * (unsigned ) sizeof *mglobs); } static void finish_up(void) { /* * Almost done. Check for unresolved references, * make the e.out file and stop. */ #ifdef DUMP c_print(); #endif check_def(); if (nerrors == 0) copyout(); } #ifdef DUMP static void c_print(void) { if (!c_flag) return; c_dprint("primary", opcnt1); c_dprint("secondary", opcnt2); c_dprint("extra long", opcnt3); } static void c_dprint(char *str, char* cnt) { register int first, curr; printf("unused %s opcodes\n", str); for (first = -1, curr = 0; curr <= 256; curr++) { if (curr == 256 || cnt[curr]) { if (first != -1) { if (first + 1 == curr) { printf("%3d\n", first); } else { printf("%3d..%3d\n", first, curr - 1); } first = -1; } } else { if (first == -1) first = curr; } } } #endif static void check_def(void) { register proc_t *p; register glob_t *g; register int count; /* * Check for unresolved references. * NOTE: The occurring of unresolved references is not fatal, * although the use of the e.out file after this * occurring must be strongly discouraged. * Every use of the symbols concerned is undefined. */ if (unresolved) { printf("Unresolved references\n Procedures:\n"); count = oursize->n_xproc; for (p = xprocs; count--; p++) if (p->p_name && (p->p_status & DEF) == 0) printf(" %s\n", p->p_name); printf(" Data:\n"); count = oursize->n_glab; for (g = xglobs; count--; g++) if (g->g_name && (g->g_status & DEF) == 0) printf(" %s\n", glostring(g)); if (!Uflag) nerrors++; } } void ertrap(void) { /* trap routine to drain input in case of compile errors */ if (ifile == stdin) while (fgetc(ifile) != EOF) ; exit(EXIT_FAILURE); }