/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 27-Nov-85 | [1.16] Created
* 28-Nov-85 | [1.37] Print symbols out unsigned
*  4-Dec-85 | [1.108] Added check_sym
*  4-Dec-85 | [1.121] Sort symbol table; do not print out literals
* 24-Nov-91 | [1.177] <jmn> memory.h => automem.h
* 24-Nov-91 | [1.177] <jmn> converted to C6.0
* 24-Nov-91 | [1.177] <jmn> mach.h => machmem.h
* 25-Nov-91 | [1.185] <jmn> include \r in output to listing file (it is in
*           | binary mode for reasons obscure to me...)
* 25-Nov-91 | [1.188] <jmn> set X field to suppress dumping X symbols
* 25-Nov-91 | [1.188] <jmn> handle zone setting for literals of form +000
* 26-Nov-91 | [1.193] <jmn> fixed bug in enter which did NULL pointer
*           | asssignment 
* 26-Nov-91 | [1.210] <jmn> better multiply-defined symbol handling
* 26-Nov-91 | [1.212] <jmn> added start of litpoolmark structure so that
*           | multiple LTORGs have separate literal pools
*  3-Dec-91 | [1.220] <jmn> use C_bits to tag valid memory locs
*  4-Dec-91 | [1.233] <jmn> added proper LTORG resolution to literals in pool
*  5-Dec-91 | [1.244] <jmn> added heap scan
*****************************************************************************/
/* Symbol table package */

#include <stdio.h>
#include <boolean.h>
#include <stdlib.h>
#include <search.h>
#include <string.h>
#include <malloc.h>

#include <ste.h>
#include <err.h>
#include <automem.h>
#include <machmem.h>
#include <sym.h>
#include <error.h>
#include <expr.h>
#include <list.h>
#include <bcd.h>

#define magic 0x07FE
int protect = magic;	/* watch for storage damage */
ste * symroot = NULL;
ste * symtail = NULL;

#define sym_debug false


extern unsigned litpool;
extern int dot;
extern int star;
extern unsigned maxdot;
extern int sdot;
extern boolean checksym;

void sort_syms(void);
extern FILE * listing;
extern boolean debug;
extern boolean debug_sym;

#if OBSOLETE
typedef struct litpoolmark {
	int thisline;	                  /* line associated with LTORG/END */
	ste * start;	           /* starting symbol for pass 2 resolution */
	struct litpoolmark * next;                 /* next litpool, or NULL */
		       } litpoolmark;

litpoolmark * ltorglist;
#endif

/****************************************************************************
*                                   muldef
* Inputs:
*       ste * sy: Symbol which is multiply-defined
*	char * msg: Message to issue, or NULL if default message only
* Result: void
*       
* Effect: 
*       Issues a multiply-defined symbol message and sets sy->muldef true
****************************************************************************/

void muldef(ste * sy, char * msg)
    {
     char msgbuf[200];

     sy->muldef = true;
     sprintf(msgbuf,"Multiply-defined symbol '%s'",sy->name);
     if(msg != NULL)
        { /* has msg */
	 strcat(msgbuf," ");
	 strcat(msgbuf,msg);
	} /* has msg */
     error(err_multiply_defined,msgbuf);     
    }

/****************************************************************************
*                                    prsym
* Inputs:
*       ste * sy: Symbol table entry
* Effect: 
*       Prints out the symbol table entry
****************************************************************************/

void prsym(ste * sy)
    {
     printf("0x%x: {\n",sy);
     printf("        next: 0x%x\n", sy->next);
     printf("        prev: 0x%x\n", sy->prev);
     printf("        name: 0x%x \"%s\"\n",sy->name,sy->name);
     printf("        val:  %u\n",sy->val);
     printf("        lit: %s\n",(sy->lit ? "true" : "false"));
     if(sy->lit)
        { /* lit stuff */
	 
	 printf("        resolved: %s\n",(sy->resolved ? "true" : "false"));
	 printf("        lit_dumped: %s\n",(sy->lit_dumped ? "true" : "false"));
	 printf("        line %d\n",sy->line);
	} /* lit stuff */
     printf("	    };\n");
    }

