/*
        Copyright 1981
        Alcyon Corporation
        8474 Commerce Av.
        San Diego, Ca.  92121
*/

#include "cout.h"
#include "lo68.h"
struct lib2hdr *lib2hd = 0;
#define BLKSIZE 512

/* 68000 linking loader*/
/*   Bill Allen*/

/* This loader is basically a two pass operation.  The first pass*/
/*  reads all the relocatable object files including any libraries.*/
/*  It builds the full symbol table, resolves all external symbol*/
/*  references if possible, and calculates the total size of the*/
/*  text, data, and bss segements.  Pass 2 then does the binding,*/
/*  the relocation, and produces the final output file.*/

#define MAGIC1 MAGIC+1  /*magic # for data & bss base specified*/

#ifdef VMS
char libname[] = "lib:lib6.a";    /*default library name*/
#define LIBCHAR libname[7]      /* Character to be modified */
#endif

#ifdef CPM
char libname[] = "lib6.a";
#define LIBCHAR libname[3]      /* Character to be modified */
#endif          

#ifdef UNIX
char libname[] = "/lib/lib6.a";   /* Default library name         */
#define LIBCHAR libname[8]      /* Character to be modified     */
#endif

char *ofilname = "c.out";         /*output file name*/

#ifdef  UNIX                    /************************/
char *tfbase = "loXXXXXX";      /* Temp base name       */
char *tdisk  = "/tmp/";         /* Temp disk name       */
#else                           /* CP/M and VMS         */
char *tfbase = "loXXXXXA";      /* Temp base name       */
char *tdisk  = "";              /* Temp disk name       */
#endif                          /************************/

char *tfchar = 0;               /* -> changeable character */
char tfilname[80] = {0};        /*first temp file name*/
#define TFCHAR *tfchar          /* Name to simplify assignments */
char etexstr[] = "_etext\0\0";
char edatstr[] = "_edata\0\0";
char eendstr[] = "_end\0\0\0\0";
int endit();
long lgetnum();
char *lemt();
char *nextsy();
char *sbrk();
int readshort();
int ignflg =0;
int debug  =0;
int exstat =0;

#ifdef VAX
struct { char lobyte; char hibyte; };
#endif

#ifdef PDP11
struct { char lobyte; char hibyte; };
#endif

#ifdef MC68000
struct { char hibyte; char lobyte; };
#endif          

long lseek();
main(argc,argv)
char **argv;
{

        register char **p;
        register char *pc;
        register i;
        register char *p1;
        register int j;

/*      signal(2,endit);*/      /*catch rubout*/
        buildf();               /* Construct temp file names */
        intsytab();                     /*initialize the symbol table*/
        firstsym = fsymp;
        libfctr = lbfictr;
        libptr = lbfioff;
        if(argc <= 1) {
usage:
                printf(": Invalid lo68 argument list\n");
                endit(-1);
        }
        p = argv+1;
        for(i=1; i<argc; i++) {         /*process each argument*/
                pc = *p++;
/*
 *      Compare for a file named ".o" and skip it for CP/M and VAX
 *      command file operation.
 */
                if(_strcmp (pc,".o") == 0)
                        continue;               /* Just skip this one */


                if(*pc == '-') {
                        switch(*++pc) {

                        case 'l':               /*library*/
                                pc--;
                                break;

                        case 'o':               /*output file name*/
                                if(*(pc+1) != '\0')     /* No space?    */
                                {
                                   printf(": Illegal option %s\n",*(--p));
                                   exit(-1);
                                }
                                ofilname = *p++;        /*next arg is file name*/
                                i++;
                                continue;

                        case 's':       /*don't output symbol table*/
                                sflag++;
                                continue;

                        case 'X':       /*keep local symbols except L**/
                        case 'x':
                                Xflag++;
                                continue;

                        case 'Z':       /*offset the text base for standaone pgm*/
                        case 'z':

                        case 'T':       /*another name for same flag*/
                        case 't':
                                Zflag++;
                                textstart = lgetnum(++pc,16);   /*get a long hex number*/
                                textbase = textstart;
                                continue;

                        case 'D':       /*data segment base*/
                        case 'd':
                                Dflag++;
                                datastart = lgetnum(++pc,16);
                                continue;

                        case 'B':       /*bss segment base*/
                        case 'b':
                                Bflag++;
                                bssstart = lgetnum(++pc,16);
                                continue;

                        case 'I':       /*ignore 16-bit addr overflow*/
                        case 'i':
                                ignflg++;
                                continue;

                        case 'r':
                                saverbits++;    /*put relocation bits on c.out*/
                                continue;

                        case 'U':       /*initial undefined symbol*/
                        case 'u':
                                p1 = lmte;
                                pc++;
                                for(j=0; j<SYNAMLEN; j++) {
                                        *p1++ = *pc;
                                        if(*pc)
                                                pc++;
                                }

                                lmte->flags = SYXR;             /*external reference*/
                                addsym(0);                              /*put into symbol table*/
                                continue;

                        case 'f':               /* Temp file prefix */
                                tdisk = *p++;   /* Prefix           */
                                i++;
                                buildf();       /* build file names */
                                continue;

                        }
                }
                p1load(pc);             /*read file or library*/
        }
        resolve();              /*resolve externals and assign addresses*/
        pass2++;                /*now in pass 2*/
        p = argv+1;
        firstsym = fsymp;
        libfctr = lbfictr;
        libptr = lbfioff;
        makeofile();    /*make the output file*/
        for(i=1; i<argc; i++) {         /*process each argument*/
                pc = *p++;

/*
 *      Skip files named ".o" again ...
 */
                if(_strcmp(pc,".o") == 0)
                        continue;

                if(*pc == '-') {
                        switch(*++pc) {

                        case 'l':               /*library*/
                                pc--;
                                break;

                        case 'f':       /* Temp file name */
                        case 'o':       /*output file name*/
                                p++;    /*skip file name*/
                                i++;
                                continue;


                        case 's':       /*don't output symbol table*/
                        case 'X':
                        case 'x':
                        case 'Z':
                        case 'z':
                        case 'I':
                        case 'i':
                        case 'U':
                        case 'u':
                        case 'T':
                        case 't':
                        case 'D':
                        case 'd':
                        case 'B':
                        case 'b':
                        case 'r':
                                continue;

                        }
                }
                p2load(pc);             /*read file or library*/
        }
        finalwr();              /*finish output file*/
}
buildf()
{
        tfilname[0]=0;          /* Null out present filename    */
        strcat (tfilname,tdisk); /* Put disk name in first      */
        strcat (tfilname,tfbase); /* Put in filename now        */

                                  /******************************/
#ifdef  UNIX                      /* On UNIX,                   */
        mktemp (tfilname);        /* Make a temp filename       */
#endif                            /******************************/

        tfchar = &tfilname[strlen(tfilname)-1]; /* Set pointer  */

}

