//  LC (Low-level compiler) for 8086

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

//  Statement -> CONST {tag = cexpr}+
//               {WORD, BYTE} {tag { (cexpr:cexpr) }? {= {data}+ }? }*
//               SPEC tag string
//               PROC tag {string}? {tag}*
//               END
//               tag :
//               {instruction, ELSE}? {IF expr comp expr}?
//               CYCLE
//               FINISH
//  Instruction -> MOVE {string opd, cexpr opd opd}
//                 JUMP tag
//                 REPEAT
//                 RETURN {expr}?
//                 opd = expr
//                 tag {expr}*
//  Data -> {string, cexpr { (cexpr) }? }
//  Cexpr -> const {op const}*
//  Expr -> {opd, const} {op {opd, const} }*
//  Opd -> tag { ( {opd, const} ) }?
//  Op -> + - & ! \ * / % << >>
//  Comp -> = # < > <= >= [ ] [= ]=

//  Comments are enclosed between { and } or | and NL.

//  Calling convention: Push parms, then XCALL or Push CS, CALL.
// Entry: Push DS, Push BP, Mov BP,SP, {Sub SP,framesize}?,
//         {Mov AX,Glaseg, Mov DS,AX}? Xor AX,AX, Push AX
// Return: Mov SP,BP, Pop BP, Pop DS, Xret parmsize.

FILE *outfile, *source, *object, *list;  // Streams

#define LINEMAX 80
#define CODEMAX 3499
#define GLAMAX 499
#define ATOMMAX 50
#define TAGMAX 200
#define TEXTMAX 20
const int linemax = LINEMAX, atommax = ATOMMAX, tagmax = TAGMAX, textmax = TEXTMAX;
const int codemax = CODEMAX, glamax = GLAMAX;

unsigned char line[LINEMAX];
#define line(x) line[(x)-1]

unsigned char code[CODEMAX + 1];
#define code(x) code[x]

unsigned char gla[GLAMAX + 1];
#define gla(x) gla[x]

int atomtype[ATOMMAX], atomval[ATOMMAX];
#define atomtype(x) atomtype[(x)-1]
#define atomval(x) atomval[(x)-1]

int tag1[TAGMAX], tag2[TAGMAX], tagtype[TAGMAX], tagval[TAGMAX];
#define tag1(x) tag1[(x)-1]
#define tag2(x) tag2[(x)-1]
#define tagtype(x) tagtype[(x)-1]
#define tagval(x) tagval[(x)-1]

int texttype[TEXTMAX], textval[TEXTMAX];
#define texttype(x) texttype[(x)-1]
#define textval(x) textval[(x)-1]

int type, val, t, v, atompos, ifpos, floc, parms;
int oldapos, oldcpos, oldtpos, access, textlevel;
int lineno = 0, cloc = 0, gloc = 0, level = 0, gpos = 0, cpos = 0, tagpos = 1;
char program[255];
char program_source[255], program_object[255], program_list[255];

// Atom types
#define WORDBIT 1
const int unknown = 0, constant = 1, string = 2, label = 3;
const int var = 4, wordbit = WORDBIT, glabit = 2, varmask = 0xFC;
const int proc = 8, xproc = 9, spec = 10;
const int op = 11, comp = 12, colon = 13, sep = 14, bracket = 15, bx = 16 | WORDBIT;

// Keyword types
#define KCONST 17
#define KREPEAT 30
const int kconst = KCONST, kword = 18, kbyte = 19, kspec = 20, kproc = 21;
const int kend = 22, kmove = 23, kjump = 24, kreturn = 25, kprogram = 26;
const int kelse = 27, kfinish = 28, kcycle = 29, krepeat = KREPEAT;

// Operator codes
#define LOAD 1
#define ADD 2
#define SUB 3
#define AND 4
#define OR 5
#define XOR 6
#define CMP 7
#define MUL 8
#define DIV 9
#define REM 10
#define LEFT 11
#define RIGHT 12
#define STORE 13
#define LEA 14
const int load = 1, add = 2, sub = 3, and = 4, or = 5, xor = 6, cmp = 7,
 mul = 8, /* div = 9,*/ rem = 10, left = 11, right = 12, store = 13, lea = 14;