/****************************************************************************
*                                  check_sym
* Effect: 
*       Checks for symbol table clobber
****************************************************************************/

void check_sym(char * msg)
    {
     ste * sym;
     struct _heapinfo hinfo;
     int heapstatus;
    
#if 1    
     if(_nullcheck())
        { /* failure */
	 error(err_label,"***NULL POINTER CLOBBER***");
	 printf("%s\n",msg);
	} /* failure */
#endif

     if(!debug_sym) 
	return;

     if(protect != magic)
        { /* the magic goes away */
         error(err_label,"***SYMBOL TABLE DAMAGED....protect word gone***");
	 printf("%s\n",msg);
	} /* the magic goes away */
     if(symroot == NULL)
        { /* dead */
         error(err_label,"***SYMBOL TABLE DAMAGED...symroot == NULL");
	 printf("%s\n",msg);
	 return;
	} /* dead */
     sym = symroot;

     while(sym != NULL)
        { /* scan */
	 if(sym->next != NULL && sym->next->prev != sym)
	    { /* dead next */
	     error(err_label,"***SYMBOL TABLE DAMAGED...bad fwdpointer***");
	     printf("0x%x: next->0x%x, prev->0x%x, name=\"%s\", val=%d\n",
	     		sym,sym->next,sym->prev,sym->name,sym->val);
	     printf("      next->prev = 0x%x != sym\n",sym->next->prev);
	     printf("%s\n",msg);

             { /* dump table */
	      ste * sye;

	      sye = symroot;

	      while(sye != NULL)
	         { /* dump it */
		  
		  prsym(sye);
		  if(sye->next != NULL && sye->next->prev != sye)
		  	break;
		  sye = sye -> next;
		 } /* dump it */
		  
	     } /* dump table */
		 

	     return;
	    } /* dead next */
     	 if(sym->prev != NULL && sym->prev->next != sym)
	    { /* dead next */
	     error(err_label,"***SYMBOL TABLE DAMAGED...bad backpointer***");
	     printf("0x%x: next->0x%x, prev->0x%x, name=\"%s\", val=%d\n",
	     		sym,sym->next,sym->prev,sym->name,sym->val);
	     printf("      prev->next = 0x%x != sym\n",sym->prev->next);
	     printf("%s\n",msg);
	     return;
	    } /* dead next */
	     
	 sym = sym->next;
	} /* scan */

     hinfo._pentry = NULL;
     while ( (heapstatus = _heapwalk(&hinfo)) == _HEAPOK)
        { /* scan heap */
	} /* scan heap */
     switch(heapstatus)
        { /* decode status */
	 case _HEAPEMPTY:
	 case _HEAPEND:
		 break;
	 case _HEAPBADPTR:
		 printf("ERROR - bad heap pointer %p (%s)\n",
		 			hinfo._pentry, msg);
		 break;
	 case _HEAPBADBEGIN:
		 printf("ERROR - bad heap begin (%s)\n",msg);
		 break;
	 case _HEAPBADNODE:
		 printf("ERROR - bad heap node (%s)\n",msg);
		 break;
	} /* decode status */
    }

/****************************************************************************
*                                    enter
* Inputs:
*       char * sym: Pointer to (NUL-terminated) label name string (in heap)
*	unsigned val: Value to attach to it
* Result: ste *
*       NULL if symbol duplicated
*	non-NULL is symbol table entry
* Effect: 
*       Enters the symbol in the symbol table
****************************************************************************/

