/***********************************************************************
*
* asm7090 - Assembler for the IBM 7090 computer.
*
* Changes:
*   05/21/03   DGP   Original.
*   08/13/03   DGP   Added XREF support.
*   12/20/04   DGP   Added MAP support and 2.0 additions.
*	
***********************************************************************/

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

#include "asmdef.h"

int absolute = FALSE;		/* In absolute section */
int absmod = FALSE;		/* In absolute module */
int genxref = FALSE;		/* Generate cross reference listing */
int widemode = FALSE;		/* Generate wide listing */
int monsym = FALSE;		/* Include Monitor Symbols (MONSYM) */
int addext = TRUE;		/* Add extern for undef'd symbols (!absolute) */
int addrel = FALSE;		/* ADDREL mode */
int termparn = TRUE;		/* Parenthesis are terminals (NO()) */
int rboolexpr = FALSE;		/* RBOOL expression */
int lboolexpr = FALSE;		/* LBOOL expression */
int exprtype = ADDREXPR;	/* Expression type */
int pc = 0;			/* the assembler pc */
int symbolcount = 0;		/* Number of symbols in symbol table */
int entsymbolcount = 0;		/* Number of symbols in entry symbol table */
int opdefcount = 0;		/* Number of user defined opcode in opdef */
int begincount = 0;		/* Number of BEGINs */
int inpass;			/* Which pass are we in */
int errcount = 0;		/* Number of errors in assembly */
int errnum = 0;			/* Index into errline array */
int linenum = 0;		/* Source line number */
int p1errcnt = 0;		/* Number of pass 0/1 errors */
int pgmlength = 0;		/* Length of program */
int radix = 10;			/* Number scanner radix */
int qualindex = 0;		/* QUALifier table index */
int litorigin = 0;		/* Literal pool origin */
int litpool = 0;		/* Literal pool size */

char errline[10][120];		/* Pass 2 error lines for current statment */
char inbuf[MAXLINE];		/* The input buffer for the scanners */
char lbl[MAXLBLLEN+2];		/* The object label */
char deckname[MAXSYMLEN+2];	/* The assembly deckname */
char ttlbuf[TTLSIZE+2];		/* The assembly TTL buffer */
char titlebuf[TTLSIZE+2];	/* The assembly TITLE buffer */
char qualtable[MAXQUALS][MAXSYMLEN+2]; /* The QUALifier table */

SymNode *addextsym = NULL;	/* Added symbol for externals */
SymNode *symbols[MAXSYMBOLS];	/* The Symbol table */
SymNode *entsymbols[MAXSYMBOLS];/* The Entry Symbol table */
OpDefCode *opdefcode[MAXDEFOPS];/* The user defined opcode table */
ErrTable p1error[MAXERRORS];	/* The pass 0/1 error table */
BeginTable begintable[MAXBEGINS];/* The USE/BEGIN table */

/*
** System nucleus defintions (MONSYM)
*/

static int mondefs = FALSE;