// Keyword tag codes (1:36 = A:Z,0:9) 1st {*37 + 2nd {*37 + 3rd}? }?
const int if_ = 339;
static int n1[KREPEAT - KCONST + 1] = {
 4676, 32060, 3683, 26608, 22585, 7367, 18374, 14480, 24847, 22585,
 7308,  8561, 5035, 24843};
#define n1(x) n1[(x)-kconst]
static int n2[KREPEAT - KCONST + 1] = {
  723,     4,    5,     3,     3,    0,     5,    16, 29429, 10250,
    5, 13032,  449,  6902};
#define n2(x) n2[(x)-kconst]

// Fault numbers
#define MAXFAULT 16
const int complex = 1, atom = 2, consterr = 3, bfull = 4, form = 5, ambig =
 6, nested = 7, declate = 8, far = 9, labund = 10, level0 = 11, varund =
 12, dfull = 13, repexp = 14, finexp = 15, context = 16, maxfault =
 MAXFAULT;

void fault (int n) {
   static char *s[MAXFAULT] = {
      "Complex",
      "Atom",
      "Not constant",
      "Buffer full",
      "Form",
      "Ambiguous",
      "Nesting",
      "Too late",
      "Too far",
      "Label missing",
      "Code misplaced",
      "Name",
      "Dict full",
      "Repeat expected",
      "Finish expected",
      "Context"
   };
#define s(x) s[(x)-1]
   int i;
   int sym;

   fprintf (stderr, "%1d ", lineno);
   i = 1;
   for (;;) {
      sym = line (i++);
      fputc (sym, stderr);
      if (sym == '\n') break;
   }
   fprintf (stderr, "%s%1d\n", s (n), atompos - 1);
   fprintf (list, "%s%s%d\n", "** ", s (n), atompos - 1);
   exit (1);
#undef s
}

void phex (int x) {
   int k, i;
   for (i = 12; i >= 0; i -= 4) {
      k = (x >> i) & 15;
      if (k > 9) k += 7;
      fputc (k + '0', list);
   }
}

void putatom (int t, int v) {
   if (atompos > atommax) fault (complex);
   atomtype (atompos) = t;
   atomval (atompos++) = v;
}