/* pass 1 load routine:*/
/*  read the file or library and build the symbol table*/
/*   symbols are organized on the initial reference tables*/
/*   with externals on eirt and globals on girt*/
int ii=0;
p1load(ap)
char *ap;
{
ii = 0;
        openfile(ap);   /*get the file opened using ibuf*/
        if(couthd.ch_magic == LIB1MAGIC) {      /*library*/
                libhdsize = LIB1HDSIZE;         /*old ar header size*/
                searchlib();
        }
        else if(couthd.ch_magic == LIB2MAGIC) { /*library*/
                libhdsize = sizeof *lib2hd;             /*new ar header size*/
                searchlib();
        }
        else if(couthd.ch_magic == LIB3MAGIC) {
                getw(&ibuf);
                getw(&ibuf);
                getw(&ibuf);
                libhdsize = LIB3HDSIZE;
                searchlib();
        }
        else {
                do1load(0);             /*load a *.o file*/
                firstsym++;
                addsizes();
        }
        close(ibuf.fildes);
}

/* search a library for files to include.  include an entry only*/
/*  if it has a global symbol which matches an external.*/

long lbctr =0;
long libfilsize =0;

searchlib()
{

        *libfctr = 0;   /*no files from this library yet*/
        lbctr = sizeof(short);  /*current library position - skip magic*/
        while(rdlibhdr()) {             /*read library file header*/
                savsymtab();    /*save current state of symbol table*/
                extmatch = 0;
                noload = 0;
                ifilname = libhd;
                readhdr();                      /*read the file header*/
                do1load(1);                     /*load this files symbol table & try match*/
                if(extmatch > noload) {         /*found a match*/
                        if(noload)
                                prdup(lastdup); /*print dup defn*/
                        addsizes();             /*add this files sizes*/
                        firstsym++;
                        *libfctr += 1; /*count files loaded from this library*/
                        *libptr++ = lbctr;      /*remember offset in library*/
                }
                else {  /*dont need this file*/
                        restsymtab();           /*restore symbol table*/
                }
                if(libfilsize&1) {      /*one byte of padding*/
                        getc(&ibuf);
                        lbctr++;
                }
                lbctr += libfilsize + libhdsize;
                lbseek(lbctr,&ibuf);    /*goto begin of next lib entry*/
        }
        libfctr++;              /*point to next library file counter*/
}

/*read a library file header*/
rdlibhdr()
{

        register char *p;
        register int i;
        register char *pc;

        p = libhd;
        for( i = 0; i < sizeof *lib2hd; i++ )
                *p++ = getc(&ibuf);
        libhd->l2modti = 0;
        libfilsize = libhd->l2fsize;
        if( libhd->l2fname[0] == '\0' )
                return(0);
        return(1);
}

/* open the file pointed to by ap*/
/*  check for the -lx type library spec*/

openfile(ap)
char *ap;
{

        register char *p;
        register short i;

        p = ap;
        if(*p=='-' && *++p=='l') {
                if(*++p)
                        LIBCHAR = *p;
                else
                        LIBCHAR = '6';  /*default library name*/
                p = libname;
        }
        else
                p = ap;
        if((ibuf.fildes=fopen(p,&ibuf,1)) < 0) {
                printf(": unable to open %s\n",p);
                endit(-1);
        }
        ifilname = p;   /*point to current file name for error msgs*/
        if( readshort(ibuf.fildes,&couthd.ch_magic) ) {
                printf(": read error on file: %s\n",ifilname);
                endit(-1);
        }
        ibuf.nunused = 0;
        if( couthd.ch_magic != LIB2MAGIC ) {
                lseek(ibuf.fildes,0L,0);
                readhdr();                              /*read file header*/
        }
        if(pass2) {             /*need file descrptr for reloc bits*/
                fopen(p,&rbuf,1);
        }
}

/* read the header of the input file*/