static SysDefs sysdefs[] =
{
   { "SYSTRA", "S.S.", 000100 },
   { "SYSDAT", "S.S.", 000101 },
   { "SYSCUR", "S.S.", 000102 },
   { "SYSRET", "S.S.", 000103 },
   { "SYSKEY", "S.S.", 000104 },
   { "SYSSWS", "S.S.", 000105 },
   { "SYSPOS", "S.S.", 000106 },
   { "SYSUNI", "S.S.", 000107 },
   { "SYSUBC", "S.S.", 000110 },
   { "SYSUAV", "S.S.", 000111 },
   { "SYSUCW", "S.S.", 000112 },
   { "SYSRPT", "S.S.", 000113 },
   { "SYSCEM", "S.S.", 000114 },
   { "SYSDMP", "S.S.", 000115 },
   { "SYSIOX", "S.S.", 000116 },
   { "SYSIDR", "S.S.", 000117 },
   { "SYSCOR", "S.S.", 000120 },
   { "SYSLDR", "S.S.", 000121 },
   { "SYSACC", "S.S.", 000122 },
   { "SYSPID", "S.S.", 000123 },
   { "SYSCYD", "S.S.", 000124 },
   { "SYSSLD", "S.S.", 000126 },
   { "SYSTCH", "S.S.", 000127 },
   { "SYSTWT", "S.S.", 000131 },
   { "SYSGET", "S.S.", 000132 },
   { "SYSJOB", "S.S.", 000133 },
   { ".CHEXI", "",     000134 },
   { ".MODSW", "",     000135 },
   /* SYSUNI table */
   { "SYSLB1", "S.S.", 000140 },
   { "SYSLB2", "S.S.", 000141 },
   { "SYSLB3", "S.S.", 000142 },
   { "SYSLB4", "S.S.", 000143 },
   { "SYSCRD", "S.S.", 000144 },
   { "SYSPRT", "S.S.", 000145 },
   { "SYSPCH", "S.S.", 000146 },
   { "SYSOU1", "S.S.", 000147 },
   { "SYSOU2", "S.S.", 000150 },
   { "SYSIN1", "S.S.", 000151 },
   { "SYSIN2", "S.S.", 000152 },
   { "SYSPP1", "S.S.", 000153 },
   { "SYSPP2", "S.S.", 000154 },
   { "SYSCK1", "S.S.", 000155 },
   { "SYSCK2", "S.S.", 000156 },
   { "SYSUT1", "S.S.", 000157 },
   { "SYSUT2", "S.S.", 000160 },
   { "SYSUT3", "S.S.", 000161 },
   { "SYSUT4", "S.S.", 000162 },
   { "SYSUT5", "S.S.", 000163 },
   { "SYSUT6", "S.S.", 000164 },
   { "SYSUT7", "S.S.", 000165 },
   { "SYSUT8", "S.S.", 000166 },
   { "SYSUT9", "S.S.", 000167 },
   { "VVEND",  "S.S.", 000170 },
   /* IOEX communications region */
   { ".ACTV",  "",     000702 },
   { ".NDSEL", "",     000704 },
   { ".MWR",   "",     000706 },
   { ".PUNCH", "",     000707 },
   { ".ENBSW", "",     000710 },
   { ".PAWS",  "",     000711 },
   { ".PAUSE", "",     000712 },
   { ".STOP",  "",     000713 },
   { ".SYMUN", "",     000714 },
   { ".DECVD", "",     000715 },
   { ".DECVA", "",     000716 },
   { ".CKWAT", "",     000717 },
   { ".BCD5R", "",     000720 },
   { ".BCD5X", "",     000721 },
   { ".CVPRT", "",     000722 },
   { ".STOPD", "",     000723 },
   { ".CHXAC", "",     000724 },
   { ".URRX",  "",     000725 },
   { ".RCTX",  "",     000726 },
   { ".RCHX",  "",     000727 },
   { ".TCOX",  "",     000730 },
   { ".TRCX",  "",     000731 },
   { ".ETTX",  "",     000732 },
   { ".TEFX",  "",     000733 },
   { ".TRAPX", "",     000734 },
   { ".TRAPS", "",     000735 },
   { ".COMM",  "",     000736 },
   { ".LTPOS", "",     000737 },
   { ".IOXSI", "",     000740 },
   { ".CHPSW", "",     000741 },
   { ".TRPSW", "",     000742 },
   { ".FDAMT", "",     000743 },
   { ".SDCXI", "",     000744 },
   { ".STCXI", "",     000745 },
   { ".COMMD", "",     000746 },
   { ".IBCDZ", "",     000747 },
   { ".CHXSP", "",     000750 },
   { ".BLKSW", "",     000751 },
   { "SYSORG", "S.S.", 002652 },
   { "SYSEND", "S.S.", 077777 },
   { "",       "",     -1     }
};

/***********************************************************************
* 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 *tabstops[] = { "as", "7", "15", "+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 */

/***********************************************************************
* definemonsyms - Define MONSYM symbols
***********************************************************************/

void
definemonsyms (void)
{
   int i;
   int oldline;

   if (!mondefs)
   {
      oldline = linenum;
      linenum = 0;
      mondefs = TRUE;
      for (i = 0; sysdefs[i].val > 0; i++)
      {
	 SymNode *s;

	 s = symlookup (sysdefs[i].name, sysdefs[i].qual, TRUE, TRUE);
	 s->value = sysdefs[i].val;
	 s->relocatable = FALSE;
      }
      linenum = oldline;
   }
}

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