void readline (void) {
   static int semi = 0;
   static int quote = 0;
   int sym, n1, n2, radix, i, quoted, lpos;

   for (;;) {
      if (semi == 0) fprintf (list, "%4d C%04X D%04X ", lineno + 1, cloc, gloc);
      lpos = 1; quoted = 0;
      for (;;) {
       next:
         sym = ((lpos + quoted == linemax) ? ((quoted == 0) ? '\n' : quote) : fgetc (source) );
                                                // line nearly too long: force end
         fputc (sym, list);
         if (quoted == 0) {
            if ((sym == '\'' || sym == '"')) quoted = 1;
            if (quoted != 0) quote = sym;
         } else if (sym == quote) quoted = 0;
         if (quoted == 0) {
            if (sym == '{') {        // Comment
               for (;;) {
                  sym = fgetc (source); fputc (sym, list);
                  if (sym == '\n') {
                     lineno += 1;
                     {int i;for (i = 0; i < 18; i++)fputc (' ', list);}
                  }
                  if (sym == '}') break;
               }
               goto next;
            }
            if (('a' <= sym) && (sym <= 'z')) sym -= 32;
            semi = 0;
            if (sym == ';') {sym = '\n'; lineno -= 1; semi = 1;}
            if (sym == '|') do { sym = fgetc (source); fputc (sym, list);} while (sym != '\n');
         }
         if (sym == '\n') lineno += 1;
         line (lpos++) = sym;
         if ((sym == '\n') && (quoted == 0)) break;
      }
      if (lpos > 2) break;
   }
   // non-empty line
   // Now decompose line into atoms
   atompos = 1; ifpos = 0; lpos = 1;
 nextatom:
   for (;;) {
      sym = line (lpos++);
      if (('A' <= sym) && (sym <= 'Z')) goto tag;
      if (('0' <= sym) && (sym <= '9')) goto num;
      if (sym == '\n') {
         putatom (sep, 0); atompos = 1; return;
      }
      if (sym == ':')
         putatom (colon, 0);
      else if (sym == '+')
         putatom (op, add);
      else if (sym == '-')
         putatom (op, sub);
      else if (sym == '&')
         putatom (op, and);
      else if (sym == '!')
         putatom (op, or);
      else if (sym == '\\')
         putatom (op, xor);
      else if (sym == '*')
         putatom (op, mul);
      else if (sym == '%')
         putatom (op, rem);
      else if (sym == '/')
         putatom (op, DIV);
      else if ((sym == '<' || sym == '>')) {
         if (line (lpos) == sym) { // '<<', '>>'
            lpos += 1;
            putatom (op, ((sym == '<') ? left : right));
         } else { // '<', '>', '<=', '>='
          square: // +'[', ']', '[=', ']='
            if (line (lpos) == '=') {
               lpos += 1; sym += 128;
            }
            putatom (comp, sym);
         }
      } else if ((sym == '(') || (sym == ')'))
         putatom (bracket, sym);
      else if ((sym == '[') || (sym == ']'))
         goto square;
      else if ((sym == '=') || (sym == '#'))
         putatom (comp, sym);
      else if (sym == '"') {
         putatom (string, lpos);
         while (line (lpos) != '"') lpos += 1; // we know it's there
         lpos += 1;
      } else if (sym == '\'') {
         sym = line (lpos);
         if (sym == '\'') lpos += 1;
         if (line (lpos + 1) != '\'') fault (atom);
         putatom (constant, sym); lpos += 2;
      } else if (sym != ' ') fault (atom);
   }
 num:
   i = 0; radix = 10; // I accumulates number, default radix is ten
   for (;;) {
      if (sym == '_') { // change radix
         radix = i; i = 0;
      } else if (('9' < sym && sym < 'A')) {
       endconst:
         putatom (constant, i); lpos -= 1; goto nextatom;
      } else {
         if (sym > '9') sym -= 7;
         sym -= '0';
         if (!((0 <= sym) && (sym < radix))) goto endconst;
         i = i * radix + sym;
      }
      sym = line (lpos++);
   }
 tag:
   n1 = sym - 'A' + 1; n2 = 0; i = 1; // I counts name length
   for (;;) {
      sym = line (lpos);
      if (('A' <= sym) && (sym <= 'Z'))
         sym = sym - 'A' + 1;
      else if (('0' <= sym) && (sym <= '9'))
         sym = sym - '0' + 27;
      else {
         if (n1 == if_) {
            if (ifpos != 0) fault (form);
            ifpos = atompos; putatom (sep, 0);
         } else {
            // Look up in dictionary
            for (i = tagpos - 1; i >= 1; i -= 1) {
               if ((tag1 (i) == n1) && (tag2 (i) == n2)) {
                  n1 = tagtype (i); n2 = tagval (i);
                  if (n1 == unknown) n2 = i;
                  putatom (n1, n2);
                  goto nextatom;
               }
            }
            // Not found: enter name and details
            tag1 (tagpos) = n1; tag2 (tagpos) = n2;
            tagtype (tagpos) = unknown; tagval (tagpos) = 0;
            putatom (unknown, tagpos);
            if (tagpos > tagmax) fault (dfull); else tagpos += 1;
         }
         goto nextatom;
      }
      if (i < 3) n1 = n1 * 37 + sym;
      else if (i < 6) n2 = n2 * 37 + sym;
      i += 1; lpos += 1;
   }
}

void nextatom (void) {
   type = atomtype (atompos); val = atomval (atompos);
   if (type != sep) atompos += 1;
}

int openbracket (void) {
   if ((atomtype (atompos) != bracket) || (atomval (atompos) != '(')) return (0 != 0);
   atompos += 1; return (0 == 0);
}

void closebracket (void) {
   if ((atomtype (atompos) != bracket) || (atomval (atompos) != ')')) fault (form);
   atompos += 1;
}

int equalsign (void) {
   if ((atomtype (atompos) != comp) || (atomval (atompos) != '=')) return (0 != 0);
   atompos += 1; return (0 == 0);
}

