
/*
 *  SYMBOLS.C
 *
 *  SHash[SHASHSIZE]
 */

/*#include "asm.h"*/

static uword hash2(char *str, int len);
SYMBOL *allocsymbol(void);

static SYMBOL org;
static SYMBOL special;

void
setspecial(int value, int flags)
{
    special.value = value;
    special.flags = flags;
}

SYMBOL *
findsymbol(char *str, int len)
{
    register uword h1;
    register SYMBOL *sym;
    char buf[64];

    if (str[0] == '.') {
	if (len == 1) {
	    if (Csegment->flags & SF_RORG) {
		org.flags = Csegment->rflags & SYM_UNKNOWN;
		org.value = Csegment->rorg;
	    } else {
		org.flags = Csegment->flags & SYM_UNKNOWN;
		org.value = Csegment->org;
	    }
	    return(&org);
	}
	if (len == 2 && str[1] == '.')
	    return(&special);
	sprintf(buf, "%ld%s", Localindex, str);
	len = strlen(buf);
	str = buf;
    }
#if OlafDol
    else if (str[len - 1] == '$') {
	sprintf(buf, "%ld$%s", Localdollarindex, str);
	len = strlen(buf);
	str = buf;
    }
#endif
    h1 = hash2(str, len);
    for (sym = SHash[h1]; sym; sym = sym->next) {
	if ((sym->namelen == len) && !memcmp(sym->name, str, len))
	    break;
    }
    return(sym);
}

SYMBOL *
createsymbol(char *str, int len)
{
    register SYMBOL *sym;
    register uword h1;
    char buf[64];

    if (str[0] == '.') {
	sprintf(buf, "%ld%s", Localindex, str);
	len = strlen(buf);
	str = buf;
    }
#if OlafDol
    else if (str[len - 1] == '$') {
	sprintf(buf, "%ld$%s", Localdollarindex, str);
	len = strlen(buf);
	str = buf;
    }
#endif
    sym = (SYMBOL *)allocsymbol();
    sym->name = permalloc(len+1);
    strcpy(sym->name, str);    /*  permalloc zero's the array for us */
    sym->namelen = len;
    h1 = hash2(str, len);
    sym->next = SHash[h1];
    sym->flags= SYM_UNKNOWN;
    SHash[h1] = sym;
    return(sym);
}

static uword
hash2(char *str, int len)
{
    register uword result = 0;

    while (len--)
	result = (result << 2) ^ *str++;
    return(result & SHASHAND);
}

/*
 *  Label Support Routines
 */

void
programlabel(void)
{
    register int len;
    register SYMBOL *sym;
    register SEGMENT *cseg = Csegment;
    register char *str;
    ubyte   rorg = cseg->flags & SF_RORG;
    ubyte   cflags = (rorg) ? cseg->rflags : cseg->flags;
    ulong   pc = (rorg) ? cseg->rorg : cseg->org;

    Plab = cseg->org;
    Pflags = cseg->flags;
    str = Av[0];
    if (*str == 0)
	return;
    len = strlen(str);
    if (str[len-1] == ':')
	--len;
#if OlafDol
    if (str[0] != '.' && str[len-1] != '$') {
	Lastlocaldollarindex++;
	Localdollarindex = Lastlocaldollarindex;
    }
#endif

    /*
     *	Redo:	unknown and referenced
     *		referenced and origin not known
     *		known and phase error	 (origin known)
     */

    if ((sym = findsymbol(str, len)) != NULL) {
	if ((sym->flags & (SYM_UNKNOWN|SYM_REF)) == (SYM_UNKNOWN|SYM_REF)) {
	    ++Redo;
	    Redo_why |= 1 << 13;
	    if (Xdebug)
		printf("redo 13: '%s' %04lx %04lx\n", sym->name, (long)sym->flags, (long)cflags);
	} else
	if ((cflags & SYM_UNKNOWN) && (sym->flags & SYM_REF)) {
	    ++Redo;
	    Redo_why |= 1 << 13;
	} else
	if (!(cflags & SYM_UNKNOWN) && !(sym->flags & SYM_UNKNOWN)) {
	    if (pc != sym->value) {
		printf("mismatch %10s %s  pc: %s\n", sym->name, sftos(sym->value,
			sym->flags), sftos(pc, cflags & 7));
		asmerr(17,0);
		++Redo;
		Redo_why |= 1 << 14;
	    }
	}
    } else {
	sym = createsymbol(str, len);
    }
    sym->value = pc;
    sym->flags = (sym->flags & ~SYM_UNKNOWN) | (cflags & SYM_UNKNOWN);
}

SYMBOL *SymAlloc;

SYMBOL *
allocsymbol(void)
{
    SYMBOL *sym;

    if (SymAlloc) {
	sym = SymAlloc;
	SymAlloc = SymAlloc->next;
	memset(sym, 0, sizeof(SYMBOL));
    } else {
	sym = (SYMBOL *)permalloc(sizeof(SYMBOL));
    }
    return(sym);
}

void
freesymbol(SYMBOL *sym)
{
    sym->next = SymAlloc;
    SymAlloc = sym;
}

void
freesymbollist(SYMBOL *sym)
{
    register SYMBOL *next;

    while (sym) {
	next = sym->next;
	sym->next = SymAlloc;
	if (sym->flags & SYM_STRING)
	    free(sym->string);
	SymAlloc = sym;
	sym = next;
    }
}