ste * enter(char * sym,unsigned val)
    {
     int i;
     ste * sy;

     if(checksym)
        { /* watch out for bogus resolution */
	 ste * this;

	 this = symtail;

	 while(this!=NULL)
	    { /* search */
	     if(strcmp(this->name,sym) == 0)
	        { /* same symbol */
		 if(!this->lit || !this->resolved)
		    { /* check */
		     if(this->val == val)
			{ /* redundant */
			 char msg[80];
			 sprintf(msg,"Redundant definition of '%s'==%d",
				    sym,this->val);
		         warning(err_label,msg);
			 return this;
			} /* redundant */
		     else
			{ /* duplicate */
			 char msg[80];
			 sprintf(msg,"Attempt to redefine '%s'==%d to %d",
				    sym,this->val,val);
		         error(err_label,msg);
			 return NULL;
			} /* duplicate */
		    } /* check */
		} /* same symbol */
	     this = this -> prev;
	    } /* search */
	} /* watch out for bogus resolution */

     /* If we get here, we didn't find it; create an entry */

     sy = (ste *) malloc(sizeof(ste));
     sy->name = sym;
     sy->val = val;
     sy->next = symroot;
     sy->prev = NULL;
     if(sy->next != NULL)
	sy->next->prev = sy;
     sy->lit = false;	/* Assume not literal */
     sy->resolved = false;
     sy->lit_dumped = false;
     sy->var_size = false;
     sy->X = false;
     sy->muldef = false;
     sy->len = 0;
     symroot = sy;
     if(symtail == NULL)
	symtail = symroot;

     if(debug || debug_sym)
        { /* new value */
	 printf("enter: new symbol \"%s\" entered @0x%x, value %s\n",
	 		sym,sy,valstr(val));
	} /* new value */

     return sy;
    }

/****************************************************************************
*                                   sym_lookup
* Inputs:
*       char * sym: Symbol to look up
*	int line: Line number we are currently at
* Result: ste *
*       NULL if not found
*	otherwise, ste of symbol
* Notes:
*	The symbol table is a linked list (yep, slow; for fast indexing
*	build an ancillary hash table, the fifoness is key to this algorithm)
*
*	A literal marked as 'resolved' is resolved.  Consider the program
*	1	mlc	@foo@,bar
*	2	mlc	@foo@,thud
*	3	ltorg
*	4	mlc	@foo@,bar
*	5	mlc	@foo@,thud
*	6	end	whever
*
*	The symbol values will be
*	... @foo@[6] ... @foo@[3]
*	
*	on pass 1, the lookup on line 1 fails, and an 'enter' operation
*	will be done to put @foo@ into the symbol table.  Its resolved
*	property is 'false'.  The lookup on line 2 succeeds, so the 
*	resolution is to the same literal.  The LTORG on line  3 resolves
*	the literals and sets the resolved property to 'true'.  The pass 1
*	lookup for @foo@ on line 4 detects that 4 is > @foo@->line, and no
*	other @foo@ is found, so it returns NULL. 'enter' is called and
*	places a new @foo@, which has an as-yet-unresolved line and resolved
*	of false.  The @foo@ on line 5 detects a resolved property on the
*	first @foo@, but the next one it finds is unresolved, so it returns
*	a pointer to it.
*
*	on pass 2, two lookups will be done for @foo@ on lines 1 and 2.
*	The list is searched in reverse order, and the first @foo@ whose
*	line is > 1 or > 2 will be found.  However, when the @foo@ on
*	line 4 is searched for, the @foo@ on line 3 is < 4 and it will be
*	ignored.  This guarantees proper literal pool behavior.
*
****************************************************************************/

static ste * sym_lookup(char * sym,int line)
    {
     ste * this;
     int i;
     
     this = symtail;

     while(this!=NULL)
        { /* search */
	 if(strcmp(this->name,sym) == 0)
	    { /* name matched */
	     /*
		Names are same.  Handle literal resolution
	     */
	     if(this->resolved)
	        { /* matching resolved literal */
		 /*
		    Either we are in pass1 following a ltorg, or
		    we are in pass2.  In either case, if the resolution
		    line event of the literal is less than our current line,
		    it is in a previous literal pool
		 */
		 if(this->line < line)
		    goto next;
		} /* matching resolved literal */

	     if(debug || debug_sym)
	        { /* new value */
		 printf("lookup: symbol \"%s\" found @0x%x, value %s\n",
	 		sym,this,valstr(this->val));
		} /* new value */
	 	return this;
	    } /* name matched */
next:
	 this = this -> prev;
	} /* search */
     return NULL;
    }

