
#include <stdio.h>
#include "cout.h"
#include <ctype.h>
#include "link68.h"

struct symtab *symptr =0;
/* parameters that define the main table*/
#define SZMT 300			/*initial size of the main table*/
					/*must be large enough to initialize*/
#define ICRSZMT 100			/*add to main table when run out*/
int cszmt =0;				/*current size of main table*/
char *bmte =0;				/*beginning of main table*/
char *emte =0;				/*end of main table*/

/*initial reference table for externals*/
# define SZIRT	64
char *eirt[SZIRT] ={0};
char *saveirt[SZIRT] ={0};

/*initial reference table for globals*/
char *girt[SZIRT] ={0};
char *savgirt[SZIRT] ={0};

char **pirt =0;

char *lmte =0;		/*last entry in main table*/
char *savlmte =0;

#define AREGLO		8
#define AREGHI		15

/* relocation bit definitions:*/
#define RBMASK		07		/*tells type of relocation*/
#define INSABS		7		/*first word of instr -- absolute*/
#define DABS		0		/*data word absolute*/
#define TRELOC		2		/* TEXT relocatable*/
#define DRELOC		1		/* DATA relocatable*/
#define BRELOC		3		/* BSS relocatable*/
#define EXTVAR		4		/* ref to external variable*/
#define LUPPER		5		/* upper word of long*/
#define EXTREL		6		/* relative mode on external variable*/



FILE *ibuf =NULL;
FILE *tbuf =NULL;
FILE *obuf =NULL;
FILE *rbuf =NULL;
FILE *rtbuf =NULL;
FILE *rdbuf =NULL;


long textbase =0;
long database =0;
long bssbase =0;
long textsize =0;
long datasize =0;
long bsssize =0;
long stacksize =0;

long textstart =0;
long datastart =0;
long bssstart =0;

char *ifilname =0;		/*points to name of current input file	*/
char *outfname =0;		/*points to name of current output file	*/

#define NFILE	256		/*max # files we can process*/
char *fsymp[NFILE] ={0};	/*points to first symbol for each .o file*/
char **firstsym =0;		/*points to entry in fsymp*/

int extmatch =0;		/*matched an external in a library entry*/
int noload =0;			/*dont load this lib file flag*/

#define NLIB	16	/*max # libraries to process*/
int lbfictr[NLIB] ={0};	/*counts files loaded from one library*/
int *libfctr =0;	/*points to lbfictr*/
long lbfioff[NFILE] ={0}; /*each file offset in library*/
long *libptr =0;	/*points to lbfioff*/

#define LIB1MAGIC	0177555
#define LIB2MAGIC	0xffffff65
#define LIB1HDSIZE	16
#define LIB2HDSIZE	26
struct libhdr {
	char l1fname[8];
	long l1modti;
	char l1userid;
	char l1fimode;
	short l1fsize;
};

struct lib2hdr {
	char l2fname[14];
	long l2modti;
	char l2userid;
	char l2gid;
	short  l2fimode;
	long l2fsize;
	short l2junk;
} *lib2hd =0;

#define	ARMAG	"!<arch>\n"
#define	SARMAG	8

#define	ARFMAG	"`\n"

struct ar_hdr {
	char	ar_name[16];
	char	ar_date[12];
	char	ar_uid[6];
	char	ar_gid[6];
	char	ar_mode[8];
	char	ar_size[10];
	char	ar_fmag[2];
} lib3_hdr ={0};

#define LIB3MAGIC 0x3c21
#define LIB3HDSIZE	(sizeof lib3_hdr)

int libhdsize =0;
long  libhd[20] ={0};		/*length is max lib head size*/

int umesflg =0;
char dafnc =0;
int pass2 =0;
long stlen =0;
int Xflag =0;
int Dflag =0;
int Bflag =0;
int Zflag =0;
int sflag =0;			/* if set, keep symbol table*/


struct symtab *etextptr =0;
struct symtab *edataptr =0;
struct symtab *erootptr =0;
struct symtab *endptr =0;
char *lastdup =0;

char rtfnc =0;
char rdfnc =0;
int saverbits =0;

struct ovtab ovtab1 = {0};

/* 68000 linking loader -- adapted from */
/*   Bill Allen's to handle overlays */

/* This loader is 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

#ifdef	UNIX				/************************/
char *tfbase = "loXXXXXX";		/* Temp base name	*/
char tdisk[DRIVELEN]  = "/tmp/";	/* Temp disk name	*/
#else					/* CP/M and VMS		*/
char *tfbase = "loXXXXXA";		/* Temp base name	*/
char tdisk[DRIVELEN]  = "";		/* 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 erootstr[]= "_eroot\0\0";
char eendstr[] = "_end\0\0\0\0";
char ovhstr[]  = "_ovhdlr\0";
int endit();
char *lemt();
char *nextsy();
char *sbrk();
int readshort();
int ignflg =0;
int debug  =0;
int exstat =0;
int ovflag =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 fseek();
long rtxsize = 0;		/* size of root's text			*/
long rdtsize = 0;		/* size of root's data			*/
long hihiaddr = 0;		/* first even word above whole program	*/

long ovtable = 0;		/* address of overlay table start	*/
struct ovcalblk ovcall = {0};


