#include <stdio.h>
#include "defs.h"
#include "externs.h"

/* colsym() collects a symbol from prlnbuf into symbol[],
 *    leaves prlnbuf pointer at first invalid symbol character,
 *    returns 0 if no symbol collected
 */

int colsym(int *ip)
{
	int	valid;
	int	i;
	char c;

	valid = 1;
	i = 0;
	while (valid == 1) {
		c = prlnbuf[*ip];
		if (c == '_' || c == '.');
		else if (c >= 'a' && c <= 'z');
		else if (c >= 'A' && c <= 'Z');
		else if (i >= 1 && c >= '0' && c <= '9');
		else valid = 0;
		if (valid == 1) {
			if (i < SBOLSZ - 1)
				symbol[++i] = c;
			(*ip)++;
		}
	}
	if (i == 1) {
		switch (symbol[1]) {
		case 'A': case 'a':
		case 'X': case 'x':
		case 'Y': case 'y':
			error("Symbol is reserved (A, X or Y)!");
			i = 0;
		}
	}
	symbol[0] = i;
	symbol[i+1] = '\0';
	return(i);
}

/* symbol table lookup
 *	if found, return pointer to symbol
 *	else, install symbol as undefined, and return pointer
 */

struct t_symbol *stlook(void)
{
	struct t_symbol *ptr;
	unsigned int hash = 0;
	char c;
	int	i;

	if (symbol[1] == '.'){		/*  Local symbol */
		if (glablptr) {
			ptr = glablptr->local;
			while (ptr) {
				if (!strcmp(symbol, ptr->name))
					break;
				ptr = ptr->next;
			}
			if (ptr == NULL)
				ptr = stinstal(hash, 1);
		} else {
			error("Local symbol not allowed!");
			return (NULL);
		}
	} else {					/*  Global symbol */
		for (i = 0; i < symbol[0]; i++) {
			c = symbol[i + 1];
			hash += c;
			hash  = (hash << 3) + (hash >> 5) + c;
		}
		hash &= 0xFF;
		ptr = hash_tbl[hash];
		while (ptr) {
			if (!strcmp(symbol, ptr->name))
				break;
			ptr = ptr->next;
		}
		if (ptr == NULL)
			ptr = stinstal(hash, 0);
	}
	return(ptr);
}

/* install symbol into symtab */

struct t_symbol *stinstal(int hash, int type)
{
	struct t_symbol *ptr;

	if ((ptr = (void *)malloc(sizeof(struct t_symbol))) == NULL) {
		error("Out of memory!");
		return (NULL);
	}
	ptr->type  = UNDEF;
	ptr->value = 0;
	ptr->local = NULL;
	ptr->bank  = -1;
	ptr->page  = -1;
	strcpy(ptr->name, symbol);

	if (type >= 1) {
		ptr->next = glablptr->local;
		glablptr->local = ptr;
	} else {
		ptr->next = hash_tbl[hash];
		hash_tbl[hash] = ptr;
	}
	return(ptr);
}

/* remove the latest defined symbol from symtab */

int stremove(void)
{
	char c;
	int i, hash = 0;

	for (i = 0; i < symbol[0]; i++) {
		c = symbol[i + 1];
		hash += c;
		hash  = (hash << 3) + (hash >> 5) + c;
	}
	hash &= 0xFF;
	if (hash_tbl[hash] != lablptr) {
		error("Internal error[2]!");
		return (0);
	}
	hash_tbl[hash] = lablptr->next;
	free(lablptr);
	return (1);
}

/* assign <value> to label pointed to by lablptr,
 *	checking for valid definition, etc.
 */

int labldef(int lval, int flag)
{
	char c;
	int	i;

	if (lablptr) {
		if (flag)
			lval = (lval & 0x1FFF) | (page << 13);
		if (pass == FIRST_PASS) {
			if (lablptr->type == UNDEF) {
				lablptr->type = DEFABS;
				lablptr->value = lval;
			} else {
				lablptr->type = MDEF;
				lablptr->value = 0;
				error("Label multiply defined!");
				return(-1);
			}
		} else {
			if ((lablptr->value != lval) && (pass == LAST_PASS)) {
				error("Internal error[1]!");
				return(-1);
			}
		}
		if (flag) {
			lablptr->bank = bank;
			lablptr->page = page;
			c = lablptr->name[1];
			if (c != '.')
				glablptr = lablptr;
		}
	}
	return(0);
}

