/* star.c -- Saturn Macro Assembler Main Module

This file is part of STAR, the Saturn Macro Assembler.

   STAR is not distributed by the Free Software Foundation. Do not ask
them for a copy or how to obtain new releases. Instead, send e-mail to
the address below. STAR is merely covered by the GNU General Public
License.

Please send your comments, ideas, and bug reports to
Jan Brittenson <bson@ai.mit.edu>

*/


/* Copyright (C) 1990, 1991 Jan Brittenson.

   STAR is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 1, or (at your option) any
later version.

   STAR is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.

   You should have received a copy of the GNU General Public License
along with STAR; see the file COPYING. If not, to obtain a copy, write
to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
USA, or send e-mail to bson@ai.mit.edu. */


#include <stdio.h>
#include "sects.h"
#include "star.h"

#ifdef USE_STDARG
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#include "literals.h"
#include "symbols.h"


#ifdef __TURBOC__
extern unsigned
  _stklen = 20000;		/* Turbo C runtime stack size */
#endif


/* Adjustable constants */
#define NIBWID	16		/* Nibbles per line in listings */

/* Global data */
extern char *malloc(), *strcat(), *strcpy();

extern struct istruct instbl[];	/* Instruction table */

extern SYM_ROOT
  *symtbl;			/* Global symbol table */

extern SYM_NODE
  *sym_list;			/* `list' symbol */

extern struct val
  (*defrdx)(),			/* Default radix function vector */
  evhex(), evdec(),		/* Radix functions */
  solidify(), localize(),
  evexpr();

int
  hitlo[128-32],
  hithi[128-32];		/* Instruction table hash data */

static char
  copyright[] = "Copyright (C) 1990, 1991 Jan Brittenson";

double
  version_real = 1.0404;	/* STAR version as real */

char
  star_version[] = "1.04.4",	/* STAR version */
  label[256],			/* Label on current line */
  label_pass3[256],		/* Same, if pass 3 */
  codebuf[256],			/* Code buffer for current line */
  *codeptr,			/* Current codebuf position */
  *src[SRCMAX];			/* Source files */

SECT
  *stdsect,			/* Value of STDSECT symbol */
  *cur_sect;			/* Current Section */

int
  pass,				/* Current pass # (1,2,3) */
  errcnt,			/* # of errors/warnings */
  lincnt,			/* # of source lines */
  seqflag,			/* Listing continuation */
  nsources,			/* # of source files */
  optpasses,			/* Max # optimizer passes */
  listx = -1,
  codex = -1,
  srcx = -1,
  linecnt,			/* # of lines in listing */
  pagecnt,			/* # of pages in listing */
  codeblock,			/* Nonzero to block out code generation */
  ciftrue,			/* If inside positive IF block */
  cifnest,			/* # of nested IF levels */
  cifsave,			/* ciftrue stack (shifted) */
  symtop,
  exit_file,			/* END indicator */
  exit_asm,			/* EXIT indicator */
  scmps,			/* # of calls to scmp() */
  inhibit_list,			/* Listing inhibit line down counter */
  gensym_ctr,			/* GENSYM counter */
  totcode,			/* # of nibbles of code output */
  curnfile,			/* Current file index */
  errors_enabled,		/* Error printouts enabled */
  srclines[MAXSRC],		/* # of lines per source file */
  srccode[MAXSRC];		/* # of nibbles of code per file */

unsigned long
  kcrc = 0;			/* Running Kermit CRC */

struct val
  (*ddefrdx)();			/* Current default radix handler */

long
  static_begin,			/* Beginning of ROM */
  static_end,			/* End of ROM */
  floating_begin,		/* Beginning of RAM */
  floating_end,			/* End of RAM */
  loc,				/* Current location pointer */
  loc0;				/* Location at start of instruction */


/* Options and their default values */
int
  quiet = FALSE,		/* Quiet, disable 'time' display	-q */
  stupid= FALSE,		/* Stupid, skip optimization pass	-s */
  opt_jumpify = TRUE,		/* Jumpify				-j */
  opt_trunc = FALSE,		/* Truncate characters exceeding width	-t */
  pagesz= 72,			/* Page size, in lines	        -p<siz> */
  width = 78,			/* Page size, in columns	-w<siz> */
  listflag = TRUE,		/* Listing				-il */
  codeflag = TRUE,		/* Binary output			-ic */
  optmax = 0,			/* Maximum number of optmizer passes	-o0 */
  opt_sharelit = FALSE,		/* Share literals		-l */
  multierrflag = FALSE		/* Multiple errors   		-e */
  ;


/* Local data */
FILE
  *listfile,
  *sourcefile,
  *codefile;

char
  inbuf[132],
  *inp,
  *curfil,
  bufcpy[132];

struct sstruct
  *symloc;

static void
  prhex(FILE *, unsigned long, int);


/* Same as fgets(), but strips trailing LF */
fgetss(buf, bufsz, fp)
  FILE *fp;
  char *buf;
  int bufsz;
{
  char *tmp;
  
  fgets(buf, bufsz, fp);
  
  if(*(tmp = &buf[strlen(buf)-1]) == '\n')
    *tmp = '\0';
}


/* Skip spaces */
#ifndef byspace
char *byspace(cp)
  char *cp;
{
  if(!cp)
    return(NULL);
  
  /* Find first nonspace character */
  for(; *cp == ' ' || *cp == '\011'; cp++);
  
  return(cp);
}
#endif


/* ISO 8859-1 uppercase conversion table */
unsigned char upper_table[0400] =
{
  '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
  '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
  '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
  '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
  '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
  '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
  '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
  '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
  '\100', '\101', '\102', '\103', '\104', '\105', '\106', '\107',
  '\110', '\111', '\112', '\113', '\114', '\115', '\116', '\117',
  '\120', '\121', '\122', '\123', '\124', '\125', '\126', '\127',
  '\130', '\131', '\132', '\133', '\134', '\135', '\136', '\137',
  '\140', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
  'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
  'Y', 'Z', '\173', '\174', '\175', '\176', '\177',
  '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
  '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
  '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
  '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
  '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
  '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
  '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
  '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
  '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
  '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
  '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
  '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
  '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
  '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
  '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\367',
  '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\377' };