char	cmdline[LINELEN] = "";
int	mapflg = FALSE;
int	absflg = FALSE;
int	libflg = FALSE;		/* set if an input file to be searched	*/
int	symflg = FALSE;		/* set if symbol table to be saved	*/
int	udfflg = FALSE;		/* set if undefined symbols allowed	*/
int	chnflg = FALSE;		/* set if linking chained program	*/
int	dmpflg = FALSE;		/* set for dumping symbol table		*/

struct ovtrnode *ovtree[MAXOVLS+1] = {NULL};	/* command tree		*/


int ovpath[MAXOVDEP+1] = {0};		/* current path in ovtree walk	*/
int ovpathtp = -1;			/* current end of path		*/


extern usage();
VOID dopass1();
VOID ovexts();
VOID dopass2();

/************************************************************************/
/*									*/
/* main() -- driver routine for entire linker.				*/
/*									*/
/*									*/
/*									*/
/*									*/
/************************************************************************/

VOID
main(argc,argv)

int argc;
char **argv;
{
	register int i;
	struct symtab *pt;

	banner();			/* print the sign-on message	*/

	if (argc == 1)				/* no command line	*/
	{
		usage();
		exit(-1);
	}

	for (i = 1; i < argc; i++)		/* assemble command line */
	{
		if ((strlen(cmdline) + strlen(argv[i])) > (LINELEN - 2))
			errorx(CMDLONG, "");	/* command line too long */
		else
		{
			strcat(cmdline, argv[i]); /* put in the command */
			strcat(cmdline, " ");	  /* leave blank between */
		}
	}

	printf("%s\n", cmdline);		/* echo command line	*/

	preproc();				/* build command tree	*/
	ovflag = (numovls > 0);			/* are there overlays?	*/
	buildf();				/* construct temp. names */
	
	/* Open and then close a file in ibuf to allocate its buffer	*/
	/* area under the symbol table.  Otherwise the buffer ends up	*/
	/* on top of any additions to the symbol table.			*/

	openfile((ovtree[ROOT]->ovflist)->fnfname);	/* first input file */
	fclose(ibuf);				/* will open again later */
	intsytab();				/* init. symbol table	*/
	firstsym = fsymp;
	libfctr = lbfictr;
	libptr = lbfioff;
	saverbits = !absflg;			/* keep relocation bits	*/
	if (ovflag && !chnflg)			/* put _ovhdlr in symbol*/
	{					/*  table		*/
		pack(ovhstr,lmte);
		lmte->flags = SYXR;		/* make it external	*/
		lmte->ovlnum= ROOT;		/* belongs to root	*/
		addsym();
	}
	walktree(ROOT, dopass1, POSTORDER);	/* pass1 on each node	*/
	if (ovflag && !chnflg)
	{		/* add room for overlay table in root's data segment*/
		ovtable = ovtree[ROOT]->ovbsbase; /* changes in ovexts	*/
		ovtree[ROOT]->ovbsbase += numovls*sizeof(ovtab1);
		ovtree[ROOT]->ovcap += numovls*sizeof(ovtab1);
		rdtsize += numovls*sizeof(ovtab1);
	}
	fixcoms();		/* allocate common and global static space */
	hihiaddr = ovtree[ROOT]->ovcap;		/* current top of bss	*/
	if (ovflag)		/* resolve calls to overlayed routines	*/
		walktree(ROOT,ovexts,PREORDER);
	resolve();		/* resolve externals, assign addresses	*/
	if (exstat)		/* quit if there are errors		*/
		endit(exstat);
	pass2++;		/* now in pass two			*/
	firstsym = fsymp;
	libfctr = lbfictr;
	libptr = lbfioff;
	Xflag = TRUE;		/* print all locals entered by pass 1	*/
	if (ovflag && !chnflg)	/* set up template overlay call block	*/
	{
		pack(ovhstr,lmte);	/* get overlay handler address	*/
		pt = lemt(girt);
		ovcall.ovhandlr = pt->vl1;
		ovcall.jsrovh = JSRL;
		ovcall.jmprout = JMPL;
	}
	walktree(ROOT, dopass2, POSTORDER);	/* pass2 on each node	*/
}







/************************************************************************/
/*									*/
/* dopass1(*ovnode) -- do pass one processing on a given node		*/
/*									*/
/*									*/
/************************************************************************/

VOID
dopass1(opt)

REG struct ovtrnode *opt;		/* pointer to node in command tree */

