/*************************************************
TABLES.C: Routines for table management for LOADER.

Written by:  Douglas Braun
             99 Summit Rd.
	     Riverside, CT 06878

July 1, 1985

**************************************************/

#include "loader.h"

/************************************
This file contains routines to manage the
symbol, chain, and offset tables.
**********************************/
extern num_errors;

OFFSET *off_list;  /* The list of offsets */
int max_offsets;
static num_offsets;    /* How many currently in list */

SYMBOL *sym_list;  /* List of symbols */
int max_symbols;
static num_symbols;



init_tables(m_offsets,m_symbols)
int m_offsets;
int m_symbols;
{

    max_offsets = m_offsets;
    max_symbols = m_symbols;

    if ((max_offsets > ((unsigned)64000/sizeof(OFFSET))) ||
 	(max_symbols > ((unsigned)64000/sizeof(SYMBOL))))
	bad_alloc();

    off_list = (OFFSET *)alloc(max_offsets * sizeof(OFFSET));
    sym_list = (SYMBOL *)alloc(max_symbols * sizeof(SYMBOL));

    num_symbols = 0;
    num_offsets = 0;
}



add_offset(addr, value)
unsigned addr, value;
{
    if (num_offsets >= max_offsets)
    {
	printf("Sorry... Ran out of space for offsets.\n");
	exit(1);
    }
    else
    {
#ifdef DEBUG
	printf("storing offset value %x for location %x\n",value,addr);
#endif
	off_list[num_offsets].o_address = addr;
	off_list[num_offsets++].o_value = value;
    }
}



dump_offsets()
{
    int curr;
    unsigned temp;
#ifdef DEBUG
    printf("There are %d offsets.\n",num_offsets);
#endif
    for (curr = 0; curr < num_offsets; curr++)
    {
#ifdef DEBUG
	printf("Filling in offset at location %x with value %x\n",
	         off_list[curr].o_address, off_list[curr].o_value);
#endif
	temp = get_word(off_list[curr].o_address);
	put_word(off_list[curr].o_value + temp, off_list[curr].o_address);
    }
}


/* This searches the symbol table for the given symbol,
and returns its index, or -1 if not there */

find_sym(name)
char *name;
{
    static curr; /* Static for speed */

    for (curr = 0; curr < num_symbols; curr++)
    {
	if (strcmp(name, sym_list[curr].s_name)==0)
	    return(curr);
    }

    return(-1);
}


/* This resolves an address chain */

do_chain(firstaddr, value)
unsigned firstaddr, value;
{
    unsigned addr;
    unsigned nextaddr;

    addr = firstaddr;
#ifdef DEBUG
    printf("resolving chain with value %x starting at %x\n",value,firstaddr);
#endif
    while (addr != 0)
    {
	nextaddr = get_word(addr);
	put_word(value,addr);
	addr = nextaddr;
#ifdef DEBUG
	printf("        Next location: %x\n",addr);
#endif
    }
}


/* This takes the definition of a symbol and
adds it to the symbol table, and resolves any
chains that the undefined symbol had */

def_symbol(value, name)
unsigned value;
char *name;
{
    int loc;

#ifdef DEBUG
    printf("Defining %s as %x\n",name,value);
#endif
    if ((loc=find_sym(name)) < 0)  /* Not in table */
    {
	if (num_symbols >= max_symbols)
	{
	    printf("Sorry... Ran out of symbol table space.\n");
	    abort();
	}
	else
	{
	    strncpy(sym_list[num_symbols].s_name,name,9);
	    sym_list[num_symbols].s_defined = TRUE;
	    sym_list[num_symbols].s_value = value;
	    sym_list[num_symbols++].s_chain = NULL;
  	}
    }
    else if (sym_list[loc].s_defined == TRUE)
    {
	printf("Multiply defined: %s\n",name);
	num_errors++;
	return;
    }
    else
    {
	sym_list[loc].s_value = value;
	sym_list[loc].s_defined = TRUE;
    }
}



/* This allocates a chain list entry, and traps failures to
allocate it */

CHAINPTR new_chain()
{
    CHAINPTR new;

    new = (CHAINPTR)sbrk(sizeof(CHAIN));
    if ((int)new == -1)
    {
	printf("Can't create any more chains.  ");
	bad_alloc();
    }

    return(new);
}



/* This takes a 'Chain external', and either adds a new entry to
the chain list of the symbol, or creates a new entry in the symbol
table for it, with one entry in its chain list */