/****************************************************************************
*                                   lookup
* Inputs:
*       char * name: Name to look up
* Result: ste *
*       Symbol table entry, or NULL if not found
* Notes: 
*       This is to simplify the interface; we need 'lit_lookup' which
*	handles the literals, and 'lookup' for names only.  The generic
*	procedure is 'sym_lookup', and this is an interface to it
****************************************************************************/

ste * lookup(char * name)
    {
     return sym_lookup(name,0);
    }

/****************************************************************************
*                                 lit_lookup
* Inputs:
*       char * name: name (literal value) to look up
*	int line: line on which it is found
* Result: ste *
*       entry for this literal
*	NULL if not found or unresolved in current pool
*	(see sym_lookup for details of literal resolution)
****************************************************************************/

ste * lit_lookup(char * name, int line)
    {
     return sym_lookup(name,line);
    }

/****************************************************************************
*                                   ixname
* Inputs:
*       unsigned val: Value which has indexed bits
* Result: char *
*       Print string of index register
****************************************************************************/

char * ixname(unsigned val)
    {
     int i;
     static char name[10];
     i = ixreg(val);
     if(i == 0)
	return "";
     sprintf(name,"+X%d",i);
     return name;
    }

/****************************************************************************
*                                  list_syms
* Effect: 
*       Prints a (sorted?) symbol table on the listing stream
****************************************************************************/

void list_syms()
    {
     ste * sy;

     if(debug_sym)
        { /* debug list */
	 
	 printf("Unsorted symbol table\n");

	 sy = symroot;

	 while(sy != NULL)
	    { /* scan and print */
	     prsym(sy);
	     sy = sy->next;
	    } /* scan and print */
	} /* debug list */

     pageheading();

     sort_syms();

     if(debug_sym)
        { /* debug sorted list */

	 printf("Sorted symbol table\n");
	 
	 sy = symroot;

	 while(sy != NULL)
	    { /* scan and print */
	     prsym(sy);
	     sy = sy->next;
	    } /* scan and print */
	} /* debug list */

     sy = symroot;

     while(sy!=NULL)
        { /* list one */
	 if(!sy->lit && !sy->X)
	     {
	      check_heading();
	      fprintf(listing,"%c %-8s %05u%s\r\n",
	      			(sy->muldef ? 'M' : ' '),
	      			sy->name,
				unindexed(sy->val),
				ixname(sy->val)
				);
	     }
	 sy = sy -> next;
	} /* list one */
    }

/****************************************************************************
*                                resolve_lits
* Inputs:
*	int thisline: Line number which activated resolution
*	char * msg: Message to print in debug mode
* Effect: 
*       Resolves all outstanding literals relative to the litpool offset
****************************************************************************/

void resolve_lits(int thisline,char * msg)
    {
     ste * sy;
     unsigned litloc;

     if(debug)
     	printf("Resolving literals (%s): litpool = %d\n",msg,litpool);

     sy = symroot;

     if(sy==NULL) return;

     while(sy->next != NULL) sy = sy->next;

     /* sy now points to the end of the symbol table.  Walk backward,
        resolving literals 
     */

     while(sy != NULL)
        { /* resolve scan */
	 if(debug)
	    printf("sy=0x%x(\"%s\" %c%c%c%c, val = %s)",
			sy, sy->name,
			(sy->lit ? 'L' : 'V'),
			(sy->resolved ? 'r' : ' '),
			(sy->lit_dumped ? 'd' : ' '),
			(sy->var_size   ? '#' : ' '),
			valstr(sy->val));
         if(debug && (sy->var_size || sy->len > 0))
	    printf(", len=%d",sy->len);
#if 0   
	    printf(", sy->next = 0x%x, sy->prev = 0x%x\n",
			sy->next, sy->prev);
#endif
	 if(debug)
	    printf("\n");

	 if(sy->lit || sy->var_size) 
	    if(! sy->resolved)
	     { /* resolve it */
	      sy -> val += litpool;
	      litloc = sy -> val;
	      sdot = star = dot = litloc+1;
	      sy->resolved = true;
	      sy->line = thisline;
	      if(debug)
	     	printf("Lit[%d] %s = %d\n",thisline,sy->name,litloc);
             } /* resolve it */
	 sy = sy -> prev;
	} /* resolve scan */

     if(debug)
         printf("End of resolution, new dot = %d\n",dot);

    }