{
	REG struct filenode *fpt, *tfpt;

	opt->ovfsym = lmte;		/* start of this modules symbols */
	if (ovpath[ovpathtp] == ROOT)
		textbase = textstart;
	else
		textbase = 0;
	opt->ovtxbase = textbase;
	database = 0;
	bssbase = 0;

	fpt = opt->ovflist;		/* get first input file		*/

	while (fpt != NULL)		/* process each file		*/
	{
		if (fpt->fnflags & FNINCL)	/* next name is a symbol*/
		{
		  tfpt = fpt->fnnext;		/* get the include symbols */
		  while ((tfpt != NULL) && (tfpt->fnflags & FNSYM))
		  {				/* put in symbol table	*/
		    pack(tfpt->fnfname, lmte);	/* copy name		*/
		    lmte->flags = SYXR;		/* external		*/
		    lmte->ovlnum = ovpath[ovpathtp];/* mark which module*/
		    addsym();			/* regular symbol	*/
		    tfpt = tfpt->fnnext;	/* get the next node	*/
		  }
		  fpt->fnnext = tfpt;		/* skip to real file node */
		}

		libflg = !(fpt->fnflags & FNALL);	/* search if lib? */
		Xflag = (fpt->fnflags & FNLOCS);	/* ommit locals? */

		p1load(fpt->fnfname);		/* process the file	*/

		fpt = fpt->fnnext;		/* get the next file	*/
	}
	opt->ovdtbase = (Dflag)? datastart : (textbase+1)&~1;
	opt->ovbsbase = (Bflag)? bssstart : (opt->ovdtbase+database+1)&~1;
	opt->ovcap = (opt->ovbsbase+bssbase+1)&~1;
	if (ovpath[ovpathtp] == ROOT)
	{
		rtxsize = textbase - textstart; 
		rdtsize = database;
	}
}


/************************************************************************/
/*									*/
/* ovexts(ovtreenode) -- resolve references to overlayed routines	*/
/*									*/
/*	This routine looks through all of the symbols for a module,	*/
/*	and checks each external reference.  If the matching global	*/
/*	is in a child overlay, an ovcall block is built containing the	*/
/*	appropriate information.  The external is resolved to that	*/
/*	ovcall block, which is placed at the end of the current 	*/
/*	module's text segment.						*/
/*									*/
/*	All overlay references are assumed to be jumps or subroutine	*/
/*	calls.  References to non-text-based addresses in overlays	*/
/*	result in an error message.					*/
/*									*/
/*	For chained programs, the routine only adjusts the bases.	*/
/*									*/
/************************************************************************/

VOID
ovexts(ovpt)

REG struct ovtrnode *ovpt;		/* points to node in command tree */

{
	REG struct symtab *cursym;	/* current symbol being examined*/
	int ovrefs;			/* count overlay references	*/
	REG long newbase;		/* use for adjusting seg. bases	*/
	REG short onum;			/* this module's number		*/

	onum = ovpath[ovpathtp];
	if (onum != ROOT)	/* adjust textbase to above parent*/
	{
		newbase = ovtree[ovpt->ovparent]->ovcap;
		ovpt->ovtxbase = newbase;
		ovpt->ovdtbase += newbase;
		ovpt->ovbsbase += newbase;
		ovpt->ovcap += newbase;
	}
	ovrefs = 0;
	cursym = ovpt->ovfsym;		/* first sy tab entry for module*/
	if (!chnflg)			/* no ovr tabs in chined program*/
	  while ((cursym->ovlnum == onum) && (cursym != lmte))
	  { 
		if (cursym->flags & SYXR)
			ovrefs += chkovext(cursym); /* check ext ref	*/
		cursym++;			/* get the next symbol	*/
	  }
	newbase = ovrefs * sizeof(ovcall); /* size of extra code	*/
	ovpt->ovbsbase += newbase;	/* add in space for ovcalls	*/
	ovpt->ovcap += newbase;		/* ovdtbase adjusted in chkovext*/
	if (onum == ROOT)
	{
		rtxsize += newbase;		
		ovtable += newbase;	/* final relocatable address	*/
	}
	hihiaddr = max(hihiaddr,ovpt->ovcap);	/* might be new top	*/
}

/************************************************************************/
/*									*/
/* dopass2(*ovnode) -- do pass two processing on a given node		*/
/*									*/
/*	Drives the pass 2 routines.  If it is processing the root, 	*/
/*	the symbol table is also written to the output file if 		*/
/*	specified.  This routine depends on the root being the first	*/
/*	module processed in order to do the symbol table right.		*/
/*									*/
/************************************************************************/

VOID
dopass2(opt)

REG struct ovtrnode *opt;		/* pointer to node in command tree */


{
	REG struct filenode *fpt;

	/* set up sizes and bases for building load file */
	if (opt == ovtree[ROOT])
	{
		textsize = rtxsize;
		datasize = rdtsize;
		bsssize = hihiaddr - opt->ovbsbase; /* for overlay area */
	}
	else
	{
		textsize = opt->ovdtbase - opt->ovtxbase;
		datasize = opt->ovbsbase - opt->ovdtbase;
		bsssize = opt->ovcap - opt->ovbsbase;
	}
	textstart = opt->ovtxbase;
	textbase = opt->ovtxbase;
	database = opt->ovdtbase;
	bssbase = opt->ovbsbase;

	sflag =((opt == ovtree[ROOT]) && symflg);/* only root module	*/

	makeofile(opt->ovfname);	/* set up output file for node	*/

	fpt = opt->ovflist;		/* get first input file		*/

	while (fpt != NULL)		/* go through whole list	*/
	{
		if (!(fpt->fnflags & FNSYM))	/* file, not symbol	*/
			p2load(fpt->fnfname);	/* load the file	*/
		fpt = fpt->fnnext;		/* get the next file	*/
	}
						  /*  has symbol table	*/
	finalwr();			/* finish the output file	*/
}