int readshort(fd,s)
int fd;
short *s;
{
        short ts;

        if( read(fd,&ts,sizeof(short)) != sizeof(short) )
                return(1);
        *s = ts;
        return(0);
}
readhdr()
{

        register int i;
        register short *p;

        p = &couthd;
        for( i = 0; i < (sizeof couthd)/2; i++ )
                *p++ = getw(&ibuf);
        if(couthd.ch_magic != MAGIC) {
                printf(": file format error: %s\n",ifilname);
                endit(-1);
        }
        if(couthd.ch_rlbflg) {
                printf(": File format error");
                printf(": no relocation bits in %s\n",ifilname);
                endit(-1);
        }
}

/* load one *.o format file using ibuf*/
/*      put the symbols into the symbol table relocating each one*/
/*      and finally add this files sizes into running totals*/

/*  libflg is set if we are in a library*/

do1load(libflg)
{

        register long i;
        register long l;

        *firstsym = lmte;               /*remember where this symbol table starts*/
        l = couthd.ch_tsize + couthd.ch_dsize + sizeof couthd;
        if( libflg )
                l += lbctr + sizeof *lib2hd;
        lbseek(l,&ibuf);
        i = couthd.ch_ssize;                    /*size of symbol table*/
        while(i > 0) {
                getsym();               /*read one symbol entry*/
                relocsym();             /*fix its address*/
                addsym(libflg);         /*add to symbol table*/
                i -= OSTSIZE;
        }
}

/* get one symbol entry from the input file and put it into*/
/*  the symbol table entry pointed to by lmte*/

getsym()
{

        register int i;
        register short *p;

        p = lmte;
        for(i=0; i<SYNAMLEN/(sizeof *p); i++)
                *p++ = getw(&ibuf);
        *p++ = getw(&ibuf);     /* flags */
/*      *p++=0; */              /* skip upp half of internal flags on VAX */
        p->hiword = getw(&ibuf); /* sym value high word */
        p->loword = getw(&ibuf); /* sym vaue low word */
}

/* relocate the symbol value pointed to by lmte according to*/
/*  symbol type and corresponding relocation base*/

relocsym()
{
        register long l;

        l = 0;
        if(lmte->flags&SYXR)            /*external*/
                return;
        if(lmte->flags&SYTX)
                l = textbase;
        else if(lmte->flags&SYDA)
                l = database;
        else if(lmte->flags&SYBS)
                l = bssbase;
        else {
                if(lmte->flags&SYEQ)    /*equated*/
                        return;                         /* abs*/
                printf(": File Format error: Invalid symbol flags = %o\n",
                        (int)lmte->flags);
                endit(-1);
        }
        lmte->vl1 += l;
}

/* add a symbol to the symbol table*/
/*  if symbol is an external, put it on eirt even if it does*/
/*   already exist*/
/*  if symbol is a global, put it on girt and error if it exists*/
/*  in any case, add it to the end of the symbol table*/
/* if libflg is set, we are searching a library.  In this case, when*/
/*  a global is encountered, search the external symbols and set*/
/*  extmatch if a match is found.*/

addsym(libflg)
{

        register char *p;

/*      if(debug) {*/
/*              printf("addsym: file=%s symbol=%s flags=%o\n",ifilname,lmte,(int)lmte->flags);*/
/*      }*/
        if(lmte->flags&SYXR) {  /*external*/
                p = lemt(eirt);
                mmte();
        }
        else if(lmte->flags&SYGL) {     /*global*/
                if(libflg) {            /*global in a library*/
                        p = lemt(eirt); /*look up in externals*/
                        if(p != lmte) {         /*found a match*/
                                extmatch++;
                        }
                }
                p = lemt(girt);
                if(p == lmte)
                        mmte();
                else {
dupdef:
                        if(libflg) {
                                noload++;
                                lastdup = p;
                        }
                        else if(p->flags != lmte->flags || p->vl1 != lmte->vl1)
                                prdup(p);               /*dup defn msg*/
                }
        }
        else {          /*normal symbol*/
                if(lmte->name[0] == 'L')        /*compiler label*/
                        return;
                if(Xflag==0)            /*dont save local symbols*/
                        return;
                addmte();
        }
}

prdup(p)
char *p;
{

        printf(": %s duplicate definition in %s\n",p,ifilname);
        exstat++;
}

/* initialize the symbol table and the heads of the hash lists*/

intsytab()
{

        register char **p1, **p2;
        register i;

        bmte = (sbrk((sizeof *symptr)*SZMT+2));
        if((long)bmte&1)
                bmte++;
        emte = bmte + (sizeof *symptr)*SZMT;            /*end of main table*/
        lmte=bmte;              /*beginning main table*/
        cszmt = SZMT;   /*current size of main table*/
        p1 = eirt;
        p2 = girt;
        for(i=0; i<32; i++) {
                *p1++ = p1;
                *p1++ = 0;
                *p2++ = p2;
                *p2++ = 0;
        }
}
/* method for looking up entries in the main table*/
/**/
/* Note:        The entry to be looked up must be placed at the end*/
/*                      of the main table.  The global cell 'lmte'(last main*/
/*                      entry) points to the next available entry in the main*/
/*                      table.  The address of an initial reference table must*/
/*                      also be provided.*/

/*      1)      Compute the hash code for the symbol and add it to the base address*/
/*              of the initial reference table given as input.  Thus, two words are*/
/*              accessed which define the chain on which the symbol must be if it is*/
/*              in the table at all.*/

/*      2)      Alter the table link of the last symbol in the chain so that it */
/*              points to the symbol being looked up.  Note that the symbol to be */
/*              looked up is always placed at the end of the main table before */
/*              calling the lookup routine.  This essentially adds one more element*/
/*              to the end of the chain, namely the symbol to be looked up.*/