void unary (void);
int cexpr (int must) {
   int value, oper;
   nextatom ();
   unary ();
   value = val;
   if (type != constant) fault (consterr);
   for (;;) {
      if (atomtype (atompos) != op) return (value);
      if ((must == 0) && (atomtype (atompos + 1) != constant)) return (value);
      oper = atomval (atompos++);
      nextatom (); if (type != constant) fault (consterr);
      switch (oper) {
        case ADD:   /* add */   value += val; continue;
        case SUB:   /* sub */   value -= val; continue;
        case AND:   /* and */   value &= val; continue;
        case OR:    /* or */    value |= val; continue;
        case XOR:   /* xor */   value ^= val; continue;
        case REM:   /* rem */   value -= (((int) (value) / (int) (val)) * val); continue;
        case DIV:   /* div */   value /= val; continue;
        case MUL:   /* mul */   value *= val; continue;
        case LEFT:  /* left */  value <<= val; continue;
        case RIGHT: /* right */ value >>= val;
      } /* end switch s */
   }
}

void unary (void) {
   int *v;
   if ((type != op) || (atomtype (atompos) != constant)) return;
   if ((val != sub) && (val != xor)) return;
   v = &atomval (atompos) /* Pointer assignment */ ;
   *v = ~*v; if (val == sub) *v += 1;
   val = cexpr (0); type = constant;
}

void gbyte (int b) {
   if (gpos > glamax) fault (bfull);
   gla (gpos++) = b & 255; gloc += 1;
}

void gword (int x) {
   gbyte (x); gbyte (x >> 8);
}

void gflush (void) {
   int i;
   if (gpos == 0) return;
   fputc (2, object); fputc (4, object);
   fputc (gpos & 255, object); fputc ((gpos >> 8) & 255, object);
   for (i = 0; i <= gpos - 1; i += 1) fputc (gla (i), object);
   gpos = 0;
}

void dumpstring (int p) {
   int l;
   l = 0;
   while (line (p) != '"') { l += 1; p += 1; }
   gbyte (l); p -= l;
   while (l > 0) { gbyte (line (p++)); l -= 1; }
}

void dump (int byte) {
   if (cpos > codemax) fault (bfull);
   code (cpos++) = byte & 255; cloc += 1;
}

void dumpw (int byte2) {
   if ((cpos+1) > codemax) fault (bfull);
   code (cpos++) = byte2 & 255; cloc += 1;
   code (cpos++) = (byte2>>8) & 255; cloc += 1;
}

void flush (void) {
   int i;
   if (cpos == 0) return;
   fputc (1, object); fputc (4, object);
   fputc (cpos & 255, object); fputc ((cpos >> 8) & 255, object);
   for (i = 0; i < cpos; i += 1) fputc (code (i), object);
   cpos = 0;
}

void satrefs (int ref, int val) {
   int abs, rel, disp;
   while (ref != 0) {
      rel = ref; abs = rel - cloc + cpos; disp = val - rel - 2;
      ref = (code (abs + 1) << 8) + code (abs);
      code (abs) = disp & 255; code (abs + 1) = (disp >> 8) & 255;
   }
}

void labelref (int type, int val) {
   if (type == label) {
      dumpw (val - (cloc + 2));
   } else {
      if (type != unknown) fault (form);
      dumpw (tagval (val));
      tagval (val) = cloc - 2;
   }
}

void pushtext (int t, int v) {
   if (++textlevel > textmax) fault (complex);
   texttype (textlevel) = t; textval (textlevel) = v;
}

void poptext (void) {
   if (textlevel == 0) fault (context);
   type = texttype (textlevel); val = textval (textlevel--);
}

void expr (void);
const int pushax = 0x50, popax = 0x58;
void call (int t, int v) {
   type = atomtype (atompos);
   if (type != op) {
      while ((type != sep) && (type != comp)) {
         expr (); dump (pushax); type = atomtype (atompos);
      }
   }
   if (t == spec) {
      dumpw (0x1EFF); dumpw (v);             // Xcall
   } else {
      dumpw (0xE80E); labelref (label, v);   // PushCs, Call
   }
}

