/***********************************************************************
*
* asmsupt.c - Support routines for the IBM 7090 assembler.
*
* Changes:
*   05/21/03   DGP   Original.
*   08/13/03   DGP   Added XREF support.
*   08/20/03   DGP   Changed to call the SLR(1) parser.
*	
***********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

#include "asmdef.h"
#include "errors.h"

extern int pc;
extern int symbolcount;
extern int linenum;
extern int genxref;
extern int filenum;
extern int xrefgened;
extern int errcount;
extern int errnum;
extern int absolute;
extern int pgmlength;
extern char inbuf[MAXLINE];
extern char incldir[MAXLINE];
extern SymNode *symbols[MAXSYMBOLS];
extern char errline[10][120];

/***********************************************************************
* Parse_Error - Print parsing error
***********************************************************************/

Parse_Error (int cause, int state, int defer)
{
   char errorstring[256];

   if (defer) return;
   strcpy (errorstring, "Unknown error");
   if (cause == PARSE_ERROR)
   {
      switch (state)
      {
      /* Include generated parser errors */
#include "asm7090.err"
      default: ;
      }
   }
   else if (cause == SCAN_ERROR)
   {
      switch (state)
      {
      case 1:
	 strcpy (errorstring, "Invalid octal number");
	 break;
      default: ;
      }
   }
   else if (cause == INTERP_ERROR)
   {
      strcpy (errorstring, "Divide by zero");
   }
   errcount++;
   sprintf (errline[errnum++], "%s in expression", errorstring);

} /* Parse_Error */

/***********************************************************************
* tokscan - Scan a token.
***********************************************************************/

char *
tokscan (char *buf, char **token, int *tokentype, int *tokenvalue, char *term)
{
   int index;
   toktyp tt;
   tokval val;
   static char t[80];
   char c;

#ifdef DEBUGTOK
   printf ("tokscan: Entered: buf = %s", buf);
#endif
   while (*buf && isspace(*buf))
   {
      /*
      ** We stop scanning on a newline or when we're passed the operands.
      */

      if (*buf == '\n' || (buf - inbuf > NOOPERAND))
      {
	 t[0] = '\0';
	 *token = t;
	 *term = '\n';
	 *tokentype = EOS;
         return (buf);
      }
      buf++;
   }
   if (!strncmp(buf, "...", 3))
   {
#ifdef DEBUGTOK
      printf ("  null opcode ... found\n");
#endif
      strcpy (t, "...");
      *tokentype = SYM;
      *tokenvalue = 0;
      *token = t;
      index = 3;
      *term = *(buf+index);
      return (buf+index);
   }

   index = 0;
#ifdef DEBUGTOK
   printf ("  calling scanner: buf = %s\n", buf);
#endif
   tt = Scanner (buf, &index, &val, t, &c); 
#ifdef DEBUGTOK
   printf ("  token = %s, tokentype = %d, term = %02x, val = %d\n",
	    t, tt, c, val);
   printf (" index = %d, *(buf+index) = %02X\n",  index, *(buf+index));
#endif
   if (tt == DECNUM || tt == OCTNUM)
      c = *(buf+index);
   if (c == ',') index++;
   *token = t;
   *tokentype = tt;
   *tokenvalue = val;
   *term = c;
   return (buf+index);
}

/***********************************************************************
* symlookup - Lookup a symbol in the symbol table.
***********************************************************************/