/*      3)      Now start at the first symbol in the chain and follow the chain*/
/*              looking for a symbol equal to the smbol being looked up.  It is*/
/*              quaranteed that such a symbol will be found because it is always */
/*              the last symbol on the chain.*/

/*      4)      When the symbol is found, check to see if it is the last symbol*/
/*              on the chain.  If not, the symbol being looked for is in the table*/
/*              and has been found.  If it is the last symbol, the symbol being */
/*              looked up is not in the table.*/

/*      5)      In the case the looked up symbol is not found, it is usually added*/
/*              to the end of the table.  This is done simply b changing the */
/*              initial reference table entry which points to the previous*/
/*              last symbol on the chain so that is now points to the symbol at the*/
/*              end of the main table.  In case the symbol just looked up is not to*/
/*              be added to the main table then no action is needed .  This means*/
/*              that the table link of the last symbol on a chain may point any-*/
/*              where.*/

/* look up entry in the main table*/
/*              call with:*/
/*                      address of initial reference table*/
/*                      entry to be looked up at the end of the main table*/
/*              returns:*/
/*                      a pointer to the entry.  if this pointer is equal to*/
/*                      lmte then the symbol was not previously in the table.*/

char *lemt(airt)
char **airt;
{

        register char *mtpt;

        pirt = airt + hash();   /*pointer to entry in irt*/
        mtpt = pirt->irfe;              /*pointer to first entry in chain*/
        if(mtpt==0)                             /*empty chain*/
                mtpt = lmte;            /*start at end of main table*/
        else
                (pirt->irle)->tlnk = lmte;      /*last entry in chain is new symbol*/
        return(nextsy(mtpt));   /*return next match on chain*/
}

char *nextsy(amtpt)
char *amtpt;
{

        register char *mtpt;
        register int *p1, *p2;
        register int i;

        mtpt = amtpt;

/*loop to locate entry in main table*/
lemtl:
        p1 = &mtpt->name[0];
        p2 = &lmte->name[0];
        for(i=0; i<SYNAMLEN/(sizeof i); i++) {
                if(*p1++ != *p2++) {
                        mtpt = mtpt->tlnk;      /*go to next entry in chain*/
                        goto lemtl;
                }
        }
        return(mtpt);
}

/*make an entry in the main table*/
/* assumes:*/
/*      entry to be made is pointed at by lmte*/
/*      pirt points to the correct initial reference table entry*/

mmte()
{

        pirt->irle = lmte;              /*pointer to last entry in chain*/
        if(pirt->irfe == 0)             /*first entry in chain*/
                pirt->irfe = lmte;
        addmte();
}

/* add the symbol pointed to by lmte to symbol table*/
addmte()
{

        lmte += (sizeof *symptr);        /*bump last main table entry pointer*/
        if(lmte >= emte) {                /*main table overflow*/
                if(sbrk((sizeof *symptr)*ICRSZMT) == -1){       /*get more memory*/
                        printf(": symbol table overflow\n");
                        exit(-1);
                }
                else {
                        emte += (sizeof *symptr)*ICRSZMT;       /*move end of main table*/
                        cszmt += ICRSZMT;
                }
        }
}

/* compute a hash code for the last entry in the main table*/
/*      returns the hash code*/

hash()
{

        register ht1, i;        /*temps*/
        register char *p;

        ht1 = 0;
        p = &lmte->name[0];
        for(i=0; i<SYNAMLEN; i++) 
                ht1 += *p++;
        return(ht1&076);        /*make hash code even and between 0 and 62*/
}

/* pack a string into an entry in the main table*/
/*      call with:*/
/*              pointer to the string*/
/*              pointer to desired entry in the main table*/

pack(apkstr,apkptr)
int *apkstr;
int *apkptr;
{

        register int i;
        register int *pkstr, *pkptr;

        pkstr = apkstr;
        pkptr = apkptr;
        for(i=0; i<SYNAMLEN/(sizeof i); i++)
                *pkptr++ = *pkstr++;
}
/* get a temp file for the intermediate text*/

gettempf()
{

        register i,j;

/*      while(tfilname[10] <= 'z') {
                if((j=creat(tfilname,0600))>=0)
                        return(j);
                tfilname[10]++;
        }
        printf(": Unable to open temporary file: %s\n",tfilname);
        endit(-1);
}*/
        (TFCHAR)++;
        if((j=creat(tfilname,0600,1)) >= 0)
        {
                        return(j);
        }
        printf(": Unable to open temporary file: %s\n",tfilname);
        endit(-1);
}
/* update the relocation counters with the sizes in the header*/

addsizes()
{

        textbase += couthd.ch_tsize;
        database += couthd.ch_dsize;
        bssbase  += couthd.ch_bsize;
}

/*save the current state of the symbol table -- it may be restored later*/

savsymtab()
{

        register char **p1, **p2;
        register i;

        savlmte = lmte;
        p2 = eirt;
        p1 = saveirt;
        for(i=0; i<SZIRT; i++)
                *p1++ = *p2++;
        p2 = girt;
        p1 = savgirt;
        for(i=0; i<SZIRT; i++)
                *p1++ = *p2++;
}

/*restore the symbol table as it was when we last saved it*/
restsymtab()
{

        register char **p1, **p2;
        register i;

        lmte = savlmte;
        p1 = eirt;
        p2 = saveirt;
        for(i=0; i<SZIRT; i++)
                *p1++ = *p2++;
        p1 = girt;
        p2 = savgirt;
        for(i=0; i<SZIRT; i++)
                *p1++ = *p2++;
}

