/***********************************************************************
*
* asm7090 - Assembler for the IBM 7090 computer.
*
* Changes:
*   05/21/03   DGP   Original.
*   08/13/03   DGP   Added XREF support.
*	
***********************************************************************/

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

#include "asmdef.h"

int pc; /* the assembler pc */
int absolute = FALSE;
int genxref = FALSE;
int xrefgened = FALSE;
int addrexpr = TRUE;
int symbolcount = 0;
int errcount = 0;
int errnum = 0;
int linenum = 0;
int p1errcnt = 0;
int pgmlength = 0;
int radix = 10;
char errline[10][120];
char inbuf[MAXLINE];

SymNode *symbols[MAXSYMBOLS];
ErrTable p1error[MAXERRORS];

/***********************************************************************
* tabpos - Return TRUE if col is a tab stop.
***********************************************************************/

static int
tabpos (int col, int tabs[]) 
{
   if (col < BUFSIZ)
      return (tabs[col]);

    return (TRUE); 

} /* tabpos */

/***********************************************************************
* detabit - Convert tabs to equivalent number of blanks.
***********************************************************************/

static void
detabit (int tabs[], FILE *ifd, FILE *ofd)
         
{
   int c, col;
 
   col = 0;
   while ((c = fgetc (ifd)) != EOF)
   {

      switch (c)
      {

         case '\t' :
            while (TRUE)
	    { 
               fputc (' ', ofd);
               if (tabpos (++col, tabs) == TRUE) 
                  break ;
            } 
            break;

         case '\n' :
            fputc (c, ofd);
            col = 0;
            break;

         default: 
            fputc (c, ofd);
            col++;

      } /* switch */

   } /* while */

} /* detab */

/***********************************************************************
* alldig - Check if all digits.
***********************************************************************/

static int
alldig (char *digs)
{
   while (*digs)
   {
      if (!isdigit (*digs))
      {
         return (FALSE);
      }
      digs++;
   }

   return (TRUE);

} /* alldig */

/***********************************************************************
* settab - Set initial tab stops.
***********************************************************************/

static void
settab (int tabs[], int argc, char *argv[])
{
   int m, p, i, j, l;
   char *bp;
  
   p = 0;

   for (i = 0; i < BUFSIZ; i++)
       tabs[i] = FALSE;

   for (j = 1; j < argc; j++)
   {

      bp = argv[j];

      if (*bp == '+')
         bp++;

      if (alldig (bp))
      {

         l = atoi (bp) ;

         if (l < 0 || l >= BUFSIZ)
             continue;

         if (*argv[j] != '+')
         { 
            p = l;
            tabs[p] = TRUE;
         } 

         else
         { 
            if (p == 0) 
               p = l;
            for (m = p; m < BUFSIZ; m += l) 
            {
               tabs[m] = TRUE;
            }
         }

      }

   } 

   if (p == 0)
   {
      for (i = 8; i < BUFSIZ; i += 8) 
      {
         tabs[i] = TRUE;
      }
   }

} /* settab */

/***********************************************************************
* detab - Detab source.
***********************************************************************/

static int
detab (FILE *ifd, FILE *ofd) 
{
   int tabs[BUFSIZ];
   char *bp;
   char *tabstops[] = { "as", "8", "16", "+8", "\0" };
  
#ifdef DEBUG
   fprintf (stderr, "detab: Entered: infile = %s, outfile = %s\n",
	    infile, outfile);
#endif

   /* set initial tab stops */

   settab (tabs, 4, tabstops);

   detabit (tabs, ifd, ofd);

   return (0);

} /* detab */

/***********************************************************************
* Main procedure
***********************************************************************/

main (int argc, char **argv)
{
   FILE *infd = NULL;
   FILE *outfd = NULL;
   FILE *lstfd = NULL;
   FILE *tmpfd = NULL;
   char *infile = NULL;
   char *outfile = NULL;
   char *lstfile = NULL;
   char *bp;
   int i;
   int listmode = FALSE;
   int status = 0;
   char mkname[32];
   char tname[64];
   char lname[64];
  
#ifdef DEBUG
   printf ("asm7090: Entered:\n");
   printf ("args:\n");
   for (i = 1; i < argc; i++)
   {
      printf ("   arg[%2d] = %s\n", i, argv[i]);
   }
#endif

   for (i = 1; i < argc; i++)
   {
      bp = argv[i];

      if (*bp == '-')
      {
         bp++;
         switch (*bp)
         {
	 case 'l':
	    i++;
	    lstfile = argv[i];
	    listmode = TRUE;
	    break;

         case 'o':
            i++;
            outfile = argv[i];
            break;

         case 'x':
	    genxref = TRUE;
	    break;

         default:
      USAGE:
	    printf ("asm7090 - version %s\n", VERSION);
	    printf ("usage: asm7090 [-options] -o file.bin file.asm\n");
            printf (" options:\n");
	    printf ("    -x           - Generate cross reference\n");
	    printf ("    -l listfile  - Generate listing to listfile\n");
	    return (ABORT);
         }
      }

      else
      {
         if (infile) goto USAGE;
         infile = argv[i];
      }

   }

   if (!infile && !outfile) goto USAGE;

#ifdef DEBUG
   printf (" infile = %s\n", infile);
   printf (" outfile = %s\n", outfile);
   if (listmode)
      printf (" lstfile = %s\n", lstfile);
#endif

   /*
   ** Create a temp file for intermediate use.
   */

   strcpy (mkname, TEMPSPEC);
   if ((bp = (char *)mktemp (mkname)) == NULL)
   {
      perror ("asm7090: Can't mktemp");
      return (ABORT);
   }

#ifdef WIN32
   sprintf (tname, "%s.s", bp);
#else
   sprintf (tname, "/tmp/%s.s", bp);
#endif

   for (i = 0; i < MAXSYMBOLS; i++)
   {
      symbols[i] = NULL;
   }

   /*
   ** Open the files.
   */

   if ((infd = fopen (infile, "r")) == NULL)
   {
      perror ("asm7090: Can't open input file");
      exit (1);
   }
   if ((outfd = fopen (outfile, "w")) == NULL)
   {
      perror ("asm7090: Can't open output file");
      exit (1);
   }
   if (listmode) {
      if ((lstfd = fopen (lstfile, "w")) == NULL)
      {
	 perror ("asm7090: Can't open listing file");
	 exit (1);
      }
   }
   if ((tmpfd = fopen (tname, "w+")) == NULL)
   {
      perror ("asm7090: Can't open temporary file");
      exit (1);
   }

   /*
   ** Detab the source, so we know where we are on a line.
   */

   status = detab (infd, tmpfd);

   /* 
   ** Call pass 1 to scan the source and get labels.
   */

   status = asmpass1 (tmpfd);

   /*
   ** Call pass 2 to generate object and optional listing
   */

   status = asmpass2 (tmpfd, outfd, listmode, lstfd);

   /*
   ** Close the files
   */

   fclose (infd);
   fclose (outfd);
   fclose (tmpfd);
   if (listmode)
      fclose (lstfd);

   /*
   ** Delete tmp file
   */

   unlink (tname);

   if (status != 0)
      fprintf (stderr, "asm7090: %d Errors in assembly\n", errcount);

   return (status == 0 ? NORMAL : ABORT);
}
