/*
	Copyright 1982
	Alcyon Corporation
	8716 Production Ave.
	San Diego, Ca.  92121
*/

#include "lo68.h"

char tfilname[];
char etexstr[];
char edatstr[];
char eendstr[];
int debug;
int exstat;

/* get one symbol entry from the input file and put it into*/
/*  the symbol table entry pointed to by lmte*/
getsym()	/* rewritten for vax */
{
	register int i;
	long lvalue;
	char *p;
	short wvalue;

	p = lmte;
	for(i=SYNAMLEN; --i != -1; )	/* 2 may 83 */
		*p++ = getc(&ibuf);	        /* get name */
	if (lgetw(&wvalue, &ibuf) == -1)
		printf(": error reading symbol table entry\n");
	if (lgetl(&lvalue ,&ibuf) == -1)
		printf(": error reading symbol table entry\n");	/* symbol value */
	((struct symtab *)lmte)->flags = wvalue;
	((struct symtab *)lmte)->vl1 = lvalue;
#ifdef DEBUG
	printf("\tname= %s, flags= %o, val= %d\n", ((struct symtab *)lmte)->name, 
			((struct symtab *)lmte)->flags, ((struct symtab *)lmte)->vl1);
#endif
}

/* relocate the symbol value pointed to by lmte according to*/
/*  symbol type and corresponding relocation base*/
relocsym()
{
	long l;

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

#define ADDSYM	"addsym: file=%s symbol=%s flags=%o\n"
/*
 * 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,ifilname,lmte,(int)((struct symtab *)lmte)->flags);
	
	if(((struct symtab *)lmte)->flags&SYXR) {  /*external*/
		p = lemt(eirt);
		mmte();
	}
	else if(((struct symtab *)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 if(((struct symtab *)p)->flags!=((struct symtab *)lmte)->flags || 
				((struct symtab *)p)->vl1 != ((struct symtab *)lmte)->vl1) {
dupdef:
			if(libflg) {
				noload++;
				lastdup = p;
			}
			else
				prdup(p);               /*dup defn msg*/
		}
		else {
			addmte();	/* put entry in for external #`s */
		}
	}
	else {          /*normal symbol*/
		if(((struct symtab *)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;
	long longtmp;

	bmte = sbrk(SYTESIZE*SZMT+2);
	longtmp = (long)bmte; /* 11 apr 83, for vax */
	if(longtmp&1L)  /* 11 apr 83, for vax */
		bmte++;
	emte = bmte + SYTESIZE*SZMT;            /*end of main table*/
	lmte=bmte;      /*beginning main table*/
	cszmt = SZMT;   /*current size of main table*/
	p1 = eirt;
	p2 = girt;
	for(i=32; --i != -1; ) {	/* 2 may 83 */
		*p1++ = (char *)p1;
		*p1++ = (char *)0;
		*p2++ = (char *)p2;
		*p2++ = (char *)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 by 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 struct symtab *mtpt;	/* 14 apr 83, from char * */

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

/*
 * locate the next symbol on a given hash chain with the same name as lmte
 *  return a pointer to it.  When this pointer is equal to lmte, the
 *  chain has been completely searched.
 */
char *
nextsy(amtpt)
char *amtpt;
{

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

	mtpt = amtpt;

/*loop to locate entry in main table*/
lemtl:
	p1 = &((struct symtab *)mtpt)->name[0];
	p2 = &((struct symtab *)lmte)->name[0];
	for(i=SYNAMLEN; --i != -1; ) {	/* 2 may 83 */
		if(*p1++ != *p2++) {	    /* changed to char ptr compares */
			mtpt = ((struct symtab *)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()
{

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

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

	lmte += SYTESIZE;	/*bump last main table entry pointer*/
	if(lmte>=emte) {		/*main table overflow*/
		if(sbrk(SYTESIZE*ICRSZMT) == (char *)-1){   /*get more memory*/
			printf(": symbol table overflow\n");
			exit(-1);
		}
		else {			/*move end of main table*/
			emte += SYTESIZE*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 = &((struct symtab *)lmte)->name[0];
	for(i=SYNAMLEN; --i != -1; ) 	/* 2 may 83 */
		ht1 += *p++;
	return(ht1&(SZIRT-2));       /*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)
char *apkstr;
char *apkptr;
{
	register i;
	register char *pkstr, *pkptr;

	pkstr = apkstr;
	pkptr = apkptr;
	for(i=SYNAMLEN; --i != -1; ) /* 2 may 83 */
		*pkptr++ = *pkstr++;	 /* changed to char ptr move */
}

/*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 = SZIRT; --i != -1; )	/* 2 may 83 */
		*p1++ = *p2++;
	p2 = girt;
	p1 = savgirt;
	for(i = SZIRT; --i != -1; )	/* 2 may 83 */
		*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 = SZIRT; --i != -1; )	/* 2 may 83 */
		*p1++ = *p2++;
	p1 = girt;
	p2 = savgirt;
	for(i = SZIRT; --i != -1; )	/* 2 may 83 */
		*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 if(shtext) {       /*shared text - move start of data*/
		if(shtext == -1) {      /* 2k boundary */
			database = (textbase+SH2BOUND)&~(SH2BOUND-1);
			datastart = database;
		}
		else {          /* 4k boundary */
			database = (textbase+SHBOUND)&~(SHBOUND-1);
			datastart = database;
		}
	}
	else if(isplit) {       /* i & d split*/
		database = 0;
		datastart = 0;
	}
	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 {
			((struct symtab *)p)->vl1 = textsize;
			((struct symtab *)p)->flags &= ~SYXR;      /*no longer external*/
			((struct symtab *)p)->flags |= SYDF|SYGL;
		} while((p = nextsy(((struct symtab *)p)->tlnk)) != lmte);
	}
	if(edataptr) {
		pack(edatstr,lmte);
		p=lemt(eirt);
		do {
			((struct symtab *)p)->vl1 = textsize+datasize;
			((struct symtab *)p)->flags &= ~SYXR;      /*no longer external*/
			((struct symtab *)p)->flags |= SYDF|SYGL;
		} while((p = nextsy(((struct symtab *)p)->tlnk)) != lmte);
	}
	if(endptr) {
		pack(eendstr,lmte);
		p = lemt(eirt);
		do {
			((struct symtab *)p)->vl1 = textsize+datasize+bsssize;
			((struct symtab *)p)->flags &= ~SYXR;      /*no longer external*/
			((struct symtab *)p)->flags |= SYDF|SYGL;
		} while((p = nextsy(((struct symtab *)p)->tlnk)) != lmte);
	}
}

/* fix symbol addresses that have been assigned by adding in*/
/*  database and bssbase*/
fixsyms()  					/*look at each symbol*/
{
	register struct symtab *p;

	for(p = (struct symtab *)bmte; p<(struct symtab *)lmte; p++) {
		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 = (char **)*sx2;             /*first entry on this chain*/
		while(1) {
			if(((struct symtab *)sx2)->vl1)
				asgncomn(sx2);  /*assign a common address*/
			else
				asgnext(sx2);   /*match to a global*/
			p = (char *)sx2;
			if(p == *sx1)       /*end of chain*/
				break;
			sx2 = (char **)((struct symtab *)sx2)->tlnk; /*next entry in chain*/
		}
	}
}

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

	p = ap;
	for(i = SYNAMLEN; --i != -1; ) {	/* 2 may 83 */
		if(*p)
			putchar(*p++);
		else
			break;
	}
	putchar('\n');
}

/* output symbol table to file*/
osymt()
{
	register struct symtab *p;
	register int cnt;

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

/* now output the symbols deleting externals*/

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

/* output symbols in a form to be read by a debugger*/
/* call with pointer to symbol table entry*/
osyme(aosypt)
struct symtab *aosypt;
{
	register struct symtab *osypt;
	register char *p1;
	register int i;

	osypt = aosypt;					/*pointer to symbol table entry*/
	stlen += OSTSIZE;				/*one more symbol out*/

/*output symbol to loader file*/
	p1 = &(osypt->name[0]);
	for(i = SYNAMLEN; --i != -1; )		/*output symbol name*/
		putc(*p1++,&obuf);

	lputw(&osypt->flags,&obuf);		/*output symbol flags*/
	lputl(&osypt->vl1,&obuf);		/*output symbol value*/
}