/* ISO 8859-1 character class table */
unsigned char cclass_table[0400] =
{
  /* Control codes, 0-31 */

  CCLASS_EOE, CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK, CCLASS_EOE|CCLASS_BLANK,
  CCLASS_EOE|CCLASS_BLANK,

  /* Digit row */

  CCLASS_BLANK, CCLASS_EXPR, 0, 0, 0, CCLASS_EXPR, 0, CCLASS_EXPR,
  CCLASS_EOE|CCLASS_EXPR, CCLASS_EOE|CCLASS_EXPR, CCLASS_EXPR,
  CCLASS_EXPR, CCLASS_EOE, CCLASS_EXPR, 0, CCLASS_EXPR,

  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_OCT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_DIGIT|CCLASS_HEX|CCLASS_SYM,
  CCLASS_EOE, CCLASS_EOE, CCLASS_EXPR, CCLASS_EXPR,
  CCLASS_EXPR, CCLASS_EXPR,

  /* Uppercase row */
  0, CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM|CCLASS_HEX,
  CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM|CCLASS_HEX,
  CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_EOE, 0,
  CCLASS_EOE, CCLASS_EXPR, CCLASS_ISYM,

  /* Lowercase row */
  CCLASS_EXPR, CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM|CCLASS_HEX,
  CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM|CCLASS_HEX,
  CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM|CCLASS_HEX, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, 0, 0, 0,
  CCLASS_EXPR, 0,

  /* Upper half */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM,
  CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM, CCLASS_ISYM };


#ifndef toupper
char toupper(c)
  char c;
{
  return(upper_table[(unsigned char) c]);
}
#endif


#ifndef hex1
/* Binary to hex conversion */
static

#ifdef __GNUC__
const inline
#endif

char hex1(binval)
  register unsigned binval;
{
  if((binval &= 15) > 9)
    return(binval + 'a' - 10);

  return(binval+'0');
}
#endif


static 

#ifdef __GNUC__
inline
#endif

char *tohex(binval, digits)
  unsigned long binval;
  int digits;
{
  register char *cp;
  static char buf[18];


  *(cp = &buf[sizeof(buf)]) = '\0';
  for(; digits > 0; digits--)
    {
      *--cp = hex1(binval);
      binval >>= 4;
    }

  return(cp);
}

  
/* Print hex number in 1-8 digits */
static void prhex(fp, val, digits)
  FILE *fp;
  unsigned long val;
  int digits;
{
  extern char *tohex();
  
  /* Write converted string */
  fputs(tohex(val, digits), fp);
}


/* Get current time.
 * Caution: returns pointer to a static buffer
 */
char *curtime()
{
  extern char *ctime();
  register char *cp;
  long timeval;
  
  time(&timeval);
  cp = ctime(&timeval);
  
  cp[strlen(cp)-1] = ' ';
  return(cp);
}


/* Write line number */
wrline(fp)
  FILE *fp;
{
  extern void newpage();

  if(++linecnt >= pagesz)
    newpage();
  
  fprintf(fp, "%3d ", lincnt);
}


/* Make sure there is a specified number of bytes left in code
 * buffer. If not, write listing (if pass 3) and clear code buffer.
 */
codchk(length) 
  int length;
{
  extern void writelist();

  if(pass != 3 || codeptr + length <= codebuf + NIBWID)
    return;
  
  writelist(listfile);

  bufcpy[0] = 0;
  codeptr = codebuf;
  seqflag = TRUE;
}


/* Add nibble of code to current section */
void code1(nibval)
  unsigned nibval;
{
  if(!ciftrue || codeblock)
    return;
  
  sc_add_code(cur_sect, 1, nibval);
  loc++;

  /* Add for listing/ref */
  if(pass == 3)
    {
      codchk(1);	/* Is there room for another nibble in codebuf? */
      *codeptr++ = nibval & 0xF;
      totcode++;
      srccode[curnfile]++;
    }
}


/* Add 2 nibbles of code to current section */
void code2(bytval)
  unsigned bytval;
{
  if(!ciftrue || codeblock)
    return;
  
  sc_add_code(cur_sect, 2, bytval);
  loc += 2;

  /* Add for listing/ref */
  if(pass == 3)
    {
      codchk(2);	/* Is there room for another 2 bytes in codebuf? */
      *codeptr++ = bytval & 0xf;
      *codeptr++ = (bytval >> 4) & 0xf;
      totcode+=2;
      srccode[curnfile]+=2;
    }
}


/* Add 3 nibbles of code to current section */
void code3(l)
  unsigned l;
{
  if(!ciftrue || codeblock)
    return;
  
  sc_add_code(cur_sect, 3, l);
  loc += 3;

  /* Add for listing/ref */
  if(pass == 3)
    {
      codchk(3);	/* Is there room for another 3 bytes in codebuf? */
      
      *codeptr++ = l & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;

      totcode+=3;
      srccode[curnfile]+=3;
    }
}


/* Add 4-nibble integer to current section */
void code4(l)
  unsigned l;
{
  if(!ciftrue || codeblock)
    return;
  
  sc_add_code(cur_sect, 4, l);
  loc += 4;

  /* Add for listing/ref */
  if(pass == 3)
    {
      codchk(4);	/* Is there room for another 4 bytes in codebuf */
      *codeptr++ = l & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;

      totcode+=4;
      srccode[curnfile]+=4;
    }
}


/* Add 20-bit integer to current section */
void code5(l)
  unsigned long l;
{
  if(!ciftrue || codeblock)
    return;
  
  sc_add_code(cur_sect, 5, l);
  loc += 5;

  if(pass == 3)
    {
      /* Make sure there is room for another 5 bytes codebuf */
      codchk(5);
      *codeptr++ = l & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;
      *codeptr++ = (l >>= 4) & 0xf;

      totcode+=5;
      srccode[curnfile]+=5;
    }
}


/* Add integer of length l nibbles to current section.
 * We can't just call sc_add_code() directly, since we
 * have to update the listings and stats.
 */
void coden(l, i)
  int l;
  INT i;
{
  if(!l || !ciftrue || codeblock)
    return;

  if(l <= NIBWID)
    codchk(l);

  switch(l)
    {
    case 4: code4(i & 0xffff); break;
    case 3: code3(i & 0xfff); break;
    case 2: code2(i & 0xff); break;
    case 1: code1(i & 0xf); break;

    default:
      
      if(l >= 5)
	{
	  code5(i);
	  coden(l-5, i >> 20);
	}
    }
}