/* resolve the external variable addresses and set the*/
/*  base address of the data and bss segments.*/
/*  also allocate storage for the common variables.*/

resolve()
{

        register char *p;

        textsize = textbase - textstart;
        datasize = database;
        bsssize = bssbase;
        if(Dflag)
                database = datastart;
        else {
                database = (textbase+1)&~1;
                datastart = database;
        }
        if(Bflag)
                bssbase = bssstart;
        else {
                bssbase = (database+datasize+1)&~1;
                bssstart = bssbase;
        }
        textbase = textstart;
        fixsyms();              /*relocate symbols with addresses*/
        fixexts();              /*fix external addresses & commons*/
        if(etextptr) {
                pack(etexstr,lmte);
                p=lemt(eirt);
                do {
                        p->vl1 = textsize;
                        p->flags &= ~SYXR;      /*no longer external*/
                        p->flags |= SYDF|SYGL;
                } while((p=nextsy(p->tlnk)) != lmte);
        }
        if(edataptr) {
                pack(edatstr,lmte);
                p=lemt(eirt);
                do {
                        p->vl1 = textsize+datasize;
                        p->flags &= ~SYXR;      /*no longer external*/
                        p->flags |= SYDF|SYGL;
                } while((p=nextsy(p->tlnk)) != lmte);
        }
        if(endptr) {
                pack(eendstr,lmte);
                p=lemt(eirt);
                do {
                        p->vl1 = textsize+datasize+bsssize;
                        p->flags &= ~SYXR;      /*no longer external*/
                        p->flags |= SYDF|SYGL;
                } while((p=nextsy(p->tlnk)) != lmte);
        }
}

/* fix symbol addresses that have been assigned by adding in*/
/*  database and bssbase*/

fixsyms()
{

        register struct symtab *p;

        for(p=bmte; p<lmte; p++) {      /*look at each symbol*/
                if(p->flags&SYXR)
                        continue;
                if(p->flags&SYDA)               /*data symbol*/
                        p->vl1 += database;
                else if(p->flags&SYBS)  /* bss symbol*/
                        p->vl1 += bssbase;
        }
}

/* get addresses for all external symbols and common symbols*/
fixexts()
{

        register char *p;
        register char **sx1, **sx2;

        for(sx1=eirt; sx1<&eirt[63]; sx1 += 2) {        /*go thru externals*/
                if(*(sx2 = sx1+1)==0)   /*this chain empty*/
                        continue;

/* go thru symbols on chain*/
                sx2 = *sx2;             /*first entry on this chain*/
                while(1) {
                        if(sx2->vl1)
                                asgncomn(sx2);  /*assign a common address*/
                        else
                                asgnext(sx2);   /*match to a global*/
                        p = sx2;
                        if(p == *sx1)           /*end of chain*/
                                break;
                        sx2 = sx2->tlnk;        /*next entry in chain*/
                }
        }
}

/* assign an address for a block of bss common*/
asgncomn(ap)
char *ap;
{

        register char *p, *p1;
        register long l;

        p = ap;
        pack(p,lmte);
        p1 = lemt(girt);
        if(p1 != lmte) {                /*matches a global entry*/
                ap->vl1 = p1->vl1;
                return;
        }
        l = 0;
        lemt(eirt);
        do {
                if(p->vl1 > l)
                        l = p->vl1;
                p->vl1 = 0;
        } while((p=nextsy(p->tlnk)) != lmte);
/*now make a global entry for this common block*/
        p->flags = SYDF|SYGL|SYBS;
        p->vl1 = bssbase + bsssize;
        ap->vl1 = p->vl1;               /*copy address to first external*/
        bsssize += l;
        lemt(girt);             /*set ptrs for global chain*/
        mmte();                         /*add to global chain*/
}

/* assign an address to an external by matching it with a global*/
/*  print an error message if no match*/
asgnext(ap)
char *ap;
{

        register char *p, *pg;

        p = ap;
        pack(p,lmte);   /*copy current symbol name*/
        pg = lemt(girt);
        if(pg == lmte) {        /*no match in global symbols*/
                pg=lemt(eirt);                  /*set ptrs for external chains*/
                if(pg==lmte) {
                        printf(": asgnext botch\n");
                        endit(-1);
                }
                while((p=nextsy(p->tlnk)) != lmte) {
                        if(p->vl1) {            /*common*/
                                asgncomn(ap);
                                return;
                        }
                }
                if(spendsym(ap))                /*end, etext or edata*/
                        return;
                if(undflg==0) {
                        printf(": Undefined symbol(s)\n");
                        undflg++;
                        exstat++;
                }
                prtsym(p);
        }
        else {
                p->vl1 = pg->vl1;       /*assign symbol value*/
        }
}

/* print a symbol name for an error message*/
prtsym(ap)
char *ap;
{

        register i;
        register char *p;

        p = ap;
        for(i=0; i<SYNAMLEN; i++) {
                if(*p)
                        putchar(*p++);
                else
                        break;
        }
        putchar('\n');
}

/* pass 2 load routine:*/
/*  read the file or library and do relocation */

p2load(ap)
char *ap;
{

        openfile(ap);   /*get the file opened using ibuf*/
        if(couthd.ch_magic == LIB1MAGIC) {
                libhdsize = LIB1HDSIZE;
                loadlib();
        }
        else if(couthd.ch_magic == LIB2MAGIC) {
                libhdsize = sizeof *lib2hd;
                loadlib();
        }
        else if(couthd.ch_magic == LIB3MAGIC) {
                libhdsize = LIB3HDSIZE;
                loadlib();
        }
        else {
                do2load(0);             /*load a *.o file*/
                addsizes();
        }
        close(ibuf.fildes);
        close(rbuf.fildes);
}

