#include "mical.h"
#include <b.out.h>

/* Allocation increments for symbol buckets and character blocks */
#define	SYM_INCR	50
#define CBLOCK_INCR	512

struct sym_bkt *Last_symbol;			/* last symbol defined */
struct sym_bkt *sym_hash_tab[HASH_MAX];		/* Symbol hash table */
struct sym_bkt *sym_free = NULL;		/* head of free list */
char *cblock = NULL;				/* storage for symbol names */
int ccnt = 0;					/* number of chars left in c block */

/* grab a new symbol bucket off of the free list; allocate space
 * for a new free list if necessary
 */
struct sym_bkt *gsbkt()
  {	register struct sym_bkt	*sbp;
	register int i;

	if ((sbp = sym_free) != NULL) sym_free = sbp->next_s;
	else {
	  sbp = (struct sym_bkt *)calloc(SYM_INCR,sizeof(struct sym_bkt));
	  if (sbp == NULL) Sys_Error("Symbol storage exceeded\n",0);
	  for (i = SYM_INCR-1; i--;) {
	    sbp->next_s = sym_free;
	    sym_free = sbp++;
	  }
	}

	return(sbp);
}

/* initialize hash table */
Sym_Init()
  {	register int i;

	for (i=0; i<HASH_MAX; i++) sym_hash_tab[i] = NULL;
}

char *sstring(string)
  register char *string;
  {	register char *p,*q;	/* working char string */
	register int i;

	i = strlen(string);	/* get length of string */

	if (++i > ccnt) {	/* if not enough room get more */
	  if ((cblock = (char *)calloc(CBLOCK_INCR,1)) == NULL)
	    Sys_Error("Symbol storage exceeded\n",0);
	  ccnt = CBLOCK_INCR;
	}

	p = q = cblock;		/* copy string into permanent storage */
	while (*p++ = *string++);
	cblock = p;
	ccnt -= i;
	return(q);
}

/* lookup symbol in symbol table */
struct sym_bkt *Lookup(s)
  register char *s;
  {	register struct sym_bkt	*sbp;	/* general purpose ptr */
	register int Save;		/* save subscript in sym_hash_tab */
	register char *p;
	char local[50];			/* used for constructing local sym */

	if (*s>='0' && *s<='9') {	/* local symbol hackery */
	  p = local;
	  while (*p++ = *s++);		/* copy local symbol */
	  p--;
	  s = Last_symbol->name_s;	/* add last symbol defined as suffix */
	  while (*p++ = *s++);
	  s = local;			/* this becomes name to deal with */
	}

	/* if the symbol is already in here, return a ptr to it */
	for (sbp = sym_hash_tab[Save=Hash(s)]; sbp != NULL ; sbp = sbp->next_s)
	  if (strcmp(sbp->name_s,s) == 0) return(sbp);

	/* Since it's not, make a bucket for it, and put the bucket in the symbol table */
	sbp = gsbkt();				/* get the bucket */
	sbp->name_s = sstring(s);		/* Store it's name */
	sbp->value_s = sbp->id_s = sbp->attr_s = 0;
	sbp->csect_s = NULL;
	sbp->next_s = sym_hash_tab[Save];	/* and insert on top of list */
	if (s == local) sbp->attr_s |= S_LOCAL;
	return(sym_hash_tab[Save] = sbp);
}

/* Sym_Fix -	Assigns index numbers
		to the symbols.  Also performs relocation of
		the symbols assuming data segment follows text
		and bss follows the data.  If global flag,
		make all undefined symbols defined to be externals.
*/
Sym_Fix()
{
	register struct sym_bkt **sbp1, *sbp2;
	int i = 0;

	for (sbp1 = sym_hash_tab; sbp1 < &sym_hash_tab[HASH_MAX]; sbp1++)
	  for (sbp2 = *sbp1; sbp2; sbp2 = sbp2->next_s) {
	    if ((sbp2->attr_s & (S_DEC|S_DEF)) == 0) {
	      sbp2->attr_s |= S_EXT | S_DEC;
	      sbp2->csect_s = NULL;
	    }
	    sbp2->value_s += sdi_inc(sbp2->csect_s, sbp2->value_s);
	    if (sbp2->csect_s == Data_csect) sbp2->value_s += tsize;
	    else if (sbp2->csect_s == Bss_csect) sbp2->value_s += tsize + dsize;
	    if (sbp2 == Dot_bkt || sbp2->attr_s & (S_REG|S_MACRO|S_LOCAL|S_PERM))
	      sbp2->id_s = -1;
	    else sbp2->id_s = i++;
	  }
}


/* Sym_Write -	Write out the symbols to the specified
		file in b.out format, while computing size
		of the symbol segment in output file.
 */
long Sym_Write(file)
  FILE *file;
  {	register struct sym_bkt  **sbp1, *sbp2;
	register char *sp;
	long size = 0;
	int slength;
	struct sym s;

	for (sbp1 = sym_hash_tab; sbp1 < &sym_hash_tab[HASH_MAX]; sbp1++)
	  for (sbp2 = *sbp1; sbp2; sbp2 = sbp2->next_s)
	    if (sbp2->id_s != -1) {
	      if (!(sbp2->attr_s&S_DEF)) s.stype = UNDEF;
	      else if (sbp2->csect_s == Text_csect) s.stype = TEXT;
	      else if (sbp2->csect_s == Data_csect) s.stype = DATA;
	      else if (sbp2->csect_s == Bss_csect) s.stype = BSS;
	      else s.stype = ABS;
	      if (sbp2->attr_s & S_EXT) s.stype |= EXTERN;
	      s.svalue = sbp2->value_s;
/**	      fwrite(&s, sizeof s, 1, file);*/
	      put68(file, &s.stype, sizeof(s.stype));
	      put68(file, &s.svalue, sizeof(s.svalue));
	      sp = sbp2->name_s;
	      slength = 0;
	      do { putc(*sp,file); slength++; } while (*sp++);
	      size += (sizeof(s.stype) + sizeof(s.svalue)) + slength;
	    }
	return(size);
}

/*
 * Perm	Flags all currently defined symbols as permanent (and therefore
 *	ineligible for redefinition.  Also prevents them from being output
 *	in the object file).
 */
Perm()
  {	register struct sym_bkt **sbp1, *sbp2;

	for (sbp1 = sym_hash_tab; sbp1 < &sym_hash_tab[HASH_MAX]; sbp1++)
		for (sbp2 = *sbp1; sbp2; sbp2 = sbp2->next_s)
			sbp2->attr_s |= S_PERM;
}