/* Signal error if in third pass; increment error counter.
 */
#if defined(UNIX) || defined(MSDOS) || defined(VMS)

#ifdef USE_STDARG
 void sgnerr(char *descr, ...)
 {
#else

 void sgnerr(va_alist)
   va_dcl
 {
   char *descr;
#endif

  va_list args;
  extern void writelist();

  /* Make sure it's the third pass */
  if(pass != 3 || !ciftrue || !errors_enabled)
    return;
  
  if(!multierrflag)
    errors_enabled = FALSE;

#ifdef USE_STDARG
   va_start(args, descr);
#else
  va_start(args);
  descr = va_arg(args, char *);
#endif

  if(listflag)
    {
      wrline(listfile);
      fprintf(listfile, "ERROR: ");
      vfprintf(listfile, descr, args);
      fprintf(listfile, "\n");
    }

#ifdef UNIX_ERRORS
  fprintf(stderr, "\"%s\", line %u: ", curfil, lincnt);
  vfprintf(stderr, descr, args);
  fputc('\n', stderr);
#else
  wrline(stderr);
  fprintf(stderr, "ERROR: ");
  vfprintf(stderr, descr, args);
  fprintf(stderr, "\n");
  writelist(stderr);
#endif

#ifdef USE_STDARG
  va_end(args);
#endif

  errcnt++;
}

#else

void sgnerr(descr, arg1, arg2, arg3, arg4)
  long descr, arg1, arg2, arg3, arg4;
{
  extern void writelist();

  /* Make sure its the third pass */
  if(pass != 3 || !ciftrue || !errors_enabled)
    return;
  
  if(!multierrflag)
    errors_enabled = FALSE;

  if(listflag)
    {
      wrline(listfile);
      fprintf(listfile, "ERROR: ");
      fprintf(listfile, descr, arg1, arg2, arg3, arg4);
      fprintf(listfile, "\n");
    }

#ifdef UNIX_ERRORS
  fprintf(stderr, "\"%s\", line %u: ", curfil, lincnt);
  fprintf(stderr, descr, arg1, arg2, arg3, arg4);
  fputc('\n', stderr);
#else
  wrline(stderr);
  fprintf(stderr, "ERROR: ");
  fprintf(stderr, descr, arg1, arg2, arg3, arg4);
  fprintf(stderr, "\n");
  writelist(stderr);
#endif

  errcnt++;
}
#endif


/* Write warning if in third pass; increment error counter.
 */
#if defined(UNIX) || defined(MSDOS) || defined(VMS)

#ifdef USE_STDARG
 void sgnwrn(char *descr, ...)
 {
#else
 void sgnwrn(va_alist)
   va_dcl
 {
   char *descr;
#endif

  va_list args;
  extern void writelist();

  /* Make sure its the third pass */
  if(pass != 3 || !ciftrue || !errors_enabled)
    return;
  
  if(!multierrflag)
    errors_enabled = FALSE;

#ifdef USE_STDARG
  va_start(args, descr);
#else
  va_start(args);
  descr = va_arg(args, char *);
#endif

  if(listflag)
    {
      wrline(listfile);
      fprintf(listfile, "WARNING: ");
      vfprintf(listfile, descr, args);
      fprintf(listfile, "\n");
    }

#ifdef UNIX_ERRORS
  fprintf(stderr, "\"%s\", line %u: Warning - ", curfil, lincnt);
  vfprintf(stderr, descr, args);
  fputc('\n', stderr);
#else
  wrline(stderr);
  fprintf(stderr, "WARNING: ");
  vfprintf(stderr, descr, args);
  fprintf(stderr, "\n");
  writelist(stderr);
#endif

#ifdef USE_STDARG
   va_end(args);
#endif

  errcnt++;
}

#else

sgnwrn(descr, arg1, arg2, arg3, arg4)
  long descr, arg1, arg2, arg3, arg4;
{
  extern void writelist();

  /* Make sure its the third pass */
  if(pass != 3 || !ciftrue || !errors_enabled)
    return;
  
  if(!multierrflag)
    errors_enabled = FALSE;

  if(listflag)
    {
      wrline(listfile);
      fprintf(listfile, "WARNING: ");
      fprintf(listfile, descr, arg1, arg2, arg3, arg4);
      fprintf(listfile, "\n");
    }

#ifdef UNIX_ERRORS
  fprintf(stderr, "\"%s\", line %u: Warning - ", curfil, lincnt);
  fprintf(stderr, descr, arg1, arg2, arg3, arg4);
  fputc('\n', stderr);
#else
  wrline(stderr);
  fprintf(stderr, "WARNING: ");
  fprintf(stderr, descr, arg1, arg2, arg3, arg4);
  fprintf(stderr, "\n");
  writelist(stderr);
#endif

  errcnt++;
}

#endif


/* Fatal error */
#if defined(UNIX) || defined(MSDOS) || defined(VMS)

#ifdef USE_STDARG
 void fatal(char *descr, ...)
 {
#else
 void fatal(va_alist)
   va_dcl
 {
  char *descr;
#endif

  va_list args;
  extern void writelist();

#ifdef USE_STDARG
  va_start(args, descr);
#else
  va_start(args);
  descr = va_arg(args, char *);
#endif

  fprintf(stderr, "\n\nAssembler Internal Error:\n  ");
  vfprintf(stderr, descr, args);
  fprintf(stderr, "\n  At or near line %d in source file %s\n",
	  lincnt, curfil);

#ifdef USE_STDARG
  va_end(args);
#endif

#ifdef FATAL_DUMP
  abort();
#else
  exit(EXIT_ERROR);
#endif
}

#else

void fatal(descr, arg1, arg2, arg3, arg4)
  long descr, arg1, arg2, arg3, arg4;
{
  fputs("\n\nAssembler Internal Error:\n  ", stderr);
  fprintf(stderr, descr, arg1, arg2, arg3, arg4);
  fprintf(stderr, "\n  At or near line %d in source file %s\n",
	  lincnt, curfil);

  exit(EXIT_ERROR);
}

#endif


/* Verify symbol name */
void verify_symbol_name(symname)
  char *symname;
{
  char
    *name, *namep = symname;
  int
    dummy, vpass = pass;
  struct fstruct *fdummy;
  extern
    mtreg(), mtid(), mtopx(), mtxopy();


  /* Empty? */
  if(!*symname)
    {
      sgnerr("Null symbol name");
      return;
    }

  /* Ignore if local symbol */
  if(*namep == '$')
    return;

  /* Check for reserved symbols */
  if(mtreg(&namep, &dummy) || mtid(&namep) || mthst(&namep) ||
     mtopx(&namep, &fdummy) || mtxopy(&namep, &fdummy))
    {
      sgnerr("Symbol name `%s' is reserved", symname);
      *symname = '_';
    }

  /* First char */
  name = symname;

  if(!isisym(*name))
    {
      sgnerr("Invalid symbol name `%s'", symname);
      *name = '_';
    }

  for(name++; *name; name++)
    if(!issym(*name))
      {
	sgnerr("Invalid symbol name `%s'", symname);
	*name = '_';
      }
}


/* Rebind symbol */
void rebind(symp, v)
  struct sstruct *symp;
  struct val v;
{
  /* First deallocate old value */
#ifdef SYMTAB_BUG
  if(!(symp->flags & F_UDF))
    free_val(symp->value);
#endif

  /* Then rebind */
  symp->value = solidify(v);
  symp->flags &= ~(F_UDF|F_HID);
}


/* Scan for symbol and convert to uppercase */
#ifdef __GCC__
inline
#endif

static void scanucsym(cpp, destp)
  char **cpp, *destp;
{
  if(**cpp == '$')
    *destp++ = *(*cpp)++;
  
  for(; issym(**cpp); *destp++ = toupper(*(*cpp)++));
  *destp = '\0';
}


/* Scan input line for label definition */
void scanlabel()
{
  char
    *inpsave, *labelbuf;
  
#ifdef DEBUG /* Fri Feb  8 07:47:59 1991 */
/**/
/**/  printf("scanlabel(): called, line=`%s'\n", inp); /* DEBUG */
/**/
#endif /* DEBUG Fri Feb  8 07:47:59 1991 */

  if(pass == 3)
    labelbuf = label_pass3;
  else
    labelbuf = label;

  
  inpsave = inp = byspace(inp);

  scanucsym(&inp, labelbuf);

  /* Not a definition */
  if(*inp != ':')
    {
      inp = inpsave;
      *labelbuf = '\0';		/* Scratch label */

#ifdef OBSOLETE /* Fri Feb  8 07:48:44 1991 */
/**/
/**/      printf("scanlabel(): not symdef, line=`%s'\n", inp); /* DEBUG */
/**/
#endif /* OBSOLETE Fri Feb  8 07:48:44 1991 */

      return;
    }
  
  /* Interpret null symbol name as no symbol name */
  /* Also step past colon */
  if(inp++ == inpsave)
    {
      *labelbuf = '\0';

#ifdef OBSOLETE /* Fri Feb  8 07:49:03 1991 */
/**/
/**/      printf("scanlabel(): null symdef, line=`%s'\n", inp); /* DEBUG */
/**/
#endif /* OBSOLETE Fri Feb  8 07:49:03 1991 */

      return;
    }
  
  inp = byspace(inp);
  verify_symbol_name(labelbuf);

#ifdef OBSOLETE /* Fri Feb  8 07:49:18 1991 */
/**/
/**/  printf("scanlabel(): successful, line=`%s'\n", inp); /* DEBUG */
/**/
#endif /* OBSOLETE Fri Feb  8 07:49:18 1991 */
}


/* Define label, if any */
void deflabel()
{
  char *cp;
  extern void local_bind(), local_new_context();
  extern long atol();
  extern ciftrue;

  if(!ciftrue)
    {
      label[0] = '\0';
      return;
    }
    
  if(!label[0])
    {
      if(pass == 3 && label_pass3[0] && label_pass3[0] != '$')
	local_new_context();

      return;
    }

  /* Put in local symbol table, if appropriate */
  if(label[0] == '$')
    {
      long symid = atol(label+1);

      local_bind((unsigned long) symid, loc0);

      label[0] = '\0';
      return;
    }

  /* Look it up */
  if(!(symloc = sm_find_sym(symtbl, label)))

    sm_enter_sym(symtbl, label, solidify(intval(loc0)), F_LBL);

  else				/* Found */
    {
      if(pass == 1 && !(symloc->flags & F_UDF))
	{
	  int shallow_pass = pass;

	  pass = 3;
	  sgnerr("Symbol `%s' multiply defined", label);
	  pass = shallow_pass;
	}
      
      /* Redefine */
      symloc->value = intval((INT) loc0);
      symloc->flags &= ~F_UDF;
    }

  label[0] = '\0';

  /* Declare new local context */
  local_new_context();
}


usage_big()
{
  fprintf(stderr, "\nSTAR - Saturn Macro Assembler Version %s\n",
	  star_version);
  fprintf(stderr, "%s\n\n", copyright);
  fputs("STAR comes with ABSOLUTELY NO WARRANTY. This is free software and you\n", stderr);
  fputs("are welcome to redistribute it under certain conditions; refer to the\n", stderr);
  fputs("file COPYING for details.\n", stderr);
  fputs("\n", stderr);
  fputs("-q	Quiet; suppress random messages		-il	Inhibit listing\n", stderr);
  fputs("-s	Stupid; skip pass 2			-ic	Inhibit binary output\n", stderr);
  fputs("-on	Maximum number of optimizer passes, 0 = infinite\n", stderr);
  fputs("-e	Print as many errors as possible to aid error diagnosis\n", stderr);
  fputs("-? -h	This help\n", stderr);
  fputs("-j	Disable jumpification\n", stderr);
  fputs("-pn	Listing page size is 'n' lines\n", stderr);
  fputs("-wn	Listing page width is 'n' columns\n", stderr);
  fputs("-x	Default radix hex\n", stderr);
  fputs("-d	Default radix dec\n", stderr);
  fputs("\nDefault: -x +qse +il +ic -p72 -w78 -o0 -a2", stderr);

  fputc('\n', stderr);

#ifdef AMIGA
  fputs("Configuration is AmigaDOS and Lattice C\n", stderr);
#else

#ifdef SYSV
  fputs("Configuartion is AT&T System V or clone\n", stderr);
#endif
#endif

#ifdef MSDOS
  fputs("Configuration is IBM-PC or compatible with MS-DOS 2.00 or later",
	stderr);
#endif

#ifdef BSD
  fputs("Configuration is 4.1BSD or later, or SunOS\n", stderr);
#endif

#ifdef VMS
  fputs("Configuration is VAX/VMS\n", stderr);
#endif

  exit(EXIT_ERROR);
}


/* Display message followed by usage */
static usage()
{
  fprintf(stderr,
	  "\nUsage:\n\
   star -qsrtdx?hel -an -on -ic -il -pn -wn listfile codefile sourcefile(s)\
 \n\n");
  
  exit(EXIT_ERROR);
}


/* Decode option value */
static optval(argv,i,j)
  char *argv[]; int i,j;
{
  register int tmp;
  
  if(!is_digit(argv[i][j]) ||
     (tmp = atoi(&argv[i][j])) > 255 || tmp < 0)
    usage();
  
  return(tmp);
}


/* Copy string and convert to uppercase */
char *ucstrcpy(dst, src)
  char *src, *dst;
{
  char *orgdst = dst;

  while(*dst++ = toupper(*src++));

  return(orgdst);
}


/* Set default filetype */
static deftype(file_array, file_index, file_type)
  char *file_array[], *file_type;
  int file_index;
{
  char *cp, *fname;
  
  if(file_index < 0 || file_index >= MAXSRC)
    return;
  
  fname = file_array[file_index];
  for(cp = fname + strlen(fname) - 1; cp >= fname; cp--)
    if(*cp == '.')
      return;
    else
      if(*cp == FILE_SUBDIR)
	break;
  
  if(!(cp = malloc(strlen(fname) + strlen(file_type) + 1)))
    fatal("Can't alloc memory for file name");
  
  file_array[file_index] =
#ifdef FILE_CASE_SIG
    strcat(strcpy(cp, file_array[file_index]), file_type);
#else
    strcat(ucstrcpy(cp, file_array[file_index]), file_type);
#endif
}


/* Parse arguments and open files */
static void parse_args(argc, argv)
  int argc;
  char *argv[];
{
  int i,j;
  
  if(argc == 1)
    usage();
  
  for(nsources = 0, i = 1; i < argc; i++)
    if(*argv[i] == '-')
      for(j=1; j<strlen(argv[i]);)
	switch(argv[i][j++])
	  {
	  case '\0':
	    usage();
	    
	  case 'd':	ddefrdx = evdec;	break;
	  case 'x':	ddefrdx = evhex;	break;
	  case 'q':	quiet = 1;	break;
	  case 's':     stupid= 1;	break;
	  case 't':	opt_trunc = 1;	break;
	  case 'i':
	    if(argv[i][j] == 'c')	codeflag = 0;
	    else
	      if(argv[i][j] == 'l')	listflag = 0;
	      else
		usage();
	    j++;
	    break;

	  case 'p':	pagesz = optval(argv,i,j);
	    j = strlen(argv[i]);
	    break;

	  case 'w':	width  = optval(argv,i,j);
	    j = strlen(argv[i]);
	    break;

	  case 'o':	optmax = optval(argv,i,j);
	    j = strlen(argv[i]);
	    break;

	  case 'e':	multierrflag = TRUE; break;
	  case 'j':	opt_jumpify = FALSE; break;
	  case '?':
	  case 'h':
	    usage_big();

	  case 'l':	opt_sharelit = TRUE; break;

	  default:
	    fprintf(stderr, "\n%c: bad option\n", argv[i][--j]);
	    usage();
	  }
    else
      if(nsources >= MAXSRC)
	{
	  fputs("\ntoo many source files\n", stderr);
	  usage();
	}
      else
	src[nsources++] = argv[i];
  
  
  /* Compute list, source, and code file indexes */
  if(!listflag)
    listx = -1;		/* = no listing */
  else
    listx = 0;
  
  if(!codeflag)
    codex = -1;		/* = no hex file */
  else
    codex = listx + 1;
  
  if((srcx = ((codex == -1) ? listx : codex) + 1) >= nsources)
    {
      fputs("\nmissing file specification(s)\n", stderr);
      usage();
    }
    
  /* Add default file types */
  deftype(src, listx, LIST_FILETYPE);
  deftype(src, codex, CODE_FILETYPE);
  for(i = srcx; i < nsources; deftype(src, i++, SOURCE_FILETYPE));
  
  
  /* Create list file */
  if(listx != -1)
    if(!(listfile = fopen(src[listx], "w")))
      {
#ifdef UNIX
	perror(src[listx]);
#else
	fprintf(stderr, "%s: can't create\n", src[listx]);
#endif
	exit(EXIT_ERROR);
      }
  
  /* Create code file */
  if(codex != -1)
    if(!(codefile = fopen(src[codex], BINARY_OPEN_FLAG)))
      {
#ifdef UNIX
	perror(src[codex]);
#else
	fprintf(stderr, "%s: can't create\n", src[codex]);
#endif
	fclose(listfile);
	exit(EXIT_ERROR);
      }
}


/* Compare strings in uppercase.
 * 's1' is converted to uppercase; for lowercase match use strcmp().
 * 's2' is assumed to be uppercase prior to call.
 * Returns -1 if s1 < s2
 *	   +1 if s1 > s2
 *	    0 if s1 == s2
 */
#ifdef __GNUC__
inline const
#endif

scmp(s1, s2)
  register char *s1, *s2;
{
  scmps++;
  for(;*s1 && *s2 && toupper(*s1) == *s2; s1++,s2++);
  
  return((!*s1 && !*s2)	? 0	:
	 (!*s1 && *s2)	? -1	:
	 (*s1 && !*s2)	? 1	:
	 (toupper(*s1) > *s2) ? 1 : -1);
}


/* Assemble line contents */
void assemble_line()
{
  register int low, hi;
  static int center;
  char *tmp, tmpc, termc, *asnarg, tc, *cp;
  struct sstruct *symp;

  
  expr_gc();

  /* Extract first word */
  if(*(inp = byspace(inp)) == ';' || *inp < ' ')
    return;
  
  seqflag = FALSE;
  for(tmp = inp; *inp > ' ' && (inp == tmp || *inp != '.') && *inp != '=';
      inp++);

  termc = *inp;
  cp = byspace(inp);
  tc = *cp;
  *inp = '\0';

  tmpc = toupper(*tmp)-32;

  /* Is it "name = expr"? */
  if(tc == '=')
    {
      char defstr[132];
      extern void ddef(), dorg();

      static struct istruct
	defs = {"=", ddef, 0, 0},
	orgs = {"=", dorg, 0, 0};

      cp = byspace(cp+1);

      /* If symbol is dot ".", then rearrange into `origin' form,
       * otherwise rearrange into a `define' form.
       */
      if(tmp[0] == '.' && !tmp[1])
	{
	  sprintf(defstr, " %s", cp);
	  dorg(&orgs, defstr);
	}
      else
	{
	  sprintf(defstr, " %s %s", tmp, cp);
	  ddef(&defs, defstr);
	}

      *inp = termc;
      return;
    }

  /* Look it up in symbol table, expand if macro */
  if((symp = sm_find_sym(symtbl, tmp)) && symp->value.type == VT_MAC)
    {
      *inp = termc;
      expand_macro(symp->value.vmacro, byspace(inp));
      return;
    }

  /* Look it up in the instruction table */
  if((low = hitlo[tmpc]) >= 0 && low <= symtop)
    for(hi = hithi[tmpc]; hi >= low;)
      switch(scmp(tmp, instbl[center = (hi + low) >> 1].name))
	{		/* Aim */
	case -1:	/* Hi  */	hi  = center-1; break;
	case 1:		/* Low */	low = center+1; break;
	default:	/* Eq  */	goto found;
	}
  
  /* Not a macro or instruction */
  sgnerr("Undefined instruction or macro - `%s'", tmp);
  return;
  
  /* Instruction found */
 found:
  
  /* Restore line  */
  *inp = termc;

  /* Translate operands and generate code */
  (*instbl[center].scandef)(&instbl[center], inp);
}


/* Start new page */
void newpage()
{
  if(!listflag)
    return;
  
  /* Formfeed between pages */
  if(pagecnt > 0)
    fprintf(listfile, "\014");
  
  /* Page header */
  fprintf(listfile, "\nSTAR %s\t%s\t\t\t\t%s - %d\n\n",
	  star_version,
	  curtime(), curfil, ++pagecnt);
  linecnt = 4;
}


/* Write assembler statistics on list file */
void write_statistics(lfile, ac, av)
  FILE *lfile;
  int ac;
  char **av;
{
  int nfile, totlines;
  char **fnamep;
  extern nhidden;
  extern SYM_ROOT *symtbl;

  curfil = "Statistics";

  newpage();

  fprintf(lfile, "\nSymbols: %d (%d gensym, %d references)\n",
	  symtbl->nsymbols, gensym_ctr-1, scmps);

  fprintf(lfile, "Hidden: %d\n\n", nhidden);
  fprintf(lfile, "Integer: %d bits\n\n", BPINT);

  for(totlines = 0, nfile = srcx; nfile < nsources; nfile++)
    {
      fprintf(lfile, "%s: %d lines, %d nibbles\n",
	      src[nfile], srclines[nfile], srccode[nfile]);

      totlines += srclines[nfile];
    }
      
  fprintf(lfile, "Total: %d lines, %d nibbles\n\nCommand line:\n  ",
	  totlines, totcode);

  while(--ac)
    fprintf(lfile, "%s ", *av++);

  fprintf(lfile, "%s\n\n", *av++);

  fprintf(lfile, "Optimizer passes: %d\n", optpasses);
  fprintf(lfile, "Errors/warnings: %d\n\n", errcnt);

  fprintf(lfile, "Literal pool start: 0x%lx   size: %lu\n",
	  lit_begin, lit_end-lit_begin);
}


/* Write assembled line to list file */
void writelist(filp)
  register FILE *filp;
{
  int cnt, tmpcnt;
  char *tmp;


  if(inhibit_list > 0)
    {
      inhibit_list--;
      return;
    }

  if(pass != 3 || !listflag || !(sym_list->value.vint))
    return;
  
  wrline(filp);		/* Write line number */
  fprintf(filp, "  ");
  
  /* Sequenced line? */
  if(seqflag)
    fprintf(filp, "     ");
  else
    {
      prhex(filp, (short) (loc0 >> 4), 4);
      prhex(filp, (short) loc0, 1);
    }
  
  fprintf(filp, "  ");
  
  cnt = 0;
  for(tmp = codebuf; tmp < codeptr; cnt++)

    /* Write nibble */
    prhex(filp, (short) (*tmp++) & 0xf, 1);
  
  /* Adjust width */
  for(tmpcnt = cnt; tmpcnt < NIBWID; tmpcnt++)
    fprintf(filp, " ");
  
  fprintf(filp, "\t%s\n", bufcpy);
}


/* Expand $name entries in string, returns static buffer.
 * If the first character of name is neither '_' nor a letter, no
 * expansion takes place.
 */
char *expand_symbols(str)
  char *str;
{
  static char result[256];
  char c, *symname, *rp;
  SYM_NODE *symp;
  struct val tmpval;
  extern ciftrue;
  extern char *scansym(), *strcpy();
  extern struct val tostr(), val_zero, val_nullstr;
  

  /* Ignore if false */
  if(!ciftrue)
    return(strcpy(result, str));

  for(rp = result; *str; )
    if(*str == '\\')
      {
	*rp++ = *str++;

	if(*str)
	  *rp++ = *str++;
      }
    else
      if(*str != '$')
	*rp++ = *str++;
      else
	{
	  str++;

	  if(*str == '$')
	    {
	      *rp++ = '$';
	      *rp++ = *str++;
	      continue;
	    }
	  
	  /* &(expr)? */
	  if(*str == '(')
	    {
	      str++;
	      tmpval = evexpr(&str);
	      mustbe(&str, ')');
	    }
	  else
	    if(!(*str == '_' ||
		 (toupper(*str) >= 'A' && toupper(*str) <= 'Z')))
	      {
		*rp++ = '$';
		*rp++ = *str++;
		continue;
	      }
	    else
	      {
		symname = scansym(&str, &c);
		
		if(!(symp = sm_find_sym(symtbl, symname)) ||
		   (symp->flags & F_UDF))
		  {
		    sgnerr("Undefined symbol `%s'", symname);
		    tmpval = localize(val_nullstr);
		  }
		else
		  tmpval = localize(symp->value);
		
		symname[strlen(symname)] = c;
	      }
	  
	  strcpy(rp, tostr(tmpval).vstr);
	  rp += strlen(rp);
	}

  *rp = '\0';
  return(result);
}


/* Open source file and assemble contents, then close file */
static assemble(filename, filex)
  char *filename;
{
  extern macro_invocation;
  extern void local_new_pass();

  /* Open file for reading */
  if((sourcefile = fopen(filename, "r")) == NULL)
    {
#ifdef UNIX
      perror(filename);
#else
      fprintf(stderr, "%s: can't open\n", filename);
#endif
      exit(EXIT_ERROR);
    }
  
  inhibit_list = lincnt = 0;
  exit_file = FALSE;
  curnfile = filex;

  srclines[curnfile] = 0;
  srccode[curnfile] = 0;

  /* Step through source file */
  while(!(exit_file || exit_asm))
    {
      macro_invocation = FALSE;
      errors_enabled = TRUE;

      lincnt++;
      fgetss(inbuf, 132, sourcefile); /* Read new line	*/

      srclines[curnfile]++;
      
      if(feof(sourcefile))
	break;
      
      strcpy(bufcpy, inbuf);	/* Save line for listing */
      inp     = inbuf;		/* Reset scan pointer */
      loc0    = loc;		/* Reloc from here */
      codeptr = codebuf;	/* No code - yet */

      strcpy(inbuf,		/* Expand &sym entries */
	     expand_symbols(inbuf)); 

      scanlabel();		/* Scan line for label */

      if(*inp)
	assemble_line();	/* Assemble line contents */

      deflabel();		/* Define label, if any */

      if(pass == 3)
	writelist(listfile);	/* Write listing */
    }
  
  /* Close file when done */
  fclose(sourcefile);
}


/* Initialize instruction table hash list */
initins()
{
  register ix;
  register char ltr;
  
  
  for(ix = 128-32; ix >= 0; hitlo[ix] = hithi[ix] = -1, --ix);
  
  for(ix = 0; *instbl[ix].name;)
    {
      ltr = toupper(*instbl[ix].name);
      hitlo[ltr-32] = ix;
      while(ltr == *instbl[++ix].name);
      hithi[ltr-32] = ix-1;
    }
}


/* Quote a string */
char *quote_string(str)
  char *str;
{
  static char deststr[256];
  char *dstr;


  for(dstr = deststr; *str; str++)
    if(isblank(*str) || (*str & 0200))
      switch(*str)
	{
	case 8: strcpy(dstr, "\\b"); dstr += 2; break;
	case 9: strcpy(dstr, "\\t"); dstr += 2; break;
	case 10: strcpy(dstr, "\\n"); dstr += 2; break;
	case 13: strcpy(dstr, "\\r"); dstr += 2; break;

	default:

	  sprintf(dstr, "\\%03o", *(unsigned char *) str);
	  dstr += strlen(dstr);
	}
    else
      *dstr++ = *str;

  *dstr = '\0';

  return(deststr);
}

	
/* Return value as string in static buffer */
char *valtostr(v)
  struct val v;
{
  static char bf[64];

  switch(v.type)
    {
    case VT_INT: sprintf(bf, "%07lx ", v.vint); break;
    case VT_REAL: 

#ifdef __GNUC__
      sprintf(bf, "%lg ", v.vdouble);
#else
      sprintf(bf, "%g ", (double) v.vdouble);
#endif
      break;

    case VT_STR:

      if(strlen(v.vstr) > 6)
	{
	  v.vstr[5] = '\0';
	  sprintf(bf, "`%s..", quote_string(v.vstr));
	}
      else
	sprintf(bf, "`%s'", quote_string(v.vstr));

      break;

    case VT_SECT:

      switch(v.vsect->sc_bits & SC_SECT)
	{
	case SC_ASECT: bf[0] = 'a'; break;
	case SC_PSECT: bf[0] = 'p'; break;
	case SC_CSECT: bf[0] = 'c'; break;
	case SC_DSECT: bf[0] = 'd'; break;
	case SC_LSECT: bf[0] = 'l'; break;
	}

      strcpy(bf+1, "sect");
      break;

    case VT_MAC:

      strcpy(bf, "macro");
      break;

    default:

      strcpy(bf, "*****");
    }

  return(bf);
}


/* Print symbol table */
static void write_symbol_table(list)
  FILE *list;
{
  extern SYM_NODE
    **create_sym_ref();
  SYM_NODE
    **symbol_table,
    **col1, **col2, **col3,
    **org_col2, **org_col3,
    **end_of_table;
  int
    nsyms,			/* # of symbols to be printed */
    npages,			/* # of pages */
    nrows,			/* # of rows per col */
    lpp;			/* Lines per page */

  /* Create symbol table reference */
  symbol_table = create_sym_ref(&nsyms);

  /* Partition pages. Each page is 3 columns. */
  newpage();
  lpp = pagesz-linecnt;
  npages = nsyms/lpp;

  if(nsyms % 3)
    nrows = (nsyms+3)/3;
  else
    nrows = nsyms/3;

  end_of_table = symbol_table + nsyms;

  /* Loop and print */
  col1 = symbol_table;
  org_col2 = col2 = symbol_table + nrows;
  org_col3 = col3 = symbol_table + nrows * 2;

  while(col1 && col1 < org_col2 && col1 < end_of_table)
    {
      if(col2 >= org_col3 || col2 >= end_of_table)
	col2 = NULL;

      if(col3 >= end_of_table)
	col3 = NULL;

      if(strlen((*col1)->name) > 16)
	(*col1)->name[16] = '\0';

      fprintf(list, "%-16s %-8s", (*col1)->name, valtostr((*col1)->value));
      col1++;
      
      if(col2)
	{
	  if(strlen((*col2)->name) > 16)
	    (*col2)->name[16] = '\0';

	  fprintf(list, "  %-16s %-8s",
		  (*col2)->name, valtostr((*col2)->value));
	  col2++;
	  
	  if(col3)
	    {
	      if(strlen((*col3)->name) > 16)
		(*col3)->name[16] = '\0';

	      fprintf(list, "  %-16s %s",
		      (*col3)->name,
		      valtostr((*col3)->value));
	      
	      col3++;
	    }
	}
      fputc('\n', list);
      
      if(++linecnt >= pagesz)
	newpage();
      
    }
}


/* STAR */
main(argc, argv)
  int argc;
  char *argv[];
{
  int i, stamp[2], prev_ncontext;
  unsigned long previous_code_size;
  SYM_NODE *sp;
  extern void
    initsym(), literal_init(), flush_literal_pool(),
    init_op_tables();
  extern
    ncontext,
    debug_print_sym();
  extern SYM_NODE
    *sym_cursect;
    
  
  /* Initialize */
  /* Set default radix to decimal.
   * Enable code generation.
   */
  ddefrdx = evdec;
  scmps = 0;
  codeblock = 0;

  /* Initialize Sections code.
   * Create standard section - the symbol backpointer is taken
   * care of in starsym.c; this is used by other init code.
   */
  sc_init();
  cur_sect = stdsect = sc_new(NULL, SC_PSECT);

  initins();
  initsym();
  expr_init();
  expr_gc();
  local_init();
  init_op_tables();

  static_begin = 0;
  static_end   = 0x6ffff;
  floating_begin = 0x70000;
  floating_end   = 0xfffff;

  
  /* Decode command line and create output files */
  parse_args(argc,argv);
  
  /* Initialize literal pool */
  literal_init(opt_sharelit);

  /* Adjust page size for header */
  if((pagesz -= 5) < 4)
    {
      fputs("\npage not long enough\n", stderr);
      usage();
    }
  
  linecnt = pagecnt = 0;
  
  /* Find last non-free entry in instruction table */
  for(symtop = 0; *(instbl[++symtop].name) != NULL;);
  --symtop;
  
  
  /* Step through code in initial pass */
  if(!quiet)
    fprintf(stderr, "\nPass 1: %s  Source load\n", curtime());
  
  gensym_ctr = ciftrue = pass = 1;
  defrdx = ddefrdx;
  loc = loc0 = 0L;
  exit_asm = cifsave = cifnest = 0;

  free_val(sym_cursect->value);
  sym_cursect->value.type = VT_SECT;
  sym_cursect->value.vsect = cur_sect = stdsect;
  
  for(i=srcx; i<nsources && !exit_asm; i++)
    assemble(curfil = src[i], i);

  /* Declare new pass */
  previous_code_size = (unsigned long) sc_code_size();

  literal_end_of_pass();
  sc_new_pass();

  prev_ncontext = ncontext;
  local_new_pass();

  /* Optimize */
  if(!stupid)
    {
      int optimized;

      if(!quiet)
	fprintf(stderr, "Pass 2: %s  Optimization\n", curtime());
      
      pass = 2;
      
      for(optimized = TRUE, optpasses = 0;
	  optimized && (optpasses < optmax || !optmax);
	  optpasses++)
	{
	  unsigned long csz;


	  gensym_ctr = ciftrue = 1;
	  cifsave = cifnest = 0;
	  loc = loc0 = 0L;
	  defrdx = ddefrdx;
	  exit_asm = FALSE;

	  free_val(sym_cursect->value);
	  sym_cursect->value.type = VT_SECT;
	  sym_cursect->value.vsect = cur_sect = stdsect;

	  for(i=srcx; i<nsources && !exit_asm; i++)
	    assemble(curfil = src[i], i);

	  csz = (unsigned long) sc_code_size();

	  optimized = csz < previous_code_size;
	  previous_code_size = csz;

	  /* Declare new pass */
	  literal_end_of_pass();
	  sc_new_pass();

	  /* Declare new context */
	  prev_ncontext = ncontext;
	  local_new_pass(pass);
	}
    }
  
  /* Declare new context, but save old context counter.
   * We do this in order to detect context phase errors.
   * These should never occur, and the measure is useful
   * mostly for debugging purposes. Also, we only check
   * this between the last two passes. It doesn't matter
   * if we're out of context phase before that. (This
   * is actually done at the end of the previous two passes.)
   */

  /* Generate code and/or listing */
  if(codeflag || listflag)
    {
      if(!quiet)
	fprintf(stderr, "Pass 3: %s  Output\n", curtime());
      
      pass = 3;
      gensym_ctr = ciftrue = 1;
      cifsave = cifnest = 0;
      loc = loc0 = 0L;
      defrdx = ddefrdx;
      exit_asm = FALSE;

      free_val(sym_cursect->value);
      sym_cursect->value.type = VT_SECT;
      sym_cursect->value.vsect = cur_sect = stdsect;

      for(i=srcx; i<nsources && !exit_asm; i++)
	{
	  curfil = src[i];
	  newpage();
	  assemble(curfil, i);
	}

      /* Check to see whether we're out of local symbol
       * context phase. See comment above.
       */
      if(ncontext != prev_ncontext)
	sgnerr("Local symbol context phase error (%d/%d)",
	       prev_ncontext, ncontext);

      /* Flush literal pool and any remaining code */
      if(codeflag)
	{
	  sc_write_code(codefile);
	  write_literal_pool(codefile);
	  sc_flush(NULL, codefile);
	}

      /* Dump symbol table and statistics */
      if(listflag)
	{
	  curfil = "Symbol table";
	  write_symbol_table(listfile);

	  curfil = "Sections";
	  sc_list_sect(listfile);

	  write_statistics(listfile, argc, argv);
	}
    }
  
  if(!quiet && !stupid && optpasses != 1)
    fprintf(stderr, "\nOptimizer passes: %d\n", optpasses);
  
  /* Display number of errors/warnings */
  if(errcnt)
    if(errcnt == 1)
      fprintf(stderr,"\nOne error or warning detected\n");
    else
      fprintf(stderr,"\nThere were %d errors and/or warnings detected\n",
	      errcnt);
  
  /* Close files and exit */
  fclose(listfile);
  fclose(codefile);

  exit(errcnt ? EXIT_ERROR : EXIT_SUCC);
}
 