void immediate (int op, int val) {
   switch (op) {
   case LOAD: /* load */ dump (0xB8); break;
   case ADD:  /* add */  dump (0x05); break;
   case SUB:  /* sub */  dump (0x2D); break;
   case AND:  /* and */  dump (0x25); break;
   case OR:   /* or */   dump (0x0D); break;
   case XOR:  /* xor */  dump (0x35); break;
   case CMP:  /* cmp */  dump (0x3D); break;
   case LEFT: /* left */
   case RIGHT:/* right */
      if (val == 1) val = 0xD1; else {
         dump (0xB1); dump (val & 15); // Mov CL,nn
         val = 0xD3;
      }
      dump (val); dump ((op == left) ? 0xE0 : 0xE8);
      return;
   case MUL: /* mul */ case DIV: /* div */ case REM: /* rem */
      dump (0xB9); dumpw (val);       // Mov CX,nn
      if (op != mul) dump (0x99);        // Cwd
      dumpw ((op == mul) ? 0xE9F7 : 0xF9F7);
      if (op != rem) return;
      dumpw (0xC28B);           // Mov ax,dx
   }
   dumpw (val);
}

#undef code
void direct (int op, int type, int val, int index) {
   static int code[LEA - LOAD + 1] = {
      0x8a, 0x02, 0x2a, 0x22, 0x0a,
      0x32, 0x3a, 0x28, 0x38, 0x30,
      0xe0, 0xe8, 0x88, 0x8d
   };
#define code(x) code[(x)-load]
   int oper, extra;

   if (type == constant) { immediate (op, val); return; }
   oper = code (op);
   if ((op != left) && (op != right)) {
     extra = 0; if (op == mul) { extra = oper; oper = 0xF6; }
     if ((op == DIV) || (op == rem)) {
        dump (((type & wordbit) != 0) ? 0x99 : 0x98);
        extra = oper; oper = 0xF6;
     }
     if ((type & wordbit) != 0) oper += 1;
     dump (oper);
     if (type == bx) { dump (0xC3 + extra); return; }
     if (type == unknown) {
        tagtype (val) = var + wordbit; tagval (val) = -2;
        fault (varund);
     }
     if ((type & varmask) != var) fault (form);
     if ((type & glabit) == 0) {
        oper = (((-128 <= val) && (val <= 127)) ? 0x42 : 0x82);
        if (index == 0) oper += 4;
     } else oper = (index == 0 ? 6 : (((-128 <= val) && (val <= 127)) ? 0x44 : 0x84));
     dump (oper + extra); dump (val);
     if ((oper & 0x40) == 0) dump (val >> 8);
     if (op == rem) { dumpw (0xC28B); }
     if (((type & wordbit) == 0) && (op == load)) { dumpw (0xE432); }
   } else {
     dump (pushax);
     direct (load, type, val, index);
     dumpw (0xC189); // Mov Cx,Ax
     dump (popax); dump (((type & wordbit) == 0) ? 0xD2 : 0xD3); dump (oper);
   }
#undef code
}
#define code(x) code[x]

void double_ (int t) { // index for word arrays
   if ((t & wordbit) == 0) return;
   dumpw (0xC003);              // Add ax,ax
}

void expr (void) {
   int oper, t, v, index;

   oper = load;
   for (;;) {
      nextatom ();
      unary ();
      if (type == constant) {
         if (oper == load) { atompos -= 1; val = cexpr (0); }
         immediate (oper, val);
      } else if ((type & varmask) == var) {
         t = type; v = val;
         if (openbracket ()) {
            if (oper != load) dump (pushax);
            expr ();
            double_ (t);
            dumpw (0xF08B);     // Mov si,ax
            if (oper != load) dump (popax);
            closebracket ();
            index = 1;
         } else index = 0;
         direct (oper, t, v, index);
      } else if ((type == proc) || (type == xproc) || (type == spec)) {
         if (oper == load) call (type, val);
         else {
            dump (pushax); call (type, val); dumpw (0xD88B);     // mov bx,ax
            dump (popax); direct (oper, bx, 0, 0);
         }
      } else fault (form);
      if (atomtype (atompos) != op) return;
      nextatom ();
      oper = val;
   }
}

void return_ (void) {
   dumpw (0xE58B);              // Mov sp,bp
   dumpw (0x1F5D);              // Pop bp, pop ds
   if (parms == 0) dump (0xCB); else {
      dump (0xCA); dumpw (parms);
   }
   access = 0;
}