/************************************************************************/
/*									*/
/* walktree(node, visit, order) -- ordered walk of overlay tree.	*/
/*									*/
/*	Processes each node in the overlay tree in a pre-order or	*/
/*	post-order walk,  starting at the specified node.		*/
/*	At each node, the routine pointed to by visit is called 	*/
/*	to work at that node.  						*/
/*									*/
/*	The routine pointed to by visit must take a single parameter,	*/
/*	which is a pointer to a filenode.  The routine should not	*/
/*	return a value.							*/
/*									*/
/************************************************************************/

VOID
walktree(index, visit, order)

register int index;			/* index into overlay tree	*/
int (*visit)();				/* routine to process node	*/
int order;				/* specifies which order walk	*/

{
	register int kidpt;

	ovpath[++ovpathtp] = index;		/* put node in path list*/
	if (order == PREORDER)
		(*visit)(ovtree[index]);	/* process the node	*/
	kidpt = ovtree[index]->ovfstkid;	/* get first descendant	*/
	while (kidpt != NULL)			/* visit each descendant */
	{
		walktree(kidpt, visit, order);	/* depth-first call	*/
		kidpt = ovtree[kidpt]->ovnxtsib; /* next descendant	*/
	}
	if (order != PREORDER)
		(*visit)(ovtree[index]);	/* process the node	*/
	ovpathtp--;				/* take node out of list */
}




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 {
		libflg = FALSE;		/* set to TRUE by dopass1 */
		do1load(0);		/* load a regular file */
		firstsym++;
		addsizes();
	}
	fclose(ibuf);
}

/* 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 lib file */
		if(extmatch > noload) {		/*found a match*/
			if(noload)
				prdup(lastdup);	/*print dup defn*/
			addsizes();		/*add this file's 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;
	char tempname[FNAMELEN];

	p = ap;
	if(*p=='-' && *++p=='l') {
		if(*++p)
			LIBCHAR = *p;
		else
			LIBCHAR = '6';	/*default library name*/
		p = libname;
	}
	else
		p = ap;
	if((ibuf = fopenb(p,"r")) == 0)
	{
		strncpy(tempname,p,FNAMELEN-DEFLEN);
		strcat(tempname,DEFTYPE);
		if((ibuf = fopenb(tempname,"r")) == 0)
			errorx(BADINFIL, p);		
		strcpy(p,tempname);
	}
	ifilname = p;	/*point to current file name for error msgs*/
	if( readshort(ibuf, &couthd.ch_magic) ) 
		errorx(READERR, ifilname);		
	if( couthd.ch_magic != LIB2MAGIC ) {
		fseek(ibuf,0L,0);
		readhdr();				/*read file header*/
	}
	if(pass2) {		/*need file descrptr for reloc bits*/
		rbuf = fopenb(p, "r");
	}
}

/* read the header of the input file*/

int readshort(fp,s)
FILE *fp;
short *s;
{
	short ts;

	if( fread(&ts,sizeof(short),1,fp) != 1 )
		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)
		errorx( FORMATERR, ifilname);
	if(couthd.ch_rlbflg) 
		errorx(NORELOC, ifilname);
}

/* 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(lflg)
int lflg;			/* set if file is in a library	*/
{

	register long i;
	register long l;

	*firstsym = lmte;		/*remember where this symbol table starts*/
	l = couthd.ch_tsize + couthd.ch_dsize + sizeof couthd;
	if( lflg )
		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();		/*add to symbol table*/
		i -= OSTSIZE;
	}
}

/************************************************************************/
/*									*/
/* getsym()								*/
/*									*/
/* Get one symbol entry from the input file and put it into the symbol	*/
/* table entry pointed to by lmte.					*/
/*									*/
/* If the program is being linked for chaining (CBASIC) and the symbol	*/
/* name is __cbmain, the name is changed to reflect the specific 	*/
/* overlay, since each overlay and the root has its own definition for	*/
/* __cbmain.								*/ 
/*									*/
/************************************************************************/

getsym()
{

	register int i;
	register short *stpt;

	stpt = lmte;
	for(i=0; i<SYNAMLEN/(sizeof *stpt); i++)
		*stpt++ = getw(ibuf);
	lmte->ovlnum = ovpath[ovpathtp];	/* mark which module it's in */
	if (chnflg && (strncmp(lmte,CBMAIN,8)==0))/* main CBASIC entry point?*/
		sprintf(lmte, "main.%03d", lmte->ovlnum);/* make name unique*/
	*stpt++ = getw(ibuf);			/* flags 		*/

/*	*stpt++=0;	*/	/* skip upper half of internal flags on VAX */
	stpt->hiword = getw(ibuf); 		/* sym value high word 	*/
	stpt->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*/
		errst1 = lmte->name;
		errorx(BADSYMFLG, ifilname);
	}
	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.  When loading a library without searching */
/*  it, return as if the symbol matches, to make sure the library file loads. */

