/*--------------------------------------------------------------*/
/*

 TinC - Tiny, in C
 tinc13.c
 Jan 2005, Peter F. Gray

 Extrapolated from Tiny 1.1 coded by Jack W. Crenshaw, in Pascal,
 as part of Jack's "Let's Build A Compiler" tutorial.

*/
/*--------------------------------------------------------------*/

#include "tinc13.h"

#include <stdio.h>

FILE *ip[MAXINCL] = {NULL};	// input (source) file
int  slevel = 0;		// source level (for #includes)
long linenumber[MAXINCL] = {1};	// current source line number
char sname[MAXINCL][STRTMPSIZE];// current source name

FILE *op = NULL;		// output file
FILE *op1 = NULL;		// output file 1
FILE *op2 = NULL;		// output file 2
FILE *tr = NULL;		// trace file
FILE *pcd = NULL;		// pcode dump file
FILE *sym = NULL;		// symbol table file

int tracing = TRUE;		// tracing flag
int opfile1 = TRUE;		// output file flag 1
int opfile2 = TRUE;		// output file flag 2
int genreguse = TRUE;		// generate reg use pcodes
int genreguse2 = FALSE;		// as above, but for the final code
int pcdump = TRUE;		// unoptimized pcode dump flag
int pcdump2 = TRUE;		// optimized pcode dump flag

int warn = TRUE;		// warning flag
int wcount = 0;			// warning count

int interactive = 0;		// interactive flag
int trindent = 0;		// trace indent level
long totallines = 0;		// total number of source lines

int endofallsource = FALSE;	// no more source to process
int ifdeflevel = 0;		// #ifdef level

int section = -1;		// section type
int optlevel = 1;		// optimization level

extern void Gen_Item (int pcode, char *s1, char *s2, char *s3,
                      int t1, int t2);
extern void InitRegs();
extern void Optimize ();

static int BoolExpression(int);
static int Expression(int);
static void Block();
static void Next();
static void Scan();
void Abort();
int  IsConst(char *);
void Header();
void Trailer();


/*--------------------------------------------------------------*/
/* Symbol Tables etc	*/

static Symbol GS_Name[MAXGLOBALS];	// global symbol table (name)
static int    GS_Type[MAXGLOBALS];	// data type
static int    GS_Size[MAXGLOBALS];	// size (in 'data type' units)

static int    GS_ParamCount[MAXGLOBALS];
static int    GS_ParamType[MAXGLOBALS][MAXPARAMS];

static Symbol LS_Name[MAXLOCALS];	// local symbol table (name)
static int    LS_Type[MAXLOCALS];	// data type
static int    LS_Size[MAXLOCALS];	// size (in 'data type' units)

static long NumGlobals = 0;		// number of globals used
static long NumLocals = 0;		// number of locals used
static int  TotLocSize;			// space required for locals

static Symbol DS_Name[MAXDEFS];		// definition table (name)
static Symbol DS_Value[MAXDEFS];	// definition table (value)
static long NumDefs = 0;		// number of Defs used

static Symbol PS_Name[MAXPARAMS];	// parameter list (name)
static int    PS_Type[MAXPARAMS];	// data type
static long NumParams = 0;

Symbol        CS_Name[MAXCONSTS];	// constant symbol table (name)
int           CS_Type[MAXCONSTS];	// data type
char          CS_Value[MAXCONSTS][MAXCONSTSIZE];
static int    CCount = 0;		// # of autogen constants
int           NumConsts = 0;		// total # of constants

static long   LCount = 0;		// label Count


/*--------------------------------------------------------------*/
/* Definition of Keywords, etc */

static char Look = LF;			// lookahead character
static char PrevLook;			// previous 'Look'
static int Token;			// encoded Token       
static char Value[STRTMPSIZE] = {0};	// unencoded Token     


/*--------------------------------------------------------------*/
/* Staging Buffer */

struct sbuff_struct {
  int pcode;
  char s1[MAXLINESIZE];		// allow for #inline
  char s2[MAXSYMSIZE];
  char s3[MAXSYMSIZE];
  int  t1;
  int  t2;
} sbuff[MAXSTAGE];

long int sbufftop = 0;		// top of the staging buffer
long int sbuffopt = 0;		// pcode reduction by optimizer


/*--------------------------------------------------------------*/
/* Register Control */

struct vreg_struct {
  char  inuse;			// reg in use flag
  int allowed;			// data types this reg can be used for
  int datatype;   		// data type being held in reg
  char type;			// type of reg (NORMAL or STACK)
} vreg[MAXREGS];


/*--------------------------------------------------------------*/
/* Return Data Type Description */

char *DataTypeDesc (int dtype)
{
  switch (dtype) {
  case DT_CHAR:
    return "char"; break;
  case DT_UCHAR:
    return "uchar"; break;
  case DT_INT:
    return "int"; break;
  case DT_UINT:
    return "uint"; break;
  case DT_CPTR:
    return "char ptr"; break;
  case DT_IPTR:
    return "int ptr"; break;
  case DT_UCPTR:
    return "uchar ptr"; break;
  case DT_UIPTR:
    return "uint ptr"; break;
  case DT_PROCEDURE:
    return "procedure"; break;
  case DT_INTERRUPT:
    return "interrupt"; break;
  default:
    return "unknown"; break;
  }
}


/*--------------------------------------------------------------*/
/* Return the datatype being held in Reg */

int RegDT (int regnum)
{
  return vreg[regnum].datatype;
}


/*--------------------------------------------------------------*/
/* Dump the symbol tables */

static void dump_symbol()
{
  int tmp,tmp2;
  char STR1[STRTMPSIZE];

  sprintf (STR1,"%%3d %%%ds %%%ds %%d\n",MAXSYMSIZE,TYPEDESCSIZE);
  fprintf (sym,"\nSymbol Table\n\n");
  for (tmp=0; tmp<NumGlobals; tmp++) {
    fprintf (sym,STR1, tmp, GS_Name[tmp], DataTypeDesc(GS_Type[tmp]),
      GS_Size[tmp]);
    if (GS_Type[tmp] == DT_PROCEDURE) {
      for (tmp2=0; tmp2<GS_ParamCount[tmp]; tmp2++)
        fprintf (sym,"#%-3d  %s\n",tmp2,DataTypeDesc(GS_ParamType[tmp][tmp2]));
    }
  }
  fprintf (sym,"\n");

  if (NumLocals>0) {
    fprintf (sym,"\nLocal Table\n\n");
    for (tmp=0; tmp<NumLocals; tmp++)
      fprintf (sym,STR1, tmp, LS_Name[tmp], DataTypeDesc(LS_Type[tmp]),
        LS_Size[tmp]);
    fprintf (sym,"\n");
  }

  if (NumDefs>0) {
    fprintf (sym,"\nDefinition Table\n\n");
    sprintf (STR1,"%%3d %%%ds %%%ds\n",MAXSYMSIZE,MAXSYMSIZE);
    for (tmp=0; tmp<NumDefs; tmp++)
      fprintf (sym,STR1, tmp, DS_Name[tmp], DS_Value[tmp]);
    fprintf (sym,"\n");
  }

  if (NumConsts>0) {
    fprintf (sym,"\nConstants Table\n\n");
    for (tmp=0; tmp<NumConsts; tmp++) {
      fprintf (sym,"#%-3d %s %s\n",tmp,CS_Name[tmp],DataTypeDesc(CS_Type[tmp]));
      tmp2 = 0;
      while (CS_Value[tmp][tmp2]) {
        fprintf(sym,"%c",CS_Value[tmp][tmp2]);
        tmp2++;
      }
      fprintf(sym,"\n");
    }
    fprintf (sym,"\n");
  }

}


/*--------------------------------------------------------------*/
/* Dump the Staging Buffer */

static void dump_pcodes ()
{
  int tmp;
  char STR1[STRTMPSIZE];

  if (sbufftop>0) {
    sprintf (STR1,"%%5d %%3d %%%ds %%%ds %%%ds %%%ds %%%ds\n",
             MAXSYMSIZE,MAXSYMSIZE,MAXSYMSIZE,TYPEDESCSIZE,TYPEDESCSIZE);
    fprintf (pcd,"\nPCODES\n\n");
    for (tmp=0; tmp<sbufftop; tmp++) {
      fprintf (pcd,STR1, tmp, sbuff[tmp].pcode,
               sbuff[tmp].s1,sbuff[tmp].s2,sbuff[tmp].s3,
               DataTypeDesc(sbuff[tmp].t1),DataTypeDesc(sbuff[tmp].t2));
    }
  }
}


/*--------------------------------------------------------------*/
/* Compiler trace routine */

void trace (char porm, char *routine, char *p1, char *p2, char *p3)
{
  int i;
  if (tracing) {
    if (porm == '-') trindent--;
    for (i=0; i<trindent; i++) fprintf (tr," ");
    if (p1[0] == 0)
      fprintf (tr,"%c%s\n",porm,routine);
    else
      if (p2[0] == 0)
        fprintf (tr,"%c%s [%s]\n",porm,routine,p1);
      else
        if (p3[0] == 0)
          fprintf (tr,"%c%s [%s] [%s]\n",porm,routine,p1,p2);
        else
          fprintf (tr,"%c%s [%s] [%s] (%s)\n",porm,routine,p1,p2,p3);

    if (porm == '+') trindent++;
  }
}


/*--------------------------------------------------------------*/
/* Write the pcode to the Staging Buffer */

void EmitLn();
void EmitLn_notab();
void Abort();
void EmitChar(char);


void Stage (int pcode, char *s1, char *s2, char *s3, int t1, int t2)
{
  sbuff[sbufftop].pcode = pcode;
  strcpy (sbuff[sbufftop].s1,s1);
  strcpy (sbuff[sbufftop].s2,s2);
  strcpy (sbuff[sbufftop].s3,s3);
  sbuff[sbufftop].t1 = t1;
  sbuff[sbufftop].t2 = t2;
  sbufftop++;
}


/*--------------------------------------------------------------*/
/* Process the Staging Buffer and generate assembly code */

static void Gen_Code (FILE *thisop)
{
  FILE *saved_op;

  saved_op = op;
  op = thisop;
  long int tmp;
  for (tmp=0; tmp<sbufftop; tmp++) {
    Gen_Item (sbuff[tmp].pcode,sbuff[tmp].s1,sbuff[tmp].s2,sbuff[tmp].s3,
              sbuff[tmp].t1,sbuff[tmp].t2);
  }
  EmitLn("");
  EmitLn("");
  op = saved_op;
}


/*--------------------------------------------------------------*/
/* Trace and Stage a Register Instruction */