void terminatedeclarations (void) {
   int i;
   if (level == 0) fault (level0);
   if (level == 1) {
      level = 2; i = -floc;
      if (i > 127) {
         dumpw (0xEC81); dumpw (i);      // Sub sp,nn
      } else if (i != 0) {
         dumpw (0xEC83); dump (i);           // sub sp,n
      }
      floc -= 2;
   }
}

void constdef (void) {
   int c;
   for (;;) {
      nextatom (); if (type == sep) return;
      if (type != unknown) fault (ambig); c = val;
      if (!equalsign ()) fault (form);
      tagtype (c) = constant; tagval (c) = cexpr (1);
   }
}

void vardef (int key) {
   int t, lb, ub, c, r;
   int *v;
   for (;;) {
      nextatom ();
    next: if (type == sep) return;
      if (type != unknown) fault (ambig);
      t = var;
      if (key == kword) t += wordbit;
      if (level == 0) t += glabit;
      tagtype (val) = t; v = &tagval (val) /* Pointer assignment */ ;
      if (openbracket ()) {
         lb = cexpr (1); nextatom (); if (type != colon) fault (form);
         ub = cexpr (1) + 1; closebracket ();
      } else { lb = 0; ub = 1; }
      if (key == kword) { lb += lb; ub += ub; }
      if (level == 0) {
         *v = gloc - lb; t = *v + ub;
         if (equalsign ()) {
            for (;;) {
               nextatom ();
               if (gloc >= t) { gflush (); goto next; }
               if (type == string) {
                  dumpstring (val);
               } else if (type == sep) {
                  readline ();
               } else {
                  atompos -= 1; c = cexpr (1); r = 1;
                  if (openbracket ()) { r = cexpr (1); closebracket (); }
                  while (r-- > 0) if (key == kword) gword (c); else gbyte (c);
               }
            }
         } else {
            fputc (2, object); fputc (3, object);
            fputc (t & 255, object);  fputc ((t >> 8) & 255, object); gloc = t;
         }
      } else {
         if (level == 2) fault (declate);
         floc = floc - ub + lb; *v = floc - lb;
      }
   }
}

int getstring (char *s) {
   int sym;
   strcpy (s, "");
   for (;;) {
      sym = line (val); if (sym == '"') return sym;
      val += 1; s[strlen (s) + 1] = '\0'; s[strlen (s)] = sym;
   }
}

void procdef (int key) {
   int p;
   int sym;
   char s[32];
   nextatom (); if (type != unknown) fault (ambig);
   if (key == kspec) {
      tagtype (val) = spec; tagval (val) = gloc;
      nextatom (); if (type != string) fault (form);
      sym = getstring (s); p = strlen (s);
      fputc (2, object);
      fputc (8, object); fputc (p, object); fprintf (object, "%s", s);
      fputc (7, object); fputc (0, object);
      gloc += 4; return;
   }
   if (level != 0) fault (nested);
   level = 1; access = 1; textlevel = 0;
   v = val; t = proc; nextatom ();
   if (type == string) {
      sym = getstring (s);
      fputc (1, object); fputc (6, object);
      fputc (strlen (s), object); fprintf (object, "%s", s);
      t = xproc; nextatom ();
   }
   tagtype (v) = t; tagval (v) = cloc;
   dumpw (0x551E);              // Push ds, push bp
   dumpw (0xEC8B);              // Mov BP,SP
   dumpw (0xC033);              // Xor AX,AX
   dump (pushax); // For event mechanism
   if (t == xproc) {
      if (*program == '\0') fault (labund);
      dump (0xB8); flush (); // Mov ax,nn=dseg
      fputc (7, object); fputc (strlen (program), object); fprintf (object, "%s", program);
      cloc += 2; dumpw (0xD88E);           // Mov ds,ax
   }
   parms = 0; oldtpos = v + 1;
   while (type != sep) {
      if (type != unknown) fault (ambig);
      tagtype (val) = var + wordbit;
      parms += 1; nextatom ();
   }
   p = tagpos - parms; parms += parms; sym = parms + 6;
   while (p < tagpos) { tagval (p++) = sym; sym -= 2; }
   floc = 0;
}