addsym()
{

	register char *p;

/*	if(debug) {*/
/*		printf("addsym: file=%s symbol=%s flags=%x\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++;
			}
		}
		else
			extmatch = 1;	/* trick searchlib to load lib file */
		p = lemt(girt);
addtry2:	if(p == lmte)
			mmte();
		else {
dupdef:			if (chnflg && (p->ovlnum != ROOT) &&
			   (lmte->ovlnum != ROOT) && 
			   (p->ovlnum != lmte->ovlnum))
			{
				p = nextsy(p->tlnk); /* try again	*/
				goto addtry2;
			}
			if(libflg) {
				noload++;
				lastdup = p;
			}
			else if(  (p->ovlnum != lmte->ovlnum) ||
				  (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;
{
	errst1 = p;
	errorx(DUPDEF, ifilname);	/* returns			*/
	exstat++;
}

/************************************************************************/
/*									*/
/* intsytab() -- initialize the symbol table and the heads of the	*/
/*	hash lists							*/
/*									*/
/*	Uses sbrk to guarantee continuity of symbol table.		*/
/*									*/
/************************************************************************/

intsytab()
{

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

	bmte = (sbrk((sizeof *symptr)*SZMT+2));
	emte = bmte + (sizeof *symptr)*SZMT;		/*end of main table*/
	if((long)bmte&1)
		bmte++;
	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();
}

/************************************************************************/
/*									*/
/* addmte() -- add the symbol pointed to by lmte to symbol table	*/
/*									*/
/*	Uses sbrk (vs. malloc) to guarantee continuity with rest of	*/
/*	symbol table.							*/
/*									*/
/************************************************************************/

addmte()
{
	lmte += (sizeof *symptr);	/*bump last main table entry pointer*/
	if(lmte>=emte) {		/*main table overflow*/
		if(sbrk((sizeof *symptr)*ICRSZMT) == -1)
			errorx(SYMOFL, "");	/* could not get more memory*/
		else 
		{				/*move end of main table*/
			emte += (sizeof *symptr)*ICRSZMT;
			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(fp)
FILE **fp;
{

	register i,j;

	(TFCHAR)++;
	if((*fp = fopenb(tfilname, "w")) == 0)
	{
		errorx(BADTEMP, tfilname);
		endit(-1);
	}
}
/* update the relocation counters with the sizes in the header*/

addsizes()
{

	textbase += ((couthd.ch_tsize + 1)&~1);
	database += ((couthd.ch_dsize + 1)&~1);
	bssbase  += ((couthd.ch_bsize + 1)&~1);
}

/*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() -- resolve the external variable addresses and set the	*/
/*	base address of the data and bss segments.			*/
/*									*/
/*	On entry, all the final base addresses are in the overlay tree.	*/
/*	If an external symbol has a value, it points to a jump block.	*/
/*	External references to common areas have been set to zero 	*/
/*	when this routine is called.					*/
/*									*/
/************************************************************************/

resolve()
{

	register char *p;

	fixsyms();		/*relocate symbols with addresses*/
	fixexts();		/*fix external addresses & commons*/
	if(etextptr) {
		pack(etexstr,lmte);
		p=lemt(eirt);
		do {
			p->vl1 = ovtree[ROOT]->ovtxbase + rtxsize;
			p->flags &= ~SYXR;	/*no longer external*/
			p->flags |= SYDF|SYGL|SYTX;
		} while((p=nextsy(p->tlnk)) != lmte);
	}
	if(edataptr) {
		pack(edatstr,lmte);
		p=lemt(eirt);
		do {
			p->vl1 = ovtree[ROOT]->ovdtbase + rdtsize;
			p->flags &= ~SYXR;	/*no longer external*/
			p->flags |= SYDF|SYGL|SYDA;
		} while((p=nextsy(p->tlnk)) != lmte);
	}
	if(erootptr) {
		pack(erootstr,lmte);
		p=lemt(eirt);
		do {
			p->vl1 = ovtree[ROOT]->ovcap;
			p->flags &= ~SYXR;	/*no longer external*/
			p->flags |= SYDF|SYGL|SYDA;
		} while((p=nextsy(p->tlnk)) != lmte);
	}
	if(endptr) {
		pack(eendstr,lmte);
		p=lemt(eirt);
		do {
			p->vl1 = hihiaddr;
			p->flags &= ~SYXR;	/*no longer external*/
			p->flags |= SYDF|SYGL|SYBS;
		} while((p=nextsy(p->tlnk)) != lmte);
	}
}
/************************************************************************/
/*									*/
/* fixsyms() --fix symbol addresses that have been assigned by adding 	*/
/*  	in textbase, database, or bssbase for symbol's overlay.		*/
/*									*/
/*	Text-based in root are already correct from pass 1.		*/
/*									*/
/************************************************************************/

fixsyms()
{

	register struct symtab *p;

	for(p=bmte; p<lmte; p++) {	/*look at each symbol*/
		if(p->flags&SYXR)
			continue;
		if(p->flags&SYTX)		/*text symbol*/
		{
			if (p->ovlnum != ROOT)
				p->vl1 += ovtree[p->ovlnum]->ovtxbase;
		}
		else if(p->flags&SYDA)		/*data symbol*/
			p->vl1 += ovtree[p->ovlnum]->ovdtbase;
		else if(p->flags&SYBS)	/* bss symbol*/
			p->vl1 += ovtree[p->ovlnum]->ovbsbase;
	}
}

/************************************************************************/
/*									*/
/* fixcoms() -- allocate the bss space for common areas and global	*/
/*	static data.							*/
/*									*/
/************************************************************************/

long bsscomm = 0;		/* use only in fixcoms and asgncomn	*/

fixcoms()

{
	register char *p;
	register char **sx1, **sx2;
	long oldtop;
	oldtop = ovtree[ROOT]->ovcap - ovtree[ROOT]->ovbsbase;
	bsscomm = oldtop;			/* current free bss	*/
	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*/
			p = sx2;
			if(p == *sx1)		/*end of chain*/
				break;
			sx2 = sx2->tlnk;	/*next entry in chain*/
		}
	}
	ovtree[ROOT]->ovcap += (bsscomm-oldtop); /* adjust for common	*/
}
/************************************************************************/
/*									*/
/* asgncomn(*symbol) -- assign an address for a block of bss common	*/
/*									*/
/*	The address of the block is zero-based in the root's bss.  It	*/
/*	will be relocated later when all the globals are adjusted.	*/
/*									*/
/************************************************************************/

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 = 0;		/* set once globals resolved*/
		return;
	}
	l = 0;
	lemt(eirt);		/* set up pointer to proper chains	*/
	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 = bsscomm;
	p->ovlnum = ROOT;		/* the global goes in the root	*/
	bsscomm += (l+1)&~1;		/* always start at even address	*/
	lemt(girt);			/* set ptrs for global chain	*/
	mmte();				/* add to global chain		*/
}

/************************************************************************/
/*									*/
/* fixexts() -- get addresses for all external symbols			*/
/*									*/
/*	On entry, any externals with values have been resolved to a	*/
/*	jump block.							*/
/*									*/
/************************************************************************/

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)) 	/* skip overlay calls	*/
				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 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);