/****************************************************************************
*                                  dump_lits
* Inputs:
*	int thisline: Current line which activated dump (must be same as
*			line which activated resolution)
*  	char * msg: Cheery debugging message to emit
* Effect: 
*       Dumps all the literals in the literal pool.  Addresses assigned
*	must correspond
****************************************************************************/

void dump_lits(int thisline,char * msg)
    {
     ste * sy;
     int i;
     int end;
     unsigned thisloc;
     unsigned char zones;

     if(debug)
     	printf("Dumping literals (%s), dot = %d\n",msg,dot);

     sy = symtail;

     if(sy==NULL)
	return;
     
     /* 
	sy now points to the end of the symbol table.  Walk backward,
        dumping literals 
     */

     while(sy != NULL)
        { /* dump scan */
	 unsigned litloc;

	 /*
	    We dump only the literals that should be dumped at this
	    point.  'thisline' is either the line # of the 'END'
	    statement or the 'LTORG' statement.
	 */
	 if(sy->line != thisline)
	    { /* not this pool */
	     sy = sy-> prev;
	     continue;
	    } /* not this pool */

	 if(!sy->lit && !sy->var_size)
	    { /* skip it */
	     sy = sy -> prev;
	     continue;
	    } /* skip it */

	 if(! sy->resolved)
	    { /* error */
	     printf("Error: unresolved literal @0x%x, %s\n",sy,sy->name);
	     sy = sy->prev;
	     continue;
	    } /* error */

	 if(sy->lit_dumped)
	    { /* already processed */
	     sy = sy->prev;
	     continue;
	    } /* already processed */

	 if(sy->lit)
	    { /* literal */
	     thisloc = dot+litlen(sy->name)-1;

	 
	     if(thisloc != sy->val)
		{ /* end of this phase */

		 char msg[150];
		 sprintf(msg,"Expected to place literal %.50s at %d, want to place it at %d\n",
	     		sy->name,sy->val,thisloc);
	         error(err_label,msg);
		} /* end of this phase */
	 
	     litloc = dot + litlen(sy->name) - 1;

	     /*
		+0000	1..<litlen(sy->name)
		-0000	1..<litlen(sy->name)
		@....@	1..<litlen(sy->name) - 1
	     */
	     zones = 0;

	     switch(sy->name[0])
		{ /* add zones */
		 case '+':
		     /*
			+0000   litlen == 4
			     ^
			     end
		     */
		     zones = BA_bits;
		     end = litlen(sy->name) + 1;
		     break;
		 case '-':
		     /*
			+0000   litlen == 4
			     ^
			     end
		     */
		     zones = B_bits;
		     end = litlen(sy->name) + 1;
		     break;
		 case '@':
		 case '\'':
		     /*
			@....@   litlen == 4
			     ^
			     end
		     */
		     zones = 0;
		     end = litlen(sy->name) + 1;
		     break;
		} /* add zones */

	     for(i=1; i < end; i++)
		{ /* stuff it */
		 memory[check_memory(dot)] = C_bits | ascii_to_bcd(sy->name[i]) | (i==1 ? word_mark : 0);
		 dot++;
		} /* stuff it */
	     memory[check_memory(dot - 1)] |= zones;

	     if(debug)
		printf("Dumped %s @ %d\n",sy->name,litloc);

	     sy->lit_dumped = true;
	     
	     /* Now put it on the listing file */

	     list_addr(litloc);

	     list_lit(sy->name);
	    } /* literal */
	 else
	 if(sy->var_size)
	    { /* var#n */
	     thisloc = dot + sy->len - 1;
	     if(thisloc != sy->val)
	        { /* phase error */
		 char msg[150];
		 sprintf(msg,"Expected to place %s#%d at %d, want to place it at %d\n",
		 		sy->name, sy->len, thisloc);
                 error(err_label,msg);
		} /* phase error */

	     litloc = dot;
	     for(i=0; i < sy->len; i++)
	        { /* stuff it */
		 memory[check_memory(dot)] = C_bits | ascii_to_bcd(' ') | (i == 0 ? word_mark : 0);
		 dot++;
		} /* stuff it */
	     if(debug)
		printf("Dumped %s#%d @ %d\n",sy->name, sy->len, litloc);

	     sy->lit_dumped = true;
	     list_addr(dot-1);
	     list_var(sy->name, sy->len);
	    } /* var#n */

	 emit_listing();

	 sy = sy -> prev;
	} /* dump scan */

     star = sdot = dot;
     maxdot = dot;
    }