int
main (int argc, char **argv)
{
   FILE *infd = NULL;
   FILE *outfd = NULL;
   FILE *lstfd = NULL;
   FILE *tmpfd0 = NULL;
   FILE *tmpfd1 = NULL;
   char *infile = NULL;
   char *outfile = NULL;
   char *lstfile = NULL;
   char *bp;
   int i;
   int listmode = FALSE;
   int status = 0;
   char mkname[32];
   char tname0[64];
   char tname1[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

   deckname[0] = '\0';
   titlebuf[0] = '\0';

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

      if (*bp == '-')
      {
         bp++;
         switch (*bp)
         {
	 case 'a': /* Absolute assembly */
	    addext = FALSE;
	    absolute = TRUE;
	    absmod = TRUE;
	    break;

	 case 'd': /* Deckname */
	    i++;
	    if (strlen (argv[i]) > MAXSYMLEN) argv[i][MAXSYMLEN] = '\0';
	    strcpy (deckname, argv[i]);
	    break;

	 case 'l': /* Listing file */
	    i++;
	    lstfile = argv[i];
	    listmode = TRUE;
	    break;

	 case 'm': /* Monsym */
	    monsym = TRUE;
	    break;

         case 'o': /* Output file */
            i++;
            outfile = argv[i];
            break;

	 case 'p': /* Parens OK == ()OK */
	    termparn = FALSE;
	    break;

	 case 'r': /* ADDREL mode */
	    addrel = TRUE;
	    absolute = FALSE;
	    absmod = FALSE;
	    break;

	 case 't': /* Title */
	    i++;
	    if (strlen (argv[i]) > WIDETITLELEN) argv[i][WIDETITLELEN] = '\0';
	    strcpy (titlebuf, argv[i]);
	    break;

	 case 'w': /* Wide mode */
	    widemode = TRUE;
	    break;

         case 'x': /* Generate Xref */
	    genxref = TRUE;
	    break;

         default:
      USAGE:
	    fprintf (stderr, "asm7090 - version %s\n", VERSION);
	    fprintf (stderr,
		     "usage: asm7090 [-options] -o file.bin file.asm\n");
            fprintf (stderr, " options:\n");
	    fprintf (stderr, "    -a           - Absolute assembly\n");
	    fprintf (stderr, "    -d deck      - Deckname for output\n");
	    fprintf (stderr,
		     "    -l listfile  - Generate listing to listfile\n");
	    fprintf (stderr, "    -m           - MONSYM mode\n");
	    fprintf (stderr, "    -o outfile   - Object file name\n");
	    fprintf (stderr, "    -p           - ParensOK == ()OK\n");
	    fprintf (stderr, "    -r           - ADDREL mode\n");
	    fprintf (stderr, "    -t title     - Title for listing\n");
	    fprintf (stderr, "    -w           - Generate wide listing\n");
	    fprintf (stderr, "    -x           - Generate cross reference\n");
	    return (ABORT);
         }
      }

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

   }
   if (!widemode) titlebuf[NARROWTITLELEN] = '\0';

   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

   /*
   ** Initalize various tables
   */

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

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

   for (i = 0; i < MAXDEFOPS; i++)
   {
      opdefcode[i] = NULL;
   }

   linenum = 0;
   qualindex = 0;
   qualtable[0][0] = '\0';

   /*
   ** 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);
      }
   }

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

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

#ifdef WIN32
   sprintf (tname0, "%s.s", bp);
#else
   sprintf (tname0, "/tmp/%s.s", bp);
#endif
#ifdef DEBUGTMPFILE
   printf ("asm7090: tmp0 = %s\n", tname0);
#endif

   if ((tmpfd0 = fopen (tname0, "w+")) == NULL)
   {
      perror ("asm7090: Can't open temporary file");
      exit (1);
   }

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

#ifdef WIN32
   sprintf (tname1, "%s.s", bp);
#else
   sprintf (tname1, "/tmp/%s.s", bp);
#endif
#ifdef DEBUGTMPFILE
   printf ("asm7090: tmp1 = %s\n", tname1);
#endif

   if ((tmpfd1 = fopen (tname1, "w+")) == NULL)
   {
      perror ("asm7090: Can't open temporary file");
      exit (1);
   }

   /*
   ** If MONSYM mode, define symbols
   */

   if (monsym)
      definemonsyms ();

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

   status = detab (infd, tmpfd0);

   /*
   ** Call pass 0 to preprocess macros, etc.
   */

   inpass = 0;
   status = asmpass0 (tmpfd0, tmpfd1);

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

   inpass = 1;
   status = asmpass1 (tmpfd1, TRUE);

   /* 
   ** Call pass 1 again to scan the source and assign storage.
   ** We have to do this twice because MAP allows forward decls of
   ** variables that affect storage.
   */

   inpass = 1;
   status = asmpass1 (tmpfd1, FALSE);
   status = asmpass1 (tmpfd1, FALSE);

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

   inpass = 2;
   status = asmpass2 (tmpfd1, outfd, listmode, lstfd);

   /*
   ** Close the files
   */

   if (listmode)
   {
      fprintf (lstfd, "\n%d Errors in assembly\n", errcount);
   }

   fclose (infd);
   fclose (outfd);
   fclose (tmpfd0);
   fclose (tmpfd1);
   if (listmode)
      fclose (lstfd);

   /*
   ** Delete tmp file
   */

   unlink (tname0);
   unlink (tname1);

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

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