astry2:	if(pg == lmte) {	/*no match in global symbols*/
		pg=lemt(eirt);		/*set ptrs for external chains*/
		if(pg==lmte)
			errorx(INTERR, "asgnext");
		if(spendsym(ap))	/*end, etext, errot, or edata*/
			return;
		if(umesflg==0) {
			errorx(UNDEF, "");
			umesflg++;
			if (!udfflg)		/* undefined allowed?	*/
				exstat++;	/* no, set for exit	*/
		}
		prtsym(p);
	}
	else {
		if (chnflg && (pg->ovlnum != ROOT) &&
		   (pg->ovlnum != p->ovlnum))
		{
		  pg = nextsy(pg->tlnk);	/* must be in right module */
		  goto astry2;
		}
		p->vl1 = pg->vl1;	/*assign symbol value*/
	}
}


/************************************************************************/
/*									*/
/* newjblk() -- allocate a new jump block and put it in the command tree*/
/*									*/
/************************************************************************/

struct jmpblock *
newjblk()

{
	REG struct jmpblock *npt, *tpt;
	REG struct ovtrnode *opt;

	if ((npt = sbrk(sizeof(*npt))) <= 0) /* get a piece of memory	*/
		errorx(NOROOM, "");
	opt = ovtree[ovpath[ovpathtp]];	/* get current command tree node */
	if ((tpt = opt->ovjblck) == NULL)	/* empty list?		*/
		opt->ovjblck = npt;
	else
	{
		while (tpt->nxtjbl != NULL)	/* find end of list	*/
			tpt = tpt->nxtjbl;
		tpt->nxtjbl = npt;		/* put at end of list	*/
	}
	npt->nxtjbl = NULL;
	return(npt);
}

/************************************************************************/
/*									*/
/* inkid(num,node) -- is num in command tree node's kidlist?		*/
/*									*/
/************************************************************************/

BOOLEAN
inkid(num,node)

REG int num, node;

{
	node = ovtree[node]->ovfstkid;	/* get index of first kid	*/
	while (node != NULL)
	{
		if (node == num)		/* same node?		*/
			return(TRUE);
		node = ovtree[node]->ovnxtsib;	/* get the next kid	*/
	}
	return(FALSE);			/* didn't find it		*/
}

/************************************************************************/
/*									*/
/* chkovext -- check an external symbol for possible overlay reference	*/
/*									*/
/*	Returns 1 if a ovcall block is built.				*/
/*									*/
/*									*/
/************************************************************************/

int
chkovext(spt)

REG struct symtab *spt;