SymNode *
symlookup (char *sym, int add, int noxref)
{
   SymNode *ret = NULL;
   int done = FALSE;
   int mid;
   int last = 0;
   int lo;
   int up;
   int r;

#ifdef DEBUGSYM
   printf ("symlookup: Entered: sym = %s\n", sym);
#endif

   /*
   ** Empty symbol table.
   */

   if (symbolcount == 0)
   {
      if (!add) return (NULL);

#ifdef DEBUGSYM
      printf ("add symbol at top\n");
#endif
      if ((symbols[0] = (SymNode *)malloc (sizeof (SymNode))) == NULL)
      {
         fprintf (stderr, "asm7090: Unable to allocate memory\n");
	 exit (ABORT);
      }
      memset (symbols[0], '\0', sizeof (SymNode));
      strcpy (symbols[0]->symbol, sym);
      symbols[0]->value = pc;
      symbols[0]->line = linenum;
#ifdef DEBUGXREF
      printf ("symlookup: sym = %s defline = %d\n", sym, linenum);
#endif
      if (absolute)
	 symbols[0]->relocatable = FALSE;
      else
	 symbols[0]->relocatable = TRUE;
      symbolcount++;
      return (symbols[0]);
   }

   /*
   ** Locate using binary search
   */

   lo = 0;
   up = symbolcount;
   last = -1;
   
   while (!done)
   {
      mid = (up - lo) / 2 + lo;
#ifdef DEBUGSYM
      printf (" mid = %d, last = %d\n", mid, last);
#endif
      if (last == mid) break;
      r = strcmp (symbols[mid]->symbol, sym);
      if (r == 0)
      {
	 SymNode *symp = symbols[mid];

	 if (add && !symp->predefine)
	    return (NULL); /* must be a duplicate */

	 if (genxref && !noxref)
	 {
	    XrefNode *new;

	    if ((new = (XrefNode *)malloc (sizeof (XrefNode))) == NULL)
	    {
	       fprintf (stderr, "asm7090: Unable to allocate memory\n");
	       exit (ABORT);
	    }
#ifdef DEBUGXREF
	    printf ("symlookup: sym = %s refline = %d\n", sym, linenum);
#endif
	    new->next = NULL;
	    new->line = linenum;
	    if (symp->xref_head == NULL)
	    {
	       symp->xref_head = new;
	    }
	    else
	    {
	       (symp->xref_tail)->next = new;
	    }
	    symp->xref_tail = new;
	 }
         return (symbols[mid]);
      }
      else if (r < 0)
      {
         lo = mid;
      }
      else 
      {
         up = mid;
      }
      last = mid;
   }

   /*
   ** Not found, check to add
   */

   if (add)
   {
      SymNode *new;

#ifdef DEBUGSYM
      printf ("add new symbol\n");
#endif
      if (symbolcount+1 > MAXSYMBOLS)
      {
         fprintf (stderr, "asm7090: Symbol table exceeded\n");
	 exit (ABORT);
      }

      if ((new = (SymNode *)malloc (sizeof (SymNode))) == NULL)
      {
         fprintf (stderr, "asm7090: Unable to allocate memory\n");
	 exit (ABORT);
      }

      memset (new, '\0', sizeof (SymNode));
      strcpy (new->symbol, sym);
#ifdef DEBUGXREF
      printf ("symlookup: sym = %s defline = %d\n", sym, linenum);
#endif
      new->value = pc;
      new->line = linenum;
      if (absolute)
	 new->relocatable = FALSE;
      else
	 new->relocatable = TRUE;

      /*
      ** Insert pointer in sort order.
      */

      for (lo = 0; lo < symbolcount; lo++)
      {
         if (strcmp (symbols[lo]->symbol, sym) > 0)
	 {
	    for (up = symbolcount + 1; up > lo; up--)
	    {
	       symbols[up] = symbols[up-1];
	    }
	    symbols[lo] = new;
	    symbolcount++;
	    return (symbols[lo]);
	 }
      }
      symbols[symbolcount] = new;
      ret = symbols[symbolcount];
      symbolcount++;
   }
   return (ret);
}

/***********************************************************************
* exprscan - Scan an expression and return its value.
***********************************************************************/

char *
exprscan (char *bp, int *val, char *tterm, int *relo, int entsize, int defer,
	  int pcinc)
{
   int index = 0;
   int lrelo = 0;
   tokval lval = 0;

#ifdef DEBUGEXPR
   printf ("exprscan: entered: bp = %s\n", bp);
#endif
   while (*bp && isspace(*bp)) bp++;
   if (bp - inbuf > NOOPERAND)
   {
      *tterm = '\n';
      *val = lval;
      *relo = lrelo;
      return (bp);
   }

#ifdef DEBUGEXPR
   printf ("   bp = %s\n", bp);
#endif
   if (!strncmp (bp, "**", 2) && isspace (*(bp+2)))
   {
      index = 2;
      *tterm = *(bp+index);
      *val = lval;
      *relo = lrelo;
      return (bp+index);
   }
   else if (*bp == LITERALSYM)
   {
      SymNode *s;
      char *cp;

      cp = bp;
      while (!isspace(*cp)) cp++;
      *cp = '\0';

      if ((s = symlookup (bp, FALSE, FALSE)) == NULL)
      {
	 if ((s = symlookup (bp, TRUE, FALSE)) == NULL)
	 {
	    errcount++;
	    sprintf (errline[errnum++], "Unable to add Literal: %s", bp);
	    lval = 0;
	 }
	 s->value = pgmlength++;
      }
      lval = s->value;
      if (s->relocatable) lrelo++;
   }
   else if (strlen(bp) && *bp != ',')
   {
      lval = Parser (bp, &lrelo, &index, entsize, defer, pcinc);
   }
#ifdef DEBUGEXPR
   printf (" index = %d, *(bp+index) = %02X\n",  index, *(bp+index));
#endif
   *tterm = *(bp+index);
   if (*tterm == ',') index++;
   *val = lval;
   *relo = lrelo;
   return (bp+index);
}