static void TraceGenReg (int pcode, char *txt, int regA, int regB, int extra)
{
  char rA[4], rB[4];

  sprintf (rA,"%d",regA);
  sprintf (rB,"%d",regB);
  switch (pcode) {
    case PC_GETREG:
      trace (' ',txt,DataTypeDesc(regB),rA,""); break;
    case PC_FREEREG:
    case PC_CLEAR_REG:
    case PC_NOT_REG:
    case PC_SET_REG_EQ:
    case PC_SET_REG_NE:
    case PC_SET_REG_GT:
    case PC_SET_REG_LT:
    case PC_SET_REG_LE:
    case PC_SET_REG_GE:
      trace (' ',txt,rA,"",""); break;
    case PC_MOVE_REG_I:
      trace (' ',txt,rA,rB,DataTypeDesc(extra));
      Stage (pcode,rA,rB,DataTypeDesc(extra),RegDT(regA),RegDT(regB));
      return;
    case PC_PUSH_REG:
      trace (' ',txt,rA,DataTypeDesc(RegDT(regA)),DataTypeDesc(regB));
      Stage (pcode,rA,"","",RegDT(regA),regB);
      return;
    default:
      trace (' ',txt,rA,rB,""); break;
  }
  if ( (pcode != PC_GETREG && pcode != PC_FREEREG) ||
      ((pcode == PC_GETREG || pcode == PC_FREEREG) && genreguse) )
    Stage (pcode,rA,rB,"",RegDT(regA),RegDT(regB));
}


/*--------------------------------------------------------------*/
/* Grab a free register */

static int GetReg(char datatype)
{
  int tmp;

  for (tmp=0; tmp<MAXREGS; tmp++) if (!vreg[tmp].inuse) {
    if (!(datatype & vreg[tmp].allowed)) continue;
    vreg[tmp].inuse = TRUE;
    vreg[tmp].datatype = datatype;
    TraceGenReg (PC_GETREG,"GetReg",tmp,datatype,0);
    return tmp;
  }

  // not enough available registers to process expression...

  Abort("Stack depth exceeded (Expression too complex)");
}


/*--------------------------------------------------------------*/
/* Free a register */

static void FreeReg(int regnum)
{
  vreg[regnum].inuse = FALSE;
  vreg[regnum].datatype = ' ';
  TraceGenReg (PC_FREEREG,"FreeReg",regnum,0,0);
}


/*--------------------------------------------------------------*/
/* Move a register */