add_chain(addr, name)
unsigned addr;
char *name;
{
    int loc;
    CHAINPTR curr_chain;
    CHAINPTR temp;

#ifdef DEBUG
    printf("Adding chain to %s location %x\n",name,addr);

    if (addr == 0)  /* Don't bother with chains that refer to nothing */
	return;
#endif

    if ((loc=find_sym(name)) < 0)  /* Not in table */
    {
	if (num_symbols >= max_symbols)
	{
	    printf("Sorry... Ran out of symbol table space.\n");
	    abort();
	}
	else
	{
	    strncpy(sym_list[num_symbols].s_name, name, 9);
	    sym_list[num_symbols].s_defined = FALSE;
  	    /* Add first entry to chain list */
	    temp = new_chain();
	    temp->c_head = addr;
	    temp->c_next = NULL;
	    sym_list[num_symbols++].s_chain = temp;
  	}
    }
    else
    {
	/* Add new entry to chain list */
	temp = new_chain();
	temp->c_head = addr;
	temp->c_next = sym_list[loc].s_chain;
	sym_list[loc].s_chain = temp;
    }
}



/* This resolves all chains whose head addresses are >= limit */
res_chains(limit)
unsigned limit;
{
    int did;  /* Flag */
    int j;

    did = TRUE;
    while (did == TRUE)
    {
	did = FALSE;
	for (j=0; j < num_symbols; j++)
	{
	    if (sym_list[j].s_defined == TRUE &&
	        sym_list[j].s_chain != NULL &&
		sym_list[j].s_chain->c_head >= limit)
	    {
		do_chain(sym_list[j].s_chain->c_head,
			   sym_list[j].s_value);
		sym_list[j].s_chain = sym_list[j].s_chain->c_next;
		did = TRUE;
	    }
	}
    }
}




/* This checks to make sure there are no remaining undefined
symbols at the end of the loading. */

chk_undefs()
{
    int curr;

    for (curr=0; curr < num_symbols; curr++)
    {
	if (sym_list[curr].s_defined == FALSE)
	{
	    printf("Undefined: %s\n",sym_list[curr].s_name);
	    num_errors++;
        }
	else if (sym_list[curr].s_chain != NULL)
	{
	    printf("Oops... Forgot to resolve a chain.  ");
	    abort();
        }
    }
}



/* This places the start address and jump instruction at location 100h */

do_start(addr, limit)
unsigned addr;
unsigned limit;
{
    if (addr < 0x103 || addr >= limit)
    {
	printf("Error: Bad or no start address.\n");
	num_errors++;
	return;
    }
    else if (get_byte(0x100) != 0 || get_byte(0x101) != 0 ||
	     get_byte(0x102) != 0)
    {
	printf("Error: Start address area written over.\n");
	num_errors++;
    }
    else
    {
	put_byte(0xc3, 0x100);
	put_word(addr,0x101);
    }
}



/* This fills in $MEMRY with addr. */

do_memry(addr)
unsigned addr;
{
    int loc;

    if ((loc = find_sym("$MEMRY")) < 0)
    {
	return;
    }
    else if (sym_list[loc].s_defined == FALSE)
    {
	return;
    }
    else
    {
	put_word(addr, sym_list[loc].s_value);
    }
}
	

/* This returns TRUE is the given name is used but not defined */

is_undefined(name)
char *name;
{
    int curr;

    for (curr=0; curr < num_symbols; curr++)
    {
	if (strcmp(sym_list[curr].s_name, name)==0 &&
            sym_list[curr].s_defined==FALSE)
	    return(TRUE);
    }
    return(FALSE);
}


/* Returns TRUE if there are any undefined symbols */
are_undefs()
{
    int curr;

    for (curr=0; curr < num_symbols; curr++)
    {
	if (sym_list[curr].s_defined == FALSE)
	{
	    return(TRUE);
        }
    }
    return(FALSE);
}



do_info(base)
unsigned base;
{

    printf("Next free address: %xH\n",base);
    printf("%d Symbols %d Free spaces\n",num_symbols,max_symbols-num_symbols);
    printf("%d Offsets %d Free spaces\n",num_offsets,max_offsets-num_offsets);
    printf("%u bytes left for chains\n",maxsbrk());
}

pr_table(filename)
char *filename;
{
    FILE *sfile;
    int j,k;

    sfile = fopen(filename,"w");
    k = 0;
    for (j=0; j < num_symbols; j++)
    {
	if ( k%4 == 0)
	    fprintf(sfile,"\n");
 	if (sym_list[j].s_defined == TRUE)
  	    fprintf(sfile,"%-8s %4x\t",sym_list[j].s_name,sym_list[j].s_value);
	else
  	    fprintf(sfile,"%-8s ????\t",sym_list[j].s_name);
	k++;
    }
    fprintf(sfile,"\n");
    fclose(sfile);
}