/*make the outut file and write the header*/

makeofile()
{

        long l;

        if((obuf.fildes=creat(ofilname,0666,1)) < 0) {
                printf(": Unable to create %s\n",ofilname);
                endit(-1);
        }
        obuf.nunused = 512;
        obuf.xfree = &obuf.buff[0];
        if(Dflag|Bflag)
                putw(MAGIC1,&obuf);             /*data & bss bases in header*/
        else
                putw(MAGIC,&obuf);              /*normal header*/
/*      putw(0,&obuf);*/        /* pad for VAX header */

#ifdef  VAX
        putw(textsize.loword,&obuf);
        putw(textsize.hiword,&obuf);
        putw(datasize.loword,&obuf);
        putw(datasize.hiword,&obuf);
        putw(bsssize.loword,&obuf);
        putw(bsssize.hiword,&obuf);
        l = lmte - bmte;
        putw(l.loword,&obuf);
        putw(l.hiword,&obuf);
        putw(stacksize.loword,&obuf);
        putw(stacksize.hiword,&obuf);
        putw(textstart.loword,&obuf);
        putw(textstart.hiword,&obuf);
        if(saverbits) {
                putw(0,&obuf);                  /*relocation bits present*/
/*              putw(0,&obuf);  */
        }
        else {
                putw(-1,&obuf); /*relocation bits removed*/
/*              putw(0,&obuf);*/        /*pad for VAX header */
        }

        if(Dflag|Bflag) {       /*output expanded header*/
                putw(datastart.loword,&obuf);
                putw(datastart.hiword,&obuf);
                putw(bssstart.loword,&obuf);
                putw(bssstart.hiword,&obuf);
        }
#endif

#ifdef  PDP11
        putw(textsize.loword,&obuf);
        putw(textsize.hiword,&obuf);
        putw(datasize.loword,&obuf);
        putw(datasize.hiword,&obuf);
        putw(bsssize.loword,&obuf);
        putw(bsssize.hiword,&obuf);
        l = lmte - bmte;
        putw(l.loword,&obuf);
        putw(l.hiword,&obuf);
        putw(stacksize.loword,&obuf);
        putw(stacksize.hiword,&obuf);
        putw(textstart.loword,&obuf);
        putw(textstart.hiword,&obuf);
        if(saverbits) {
                putw(0,&obuf);                  /*relocation bits present*/
/*              putw(0,&obuf);  */
        }
        else {
                putw(-1,&obuf); /*relocation bits removed*/
/*              putw(0,&obuf);*/        /*pad for VAX header */
        }

        if(Dflag|Bflag) {       /*output expanded header*/
                putw(datastart.loword,&obuf);
                putw(datastart.hiword,&obuf);
                putw(bssstart.loword,&obuf);
                putw(bssstart.hiword,&obuf);
        }
#endif

#ifdef  MC68000
        putw(textsize.hiword,&obuf);
        putw(textsize.loword,&obuf);
        putw(datasize.hiword,&obuf);
        putw(datasize.loword,&obuf);
        putw(bsssize.hiword,&obuf);
        putw(bsssize.loword,&obuf);
        l = lmte - bmte;
        putw(l.hiword,&obuf);
        putw(l.loword,&obuf);
        putw(stacksize.hiword,&obuf);
        putw(stacksize.loword,&obuf);
        putw(textstart.hiword,&obuf);
        putw(textstart.loword,&obuf);
        if(saverbits) {
                putw(0,&obuf);                  /*relocation bits present*/
/*              putw(0,&obuf);  */
        }
        else {
                putw(-1,&obuf); /*relocation bits removed*/
/*              putw(0,&obuf);*/        /*pad for VAX header */
        }

        if(Dflag|Bflag) {       /*output expanded header*/
                putw(datastart.hiword,&obuf);
                putw(datastart.loword,&obuf);
                putw(bssstart.hiword,&obuf);
                putw(bssstart.loword,&obuf);
        }
#endif

        tmpbinit(&tbuf);        /*temp for data words*/
        dafnc = TFCHAR;
        if(saverbits) {
                tmpbinit(&rtbuf);       /*temp for text relocatin bits*/
                rtfnc = TFCHAR;
                tmpbinit(&rdbuf);       /*temp for data relocation bits*/
                rdfnc = TFCHAR;
        }
}

tmpbinit(abufp)
struct buf *abufp;
{

        register struct buf *bufp;

        bufp = abufp;
        bufp->fildes = gettempf();
        bufp->nunused = BUFSIZE;
        bufp->xfree = &bufp->buff[0];
}

endit(stat)
{

        if(dafnc) {
                TFCHAR = dafnc;
                unlink(tfilname);
        }
        if(saverbits) {
                TFCHAR = rtfnc;
                unlink(tfilname);
                TFCHAR = rdfnc;
                unlink(tfilname);
        }
        exit(stat);
}

/* load files from a library.  the library is open in ibuf, the*/
/*  count of files to load is pointed to by libfctr, and the offset*/
/*  of each file is pointed to by libptr.*/

loadlib()
{

        register i,j;
        register long l;

        i = *libfctr++; /*# files to load from this library*/
        if(i==0)
                return;         /*none to load*/
        while(i--) {    /*load the files*/
                l = *libptr++;                  /*library offset for this file*/
                lbseek(l,&ibuf);                /*seek to beginning of file*/
                lbseek(l,&rbuf);
                rdlibhdr();                             /*read the library header*/
                readhdr();                              /*read the file header*/
                lbctr = l;
                do2load(1);                             /*load it*/
                addsizes();
        }
}