void instruction (void) {
   int t, v, i, oper, st, sv, si, dt, dv;
   nextatom ();
   if (type == kmove) {
      nextatom ();
      oper = 0xA4;           // Movb
      if (type == string) {
         st = var + glabit; sv = gloc; si = 0;
         dumpstring (val); val = gloc - sv; gflush ();
         if ((val & 1) == 0) {
            val >>= 1; oper = 0xA5;     // Movw
         }
         immediate (load, val); dump (pushax);
      } else {
         expr (); dump (pushax);
         nextatom (); st = type; sv = val; si = 0;
         if ((st & varmask) != var) fault (form);
         if (openbracket ()) {
            expr (); double_ (st); si = 1; dump (pushax);
            closebracket ();
         }
         if ((st & wordbit) != 0) oper = 0xA5;     // Movw
      }
      nextatom ();
      dt = type; dv = val;
      if ((dt & varmask) != var) fault (form);
      if (openbracket ()) {
         expr (); double_ (dt); dumpw (0xF88B);        // Mov di,ax
         direct (lea, dt, dv, 0); dumpw (0xF803);      // add di,ax
         closebracket ();
      } else {
         direct (lea, dt, dv, 0); dumpw (0xF88B);      // Mov di,ax
      }
      direct (lea, st, sv, 0);
      dumpw (0xF08B);           // Mov si,ax
      if (si != 0) { dump (popax); dumpw (0xF003); } // add,si,ax
      dump (((dt & glabit) == 0) ? 0x16 : 0x1E);     // Push SS/DS
      dump (0x07);           // Pop ES
      if ((st & glabit) == 0) { dump (0x1E); dumpw (0x1F16); }       // Push DS,SS, Pop DS
      dumpw (0xF259); dump (oper);           // Pop cx, rep
      if ((st & glabit) == 0) dump (0x1F);        // Pop DS
   } else if (type == krepeat) {
      poptext (); if (type != repexp) fault (type);
      dump (0xE9); labelref (label, val);
   } else if (type == kjump) {
      nextatom (); dump (0xE9); labelref (type, val);
   } else if (type == kreturn) {
      if (atomtype (atompos) != sep) expr ();
      return_ ();
   } else if ((type & varmask) == var) {
      t = type; v = val; i = 0;
      if (openbracket ()) {
         expr (); double_ (t); dump (pushax);
         closebracket ();
         i = 1;
      }
      if (!equalsign ()) fault (form);
      expr (); if (i != 0) dump (0x5E);        // pop si
      direct (store, t, v, i);
   } else if ((type == proc) || (type == xproc) || (type == spec))
      call (type, val);
   else
      atompos -= 1;
}

int cond (void) {
   // Evaluate condition, return value of jump opcode
   expr (); nextatom (); if (type != comp) fault (form);
   v = val; nextatom (); unary ();
   if ((atomtype (atompos) != sep)
       || ((type != constant) && ((type & varmask) != var))) {
      dump (pushax); atompos -= 1; expr ();
      dumpw (0xD88B); dump (popax);          // Mov bx,ax
      type = bx;
   }
   direct (cmp, type, val, 0);
   if (v == ']' + 128) return (0x72);
   if (v == '[') return (0x73);
   if (v == '#') return (0x74);
   if (v == '=') return (0x75);
   if (v == ']') return (0x76);
   if (v == '[' + 128) return (0x77);
   if (v == '>' + 128) return (0x7C);
   if (v == '<') return (0x7D);
   if (v == '>') return (0x7E);
   return (0x7F);            // %if v='<'+128
}


// Main Program

