/* lksym.c */

/*
 * (C) Copyright 1989
 * All Rights Reserved
 *
 * Alan R. Baldwin
 * 721 Berkeley St.
 * Kent, Ohio  44240
 */

#include <stdio.h>
#include "aslink.h"

/*
 * This routine is called early in the
 * game to set up the symbol hashtable.
 */
VOID
syminit()
{
	register h;
	struct sym **spp;

	spp = &symhash[0];
	while (spp < &symhash[NHASH])
		*spp++ = NULL;
}

/*
 * Find/Create a global symbol entry.
 *
 * S xxxxxx Defnnnn
 *   |      |  |
 *   |      |  `-- sp->s_addr
 *   |      `----- sp->s_type
 *   `------------ sp->s_id
 *
 */
struct sym *
newsym()
{
	register c, i, nglob;
	struct sym *tsp;
	struct sym **s;
	char id[NCPS];

	getid(id, -1);
	tsp = lkpsym(id, 1);
	c = getnb();get();get();
	if (c == 'R') {
		tsp->s_type |= S_REF;
		if (eval())
			fprintf(stderr, "Non zero S_REF\n");
	} else
	if (c == 'D') {
		i = eval();
		if (tsp->s_type & S_DEF && tsp->s_addr != i)
			fprintf(stderr, "Multiple definition of %.8s\n", id);
		tsp->s_type |= S_DEF;
		/*
		 * Set value and area extension link.
		 */
		tsp->s_addr = i;
		tsp->s_axp = axp;
	} else {
		fprintf(stderr, "Invalid symbol type %c for %.8s\n", c, id);
		exit(1);
	}
	/*
	 * Place pointer in header symbol list
	 */
	if (headp == NULL) {
		fprintf(stderr, "No header defined\n");
		exit(1);
	}
	nglob = hp->h_nglob;
	s = (struct sym **) hp->s_list;
	for (i=0; i < nglob ;++i) {
		if (s[i] == NULL) {
			s[i] = tsp;
			return(tsp);
		}
	}
	fprintf(stderr, "Header symbol list overflow\n");
	exit(1);
}

/*
 * Lookup the name `id' in the hashtable.
 * If it is not found either return a
 * `NULL' (`f' is false) or a
 * pointer to a newly created hash table
 * entry (`f' is true).
 */
struct sym *
lkpsym(id, f)
char *id;
{
	register struct sym *sp;
	register h;

	h = hash(id);
	sp = symhash[h];
	while (sp != NULL) {
		if (symeq(id, sp->s_id))
			return (sp);
		sp = sp->s_sp;
	}
	if (f == 0)
		return (NULL);
	sp = (struct sym *) new (sizeof(struct sym));
	sp->s_sp = symhash[h];
	symhash[h] = sp;
	strncpy(sp->s_id, id, NCPS);
	return (sp);
}

/*
 * Get value of relocated symbol
 */
int
symval(tsp)
register struct sym *tsp;
{
	register val;

	val = tsp->s_addr;
	if (tsp->s_axp) {
		val += tsp->s_axp->a_addr;
	}
	return(val);
}

/*
 * Check for undefined symbols
 */
VOID
symdef(fp)
FILE *fp;
{
	register struct sym *sp;
	register i;

	for (i=0; i<NHASH; ++i) {
		sp = symhash[i];
		while (sp) {
			if (sp->s_axp == NULL)
				sp->s_axp = areap->a_axp;
			if ((sp->s_type & S_DEF) == 0)
				symmod(fp, sp);
			sp = sp->s_sp;
		}
	}
}

VOID
symmod(fp, tsp)
FILE *fp;
struct sym *tsp;
{
	register i, j;
	struct sym **p;

	if (hp = headp) {
	    while(hp) {
		p = (struct sym **) hp->s_list;
		for (i=0; i<hp->h_nglob; ++i) {
		    if (p[i] == tsp) {
			fprintf(fp, "Undefined Global %8.8s ", tsp->s_id);
			fprintf(fp, "referenced by module %8.8s\n", hp->m_id);
		    }
		}
	    hp = hp->h_hp;
	    }
	}
}

/*
 * Compare two symbol names.
 */
int
symeq(p1, p2)
register char *p1, *p2;
{
	register n;

	n = NCPS;
	do {

#if	CASE_SENSITIVE
		if (*p1++ != *p2++)
			return (0);
#else
		if (ccase[*p1++] != ccase[*p2++])
			return (0);
#endif

	} while (--n);
	return (1);
}

/*
 * Given a pointer to a symbol name
 * compute and return the hash table
 * bucket.
 * The `sum of all the characters mod
 * table size' algorithm is perhaps
 * not the best.
 */
int
hash(p)
register char *p;
{
	register h, n;

	h = 0;
	n = NCPS;
	do {

#if	CASE_SENSITIVE
		h += *p++;
#else
		h += ccase[*p++];
#endif

	} while (--n);
	return (h&HMASK);
}

/*
 * Allocate and clear a block of space.
 * Leave if there is no space left
 * at all.
 */
VOID *
new(n)
{
	register VOID *p;

	if ((p = (VOID *) calloc(n,1)) == NULL) {
		fprintf(stderr, "Out of space!\n");
		exit(1);
	}
	return (p);
}