/* do a long seek on buffer bp  given a long file offset*/
/*  last argument indicates relative or absolute seek*/

lbseek(al,bp)
long al;
struct buf *bp;
{
        register long bn;

        bn = (al/BLKSIZE)*BLKSIZE;
        if(lseek(bp->fildes,bn,0) < 0) {
                printf(": seek error on file %s\n",ifilname);
                endit();
        }
        bp->nunused = 0;        /*input buff empty*/
        bp->xfree = &bp->buff[0];
        for( ; bn < al; bn++ )
                getc(bp);
}

/* look up the value of an external symbol given the external symbol*/
/*  number.  Since externals are not duplicated on the hash chains,*/
/*  all externals don't have values assigned in the symbol table,*/
/*  so the external name must be looked on the global chains and*/
/*  the value of the matching symbol used.  symptr points to the*/
/*  first symbol for the current file's symbol table*/

long extval(extno)
{

        register struct symtab *p;
        register char *pg;

        p = symptr + extno;
        if((p->flags&SYXR)==0) {
                return(p->vl1);
        }
        pack(p,lmte);
        pg = lemt(girt);
        if(pg == lmte)
                pg = lemt(eirt);        /*may be common*/
        return(pg->vl1);        /*return globals address*/
}

/* look up the relocation base for an external symbol.  must use same*/
/*  method as in extval for the same reasons (see comment above)*/

extbase(extno)
{

        register struct symtab *p;
        register char *pg;
        register int i;

        p = symptr + extno;
        if((p->flags&SYXR)==0) {
                pg = p;
        }
        else {
                pack(p,lmte);
                pg = lemt(girt);
                if(pg == lmte)
                        pg = lemt(eirt);        /*may be common*/
        }
        i = pg->flags;
        if(i&SYDA)
                return(DRELOC);
        else if(i&SYTX)
                return(TRELOC);
        else if(i&SYBS)
                return(BRELOC);
        return(DABS);
}

/* load a file doing relocation and external resolution*/
/*  libflg is set if we are loading from a library*/


do2load(libflg)
{

        register i,j;
        int longf;
        register struct buf *p;
        register struct buf *pr;
        int saof;
        register long tpc;
        register long l;
        long l1;
        int wasext;

        tpc = 0;
        p = &obuf;
        pr = &rtbuf;
        saof = -1;
        symptr = *firstsym++;   /*beginning of this symbol table*/
        l = couthd.ch_tsize + couthd.ch_dsize + couthd.ch_ssize + HDSIZE;
        if(libflg)
                l += lbctr + sizeof *lib2hd;
        lbseek(l,&rbuf);        /*long seek */
        l = couthd.ch_tsize;
do2l1:
        while((l -= 2) >= 0) {   /*relocate the text*/
                longf = 0;
                i = getw(&ibuf);
                j = getw(&rbuf);
                tpc += 2;               /*keep pc in this file*/
                wasext = 0;
                switch(j&7) {                   /*relocation bits*/

                        case INSABS:    /*first word of instr*/
                        case DABS:              /*data absolute*/
                                putw(i,p);
                                if(saverbits)
                                        putw(j,pr);             /*relocation bits*/
                                break;

                        case LUPPER:    /*high word of long*/
                                l1.hiword = i;
                                if(saverbits)
                                        putw(j,pr);             /*upper word relocation bits*/
                                l1.loword = getw(&ibuf);
                                j = getw(&rbuf);
                                tpc += 2;
                                longf++;        /*doing two words*/
                                l -= 2;         /*count lower half*/
dorelc:
                                switch(j&7) {

                                        case DABS:
                                                if(saverbits)
                                                        putw(j,pr);
                                                break;
                                        case INSABS:
                                        case LUPPER:
                                        default:
                                                goto do2199;

                                        case TRELOC:
                                                l1 += textbase;
                                                if(saverbits)
                                                        putw(j,pr);
                                                break;

                                        case DRELOC:
                                                l1 += database;
                                                if(saverbits)
                                                        putw(j,pr);
                                                break;

                                        case BRELOC:
                                                l1 += bssbase;
                                                if(saverbits)
                                                        putw(j,pr);
                                                break;

                                        case EXTVAR:
                                                wasext++;
                                                l1 += extval(j>>3);
                                                if(saverbits)
                                                        putw(extbase(j>>3),pr);
                                                break;

                                        case EXTREL:
                                                l1 = l1+extval(j>>3)-textbase-tpc+2;
                                                if(l1< (-32768L) || l1>0x7fff) {
                                                        printf(": relative address overflow at %lx in %s\n",
                                                          tpc,ifilname);
                                                        prextname(j>>3);        /*give name referenced*/
                                                        exstat++;
                                                }
                                                l1.hiword = 0;
                                                if(saverbits)
                                                        putw(DABS,pr);
                                                goto outlowd;

                                }
                                if(ignflg==0 && longf==0 && l1&0xffff8000 && saof) {
                                        printf(": short address overflow in %s\n",ifilname);
                                        if(wasext)
                                                prextname(j>>3);
                                        exstat++;
                                        if(libflg) {
                                                l1 = *(libptr-1);
                                                printf("library offset = %x\n",(int)l1.loword);
                                                l1 = 0;
                                        }
                                        saof = 0;
                                }
                                if(longf)
                                        putw(l1.hiword,p);
outlowd:
                                putw(l1.loword,p);
                                break;

                        case TRELOC:
                        case DRELOC:
                        case BRELOC:
                        case EXTVAR:
                        case EXTREL:
#ifdef NOMMU
                                l1 = *(libptr-1);
                                printf("library offset = %u\n",l1.loword);
                                printf("16-bit reloc in %s\n",ifilname);
#endif
                                l1 = i;         /*sign extend to long like 68000*/
                                goto dorelc;

                        default:
do2199:
                                printf(": File format error: invalid relocation flag in %s\n",ifilname);
                                endit(-1);

                }
        }
        if(p == &obuf) {
                p = &tbuf;              /*place to put data*/
                pr = &rdbuf;    /*file for data relocatin bits*/
                l = couthd.ch_dsize;
                goto do2l1;             /*now do the data*/
        }
}