int main (int argc, char **argv) {
   if (tagpos == 1) {
      if (argc != 2) {
         fprintf (stderr, "syntax: lc basename\n");
         exit (1);
      }
      sprintf (program, "%s", argv[1]);
      sprintf (program_source, "%s.lc", argv[1]);
      sprintf (program_object, "%s.iob", argv[1]);
      sprintf (program_list, "%s.lis", argv[1]);
      source = fopen (program_source, "r");
      if (source == NULL) {
         // Maybe switch to using stdin?
         fprintf (stderr, "lc: %s - %s\n", strerror (errno), program_source);
         exit (1);
      }
      object = fopen (program_object, "wb");
      if (object == NULL) {
         // Or send to stdout?
         fprintf (stderr, "lc: %s - %s\n", strerror (errno), program_object);
         exit (1);
      }
      list = fopen (program_list, "w");
      if (list == NULL) {
         fprintf (stderr, "lc: %s - %s\n", strerror (errno), program_list);
         exit (1);
      }
      fprintf (list, "                  LC Cross-compiler  06/01/81\n\n");
      for (type = kconst; type <= krepeat; type += 1) {
         tag1 (tagpos) = n1 (type); tag2 (tagpos) = n2 (type);
         tagtype (tagpos) = type; tagval (tagpos++) = -1;
      }
      *program = '\0';
   }

   for (;;) {
      readline ();
      for (;;) {
         // Deal with labels
         nextatom ();
         if ((type != unknown) || (atomtype (atompos) != colon)) break;
         tagtype (val) = label; satrefs (tagval (val), cloc);
         tagval (val) = cloc;
         atompos += 1;
         access = 1;
      }
      if ((type == sep) && (ifpos == 0)) continue;
      if (type == kconst)
         constdef ();
      else if ((type == kbyte) || (type == kword))
         vardef (type);
      else if ((type == kspec) || (type == kproc))
         procdef (type);
      else if (type == kend) {
         if (level == 0) break; level = 0;
         if (access != 0) return_ (); flush ();
         val = oldtpos; oldtpos = tagpos; tagpos = val; // Reset tagpos before moaning
         while (oldtpos > tagpos) { if (tagtype (--oldtpos) == unknown) fault (labund); }
         if (textlevel != 0) { poptext (); textlevel = 0; fault (type); }
      } else if (type == kprogram) {
         if (*program != '\0') fault (ambig);
         nextatom (); if (type != string) fault (form);
         if (val == '"') fault (form);       // Null name not allowed
         v = line (val);
         for (;;) {
            program[strlen (program) + 1] = '\0'; program[strlen (program)] = v;
            if ((v = line (++val)) == '"') break;
         }
         if (program[(strlen (program)) - 1] != '_') {
            program[strlen (program) + 1] = '\0'; program[strlen (program)] = '_';
         }
         fputc (2, object); fputc (6, object);
         fputc (strlen (program), object); fprintf (object, "%s", program);
      } else if (type == kcycle)
         pushtext (repexp, cloc);
      else if (type == kelse) {
         poptext (); if (type != finexp) fault (type);
         satrefs (val, cloc + 3);
         poptext (); if (type != finexp) fault (type);
         dump (0xE9); dumpw (val);
         if (ifpos == 0) pushtext (context, 0);
         else {
            pushtext (finexp, cloc - 2);
            atompos = ifpos + 1;
            dump (cond () ^ 1); dumpw (0xE903); dumpw (0x0000);
            if (ifpos != 2) { atompos = ifpos; fault (form); }
            ifpos = 0;
         }
         pushtext (finexp, cloc - 2);
      } else if (type == kfinish) {
         poptext (); if ((type != finexp) && (type != context)) fault (type);
         satrefs (val, cloc);
         poptext (); satrefs (val, cloc); access = 1;
      } else {
         if (type == unknown) fault (varund);
         terminatedeclarations ();
         if (ifpos == 0) { // Statement
            atompos -= 1; instruction ();
         } else if (type == sep) { // IF condition
            if (ifpos != atompos) fault (form);
            atompos = ifpos + 1;
            dump (cond () ^ 1); dumpw (0xE903); dumpw (0x0000);
            pushtext (finexp, 0); pushtext (finexp, cloc - 2);
         } else { // Statement IF condition
            oldapos = atompos - 1; atompos = ifpos + 1;
            dump (cond ());
            oldcpos = cpos; dump (0);
            atompos = oldapos; instruction ();
            v = cpos - oldcpos - 1; if (v > 127) fault (far);
            code (oldcpos) = v;
            access = 1;
         }
         ifpos = 0;
      }
      nextatom (); if ((type != sep) || (ifpos != 0)) fault (form);
   }
   fputc (10, object);
   fputc ('\n', list);
   exit (0);
   return (1);
}