/****************************************************************************
*                                   symcomp
* Inputs:
*       ste * * sy1: Pointer to first symbol table entry
*	ste * * sy2: Pointer to second symbol table entry
* Result: int
*       -1: sy1 < sy2
*	0:  sy1 == sy2
*	+1: sym > sy2
****************************************************************************/

int symcomp(ste * * sy1,ste * * sy2)
    {
#if sym_debug
     printf("symcomp(0x%x (\"%s\"), 0x%x (\"%s\"))\n",*sy1,(*sy1)->name,
     					*sy2, (*sy2)->name);
#endif
     return strcmp((*sy1)->name, (*sy2)->name);
    }

/****************************************************************************
*                                  fill_syms
* Inputs:
*       ste * syms[]: Array of pointer to stes
* Effect: 
*       copies all entries from sy to syms
****************************************************************************/

static void fill_syms(ste * syms[])
    {
     int i;
     ste * sy;

     sy = symroot;
     i = 0;

     while(sy != NULL)
        { /* fill table */
#if sym_debug
	 printf("0x%x = 0x%x (\"%s\")\n",&syms[i],sy,sy->name);
#endif
	 syms[i] = sy;
	 sy = sy->next;
	 i++;
	} /* fill table */
    }

/****************************************************************************
*                                   resort
* Inputs:
*       ste * syms[]: Pointer to vector of stes
*	int nsyms: Number of entries in vector
* Effect: 
*       Relinks the symbol table so it is in sorted order
****************************************************************************/

static void resort(ste * syms[],int nsyms)
    {
     int i;

     if(debug_sym)
        { /* print debug */
	 printf("resort(0x%x,%d)\n",syms,nsyms);
	 for(i=0;i<nsyms;i++)
	 	printf("0x%x: {next 0x%x; prev 0x%x; name \"%s\"\n",
				syms[i],syms[i]->next, syms[i]->prev,
				syms[i]->name);
	} /* print debug */

     for(i=0;i<nsyms;i++)
        { /* resort */

#if sym_debug
	 printf( "0x%x: %s\n", syms[i],syms[i] ->name);
#endif

	 syms[i] -> next = (i+1 < nsyms ? syms[i+1] : NULL);
	 syms[i] -> prev = (i==0 ? NULL : syms[i-1]);
	 if(debug_sym)
	    { /* resort msg */
	     printf("syms[%d]=0x%x, nsyms = %d, next = 0x%x, prev = 0x%x\n",
	     		i, syms[i], nsyms, syms[i]->next, syms[i]->prev);
	    } /* resort msg */
	     

	} /* resort */

     symroot = *syms;
    }

/****************************************************************************
*                                  sort_syms
* Effect: 
*       Sorts the symbol table
****************************************************************************/

static void sort_syms()
    {
     int i;
     int nsyms;
     ste * sy;
     ste * * syms;

     nsyms = 0;

     sy = symroot;

     while(sy!=NULL)
        { /* count */
	 nsyms++;
	 sy = sy->next;
	} /* count */

     syms = (ste * *) malloc(nsyms * sizeof(ste *));

     /* syms is now a pointer to an array of symbol table pointers.
        fill it, then sort it
     */

     fill_syms(syms);

     qsort(syms,nsyms,sizeof(ste *),symcomp);

     /* List is now sorted.  Rearrange its links */

     resort(syms,nsyms);

     free(syms);

    }