/* do the final writting to the output file*/
/*  copy the initialized data from the temp file to the output file*/
/*  write the symbol table to the output file*/

finalwr()
{

        if((textsize+textstart) != textbase) {
                printf(": finalwr: text size error\n");
                exstat++;
        }
        cpdata(&tbuf,dafnc,datasize);
        osymt();                /*write the symbol table*/
        if(saverbits) {
                cpdata(&rtbuf,rtfnc,textsize);
                cpdata(&rdbuf,rdfnc,datasize);
        }
        myfflush(&obuf);
        if(lseek(obuf.fildes,14L,0)<0 || write(obuf.fildes,&stlen,4)!=4) {
                printf(": output file write error\n");
                exstat++;
        }
        endit(exstat);
}

/* copy the initialized data words from temp file to output file*/
cpdata(pb,fnc,size)
register struct buf *pb;
long    size;
{
        register j;
        myfflush(pb);
        close(pb->fildes);
        TFCHAR = fnc;
        if((fopen(tfilname,pb,1)) < 0) {
                printf(": unable to reopen %s\n",tfilname);
                endit(-1);
        }
        while (size > 0)
        {
                j = getw(pb);           /* Fetch word from source buffer */
                putw(j,&obuf);
                size -= 2;              /* Down by 2 bytes              */
        }
}

/* output symbol table to file*/

osymt()
{

        register struct symtab *p;

        stlen = 0;
        if(sflag)       /*no symbol table desired*/
                return;

/* now output the symbols deleting externals*/

        for(p=bmte; p<lmte; p++) {
                if(p->flags&SYXR)                               /*external symbol*/
                        continue;
                if((p->flags&SYGL)==0 && (p->name[0]=='L' || Xflag==0))
                        continue;
                osyme(p);
        }
}

/* output symbols in a form to be read by a debugger*/
/* call with pointer to symbol table entry*/

long ll =0;

osyme(aosypt)
struct symtab *aosypt;
{
        register struct symtab *osypt;
        register short *p1;
        register int i;

        osypt = aosypt;         /*pointer to symbol table entry*/

        p1 = &(osypt->name[0]);

        stlen += OSTSIZE;       /*one more symbol out*/

/*output symbol to loader file*/
        p1 = &(osypt->name[0]);
        for(i=0; i<SYNAMLEN/2; i++) {           /*output symbol name*/
                putw(*p1++,&obuf);
        }

        putw(osypt->flags,&obuf);                       /*output symbol flags*/
        ll = osypt->vl1;
        putw(ll.hiword,&obuf);          /*output symbol value*/
        putw(ll.loword,&obuf);
}

/*get constant given radix*/
/*  this routine only works for radixes of 2, 8, 16*/
/*  so as to avoid lmul because of floating point*/
long lgetnum(apc,ardx)
char *apc;
int ardx;
{

        register trdx,j;
        register char *pc;
        register long i;

        pc = apc;
        if(ardx==16)
                trdx = 4;               /*radix as power of 2*/
        else if(ardx==8)
                trdx = 3;
        else if(ardx==2)
                trdx = 1;
        else
                return(0);
        i=0;
        while(1) {
                j = *pc++;
                if(j>='0' && j<='9')
                        j -= '0';
                else
                        if(j>='a' && j<='f')
                                j = j-'a'+10;
                        else if(j>='A' && j<='F')
                                j = j-'A'+10;
                        else
                                break;                  /*not valid numeric char*/
                if(j>=0 && j<ardx)
                        i = (i<<trdx)+j;
                else
                        break;
        }
        return(i);
}

/* look for and define if found etext, edata, and end*/

spendsym(ap)
struct symtab *ap;
{

        register struct symtab *p;

        p = ap;
        if(eqstr(etexstr,&p->name[0])) {
                etextptr = p;
                return(1);
        }
        if(eqstr(edatstr,&p->name[0])) {
                edataptr = p;
                return(1);
        }
        if(eqstr(eendstr,&p->name[0])) {
                endptr = p;
                return(1);
        }
        return(0);
}

/* test two symbol names for equality*/

eqstr(ap1,ap2)
char *ap1, *ap2;
{

        register char *p1, *p2;
        register int i;

        p1 = ap1;
        p2 = ap2;
        for(i=0; i<SYNAMLEN; i++) {
                if(*p1++ != *p2++) {
                        return(0);
                }
        }
        return(1);
}

/* print an error message giving an external name*/

prextname(extno)
{

        register struct symtab *p;
        register char *pc;

        p = symptr + extno;
        printf(": external name: ");
        for(pc = &p->name[0]; pc < &p->name[SYNAMLEN]; pc++) {
                if(*pc == 0)
                        break;
                putchar(*pc);
        }
        putchar('\n');
}