static void MoveReg(int fromreg, int toreg)
{
  TraceGenReg (PC_MOVE_REG,"MoveReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* Add a register */

static void AddReg(int fromreg, int toreg)
{
  TraceGenReg (PC_ADD_REG,"AddReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* Subtract a register */

static void SubReg(int fromreg, int toreg)
{
  TraceGenReg (PC_SUB_REG,"SubReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* Multiply a register */

static void MulReg(int fromreg, int toreg)
{
  TraceGenReg (PC_MUL_REG,"MulReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* Divide a register */

static void DivReg(int fromreg, int toreg)
{
  TraceGenReg (PC_DIV_REG,"DivReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* Compare a register */

static void CompareReg(int fromreg, int toreg)
{
  TraceGenReg (PC_COMPARE_REG,"CompareReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* AND a register */

static void AndReg(int fromreg, int toreg)
{
  TraceGenReg (PC_AND_REG,"AndReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* OR a register */

static void OrReg(int fromreg, int toreg)
{
  TraceGenReg (PC_OR_REG,"OrReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* XOR a register */

static void XorReg(int fromreg, int toreg)
{
  TraceGenReg (PC_XOR_REG,"XorReg",fromreg,toreg,0);
}


/*--------------------------------------------------------------*/
/* Move [thisreg] to thatreg */

static void MoveIReg(int thisreg, int thatreg)
{
  TraceGenReg (PC_MOVE_I_REG,"MoveIReg",thisreg,thatreg,0);
}


/*--------------------------------------------------------------*/
/* Move thisreg to [thatreg] */

static void MoveRegI(int thisreg, int thatreg, int targettype)
{
  TraceGenReg (PC_MOVE_REG_I,"MoveRegI",thisreg,thatreg,targettype);
}


/*---------------------------------------------------------------*/
/* Clear a Register */

static void ClearReg(int thisreg)
{
  TraceGenReg (PC_CLEAR_REG,"ClearReg",thisreg,0,0);
}


/*---------------------------------------------------------------*/
/* NOT (Completement) a Register */

static void NotReg(int thisreg)
{
  TraceGenReg (PC_NOT_REG,"NotReg",thisreg,0,0);
}


/*---------------------------------------------------------------*/
/* Set Reg If Compare was = */

static void SetEqual(int thisreg)
{
  TraceGenReg (PC_SET_REG_EQ,"SetRegEQ",thisreg,0,0);
}


/*---------------------------------------------------------------*/
/* Set Reg If Compare was <> */

static void SetNEqual(int thisreg)
{
  TraceGenReg (PC_SET_REG_NE,"SetRegNE",thisreg,0,0);
}


/*---------------------------------------------------------------*/
/* Set Reg If Compare was > */

static void SetGreater(int thisreg)
{
  TraceGenReg (PC_SET_REG_GT,"SetRegGT",thisreg,0,0);
}


/*---------------------------------------------------------------*/
/* Set Reg If Compare was < */

static void SetLess(int thisreg)
{
  TraceGenReg (PC_SET_REG_LT,"SetRegLT",thisreg,0,0);
}


/*---------------------------------------------------------------*/
/* Set Reg If Compare was <= */

static void SetLessOrEqual(int thisreg)
{
  TraceGenReg (PC_SET_REG_LE,"SetRegLE",thisreg,0,0);
}


/*---------------------------------------------------------------*/
/* Set Reg If Compare was >= */

static void SetGreaterOrEqual(int thisreg)
{
  TraceGenReg (PC_SET_REG_GE,"SetRegGE",thisreg,0,0);
}


/*--------------------------------------------------------------*/
/* Read New character From Input Stream */

void GetcharX()
{
  PrevLook = Look;
  if (ip[slevel] == NULL)
    Look = getchar();
  else
    Look = fgetc(ip[slevel]);
  if (Look == -1) endofallsource = TRUE;
  if (endofallsource && slevel > 0) {
    fclose (ip[slevel]);
    linenumber[slevel] = 0;
    slevel--;
    Look = LF;
    endofallsource = FALSE;
  }
}


/*--------------------------------------------------------------*/
/* Read New character, intercept comment */

void Getchar()
{
  static char Tempchar = ' ';

  if (Tempchar != ' ') {
    Look = Tempchar;
    Tempchar = ' ';
  }
  else {
    GetcharX();
    if (Look == '/') {		// possible comment...
      if (ip[slevel] == NULL)
        Tempchar = getchar();
      else
        Tempchar = fgetc(ip[slevel]);
      if (Tempchar == '*') {	// yes, block comment...
        Look = TOK_BLOCK_COMMENT;
        Tempchar = ' ';
      }
      if (Tempchar == '/') {	// yes, single-line comment...
          Look = TOK_LINE_COMMENT;
          Tempchar = ' ';
      }
    }
  }
}


/*--------------------------------------------------------------*/
/* Report Error and Halt */

void Abort(char *s)
{
  char STR1[STRTMPSIZE];

  interactive = TRUE;
  sprintf(STR1,"Error, line %d of %s : (%s) %s.",
    linenumber[slevel],sname[slevel],Value,s);
  EmitLn_notab (STR1);
  dump_symbol();
  exit(EXIT_FAIL);
}


/*--------------------------------------------------------------*/
/* Report an Warning */

static void Warn(char *s)
{
  char STR1[STRTMPSIZE];
  int tmp;

  wcount++;
  if (!warn) return;
  tmp = interactive;
  interactive = TRUE;
  sprintf(STR1,"Warning, line %d of %s : (%s) %s.",
    linenumber[slevel],sname[slevel],Value,s);
  EmitLn_notab (STR1);
  interactive = tmp;
}


/*--------------------------------------------------------------*/
/* Return the size (in base units) of a standard data type */

static int SizeOfType (int datatype)
{
  switch (datatype) {
  case DT_CHAR:
  case DT_UCHAR:
    return SIZEOFCHAR;
  case DT_INT:
  case DT_UINT:
  case DT_CPTR:
  case DT_IPTR:
  case DT_UCPTR:
  case DT_UIPTR:
    return SIZEOFINT;
  default:
    Abort ("SizeOfType() - Unknown Data Type");
  }
}

/*--------------------------------------------------------------*/
/* Pointer test for a data type (true or false returned) */
static int IsPointer (int datatype)
{
  switch (datatype) {
  case DT_CPTR:
  case DT_IPTR:
  case DT_UCPTR:
  case DT_UIPTR:
    return TRUE;
  default:
    return FALSE;
  }
}


/*--------------------------------------------------------------*/
/* Report What Was Expected */

static void Expected(char *s)
{
  char STR1[STRTMPSIZE];

  sprintf(STR1, "%s expected", s);
  Abort(STR1);
}


/*--------------------------------------------------------------*/
/* Report an Undefined Identifier */

static void Undefined(char *n)
{
  char STR1[STRTMPSIZE];

  sprintf(STR1, "Undefined Identifier '%s'", n);
  Abort(STR1);
}


/*--------------------------------------------------------------*/
/* Report a Duplicate Identifier */

static void Duplicate(char *n)
{
  char STR1[STRTMPSIZE];

  sprintf(STR1, "Duplicate Identifier '%s'", n);
  Abort(STR1);
}


/*--------------------------------------------------------------*/
/* Recognize an Alpha character */

static int IsAlpha(char c)
{
  return isupper(toupper(c));
}


/*--------------------------------------------------------------*/
/* Recognize a Decimal Digit */

static int IsDigit(char c)
{
  return isdigit(c);
}


/*--------------------------------------------------------------*/
/* Recognize a Hex Digit */

static int IsHex(char c)
{
  return ( (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F') );
}


/*--------------------------------------------------------------*/
/* Recognize an AlphaNumeric character (allow underscores) */

static int IsAlNum(char c)
{
  return (IsAlpha(c) | IsDigit(c) | c=='_');
}


/*--------------------------------------------------------------*/
/* Recognize an Addop */

static int IsAddop(char c)
{
  return (c == '-' || c == '+');
}


/*--------------------------------------------------------------*/
/* Recognize a Mulop */

static int IsMulop(char c)
{
  return (c == '/' || c == '*');
}


/*--------------------------------------------------------------*/
/* Recognize a int Orop */

static int IsOrop(char c)
{
  return (c == '~' || c == '|');
}


/*--------------------------------------------------------------*/
/* Recognize a Relop */

static int IsRelop(char c)
{
  return (c == '>' || c == '<' || c == '=' || c == '!');
}


/*--------------------------------------------------------------*/
/* Recognize White Space */

static int IsWhite(char c)
{
  if (c == LF) { linenumber[slevel]++; totallines++; }
  return (c == LF || c == CR || c == TAB || c == ' '
          || c == TOK_BLOCK_COMMENT || c == TOK_LINE_COMMENT);
}


/*--------------------------------------------------------------*/
/* Skip A Block Comment */

static void SkipCommentBlock()
{
  do {
    do {
      GetcharX();
      if (Look == LF) { linenumber[slevel]++; totallines++; }
    } while (Look != '*' && !endofallsource);
    GetcharX();
    if (Look == LF) { linenumber[slevel]++; totallines++; }
  } while (Look != '/' && !endofallsource);
  Getchar();
}


/*--------------------------------------------------------------*/
/* Skip A single-line Comment */

static void SkipCommentLine()
{
  do GetcharX(); while (Look != LF && !endofallsource);
  linenumber[slevel]++;
  totallines++;
  Getchar();
}


/*--------------------------------------------------------------*/
/* Skip Over Leading White Space */

static void SkipWhite()
{
  while (IsWhite(Look)) {
    switch (Look) {
    case TOK_LINE_COMMENT: SkipCommentLine(); break;
    case TOK_BLOCK_COMMENT: SkipCommentBlock(); break;
    default: Getchar(); break;
    }
  }
}


/*--------------------------------------------------------------*/
/* Table Lookup */

static long Lookup(Symbol *T, char *s, long n)
{
  long i;
  int found;

  found = FALSE;
  i = n;
  while (i > 0 && !found) {
    if (!strcmp(s, T[i - 1]))
      found = TRUE;
    else
      i--;
  }
  return i;
}


/*--------------------------------------------------------------*/
/* Locate a Symbol in the Global Table */
/* Returns the index of the entry. Zero if not present. */

static long Locate (char *N)
{
  return (Lookup(GS_Name, N, NumGlobals));
}


/*--------------------------------------------------------------*/
/* Locate a Symbol in the Local Table */
/* Returns the index of the entry. Zero if not present. */

static long IsLocal (char *name)
{
  return (Lookup(LS_Name, name, NumLocals));
}


/*--------------------------------------------------------------*/
/* Locate a Symbol in the Param Table */
/* Returns the index of the entry. Zero if not present. */

static int IsParam (char *name)
{
  return (Lookup(PS_Name, name, NumParams));
}


/*--------------------------------------------------------------*/
/* Locate a Symbol in the Definition Table */
/* Returns the index of the entry. Zero if not present. */

static long IsDef (char *name)
{
  return (Lookup(DS_Name, name, NumDefs));
}


/*--------------------------------------------------------------*/
/* Locate a Symbol in the Constants Table */
/* Returns the index of the entry. Zero if not present. */

int IsConst (char *name)
{
  return (Lookup(CS_Name, name, NumConsts));
}


/*--------------------------------------------------------------*/
/* Test to see if 'name' is already known */

static int IsKnown (char *name)
{
  return (Locate(name) || IsLocal(name) ||
          IsParam(name) || IsConst(name));
}


/*--------------------------------------------------------------*/
/* Return the offset of a local within the stack frame */

static int LocalOffset (char *Name)
{
  int offset = 0,i;
  long element;

  element = Lookup(LS_Name, Name, NumLocals);
  for (i=0; i<element-1; i++)
    offset += (LS_Size[i] * SizeOfType(LS_Type[i]));
  return offset;
}


/*--------------------------------------------------------------*/
/* Return the offset of a param within the stack frame */

static int ParamOffset (char *Name)
{
  int offset = 0,i;
  long element;

  element = Lookup(PS_Name, Name, NumParams);
  for (i=NumParams-1; i>=element-1; i--)
    offset += SizeOfType(PS_Type[i]);
  return offset-1;
}


/*--------------------------------------------------------------*/
/* Look for Symbol in Table */

static int InTable(char *n)
{
  return (Lookup(GS_Name, n, NumGlobals) != 0);
}


/*--------------------------------------------------------------*/
/* Check the Symbol Table for a Duplicate Identifier */
/* Report an error if identifier is already in table. */

static void CheckDup(char *N)
{
  if (InTable(N)) Duplicate(N);
  if (IsConst(N)) Duplicate(N);
}


/*--------------------------------------------------------------*/
/* Add a New Entry to Symbol Table */

static void AddEntry(char *N, int T)
{
  trace ('+',"AddEntry",N,"","");
  CheckDup(N);
  if (NumGlobals == MAXGLOBALS) Abort("Symbol Table Full");
  NumGlobals++;
  strcpy(GS_Name[NumGlobals - 1], N);
  GS_Type[NumGlobals - 1] = T;
  trace ('-',"AddEntry","","","");
}


/*--------------------------------------------------------------*/
/* Add a New Entry to Definition Table */

static void AddDef(char *N, char *V)
{
  trace ('+',"AddDef",N,V,"");
  if (NumDefs == MAXDEFS) Abort("Definition Table Full");
  NumDefs++;
  strcpy(DS_Name[NumDefs - 1], N);
  strcpy(DS_Value[NumDefs - 1], V);
  trace ('-',"AddDef","","","");
}


/*--------------------------------------------------------------*/
/* Get an Identifier */

static void GetName()
{
  trace ('+',"GetName","","","");
  SkipWhite();
  if (!IsAlpha(Look)) Expected("Identifier");
  Token = TOK_IDENTIFIER;
  *Value = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    Getchar();
  } while (IsAlNum(Look));
  trace ('-',"GetName",Value,"","");
}


/*--------------------------------------------------------------*/
/* Get a Number */

static void GetNum()
{
  trace ('+',"GetNum","","","");
  SkipWhite();
  if (!IsDigit(Look)) Expected("Number");
  Token = TOK_NUM;
  *Value = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    Getchar();
  } while (IsDigit(Look));
  trace ('-',"GetNum",Value,"","");
}


/*--------------------------------------------------------------*/
/* Get a Hex Number */

static void GetHexNum()
{
  trace ('+',"GetHexNum","","","");
  SkipWhite();
  Getchar();		// skip the $
  if (!IsHex(Look)) Expected("Hex number");
  Token = TOK_HEX;
  *Value = '$';
  *(Value+1) = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    Getchar();
  } while (IsHex(Look));
  trace ('-',"GetHexNum",Value,"","");
}


/*--------------------------------------------------------------*/
/* Get a Character Literal */

static void GetCharLit()
{
  trace ('+',"GetCharLit","","","");
  SkipWhite();
  Getchar();		// skip the '
  Token = TOK_NUM;
  sprintf(Value,"%d",Look);
  Getchar();		// skip the '
  if (Look != '\'') Expected ("Character literal");
  Getchar();
  trace ('-',"GetCharLit",Value,"","");
}


/*--------------------------------------------------------------*/
/* Get a Directive */

static void GetDirective()
{
  int tracesave;

  trace ('+',"GetDirective","","","");
  if (PrevLook != LF) Expected("Directive at start of line");
  SkipWhite();
  Token = TOK_DIRECTIVE;
  *Value = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    Getchar();
  } while (IsAlpha(Look));
  Scan();

  trace ('-',"GetDirective",Value,"","");
}


/*--------------------------------------------------------------*/
/* Get an Operator */

static void GetOp()
{
  trace ('+',"GetOp","","","");
  SkipWhite();
  Token = Look;
  sprintf(Value, "%c", Look);
  Getchar();
  trace ('-',"GetOp",Value,"","");
}


/*--------------------------------------------------------------*/
/* Process a #define directive */

static void DoDefine()
{
  char STR1[STRTMPSIZE], STR2[STRTMPSIZE];

  trace ('+',"DoDefine","","","");
  GetName();
  if (IsDef(Value)) Abort("Already defined");
  strcpy(STR1,Value);
  Next();
  strcpy(STR2,Value);
  AddDef(STR1,STR2);
  Next();
  Scan();
  trace ('-',"DoDefine","","","");
}


/*--------------------------------------------------------------*/
/* Process a #inline directive */

static void DoInline()
{
  SkipWhite();
  if (Look != '\"') Expected(" \" ");
  GetcharX();
  *Value = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    GetcharX();
  } while (Look != '\"' && !endofallsource);
  Stage (PC_INLINE,Value,"","",0,0);
  trace (' ',"Inline",Value,"","");
  Getchar();
  Next();
  Scan();
}


/*--------------------------------------------------------------*/
/* Translate to definition, if required */

static void translate_def ()
{
  if (IsDef(Value)) {
    trace (' ',"translate_def","","","");
    strcpy(Value,DS_Value[IsDef(Value)-1]);
    Token=Value[0];
    if (IsAlpha(Token)) Token = TOK_IDENTIFIER;
    if (IsDigit(Token)) Token = TOK_NUM;
    if (Token == '$') Token = TOK_HEX;
    if (Token == '\'') Token = TOK_NUM;
  }
}


/*--------------------------------------------------------------*/
/* Get the Next Input Token */

static void Next()
{
  trace ('+',"Next","","","");
  SkipWhite();

  if (Look == '#') {		// a directive
    GetDirective();
    trace ('-',"Next#","","","");
    return;
  }

  if (Look == '\'') {		// a character literal
    GetCharLit();
    trace ('-',"NextC","","","");
    return;
  }

  if (IsAlpha(Look)) {		// a name
    GetName();
    translate_def();
    trace ('-',"NextN","","","");
    return;
  }

  if (IsDigit(Look))		// a number
    GetNum();
  else
    if (Look == '$')		// a hex number
      GetHexNum();
    else
      GetOp();			// an operator
  trace ('-',"Next","","","");
}


/*--------------------------------------------------------------*/
/* Scan the Current Identifier for Keywords */

static void Scan()
{
  trace ('+',"Scan",Value,"","");
  if (Token == TOK_IDENTIFIER || Token == TOK_DIRECTIVE)
    Token = KWcode[Lookup(KWlist, Value, (long)NKW)];
  trace ('-',"Scan","","","");
}


/*--------------------------------------------------------------*/
/* Match a Specific Input String */

static void MatchString(char *x)
{
  char STR2[STRTMPSIZE];

  if (strcmp(Value, x)) {
    sprintf(STR2, "'%s'", x);
    Expected(STR2);
  }
  Next();
}


/*--------------------------------------------------------------*/
/* Skip semicolons, if present */

static void Semi()
{
  if (Token == ';') Next();
}


/*--------------------------------------------------------------*/
/* Output a Char */

void EmitChar(char s)
{
  if (interactive) printf("%c", s);
  if (op != NULL) fprintf(op,"%c", s);
}


/*--------------------------------------------------------------*/
/* Output a String with Tab */

static void Emit(char *s)
{
  if (interactive) printf("%c%s", TAB, s);
  if (op != NULL) fprintf(op,"%c%s", TAB, s);
}


/*--------------------------------------------------------------*/
/* Output a String without Tab */

static void Emit_notab(char *s)
{
  if (interactive) printf("%s", s);
  if (op != NULL) fprintf(op,"%s", s);
}


/*--------------------------------------------------------------*/
/* Output a String with Tab and CRLF */

void EmitLn(char *s)
{
  Emit(s);
  if (interactive) putchar('\n');
  if (op != NULL) fprintf (op,"\n");
}


/*--------------------------------------------------------------*/
/* Output a String with CRLF, but no TAB */

void EmitLn_notab(char *s)
{
  Emit_notab(s);
  if (interactive) putchar('\n');
  if (op != NULL) fprintf (op,"\n");
}


/*--------------------------------------------------------------*/
/* Generate a Unique Label */

static char *NewLabel(char *Result)
{
  char S[STRTMPSIZE];

  sprintf(S, "%ld", LCount);
  sprintf(Result, "L%s", S);
  LCount++;
  return Result;
}


/*--------------------------------------------------------------*/
/* Post a Label To Output */

static void PostLabel(char *L)
{
  Stage (PC_LABEL,L,"","",0,0);
}


/*---------------------------------------------------------------*/
/* Load a Constant Value to a Register */

static int LoadConst(char *n, int dt)
{
  char cha[4], STR1[STRTMPSIZE];
  int tmp, thisreg, datatype, newdt;

  sprintf (STR1,"%s",DataTypeDesc(dt));
  trace ('+',"LoadConst",n,STR1,"");
  if (*n == '$') {
    strcpy (STR1,"0x");
    strcat (STR1,n+1);
    tmp = strtol(STR1, (char **)NULL, 16);
  } else {
    tmp = atoi(n);
  }
  datatype = DT_INT;
  if (tmp >= -32767 && tmp <= 32767) datatype = DT_INT;
  if (tmp >= 0 && tmp < 256) datatype = DT_UCHAR;
  if (tmp >= -127 && tmp <= 127) datatype = DT_CHAR;
  newdt = datatype;
  if (dt == DT_CPTR || dt == DT_UCPTR) newdt = DT_INT;
  if ((newdt == DT_CHAR || newdt == DT_UCHAR) && dt == DT_INT) newdt = DT_INT;
  if ((newdt == DT_CHAR || newdt == DT_UCHAR) && dt == DT_UINT) newdt = DT_UINT;
  sprintf (STR1,"%s",DataTypeDesc(newdt));
  trace (' ',"LoadConst",STR1,"","");
  thisreg = GetReg(newdt);
  sprintf (cha,"%d",thisreg);
  Stage (PC_MOVE_CONST_REG,n,cha,"",0,RegDT(thisreg));
  trace ('-',"LoadConst","","","");
  return thisreg;
}


/*--------------------------------------------------------------*/
/* Initialize Parameter Table to Null */

static void ClearParams()
{
  int i;

  trace (' ',"ClearParams","","","");
  for (i=0; i<MAXPARAMS; i++) *PS_Name[i] = 0;
  NumParams = 0;
}


/*--------------------------------------------------------------*/
/* Add a Parameter to Table */

static void AddParam (char *name, int ptype)
{
  trace (' ',"AddParam",name,"","");
  if (IsParam(name)) Duplicate(name);
  strcpy(PS_Name[NumParams],name);
  PS_Type[NumParams] = ptype;
  NumParams++;
}


/*---------------------------------------------------------------*/
/* Return the scope of a variable */

static int WhatIs(char *Name)
{
  if (IsParam(Name)) return PARAM;
  if (IsLocal(Name)) return LOCAL;
  if (InTable(Name)) return GLOBAL;
  return UNKNOWN;
}


/*---------------------------------------------------------------*/
/* Return the data type of a variable */

static int DataType(char *Name)
{
  switch (WhatIs(Name)) {
  case PARAM:
    return PS_Type[IsParam(Name)-1];
    break;
  case LOCAL:
    return LS_Type[IsLocal(Name)-1];
    break;
  case GLOBAL:
    return GS_Type[Locate(Name)-1];
    break;
  case UNKNOWN:
    Abort("Unknown data type");
  }
}


/*---------------------------------------------------------------*/
/* Load a Variable to Register */

static int LoadVar(char *Name, int dt)
{
  int offset, thisreg, newdt;
  char STR1[STRTMPSIZE],STR2[STRTMPSIZE];

  trace ('+',"LoadVar",Name,"","");
  newdt = DataType(Name);
  if (newdt == DT_PROCEDURE) Abort("Can not assign to/from a procedure");
  if (newdt == DT_INTERRUPT) Abort("Can not assign to/from an interrupt");
  if ((newdt == DT_CHAR || newdt == DT_UCHAR) && dt == DT_INT) newdt = DT_INT;
  if ((newdt == DT_CHAR || newdt == DT_UCHAR) && dt == DT_UINT) newdt = DT_UINT;

  switch (WhatIs(Name)) {
  case PARAM:
    offset = ParamOffset(Name);
    sprintf(STR1,"%d",offset);
    thisreg = GetReg(newdt);
    sprintf(STR2,"%d",thisreg);
    Stage (PC_MOVE_PVAR_REG,Name,STR1,STR2,DataType(Name),RegDT(thisreg));
    break;
  case LOCAL:
    offset = LocalOffset(Name);
    sprintf(STR1,"%d",offset);
    thisreg = GetReg(newdt);
    sprintf(STR2,"%d",thisreg);
    Stage (PC_MOVE_LVAR_REG,Name,STR1,STR2,DataType(Name),RegDT(thisreg));
    break;
  case GLOBAL:
    thisreg = GetReg(newdt);
    sprintf(STR2,"%d",thisreg);
    Stage (PC_MOVE_VAR_REG,Name,STR2,"",DataType(Name),RegDT(thisreg));
    break;
  case UNKNOWN:
    Undefined(Name);
  default:
    Abort("Unsupported LoadVar mechanism");
  }
  trace ('-',"LoadVar","","","");
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Store Register to Variable */

static void Store(int thisreg, char *Name)
{
  int offset;
  char STR1[STRTMPSIZE],STR2[STRTMPSIZE];

  trace ('+',"Store",Name,"","");
  if (DataType(Name) == DT_PROCEDURE)
    Abort("Can not assign to/from a procedure");
  if (DataType(Name) == DT_INTERRUPT)
    Abort("Can not assign to/from an interrupt");
  sprintf(STR2,"%d",thisreg);
  switch (WhatIs(Name)) {
  case PARAM:
    offset = ParamOffset(Name);
    sprintf(STR1,"%d",offset);
    Stage (PC_MOVE_REG_PVAR,STR2,Name,STR1,RegDT(thisreg),DataType(Name));
    break;
  case LOCAL:
    offset = LocalOffset(Name);
    sprintf(STR1,"%d",offset);
    Stage (PC_MOVE_REG_LVAR,STR2,Name,STR1,RegDT(thisreg),DataType(Name));
    break;
  case GLOBAL:
    Stage (PC_MOVE_REG_VAR,STR2,Name,"",RegDT(thisreg),DataType(Name));
    break;
  case UNKNOWN:
    Undefined(Name);
  default:
    Abort("Unsupported Store mechanism");
  }
  FreeReg(thisreg);
  trace ('-',"Store",Name,"","");
}


/*---------------------------------------------------------------*/
/* Branch Unconditional  */

static void Branch(char *L)
{
  trace (' ',"Branch",L,"","");
  Stage (PC_BRANCH,L,"","",0,0);
}


/*---------------------------------------------------------------*/
/* Branch False */

static void BranchFalse(char *L, int thisreg)
{
  char cha[4];

  sprintf (cha,"%d",thisreg);
  trace (' ',"BranchFalse",L,cha,"");
  Stage (PC_BRANCH_FALSE,L,cha,"",RegDT(thisreg),0);
}


/*--------------------------------------------------------------*/
/* Write the Epilog */

static void Epilog(char *name, int isinterrupt)
{
  char STR1[STRTMPSIZE];

  sprintf (STR1,"%d",TotLocSize);
  if (TotLocSize) Stage (PC_SHRINKSTACK,STR1,"Epilog - locals","",0,0);
  if (isinterrupt) Stage (PC_POPFP,"","","",0,0);
  if (isinterrupt)
    Stage(PC_INTRET,"","","",0,0);
  else
    Stage(PC_CALLRET,"","","",0,0);
  Stage (PC_EPILOG,name,"","",0,0);
}


/*--------------------------------------------------------------*/
/* Allocate Storage for a static Variable */

static void Allocate(char *Name, char *Val)
{
  if (section != PC_DATASEC) {
    section = PC_DATASEC;
    Stage (PC_DATASEC,"","","",0,0);
  }
  Stage (PC_ALLOCATE,Name,Val,"",0,0);
}


/*--------------------------------------------------------------*/
/* Move address of var to Register */

static int LoadAVR (char *varname)
{
  int thisreg;
  char cha[4], coffset[6];

  thisreg = GetReg(DT_INT);
  sprintf (cha,"%d",thisreg);

  switch (WhatIs(varname)) {
  case PARAM:
    sprintf(coffset,"%d",ParamOffset(varname));
    Stage (PC_MOVE_AP_REG,varname,cha,coffset,DT_INT,RegDT(thisreg));
    break;
  case LOCAL:
    sprintf(coffset,"%d",LocalOffset(varname));
    Stage (PC_MOVE_AL_REG,varname,cha,coffset,DT_INT,RegDT(thisreg));
    break;
  case GLOBAL:
    Stage (PC_MOVE_A_REG,varname,cha,"",DT_INT,RegDT(thisreg));
    break;
  case UNKNOWN:
    Undefined(varname);
  }
  return thisreg;
}


/*--------------------------------------------------------------*/
/* Adjust Register according to variable's data type size */

static void AdjustReg(int thisreg, char *varname)
{
  int dsize;
  char STR1[STRTMPSIZE],STR2[STRTMPSIZE];

  trace ('+',"AdjustReg",varname,"","");
  switch (WhatIs(varname)) {
  case PARAM:
    dsize = SizeOfType(PS_Type[IsParam(varname)-1]);
    break;
  case LOCAL:
    dsize = SizeOfType(LS_Type[IsLocal(varname)-1]);
    break;
  case GLOBAL:
    dsize = SizeOfType(GS_Type[Locate(varname)-1]);
    break;
  case UNKNOWN:
    Undefined(varname);
  }
  if (dsize != 1) {
    sprintf(STR1,"%d",dsize);
    sprintf(STR2,"%d",thisreg);
    Stage (PC_ADJUST_REG,STR2,varname,STR1,RegDT(thisreg),0);
  }
  trace ('-',"AdjustReg","","","");
}


/*--------------------------------------------------------------*/
/* Parse and Translate a Math Factor */

static int Factor(int dt)
{
  char SavedValue[STRTMPSIZE];
  int SavedToken;
  int thisreg, thatreg, dt2;

  trace ('+',"Factor",Value,"","");
  if (Token == '(') {
    Next();
    thisreg = BoolExpression(dt);
    MatchString(")");
    trace ('-',"Factor","","","");
    return thisreg;
  }
  strcpy(SavedValue,Value);
  SavedToken=Token;
  Next();

  switch (SavedToken) {
  case TOK_IDENTIFIER:
    if (Token == '[') {			// an array element...
      Next();
      thisreg = Expression(DT_INT);	// evaluate element
      MatchString("]");
      thatreg = LoadAVR(SavedValue);	// load address of array
      AdjustReg(thisreg,SavedValue);	// adjust element by array's data type
      AddReg(thatreg,thisreg);		// add element to address
      MoveIReg(thisreg,thatreg);	// move [thisreg] to thatreg
      FreeReg(thisreg);
      trace ('-',"Factor[]","","","");
      return thatreg;
    }
    else
      thisreg = LoadVar(SavedValue,dt);
    break;
  case TOK_NUM:
  case TOK_HEX:
    thisreg = LoadConst(SavedValue,dt);
    break;
  case OP_PTR:
    if (Token == TOK_IDENTIFIER) {
      if (!IsPointer(DataType(Value))) Expected ("Pointer Data Type");
      thatreg=LoadVar(Value,dt);	// load var to thatreg
      dt2 = DataType(Value);
      if (dt2 == DT_CPTR) dt2 = DT_CHAR;
      if (dt2 == DT_UCPTR) dt2 = DT_UCHAR;
      if (dt2 == DT_IPTR) dt2 = DT_INT;
      if (dt2 == DT_UIPTR) dt2 = DT_UINT;
      thisreg=GetReg(dt2);		// grab a register matching this datatype
    }
    else {
      thatreg=LoadConst(Value,0);	// load const to thatreg
      thisreg=GetReg(DT_DEFAULT);	// grab a register matching this datatype
    }
    MoveIReg(thatreg,thisreg);		// move [thatreg] to thisreg
    FreeReg(thatreg);
    Next();
    break;
  case OP_ADDR:				// address-of operator
    switch (Token) {
    case TOK_IDENTIFIER:
      strcpy(SavedValue,Value);
      SavedToken=Token;
      Next();
      if (Token == '[') {		// an array element...
        Next();
        thisreg=Expression(DT_INT);	// evaluate element
        MatchString("]");
        thatreg=LoadAVR(SavedValue);	// load address of array
        AdjustReg(thisreg,SavedValue);	// adjust by array's data type
        AddReg(thatreg,thisreg);	// add address to adjusted element
        FreeReg(thatreg);
        trace ('-',"Factor&[]","","","");
        return thisreg;
      }
      else
        thisreg = LoadAVR(SavedValue);
      trace ('-',"Factor&","","","");
      return thisreg;

    default:  
      Expected("Identifier after Address operator");
      break;
    }
  default:
    Expected("Math Factor");
    break;
  }

  trace ('-',"Factor.","","","");
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Multiply */

static int Multiply(int nextreg)
{
  int thisreg, newdt;

  trace ('+',"Multiply","","","");
  Next();
  if (OP_PTR == '*') {		// C-style pointer op in use ?...
    if (IsParam(Value) || InTable(Value) || IsLocal(Value)) {
      if (IsPointer(DataType(Value)))
        Warn ("Possible pointer misuse or missing ';'");
    }
  }
  newdt = vreg[nextreg].datatype;
  if (newdt == DT_CHAR) newdt = DT_INT;
  if (newdt == DT_UCHAR) newdt = DT_UINT;
  thisreg = Factor(newdt);
  MulReg (nextreg,thisreg);	//  PopMul();
  FreeReg (nextreg);
  trace ('-',"Multiply","","","");
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Divide */

static int Divide(int nextreg)
{
  int thisreg;

  trace ('+',"Divide","","","");
  Next();
  if (OP_PTR == '*') {		// C-style pointer op in use ?...
    if (IsParam(Value) || InTable(Value) || IsLocal(Value)) {
      if (IsPointer(DataType(Value)))
        Warn ("Possible pointer misuse");
    }
  }
  thisreg = Factor(vreg[nextreg].datatype);
  DivReg (nextreg,thisreg);	//  PopDiv();
  FreeReg (nextreg);
  trace ('-',"Divide","","","");
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Parse and Translate a Math Term */

static int Term(int dt)
{
  int thisreg, nextreg = -1;
  int savedtype;

  trace ('+',"Term","","","");
  thisreg = Factor(dt);
  savedtype = vreg[thisreg].datatype;

  while (IsMulop(Token)) {
    if (nextreg = -1)
      nextreg = thisreg;
    else {
      nextreg = GetReg(savedtype);	//    Push();
      MoveReg (thisreg,nextreg);
      FreeReg (thisreg);
    }
    switch (Token) {
    case '*':
      thisreg = Multiply(nextreg);
      break;
    case '/':
      thisreg = Divide(nextreg);
      break;
    }
  }
  trace ('-',"Term","","","");
  return thisreg;
}


/*--------------------------------------------------------------*/
/* Recognize and Translate an Add */

static int Add(int nextreg)
{
  int thisreg,newdt;

  trace ('+',"Add","","","");
  Next();
  newdt = vreg[nextreg].datatype;
  if (newdt == DT_CHAR) newdt = DT_INT;
  if (newdt == DT_UCHAR) newdt = DT_UINT;
  thisreg = Term(newdt);
  AddReg (nextreg,thisreg);		//  PopAdd();
  FreeReg (nextreg);
  trace ('-',"Add","","","");
  return thisreg;
}


/*-------------------------------------------------------------*/
/* Recognize and Translate a Subtract */

static int Subtract(int nextreg)
{
  int thisreg,newdt;

  trace ('+',"Subtract","","","");
  Next();
  newdt = vreg[nextreg].datatype;
  if (newdt == DT_CHAR) newdt = DT_INT;
  if (newdt == DT_UCHAR) newdt = DT_UINT;
  thisreg = Term(newdt);
  SubReg (thisreg,nextreg);		//  PopSub();
  FreeReg (thisreg);
  trace ('-',"Subtract","","","");
  return nextreg;
}


/*--------------------------------------------------------------*/
/* Parse and Translate an Expression */

static int Expression(int dt)
{
  int thisreg,nextreg = -1;
  int savedtype;

  trace ('+',"Expression","","","");
  if (IsAddop(Token)) {
    thisreg = GetReg(dt);
    ClearReg(thisreg);			//    Clear();
  }
  else
    thisreg = Term(dt);
  savedtype = vreg[thisreg].datatype;

  trace (' ',"Expression","","","");
  while (IsAddop(Token)) {

    if (nextreg = -1)
      nextreg = thisreg;
    else {
      nextreg = GetReg(savedtype);	//    Push();
      MoveReg (thisreg,nextreg);
      FreeReg (thisreg);
    }
    switch (Token) {
    case '+':
      thisreg = Add(nextreg);
      break;
    case '-':
      thisreg = Subtract(nextreg);
      break;
    }
  }

  trace ('-',"Expression","","","");
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Get Another Expression and Compare */

static int CompareExpression(int nextreg)
{
  int thisreg;

  thisreg = Expression(0);
  CompareReg (nextreg,thisreg);		//  PopCompare();
  FreeReg (nextreg);
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Get The Next Expression and Compare */

static int NextExpression(int nextreg)
{
  Next();
  return CompareExpression(nextreg);
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Equals" */

static int Equal(int nextreg)
{
  int thisreg;

  thisreg = NextExpression(nextreg);
  SetEqual(thisreg);
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Less Than or Equal" */

static int LessOrEqual(int nextreg)
{
  int thisreg;

  thisreg = NextExpression(nextreg);
  SetLessOrEqual(thisreg);
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Not Equals" */

static int NotEqual(int nextreg)
{
  int thisreg;

  thisreg = NextExpression(nextreg);
  SetNEqual(thisreg);
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Less Than" */

static int Less(int nextreg)
{
  int thisreg;

  Next();
  switch (Token) {
  case '=':
    thisreg = LessOrEqual(nextreg);
    break;
  case '>':
    thisreg = NotEqual(nextreg);
    break;
  case '<':
    Next();
    thisreg = Expression(0);
    TraceGenReg (PC_SHIFTLEFT,"ShiftLeft",nextreg,thisreg,0);
    FreeReg(thisreg);
    return nextreg;
  default:
    thisreg = CompareExpression(nextreg);
    SetLess(thisreg);
    break;
  }
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a Relational "Greater Than" */

static int Greater(int nextreg)
{
  int thisreg;

  Next();
  switch (Token) {
  case '=':
    thisreg = NextExpression(nextreg);
    SetGreaterOrEqual(thisreg);
    break;
  case '>':
    Next();
    thisreg = Expression(0);
    TraceGenReg (PC_SHIFTRIGHT,"ShiftRight",nextreg,thisreg,0);
    FreeReg(thisreg);
    return nextreg;
  default:
    thisreg = CompareExpression(nextreg);
    SetGreater(thisreg);
    break;
  }
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Recognize and Translate a possible Relational "Not Equal" */

static int NEqual(int nextreg)
{
  int thisreg;

  Next();
  if (Token == '=')
    thisreg = NotEqual(nextreg);
  else
    Expected ("'!='");
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Parse and Translate a Relation */


static int Relation(int dt)
{
  int thisreg, nextreg;

  trace (' ',"Relation","","","");
  thisreg = Expression(dt);
  if (!IsRelop(Token)) return thisreg;
  trace (' ',"Relation2","","","");

//  Push();

  switch (Token) {
  case '=':
    nextreg = Equal(thisreg);
    break;
  case '<':
    nextreg = Less(thisreg);
    break;
  case '>':
    nextreg = Greater(thisreg);
    break;
  case '!':
    nextreg = NEqual(thisreg);
    break;
  }
  trace (' ',"Relation3","","","");
  return nextreg;
}


/*---------------------------------------------------------------*/
/* Parse and Translate a int Factor with Leading NOT */

static int NotFactor(int dt)
{
  int thisreg;

  trace ('+',"NotFactor","","","");
  if (Token != '!') {
    thisreg = Relation(dt);
    trace ('-',"NotFactor","","","");
    return thisreg;
  }
  Next();
  thisreg = Relation(dt);
  NotReg(thisreg);
  trace ('-',"NotFactor.","","","");
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Parse and Translate a int Term */

static int BoolTerm(int dt)
{
  int thisreg, nextreg = -1;
  int savedtype;

  trace ('+',"BoolTerm","","","");
  thisreg = NotFactor(dt);
  savedtype = vreg[thisreg].datatype;

  while (Token == '&') {
    if (nextreg == -1)
      nextreg = thisreg;
    else {
      nextreg = GetReg(savedtype);		//    Push();
      MoveReg (thisreg,nextreg);
      FreeReg (thisreg);
    }
    Next();
    thisreg = NotFactor(dt);			//    PopAnd();
    AndReg (nextreg,thisreg);
    FreeReg (nextreg);
  }
  trace ('-',"BoolTerm","","","");
  return thisreg;
}


/*--------------------------------------------------------------*/
/* Recognize and Translate a int OR */

static int BoolOr(int nextreg)
{
  int thisreg, newdt;

  Next();
  newdt = vreg[nextreg].datatype;
  thisreg = BoolTerm(newdt);			//  PopOr();
  OrReg (nextreg,thisreg);
  FreeReg (nextreg);
  return thisreg;
}


/*--------------------------------------------------------------*/
/* Recognize and Translate an Exclusive Or */

static int BoolXor(int nextreg)
{
  int thisreg, newdt;

  Next();
  newdt = vreg[nextreg].datatype;
  thisreg = BoolTerm(newdt);			//  PopXor();
  XorReg (nextreg,thisreg);
  FreeReg (nextreg);
  return thisreg;
}


/*---------------------------------------------------------------*/
/* Parse and Translate a int Expression */

static int BoolExpression(int dt)
{
  int thisreg, nextreg = -1;
  int savedtype;

  trace ('+',"BoolExpression","","","");
  thisreg = BoolTerm(dt);
  savedtype = vreg[thisreg].datatype;

  while (IsOrop(Token)) {

    if (nextreg = -1)
      nextreg = thisreg;
    else {
      nextreg = GetReg(savedtype);		//    Push();
      MoveReg (thisreg,nextreg);
      FreeReg (thisreg);
    }
    switch (Token) {
    case '|':
      thisreg = BoolOr(nextreg);
      break;
    case '~':
      thisreg = BoolXor(nextreg);
      break;
    }
  }

  trace ('-',"BoolExpression","","","");
  return thisreg;
}


/*--------------------------------------------------------------*/
/* Push a Register */

static void PushReg (int regnum, int dt)
{
  TraceGenReg (PC_PUSH_REG,"PushReg",regnum,dt,0);
}


/*--------------------------------------------------------------*/
/* Process a String Constant */

static int StringConst ()
{
  char LitName[MAXSYMSIZE], LitVal[MAXCONSTSIZE], cha[4];
  int thisreg, isnull = FALSE;

  trace ('+',"StringConst","","","");
  if (NumConsts >= MAXCONSTS) Abort ("Constants Table is full");
  sprintf(LitName,"CS%d",CCount);
  if (IsKnown(LitName)) Duplicate(LitName);
  *Value = '\0';
  if (Look == '"')	// a null string?...
    isnull = TRUE;
  else {
    do {
      sprintf(Value + strlen(Value), "%c", Look);
      GetcharX();
    } while (Look != '"' && !endofallsource);
    strcpy(CS_Value[NumConsts],Value);
  }
  strcpy(CS_Name[NumConsts],LitName);
  if (isnull) {
    CS_Type[NumConsts] = DT_INT;
    strcpy(CS_Value[NumConsts],"0");
    }
  else {
    CS_Type[NumConsts] = DT_CHAR;
  }
  Next();		// skip the end "
  NumConsts++;
  CCount++;
  thisreg = GetReg(DT_INT);
  sprintf(cha,"%d",thisreg);
  Stage (PC_MOVE_A_REG,LitName,cha,"",0,RegDT(thisreg));

  Next();
  trace ('-',"StringConst","","","");
  return thisreg;
}


/*--------------------------------------------------------------*/
/* Call a procedure */

static void DoCall(char *procname)
{
  int protocount = 0, acount = 0, procpos, dt, resreg;
  int pspace = 0;
  char STR1[STRTMPSIZE];

  trace ('+',"DoCall","","","");
  procpos = Locate(procname)-1;
  protocount = GS_ParamCount[procpos];
  Next();
  MatchString("(");
  while (Token != ')') {
    if (acount == protocount) Abort("Too many parameters");
    dt = GS_ParamType[procpos][acount];
    pspace += SizeOfType(dt);
    if (Token == '"')
      resreg = StringConst();
    else
      resreg = Expression(dt);
    PushReg(resreg,dt);
    FreeReg(resreg);
    acount++;
    Scan();
    if (Token == ',') { Next(); Scan(); }
  } // while (Token != ')')

  if (protocount != acount) Abort("Too few parameters");
  MatchString(")");
  Stage (PC_PUSHFP,"","","",0,0);
  Stage (PC_DOCALL,procname,"","",0,0);
  Scan();
  Stage (PC_POPFP,"","","",0,0);
  if (pspace) {
    sprintf (STR1,"%d",pspace);
    Stage (PC_SHRINKSTACK,STR1,"remove params","",0,0);
  }
  trace ('-',"DoCall","","","");
}


/*--------------------------------------------------------------*/
/* Parse and Translate an Assignment Statement */

static void Assignment()
{
  char Name[STRTMPSIZE];
  int SavedToken;
  int thisreg,thatreg = -1, dt;

  trace ('+',"Assignment",Value,"","");

  if (Token == OP_PTR) {	// a pointer assignment...
    dt=DT_DEFAULT;
    Next();
    SavedToken = Token;
    switch (Token) {
    case TOK_IDENTIFIER:	// e.g. *name
      dt=DataType(Value);
      if (!IsParam(Value) && !InTable(Value) && !IsLocal(Value))
        Undefined(Value);
      if (!IsPointer(dt)) Expected ("Pointer Data Type");
    case TOK_HEX:		// e.g. *$FF00
      strcpy(Name, Value);
      Next();
      MatchString("=");
      if (SavedToken == TOK_IDENTIFIER) {
        if (dt == DT_CPTR) dt = DT_CHAR;
        if (dt == DT_UCPTR) dt = DT_UCHAR;
        if (dt == DT_IPTR) dt = DT_INT;
        if (dt == DT_UIPTR) dt = DT_UINT;
      }
      thisreg=BoolExpression(dt);	// evaluate RHS
      if (SavedToken == TOK_IDENTIFIER)
        thatreg=LoadVar(Name,dt);
      if (SavedToken == TOK_HEX)
        thatreg=LoadConst(Name,dt);
      MoveRegI(thisreg,thatreg,dt);	// move RHS to [LHS]
      FreeReg(thisreg);
      FreeReg(thatreg);

      trace ('-',"Assignment*","","","");
      return;
    default:
      Abort("Unsupported pointer use");
    }
  }

  if (!IsParam(Value) && !InTable(Value) && !IsLocal(Value))
    Undefined(Value);
  strcpy(Name, Value);
  dt=DataType(Name);
  if (dt == DT_INTERRUPT) Abort ("Invalid interrupt use");
  if (dt == DT_PROCEDURE)	// a procedure...
    DoCall(Name);
  else {			// a non-pointer assignment...
    Next();
    if (Token == '[') {		// an array element...
      Next();
      thisreg=Expression(DT_INT);	// evaluate element
      MatchString("]");		
      thatreg=LoadAVR(Name);	// load address of array
      AdjustReg(thisreg,Name);	// adjust element by array's data type
      AddReg(thatreg,thisreg);	// sum
      FreeReg(thatreg);
      MatchString("=");
      thatreg=BoolExpression(dt);	// evaluate RHS
      MoveRegI(thatreg,thisreg,dt);	// move RHS to [sum]
      FreeReg(thisreg);
      FreeReg(thatreg);
      }
    else {
      MatchString("=");
      thisreg = BoolExpression(dt);
      Store(thisreg,Name);
    }
  }
  trace ('-',"Assignment","","","");
}


/*--------------------------------------------------------------*/
/* Recognize and Translate an IF Construct */

static void DoIf()
{
  char L1[STRTMPSIZE], L2[STRTMPSIZE];
  int thisreg;

  trace ('+',"DoIf","","","");
  Next();
  MatchString("(");
  thisreg = BoolExpression(0);
  MatchString(")");
  NewLabel(L1);
  strcpy(L2, L1);
  BranchFalse(L1,thisreg);
  FreeReg(thisreg);
  Block();
  if (Token == TOK_ELSE) {
    Next();
    NewLabel(L2);
    Branch(L2);
    PostLabel(L1);
    Block();
  }
  PostLabel(L2);
  MatchString("endif");
  trace ('-',"DoIf","","","");
}


/*--------------------------------------------------------------*/
/* Parse and Translate a WHILE Statement */

static void DoWhile()
{
  char L1[STRTMPSIZE], L2[STRTMPSIZE];
  int thisreg;

  trace ('+',"DoWhile","","","");
  Next();
  MatchString("(");
  NewLabel(L1);
  NewLabel(L2);
  PostLabel(L1);
  thisreg = BoolExpression(0);
  MatchString(")");
  BranchFalse(L2,thisreg);
  FreeReg(thisreg);
  Block();
  MatchString("endwhile");
  Branch(L1);
  PostLabel(L2);
  trace ('-',"DoWhile","","","");
}


/*--------------------------------------------------------------*/
/* Parse and Translate a FOR Statement */

static void DoFor()
{
  char L1[STRTMPSIZE], L2[STRTMPSIZE], L3[STRTMPSIZE], L4[STRTMPSIZE];
  int thisreg;

  trace ('+',"DoFor","","","");
  Next();
  MatchString("(");
  NewLabel(L1);
  NewLabel(L2);
  NewLabel(L3);
  NewLabel(L4);
  Assignment();			// initial assignment
  Semi();
  PostLabel(L1);
  thisreg = BoolExpression(0);	// evaluate the condition
  Semi();
  BranchFalse(L2,thisreg);	// skip out if condition false
  FreeReg(thisreg);
  Branch(L3);			// jump to do the block
  PostLabel(L4);
  Assignment();			// end-of-block assignment
  Semi();
  MatchString(")");
  Branch(L1);			// jump to test the condition
  PostLabel(L3);
  Block();			// do the block
  MatchString("endfor");
  Branch(L4);			// jump to do end-of-block assignment
  PostLabel(L2);
  FreeReg(thisreg);
  trace ('-',"DoFor","","","");
}


/*--------------------------------------------------------------*/
/* Skip source until we have 1 less #endif */

static void IfDefSkip()
{
  int newifdef;

  newifdef = ifdeflevel - 1;
  trace ('+',"IfDefSkip","","","");
  while (ifdeflevel != newifdef && !endofallsource) {
    do GetcharX(); while (Look != LF && !endofallsource);
    linenumber[slevel]++; totallines++;
    GetcharX();
    if (Look == '#') {
      *Value = '\0';
      do {
        sprintf(Value + strlen(Value), "%c", Look);
        GetcharX();
      } while (IsAlNum(Look));
      Token = TOK_DIRECTIVE;
      Scan();
      if (Token == TOK_IFDEF) ifdeflevel++;
      if (Token == TOK_ENDIFDEF) ifdeflevel--;
    }
  }
  Next(); Scan();
  trace ('-',"IfDefSkip","","","");
}


/*--------------------------------------------------------------*/
/* Process a #ifdef directive */

static void DoIfDef ()
{
  trace ('+',"DoIfDef","","","");
  ifdeflevel++;
  SkipWhite();
  if (IsAlpha(Look)) {
    GetName();
    if (IsDef(Value)) {		// it's defined...
      Next(); Scan();
    }
    else			// it's not defined...
      IfDefSkip();
  }
  else
    Expected("Definition Name");
  trace ('-',"DoIfDef","","","");
}


/*--------------------------------------------------------------*/
/* Process a #ifdef directive */

static void DoEndIfDef ()
{
  if (ifdeflevel) {
    ifdeflevel--;
    Next(); Scan(); return;
  }
  else
    Abort("#endif without #ifdef");
}


/*--------------------------------------------------------------*/
/* Parse and Translate a Block of Statements */

static void Block()
{
  trace ('+',"Block","","","");
  Scan();
  while (Token != TOK_ELSE && Token != TOK_END && !endofallsource) {
    switch (Token) {
    case TOK_IF:
      DoIf();
      break;
    case TOK_WHILE:
      DoWhile();
      break;
    case TOK_FOR:
      DoFor();
      break;
    case TOK_DEFINE:
      DoDefine();
      break;
    case TOK_INLINE:
      DoInline();
      break;
    case TOK_IDENTIFIER:
    case OP_PTR:
      Assignment();
      break;
    case TOK_IFDEF:
      DoIfDef();
      break;
    case TOK_ENDIFDEF:
      DoEndIfDef();
      break;
    default:
      Expected("Valid language token");
      break;
    }
    Semi();
    Scan();
  }
  trace ('-',"Block","","","");
}


/*--------------------------------------------------------------*/
/* Calculate array size of a declaration */

static int ArraySize()
{
  int  asize = 1;

  if (Token == '[') {
    Next();
    Scan();
    if (Token != TOK_NUM) Expected ("Number");
    asize = atoi(Value);
    Next();
    MatchString("]");
  }
  return asize;
}


/*--------------------------------------------------------------*/
/* Translate a declaration into a datatype */

static int TransDec (int isunsigned, int ispointer, int datatype)
{
  int retcode;

  if (isunsigned) {
    if (datatype == TOK_CHARDEF) retcode = DT_UCHAR;
    if (datatype == TOK_INTDEF) retcode = DT_UINT;
  } else {
    if (datatype == TOK_CHARDEF) retcode = DT_CHAR;
    if (datatype == TOK_INTDEF) retcode = DT_INT;
  }
  if (ispointer) {
    if (retcode == DT_CHAR) retcode = DT_CPTR;
    if (retcode == DT_UCHAR) retcode = DT_UCPTR;
    if (retcode == DT_INT ) retcode = DT_IPTR;
    if (retcode == DT_UINT ) retcode = DT_UIPTR;
  }
  return retcode;
}


/*--------------------------------------------------------------*/
/* Allocate Storage for a Variable */

static void Alloc(int datatype, int isunsigned)
{
  char STR1[STRTMPSIZE], name[MAXSYMSIZE+1];
  int  asize, ispointer = FALSE;

  trace ('+',"Alloc","","","");
  Next();
  if (Token == OP_PTR) { ispointer=TRUE; Next(); }
  if (Token != TOK_IDENTIFIER) Expected("Variable Name");
  strcpy (name,Value);
  CheckDup(name);
  datatype = TransDec(isunsigned,ispointer,datatype);
  AddEntry(name, datatype);
  Next();
  asize = ArraySize();
  sprintf(STR1,"%d",SizeOfType(datatype)*asize);
  GS_Size[NumGlobals-1] = asize;
  if (ispointer && (asize!=1)) Abort("*name[] not allowed");
  Allocate(name, STR1);

  trace ('-',"Alloc","","","");
}


/*--------------------------------------------------------------*/
/* Get a file name */

static void GetFileName()
{
  trace ('+',"GetFileName","","","");
  SkipWhite();
//  if (!IsAlpha(Look)) Expected("Identifier");
//  Token = TOK_IDENTIFIER;
  *Value = '\0';
  do {
    sprintf(Value + strlen(Value), "%c", Look);
    Getchar();
  } while (Look != '"');
  trace ('-',"GetFileName",Value,"","");
}


/*--------------------------------------------------------------*/
/* Process a #include directive */

static void DoInclude()
{
  trace ('+',"DoInclude","","","");
  if (slevel+1 >= MAXINCL) Abort ("Maximum include depth exceeded");
  Next();
  if (Token != '"') Expected ("\"filename\"");
  GetFileName();
  trace (' ',"DoInclude",Value,"","");
  if ((ip[slevel+1]=fopen(Value,"r"))==NULL)
    Abort ("Include file not found");
  slevel++;
  strcpy (sname[slevel],Value);
  Look = LF;
  Next();
  Scan();
  trace ('-',"DoInclude","","","");
}


/*--------------------------------------------------------------*/
/* Parse and Translate Global Declarations */

static void TopDecls()
{
  int  datatoken;
  int  isunsigned = FALSE;

  trace ('+',"TopDecls","","","");
  Scan();

  while (Token != TOK_PROGRAM && Token != TOK_PROCEDURE &&
	 Token != TOK_INTERRUPT && !endofallsource) {
    switch (Token) {
    case TOK_DEFINE:
      DoDefine();
      break;
    case TOK_UNSIGNED:
      Next(); Scan(); isunsigned = TRUE;
    case TOK_CHARDEF:
    case TOK_INTDEF:
      datatoken = Token;
      Alloc(datatoken,isunsigned);
      while (Token == ',') Alloc(datatoken,isunsigned);
      Semi();
      Scan();
      isunsigned = FALSE;
      break;
    case TOK_IFDEF:
      DoIfDef();
      break;
    case TOK_ENDIFDEF:
      DoEndIfDef();
      break;
    case TOK_INCLUDE:
      DoInclude();
      break;
    default:
      Expected("Valid Header Block Statement");
    }
  }
  trace ('-',"TopDecls","","","");

}


/*--------------------------------------------------------------*/
/* Add a Local to Table */

static void AddLocal (char *name, char ltype)
{
  trace ('+',"AddLocal","","","");
  if (IsParam(name) || IsLocal(name)) Duplicate(name);
  strcpy(LS_Name[NumLocals],name);
  LS_Type[NumLocals]=ltype;
  NumLocals++;
  trace ('-',"AddLocal","","","");
}


/*--------------------------------------------------------------*/
/* Allocate Storage for a Local Variable */

static void AllocLocal(int vtype, int isunsigned)
{
  char name[STRTMPSIZE], STR1[STRTMPSIZE], STR2[STRTMPSIZE], *sptr;
  int  asize, ispointer = FALSE;

  trace ('+',"AllocLocal","","","");
  Next();
  if (Token == OP_PTR) { ispointer=TRUE; Next(); }
  if (Token != TOK_IDENTIFIER) Expected("Variable Name");
  vtype = TransDec(isunsigned,ispointer,vtype);
  AddLocal(Value, vtype);
  strcpy(name,Value);
  Next();
  asize = ArraySize();
  LS_Size[NumLocals-1] = asize;
  if (ispointer && (asize!=1)) Abort("*name[] not allowed");
  sprintf (STR1,"%d",asize);
  sptr = DataTypeDesc(vtype);
  strcpy(STR2,sptr);
  Stage (PC_GROWSTACK,name,STR1,STR2,0,0);
  TotLocSize += (asize * SizeOfType(vtype));
  trace ('-',"AllocLocal",name,"","");
}


/*--------------------------------------------------------------*/
/* Initialize Locals Table to Null */

static void ClearLocals()
{
  int i;
  trace ('+',"ClearLocals","","","");
  for (i=0; i<MAXLOCALS; i++) {
    *LS_Name[i] = 0; LS_Size[i] = 0; LS_Type[i] = 0;
  }
  NumLocals = 0;
  trace ('-',"ClearLocals","","","");
  TotLocSize = 0;
}


/*--------------------------------------------------------------*/
/* Process Local Variable Declarations */

static void DoLocals()
{
  int datatoken;
  int isunsigned = FALSE;

  trace ('+',"DoLocals","","","");
  ClearLocals();
  Scan();

  while ((Token == TOK_DEFINE || Token == TOK_UNSIGNED ||
          Token == TOK_CHARDEF || Token == TOK_INTDEF ||
          Token == TOK_IFDEF || Token == TOK_ENDIFDEF)
          && !endofallsource) {
    switch (Token) {
    case TOK_DEFINE:
      DoDefine();
      break;
    case TOK_UNSIGNED:
      Next(); Scan(); isunsigned = TRUE;
    case TOK_CHARDEF:
    case TOK_INTDEF:
      datatoken = Token;
      AllocLocal(datatoken,isunsigned);
      while (Token == ',') AllocLocal(datatoken,isunsigned);
      Semi();
      Scan();
      isunsigned = FALSE;
      break;
    case TOK_IFDEF:
      DoIfDef();
      break;
    case TOK_ENDIFDEF:
      DoEndIfDef();
      break;
    default:
      Expected("Valid Program Block Statement");
    }
  }

  trace ('-',"DoLocals","","","");
}


/*--------------------------------------------------------------*/
/* Write the Prolog */

static void Prolog(char *Name, int isinterrupt)
{
  trace (' ',"Prolog","","","");
  Stage(PC_PROLOG,Name,"","",0,0);
  PostLabel(Name);
  if (isinterrupt) Stage (PC_PUSHFP,"","","",0,0);
  Stage (PC_SETFP,"","","",0,0);
}


/*--------------------------------------------------------------*/
/* Process the Formal Parameter List of a Procedure */

static int FormalList(int protoexists, char *procname)
{
  int protocount = 0, ptype, isunsigned, pltype = 0;
  int pcount = 0, procpos, ispointer;
  char varnam[MAXSYMSIZE];

  trace ('+',"FormalList","","","");
  procpos = Locate(procname)-1;
  if (protoexists) protocount = GS_ParamCount[procpos];
  while (Token != ')') {

    Scan();
    if (Token == TOK_UNSIGNED) {
      if (protoexists) Expected("Parameter Name or Blank");
      isunsigned = TRUE; Next(); Scan(); pltype = 1;
    }
    else
      isunsigned = FALSE;
    if (Token == TOK_CHARDEF || Token == TOK_INTDEF) {
      if (protoexists) Expected("Parameter List");
      ptype = Token;
      pltype = 1;
      Next(); Scan();
      if (Token == OP_PTR) {
        if (protoexists) Expected("Parameter name or Blank");
        ispointer = TRUE; Next(); Scan(); pltype = 1;
      }
      else {
        if (Token != ',' && Token != ')' && Token != TOK_IDENTIFIER)
          Abort("Unexpected character encountered");
        ispointer = FALSE;
      }
    } else
      if (isunsigned) Abort("Missing Data Type");

    if (Token == TOK_IDENTIFIER) {
      strcpy (varnam,Value);
      Next(); Scan();
      if (pltype == 1) pltype=3; else pltype = 2;
    }
    ptype = TransDec(isunsigned,ispointer,ptype);
    switch (pltype) {
    case 1:				// datatype only
      GS_ParamType[procpos][protocount] = ptype;
      if (protocount >= MAXPARAMS) Abort("Max param count exceeded");
      protocount++;
      break;
    case 2:				// var name only
      if (!protoexists) Expected ("Data type");
      if (pcount >= MAXPARAMS) Abort("Max param count exceeded");
      pcount++;
      if (pcount > protocount) Abort ("Too many arguments");
      AddParam(varnam,GS_ParamType[procpos][pcount-1]);
      break;
    case 3:				// datatype and var
      if (protoexists) Abort ("Data types already defined in prototype");
      GS_ParamType[procpos][protocount] = ptype;
      if (protocount >= MAXPARAMS) Abort("Max param count exceeded");
      protocount++;
      pcount++;
      AddParam(varnam,ptype);
      break;
    }
    while (Token == ',') {

      Next(); Scan();
      if (pltype == 1 || pltype == 3) {	// datatype required
        if (Token == TOK_UNSIGNED) {
          isunsigned = TRUE; Next(); Scan();
        }
        else
          isunsigned = FALSE;
        if (Token == TOK_CHARDEF || Token == TOK_INTDEF) {
          ptype = Token;
          Next(); Scan();
          if (Token == OP_PTR) {
            ispointer = TRUE; Next(); Scan();
          }
          else
            ispointer = FALSE;
        } else
          if (isunsigned) Abort("Missing Data Type");
        ptype = TransDec(isunsigned,ispointer,ptype);
      }
      if (pltype == 2 || pltype == 3) {	// var name required
        if (Token == TOK_IDENTIFIER) {
          strcpy (varnam,Value);
          Next(); Scan();
        } else
          Expected ("Variable Name");
      }

      switch (pltype) {
      case 1:				// datatype only
        GS_ParamType[procpos][protocount] = ptype;
        if (protocount >= MAXPARAMS) Abort("Max param count exceeded");
        protocount++;
        break;
      case 2:				// var name only
        if (pcount >= MAXPARAMS) Abort("Max param count exceeded");
        pcount++;
        if (pcount > protocount) Abort ("Too many arguments");
        AddParam(varnam,GS_ParamType[procpos][pcount-1]);
        break;
      case 3:				// datatype and var
        GS_ParamType[procpos][protocount] = ptype;
        if (protocount >= MAXPARAMS) Abort("Max param count exceeded");
        protocount++;
        pcount++;
        AddParam(varnam,ptype);
        break;
      }

    }	// while (Token == ',')

  } // while (Token != ')')

  if (protoexists && (pcount != protocount)) Abort ("Too few arguments");
  switch (pltype) {
  case 0:
    trace (' ',"FormalList PL0","","","");
    break;
  case 1:
    trace (' ',"FormalList PL1","","","");
    GS_ParamCount[procpos] = protocount;
    break;
  case 2:
    trace (' ',"FormalList PL2","","","");
    GS_ParamCount[procpos] = pcount;
    break;
  case 3:
    trace (' ',"FormalList PL3","","","");
    GS_ParamCount[procpos] = pcount;
    break;
  }
  trace ('-',"FormalList","","","");
  return pltype;
}


/*--------------------------------------------------------------*/
/* Parse and Translate procedure (and interrupt) blocks */

static void ProcedureBlock()
{
  char Name[MAXSYMSIZE], STR1[STRTMPSIZE];
  int protoexists, procexists, isinterrupt, pltype;

  trace ('+',"ProcedureBlock","","","");
  if (section != PC_PROGSEC) {
    section = PC_PROGSEC;
    Stage (PC_PROGSEC,"","","",0,0);
  }
  Branch(MAINNAME);			// skip to "main"
  while (Token == TOK_PROCEDURE || Token == TOK_INTERRUPT) {
    isinterrupt = (Token == TOK_INTERRUPT);
    Next();				// gets the procedure name
    if (Token != TOK_IDENTIFIER) Expected ("Procedure Name");
    strcpy(Name,Value);
    procexists = Locate(Name);
    protoexists = FALSE;
    if (procexists) {			// already exists...
      if (isinterrupt && (GS_Type[procexists-1] != DT_INTERRUPT))
        Duplicate(Name);
      if ((!isinterrupt) && (GS_Type[procexists-1] != DT_PROCEDURE))
        Duplicate(Name);
      if (GS_Size[procexists-1] == 0)	// as just a prototype?
        protoexists = TRUE;
      else
        Duplicate(Name);
    }
    else {				// a new procedure
      trace (' ',"ProcedureBlockN","","","");
      if (isinterrupt)
	AddEntry(Name, DT_INTERRUPT);
      else
	AddEntry(Name, DT_PROCEDURE);
    }

    Next();
    MatchString("(");
    pltype = FormalList(protoexists, Name);
    MatchString(")");
    Scan();

    if (Token == TOK_BEGIN) {		// 'begin' found
      trace (' ',"ProcedureBlockB","","","");
      if (pltype == 1) Abort("Not valid for a prototype");
      MatchString("begin");
      if (protoexists)
        GS_Size[procexists-1] = 1;
      else
        GS_Size[NumGlobals-1] = 1;
      Prolog(Name,isinterrupt);
      DoLocals();
      Block();
      MatchString("end");
      Epilog(Name,isinterrupt);
      Scan();
    }
    else {				// no 'begin' - prototype
      trace (' ',"ProcedureBlockP","","","");
      if (protoexists) Expected("Prototype already defined - 'begin'");
      Scan();
    }
    ClearParams();
  }

  trace ('-',"ProcedureBlock","","","");
}


/*--------------------------------------------------------------*/
/* Parse and Translate the main program block */

static void ProgramBlock()
{
  trace (' ',"ProgramBlock","","","");
  Next();
  MatchString("begin");
  if (section != PC_PROGSEC) {
    section = PC_PROGSEC;
    Stage (PC_PROGSEC,"","","",0,0);
  }
  Prolog(MAINNAME,FALSE);
  DoLocals();
  Block();
  MatchString("end");
  Epilog(MAINNAME,FALSE);
}


/*--------------------------------------------------------------*/
/* Initialize */

static void Init()
{
  if (tracing) {
    if ((tr=fopen("trace.txt","w"))==NULL)
      Abort ("Can not open trace file");
    fprintf (tr,COMPILER);
  }
  if (opfile1) {
    if ((op1=fopen("out1.txt","w"))==NULL)
      Abort ("Can not open output file 1");
    fprintf (op1,"; "); fprintf (op1,COMPILER);
    op = op1;
  }
  if (opfile2) {
    if ((op2=fopen("out2.txt","w"))==NULL)
      Abort ("Can not open output file 2");
    fprintf (op2,"; "); fprintf (op2,COMPILER);
  }
  if (pcdump || pcdump2) {
    if ((pcd=fopen("pcodes.txt","w"))==NULL)
      Abort ("Can not open pcode dump file");
    fprintf (pcd,COMPILER);
  }
  if ((sym=fopen("symbol.txt","w"))==NULL)
    Abort ("Can not open symbol file");
  fprintf (sym,COMPILER);

  trace (' ',"Init","","","");
  totallines = 1;
  InitRegs();
  ClearParams();
  Getchar();
  Next();
}


/*--------------------------------------------------------------*/
/* Main Program */

main(int argc, char *argv[])
{
  printf ("\n"); printf (COMPILER);
  if (argc < 2) {
    interactive = TRUE;
    EmitLn_notab ("Interactive Mode");
    strcpy (sname[slevel],"STDIN");
  }
  else {
    if ((ip[slevel]=fopen(argv[1],"r"))==NULL)
      Abort ("File not found");
    strcpy (sname[slevel],argv[1]);
  }
  Init();
  Header();
  TopDecls();
  if (Token == TOK_PROCEDURE || Token == TOK_INTERRUPT) ProcedureBlock();
  ProgramBlock();
  Trailer();
  if (ifdeflevel) Expected("#endif");
  dump_symbol();		// dump the symbol tables to the trace file
  if (pcdump) dump_pcodes();	// dump the unoptimized pcodes
  Gen_Code(op1);		// generate o/p from unopt pcodes to file 1
  if (opfile2) {
    Optimize();
    if (pcdump2) dump_pcodes();
    genreguse=genreguse2;
    Gen_Code(op2);	// generate output from optimized pcodes to file 2
  }
  printf ("\n%d source lines\n",totallines);
  printf ("%d symbols used\n%d pcodes generated (%d after optimization)\n",
    NumGlobals,sbufftop,sbufftop-sbuffopt);
  printf ("%d warnings\n",wcount);
  printf ("End of Compilation\n");

  if (tracing) fclose (tr);
  if (opfile1) fclose (op1);
  if (opfile2) fclose (op2);
  if (pcdump || pcdump2) fclose (pcd);
  fclose (sym);
  exit(EXIT_SUCCESS);
}
/*--------------------------------------------------------------*/