#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include "p88symboltable.h"

#define HASH_TABLE_BITW	10
#define HASH_TABLE_SIZE	(1 << HASH_TABLE_BITW)
#define HASH_TABLE_MASK	(HASH_TABLE_SIZE - 1)
#define HASH_INC	311 /* prime number */

/* Strings should be arranged in the same order as operations. */
const char *oprations[] = {
	"END", "COPY",
	"ADD", "SUB", "MUL", "DIV", "CMP",
	"JMP", "JNB", "JB",
	"CALL", "RTN", "PUSH", "POP",
	"IN", "OUT",
	NULL
};

const char *registers[] = {
	"AX", "BX", "CX", "DX",
	NULL
};

static struct {
	char name[MAX_ID_LENGTH+1];
	long address;
	unsigned char kind;
	/* Contents of address:
		k_opcode:   opcode,
		k_register: register number,
		k_label:    address,
		k_undef:    line number of src
	 */
} hashtable[HASH_TABLE_SIZE];

#ifndef _HAS_STRCASECMP_
static int strcasecmp(const char *p, const char *q)
{
	int d;
	do {
		if (*p == 0 && *q == 0)
			return 0;
		d = tolower(*p) - tolower(*q);
		p++;
		q++;
	} while(d == 0);
	return d;
}
#endif

static int search_id(const char *str)
{
	const int ChrUsed = 4;
	int i, v, x;

	/* mask 0x1f ignored case of letters */
	v = (str[0] & 0x1f) << (HASH_TABLE_BITW - 5);
	for (i = 1; i < ChrUsed && str[i] != 0; i++)
		v ^= (str[i] & 0x1f) << (i - 1);
	x = v & HASH_TABLE_MASK;
	while (hashtable[x].kind != null
	&& strcasecmp(hashtable[x].name, str) != 0)
		x = (x + HASH_INC) & HASH_TABLE_MASK;
	return x;
}

void init_table(void)
{
	int i, x;

	for (i = 0; i < HASH_TABLE_SIZE; i++)
		hashtable[i].kind = null;
	for (i = 0; oprations[i]; i++) {
		x = search_id(oprations[i]);
		strcpy(hashtable[x].name, oprations[i]);
		hashtable[x].kind = k_opcode;
		hashtable[x].address = i;
	}
	for (i = 0; registers[i]; i++) {
		x = search_id(registers[i]);
		strcpy(hashtable[x].name, registers[i]);
		hashtable[x].kind = k_register;
		hashtable[x].address = i;
	}

	x = search_id("RES");	/* Directive to reserve memory area */
	strcpy(hashtable[x].name, "RES");
	hashtable[x].kind = k_directive;
	hashtable[x].address = 0;

	x = search_id("RANDOM");	/* Special address */
	strcpy(hashtable[x].name, "RANDOM");
	hashtable[x].kind = k_label;
	hashtable[x].address = RANDOM_ADDR;
}

static void copylabel(int x, const char *s)
{
	int i;
	char *p = hashtable[x].name;
	for (i = 0; i < MAX_ID_LENGTH && s[i]; i++) {
		p[i] = toupper(s[i]);
	}
	p[i] = 0;
}

int def_label(const char *lab, long addr)
{
	int x, knd;

	x = search_id(lab);
	knd = hashtable[x].kind;
	if (knd == null) /* new label */
		copylabel(x, lab);
	if (knd == null || knd == k_undef) {
		hashtable[x].kind = k_label;
		hashtable[x].address = addr;
	}
	return knd;
}

int check_id(const char *str, int linenum, long *addrp)
{
	int x, knd;

	x = search_id(str);
	knd = hashtable[x].kind;
	if (knd == null) {
		copylabel(x, str);
		hashtable[x].kind = k_undef;
		hashtable[x].address = linenum;
	}else if (knd != k_undef && addrp != NULL)
		*addrp = hashtable[x].address;
	return knd;
}

long undef_words(long memsize)
{
	int i;
	long count = memsize;

	for (i = 0; i < HASH_TABLE_SIZE; i++) {
		if (hashtable[i].kind == k_undef) {
			fprintf(stderr, "!!Undefined Label ... Memory allocated: ");
			fprintf(stderr, "%s = [%ld], Line=%ld\n",
				hashtable[i].name, count,
				hashtable[i].address + 1 /* line of src */);
			hashtable[i].kind = k_label;
			hashtable[i].address = count;
			count += 2;
		}
	}
	return count;
}