{
	REG struct symtab *gpt;
	REG int i;
	REG struct jmpblock *jpt;
	REG struct ovtrnode *ovpt;
	REG long cbadd;

	if (spt->vl1)			/* already been processed	*/
		return(0);
	pack(spt->name,lmte);		/* set up for search		*/
	gpt = lemt(girt);		/* look for global match	*/
	if (gpt == lmte)		/* no match? -- return and let	*/
		return(0);		/*   fixexts handle unresolved	*/
	
	/* validate global -- must be either non-overlayed (relative to	*/
	/* module) or in child module and text-based			*/

	for (i = 0; i <= ovpathtp; i++)		/* non-overlayed?	*/
		if (gpt->ovlnum == ovpath[i])
			return(0);		/* no jump block	*/
	if (!(gpt->flags & SYTX))
	{
		errst1 = ovtree[spt->ovlnum]->ovfname;
		errorx(BADOVREF, spt->name);	/* returns		*/
		return(0);
	}
	i = ovpath[ovpathtp];			/*current overlay number*/
	ovpt = ovtree[i];
	if (!(inkid(gpt->ovlnum, i)))
	{
		errst1 = ovtree[spt->ovlnum]->ovfname;
		errorx(BADOVREF, spt->name);
		return(0);
	}
	jpt = newjblk();		/* put a new jump block in 	*/
	jpt->globref = gpt;		/*   ovtree, remember symbol	*/
	cbadd = ovpt->ovdtbase;		/* get address of new code	*/
	if (i != ROOT)			/* non-root text-based globals	*/
		cbadd -= ovpt->ovtxbase; /* are relocated later		*/
	ovpt->ovdtbase += sizeof(ovcall);  /* bump size up for new code*/
	lemt(eirt);			/* get pointers right		*/
	while(1)
	{
		spt->vl1 = cbadd;	/* actual address of call block	*/
		spt->flags |= SYTX;	/* text-based relocatable	*/
		spt->flags &= ~SYXR;	/* no longer external		*/
		spt = nextsy(spt->tlnk);	/* get all references	*/
		if ((spt == lmte) || (spt->ovlnum != i))
			return(1);	/* done with this symbol	*/
	}
}

/************************************************************************/
/*									*/
/* prtsym(sym) -- 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');
}

/************************************************************************/
/*									*/
/* p2load(fname) -- 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();
	}
	fclose(ibuf);
	fclose(rbuf);
}

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

makeofile(ofilname)
char *ofilname;
{

	long l;

	outfname = ofilname;		/* save name in case of error	*/
	if((obuf = fopenb(ofilname, "w")) == 0)
		errorx(BADOUT, ofilname);
	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 = (sflag)? lmte - bmte : 0;
	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 = (sflag)? lmte - bmte : 0;
	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 = (sflag)? lmte - bmte : 0;
	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

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


endit(stat)
{
	if (stat == 0)
		fclose(obuf);
	if(dafnc) {
		TFCHAR = dafnc;
		unlink(tfilname);
	}
	if(saverbits) {
		TFCHAR = rtfnc;
		unlink(tfilname);
		TFCHAR = rdfnc;
		unlink(tfilname);
	}
	if (dmpflg)
		dumpsyms();
	if (stat != 0)
		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;
FILE *bp;
{
	if(fseek(bp,al,0) < 0)
		errorx(SEEKERR, ifilname);
}

/************************************************************************/
/*									*/
/* extval(snum) -- look up the value of an external symbol given	*/
/*	the external symbol number.					*/
/*									*/
/*	Since externals are 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;		/* symptr + extno*sizeof(symbol)*/
	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*/
/*  lflg is set if we are loading from a library*/


do2load(lflg)
{

  register i,j;
  int longf;
  register FILE *p;
  register FILE *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(lflg)
    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) {
	      errnum1 = tpc-2;
	      errorx(RELADROFL, 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) {
	  errnum1 = tpc-2;
          errorx(SHRTOFL, ifilname);
          if(wasext)
            prextname(j>>3);
          exstat++;
          if(lflg) {
            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:
      errorx(BADRELOC, ifilname);

    }
  }
  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*/
  }
}

/************************************************************************/
/*									*/
/* wrjumps() -- write the overlay call blocks in the text segment	*/
/*									*/
/************************************************************************/

VOID
wrjumps()

{
	REG int onum;
	REG struct jmpblock *jpt;


	onum = ovpath[ovpathtp];
	jpt = ovtree[onum]->ovjblck;
	while (jpt != NULL)
	{
		onum = (jpt->globref)->ovlnum;	/* where is global?	*/
		ovcall.ovtabad = ovtable + ((onum-1) * sizeof(ovcall));
		ovcall.routaddr = (jpt->globref)->vl1;

		putw(ovcall.jsrovh,obuf);		/* jsr	_ovhdlr	*/
		putw((ovcall.ovhandlr).hiword, obuf);
		putw((ovcall.ovhandlr).loword, obuf);
		putw((ovcall.ovtabad).hiword, obuf);	/* .dc.l ovtab	*/
		putw((ovcall.ovtabad).loword, obuf);
		putw(ovcall.jmprout, obuf);		/* jmp	routine	*/
		putw((ovcall.routaddr).hiword, obuf);
		putw((ovcall.routaddr).loword, obuf);
		
		textbase += sizeof(ovcall);	/* bump for block size	*/

		if (saverbits)
		{
			putw(INSABS,rtbuf);	/* jsr			*/
			putw(LUPPER,rtbuf);	/* address of ovhandler */
			putw(TRELOC,rtbuf);
			putw(LUPPER,rtbuf);	/* address of ovtable	*/
			putw(DRELOC,rtbuf);
			putw(INSABS,rtbuf);	/* jmp			*/
			putw(LUPPER,rtbuf);	/* address of routine	*/
			putw(TRELOC,rtbuf);
		}

		jpt = jpt->nxtjbl;		/* next block in list	*/
	}
}


/************************************************************************/
/*									*/
/* wrovtab() -- write overlay table to root's data segment		*/
/*									*/ 
/************************************************************************/

VOID
wrovtab()

{
	REG int i, j;
	REG struct ovtrnode *opt;
	REG short *pt;

	for (i = 1; i <= numovls; i++)
	{
		opt = ovtree[i];
		fixname(opt);		/* put name in ovtab1		*/
		ovtab1.tbldpt = opt->ovtxbase;	/* load point for module*/
		pt = (&ovtab1);		/* first word of ovtab1		*/
		for (j=1; j <= 6; j++, pt++)	/* write filename	*/
		{
#ifdef	MC68000
			putw(*pt,tbuf);
#endif

#ifdef	VAX
			putc(pt->hibyte,tbuf);
			putc(pt->lobyte,tbuf);
#endif

#ifdef	PDP11
			putc(pt->hibyte,tbuf);
			putc(pt->lobyte,tbuf);
#endif

			if (saverbits)
				putw(DABS,rdbuf);
		}
		putw((ovtab1.tbldpt).hiword,tbuf); /* write loadpt	*/
		putw((ovtab1.tbldpt).loword,tbuf);
		if (saverbits)
		{
			putw(LUPPER,rdbuf);
			putw(TRELOC,rdbuf);
		}
	}
}

/************************************************************************/
/*									*/
/* fixname(trnode) -- put an output file name into ovtab1		*/
/*									*/
/*	The name must be in the proper form for direct BDOS calls.	*/
/*	The filename is padded with blanks, and the type is left-	*/
/*	justified in its field.  There is no punctuation between	*/
/*	the two parts.							*/
/*									*/
/************************************************************************/

VOID
fixname(opt)

REG struct ovtrnode *opt;

{
	REG int i, j;

	for (i = 0; i < 8 ; i++)	/* sizeof(ovtab1.tbname) */
		if (!isalnum(ovtab1.tbname[i] = opt->ovfname[i]))
			break;		/* found end of name		*/
	for ( ;i < 8; i++)		/* sizeof(ovtab1.tbname) */

		ovtab1.tbname[i] = ' ';	/* blank out rest of name	*/
	for (i = 0; i < FNAMELEN; i++)
		if (opt->ovfname[i] == '.')	/* find file type	*/
			break;
	i++;					/* skip dot, if there	*/
	for (j = 0; (j < 3) && (i < FNAMELEN); i++, j++)
		if (!isalnum(ovtab1.tbext[j] = opt->ovfname[i]))
			break;		/* copy only letters and digits	*/
	for ( ;j < 3; j++)
		ovtab1.tbext[j] = ' ';	/* blank fill rest of type field*/
	ovtab1.tbext[3] = '\0';		/* zero for overlay handler	*/
}


/************************************************************************/
/*									*/
/* finalwr() -- 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 (ovflag && !chnflg)
		wrjumps();
	if((textsize+textstart) != textbase) {
		errorx(TSZERR, outfname);
		exstat++;
	}
	if (ovflag && !chnflg && (ovpath[ovpathtp] == ROOT))
		wrovtab();
	cpdata(tbuf,dafnc,datasize);
	osymt();		/*write the symbol table*/
	if(saverbits) {
		cpdata(rtbuf,rtfnc,textsize);
		cpdata(rdbuf,rdfnc,datasize);
	}
	fflush(obuf);
	if(fseek(obuf,14L,0)<0 || fwrite(&stlen,4,1,obuf)!=1) {
		errorx(OWRTERR, outfname);
		exstat++;
	}
	endit(exstat);
}

/* copy the initialized data words from temp file to output file*/
cpdata(pb,fnc,size)
register FILE *pb;
long	size;
{
	register j;
	fflush(pb);
	fclose(pb);
	TFCHAR = fnc;
	if((pb = fopenb(tfilname, "r")) == 0)
		errorx(REOPNERR, tfilname);
	while (size > 0)
	{
		j = getw(pb);		/* Fetch word from source buffer */
		putw(j,obuf);
		size -= 2;		/* Down by 2 bytes		*/
	}
	fclose(pb);
}

/* 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);
}


/* look for and define if found etext, edata, eroot, 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(erootstr,&p->name[0])) {
		erootptr = 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');
}


/************************************************************************/
/*									*/
/* dumpsyms() -- dump symbol table.  Undocumented debugging aid		*/
/*									*/
/*									*/
/************************************************************************/

dumpsyms()
{
	struct symtab *p;

	printf("\nDUMP OF INTERNAL SYMBOL TABLE\n");
	printf("BMTE = %lx, LMTE = %lx\n\n", bmte, lmte);

	for (p=bmte;p<lmte;p++)
	{
		printf("NAME:    %s\n", p->name);
		printf("FLAGS:   ");
		if (p->flags & SYDF) printf("DEF ");
		if (p->flags & SYEQ) printf("EQU ");
		if (p->flags & SYGL) printf("GLB ");
		if (p->flags & SYER) printf("REG ");
		if (p->flags & SYXR) printf("EXT ");
		if (p->flags & SYDA) printf("DAT ");
		if (p->flags & SYTX) printf("TEX ");
		if (p->flags & SYBS) printf("BSS");
		printf("\n");
		printf("VALUE:   %lx\n", p->vl1);
		printf("OVERLAY: %d\n", p->ovlnum);
		printf("INTERNAL ADDRESS = %lx, LINK = %lx\n\n",
			p, p->tlnk);
	}
	dmpflg = FALSE;
}

