/********************************************************************/
/*                              NEGA.C                              */
/********************************************************************/
/*                     The NEGA compiler, v1.04                     */
/*          Copyright (C) Jan. 1997 by Tylisha C. Andersen          */
/*           Original idea developed by Jim Neil, c. 1989           */
/********************************************************************/

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

int  FindCond(char *in, char *out);
int  GetLine(char *buf);
int  GetStatement(void);
int  GetToken(char *buf, int flg);
void InitToken(void);
int  IsEmpty(char *buf);
int  ParseBumping(void);
int  ParseCond(char *in, char *out, int flip);
int  ParseStatement(void);
int  PeekToken(char *buf);
int  ReadLine(char *buf);
void StripComments(char *buf);

void soutput(char *s, char *fmt, ...);
void xerror(int en);

/********************************************************************/
/* Data section                                                     */
/********************************************************************/

char InName[128], OutName[128];
char ErrorBuf[400], GenBuf[300];

char SendBuf[1000], FollowBuf[300];

char LineBuf[300];
long LineCtr = 0;
int  LinePos;

int  BlkStack[30], BlkType[30];
int  BlkPtr = -1, NextBlk = 1;
int  MainBlk = -1;

char Strs[32][128];
char Stat[64];

FILE *InFile = NULL, *OutFile = NULL;

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

main(int argc, char *argv[])
{
    printf("ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ\n"
           "Û³  The NEGA compiler v1.04 (symbolic assembler)  ³Û\n"
           "Û³ Copyright (C) Jan. 1997 by Tylisha C. Andersen ³Û\n"
           "ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß\n\n");

    if(argc < 2) {
        printf("Syntax: NEGA file[.N]\n");
        exit(1);
    }
    strcpy(InName, argv[1]);
    strupr(InName);
    if(!strchr(InName, '.'))
        strcat(InName, ".N");
    strcpy(OutName, InName);
    strcpy(strchr(OutName, '.'), ".ASM");
    if((InFile = fopen(InName, "rb")) == NULL) {
        sprintf(ErrorBuf, "can't open input file %s", InName);
        xerror(0);
    }
    if((OutFile = fopen(OutName, "wb")) == NULL) {
        sprintf(ErrorBuf, "can't open output file %s", OutName);
        xerror(1);
    }

    setvbuf(InFile, NULL, 16384, _IOFBF);
    setvbuf(OutFile, NULL, 16384, _IOFBF);
    printf("input:  %s\n", InName);
    printf("output: %s\n\n", OutName);

    while(ParseStatement());
    printf("\nprocessed %ld lines\n", LineCtr);

    fclose(InFile);
    fclose(OutFile);
    return 0;
}

/********************************************************************/
/* Parse one statement                                              */
/********************************************************************/

int ParseStatement(void)
{
    int i, j, true_blk, to_follow = 0;
    int sshift;
    char f_char, *p;
    static char cond1[5], cond2[5];
    static char sbuf[5], ibuf[64];

    if(!PeekToken(&f_char)) return 0;               // peek token to get char
    if(!GetStatement()) return 0;                   // get the statement
    if(BlkType[true_blk = BlkPtr] == 0)             // find the true block
        true_blk--;
    FollowBuf[0] = SendBuf[0] = 0;                  // clear output buffers
    to_follow = ParseBumping();                     // parse pointer bumping

    if(strchr(Stat, '{') || strchr(Stat, '}')) {

////////////////////////////////////////////////////// BLOCK STRUCTURES

        FindCond(Stat, cond1);                      // decode condition(s)
        FindCond(Stat, cond2);
        if((p = strchr(Stat, ',')) != NULL) {

////////////////////////////////////////////////////// ELSE CLAUSE

            if(!strchr(Stat, '{')) goto ps_blkerr;  // ELSE must have '{'
            strcpy(ibuf, p + 1); *(p + 1) = 0;      // separate into halves

            if(ibuf[j = 0] == '.') {                // check for long jump indicator
                memmove(ibuf, ibuf+1, 64 - 1);      // shift out indicator,
                j = 1;                              // j = long jump flag
            }

            if(!strcmp(Stat, "},"))                 // nothing
                ;
            else if(!strcmp(Stat, ".},") || !strcmp(Stat, "..},")) {
                i = true_blk;                       // break to penultimate block
                if(--i < 0) goto ps_noblk;          // not there?
                if(BlkType[i] == 0) i--;
                soutput(SendBuf, "\tjmp %s?End%04X\n",
                    Stat[1] == '.' ? "" : "short ", BlkStack[i]);
            } else {
                ParseCond(cond1, sbuf, 0);          // parse other conditions
                     if(!strcmp(Stat, "}C,"))   soutput(SendBuf, "\tj%s ?Top%04X\n", sbuf, BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}.,"))   soutput(SendBuf, "\tjmp ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}?==,")) soutput(SendBuf, "\tjcxz ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}-.,"))  soutput(SendBuf, "\tloop ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}-==,")) soutput(SendBuf, "\tloopz ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}-<>,")) soutput(SendBuf, "\tloopnz ?Top%04X\n", BlkStack[BlkPtr]);
                else goto ps_blkerr;
            }

            soutput(SendBuf, "\tjmp %s?End%04X\n",  // output jump to end
                j ? "" : "short ", BlkStack[true_blk]);
            soutput(SendBuf, "?Els%04X:\n", BlkStack[BlkPtr]);
            if(BlkType[BlkPtr] == 1)                // if true block, add an else block
                if(++BlkPtr >= 30) goto ps_nesterr;
            BlkStack[BlkPtr] = NextBlk++;           // set up new block
            BlkType[BlkPtr] = 0;

            if(!strcmp(ibuf, "{"))                  // nothing
                ;
            else if(!strcmp(ibuf, "C{")) {          // conditional else
                ParseCond(cond1, sbuf, 1);
                soutput(SendBuf, "\tj%s ?Els%04X\n", sbuf, BlkStack[BlkPtr]);
            } else if(!strcmp(ibuf, ".C{")) {       // IF clause (near jump)
                ParseCond(cond1, sbuf, 0);
                soutput(SendBuf, "\tj%s ?Top%04X\n", sbuf, BlkStack[BlkPtr]);
                soutput(SendBuf, "\tjmp ?Els%04X\n", BlkStack[BlkPtr]);
            } else if(!strcmp(ibuf, "?C{") && !strcmp(cond1, "<>")) {
                soutput(SendBuf, "\tjcxz ?Els%04X\n", BlkStack[BlkPtr]);
            } else goto ps_blkerr;
            soutput(SendBuf, "?Top%04X:\n", BlkStack[BlkPtr]);
        } else if(strchr(Stat, ';')) {

////////////////////////////////////////////////////// END OF BLOCK

            if(BlkPtr < 0) goto ps_blkerr;          // no block here?
            if(!strcmp(Stat, "};"))                 // nothing
                ;
            else if(!strcmp(Stat, ".};") || !strcmp(Stat, "..};")) {
                i = true_blk;                       // break to penultimate block
                if(--i < 0) goto ps_noblk;          // not there?
                if(BlkType[i] == 0) i--;
                soutput(SendBuf, "\tjmp %s?End%04X\n",
                    Stat[1] == '.' ? "" : "short ", BlkStack[i]);
            } else {
                ParseCond(cond1, sbuf, 0);          // parse other conditions
                     if(!strcmp(Stat, "}C;"))   soutput(SendBuf, "\tj%s ?Top%04X\n", sbuf, BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}.;"))   soutput(SendBuf, "\tjmp ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}?==;")) soutput(SendBuf, "\tjcxz ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}-.;"))  soutput(SendBuf, "\tloop ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}-==;")) soutput(SendBuf, "\tloopz ?Top%04X\n", BlkStack[BlkPtr]);
                else if(!strcmp(Stat, "}-<>;")) soutput(SendBuf, "\tloopnz ?Top%04X\n", BlkStack[BlkPtr]);
                else goto ps_blkerr;
            }

            soutput(SendBuf, "?Els%04X:\n", BlkStack[BlkPtr]);
            if(BlkType[BlkPtr] == 0) BlkPtr--;
            soutput(SendBuf, "?End%04X:\n", BlkStack[BlkPtr--]);
        } else {

////////////////////////////////////////////////////// START OF BLOCK

            if(++BlkPtr >= 30) goto ps_nesterr;     // add a true block
            BlkStack[BlkPtr] = NextBlk++;
            BlkType[BlkPtr] = 1;
            if(!strcmp(Stat, "{"))                  // nothing
                ;
            else if(!strcmp(Stat, "C{")) {          // IF clause (short jump)
                ParseCond(cond1, sbuf, 1);
                soutput(SendBuf, "\tj%s ?Els%04X\n", sbuf, BlkStack[BlkPtr]);
            } else if(!strcmp(Stat, ".C{")) {       // IF clause (near jump)
                ParseCond(cond1, sbuf, 0);
                soutput(SendBuf, "\tj%s ?Top%04X\n", sbuf, BlkStack[BlkPtr]);
                soutput(SendBuf, "\tjmp ?Els%04X\n", BlkStack[BlkPtr]);
            } else if(!strcmp(Stat, "?C{") && !strcmp(cond1, "<>")) {
                soutput(SendBuf, "\tjcxz ?Els%04X\n", BlkStack[BlkPtr]);
            } else goto ps_blkerr;
            soutput(SendBuf, "?Top%04X:\n", BlkStack[BlkPtr]);
        }
    } else if(f_char == 'A') {

////////////////////////////////////////////////////// ALPHA STATEMENT

        if(!memcmp(Stat, "A=A=", 4)) {              // reverse-output command?
            p = Stat;
            while(!memcmp(p, "A=", 2))              // check for POP sequence
                p += 2;
            if(*p == ';') {                         // if POP sequence, then
                i = (p - 2 - Stat) / 2;
                for(; i >= 0; i--)                  // output the POPs
                    soutput(SendBuf, "\tpop %s\n", Strs[i]);
                goto ps_output;
            } else {                                // chained assignments
                j = (p - 2 - Stat) / 2;             // j = work register
                p = Stat; i = 0;
                for(i = 0; i < j; i++) {            // output in order
                    soutput(FollowBuf, "\tmov %s,%s\n",
                        Strs[i], Strs[j]);
                }
                for(i = 0; i < 32 - j; i++)         // shift out assignment commands
                    strcpy(Strs[i], Strs[i + j]);
                memmove(Stat, Stat + j * 2, 64 - j * 2);
                to_follow = 1;                      // set follow flag
            }
        }

        j = 1;                                      // current operand
        sshift = 0;                                 // no shift for now

////////////////////////////////////////////////////// First-op-only commands

        if(!memcmp(Stat, "A,", 2)) {
            if(!strcmp(Stat, "A,A*A;")) {               // three-operand IMUL
                soutput(SendBuf, "\timul %s,%s,%s\n", Strs[0], Strs[1], Strs[2]);
                goto ps_output;
            } else if(!strcmp(Stat, "A,A<A;")) {        // shift left double
                soutput(SendBuf, "\tshld %s,%s,%s\n", Strs[0], Strs[1], Strs[2]);
                goto ps_output;
            } else if(!strcmp(Stat, "A,A>A;")) {        // shift right double
                soutput(SendBuf, "\tshrd %s,%s,%s\n", Strs[1], Strs[0], Strs[2]);
                goto ps_output;
            } else if(!strcmp(Stat, "A,A=A;")) {        // LDS, LES, etc.
                soutput(SendBuf, "\tl%s %s,%s\n", Strs[0], Strs[1], Strs[2]);
                goto ps_output;
            }
        } else if(!strcmp (Stat, "A=;")) {          // single POP
            soutput(SendBuf, "\tpop %s\n", Strs[0]); goto ps_output;
        } else if(!memcmp(Stat, "A=A*A", 5)) {      // 3-op IMUL (optimization!)
            soutput(SendBuf, "\timul %s,%s,%s\n", Strs[0], Strs[1], Strs[2]);
            sshift = 4; j++;                        // skip 1 extra operand
        } else if(!memcmp(Stat, "A=A", 3)) {    soutput(SendBuf, "\tmov %s,%s\n", Strs[0], Strs[1]);    sshift = 2;        } else if(!memcmp(Stat, "A=+A", 4)) {   soutput(SendBuf, "\tmovzx %s,%s\n", Strs[0], Strs[1]);  sshift = 3;
        } else if(!memcmp(Stat, "A=+A", 4)) {   soutput(SendBuf, "\tmovzx %s,%s\n", Strs[0], Strs[1]);  sshift = 3;
        } else if(!memcmp(Stat, "A=+-A", 5)) {  soutput(SendBuf, "\tmovsx %s,%s\n", Strs[0], Strs[1]);  sshift = 4;
        } else if(!memcmp(Stat, "A==A", 4)) {   soutput(SendBuf, "\txchg %s,%s\n", Strs[0], Strs[1]);   sshift = 3;
        } else if(!memcmp(Stat, "A===A", 5)) {  soutput(SendBuf, "\tlea %s,%s\n", Strs[0], Strs[1]);    sshift = 4;
        } else if(!memcmp(Stat, "A=", 2)) {
            strcpy(ibuf, Stat + 2);                 // conditional set (SETNZ, SETBE, etc.)
            i = strlen(ibuf) - 1;
            if(ibuf[i] == ';') {
                ibuf[i] = 0;
                if(ParseCond(ibuf, sbuf, 0)) {
                    soutput(SendBuf, "\tset%s %s\n", sbuf, Strs[0]);
                    goto ps_output;
                }
            }
        }

////////////////////////////////////////////////////// General commands

        while(1) {
            if(sshift > 0) {                        // shift out first part
                memmove(Stat, Stat + sshift, 64 - sshift);
                j++;                                // next operand
            }

            if(!strcmp(Stat, "A;") && j > 1) {
                goto ps_output;
            } else if(!strcmp(Stat, "A+;")) {   soutput(SendBuf, "\tinc %s\n", Strs[0]);                goto ps_output;
            } else if(!strcmp(Stat, "A-;")) {   soutput(SendBuf, "\tdec %s\n", Strs[0]);                goto ps_output;
            } else if(!strcmp(Stat, "A++;")) {  soutput(SendBuf, "\tinc %s\n\tinc %s\n", Strs[0], Strs[0]);
                                                                                                        goto ps_output;
            } else if(!strcmp(Stat, "A--;")) {  soutput(SendBuf, "\tdec %s\n\tdec %s\n", Strs[0], Strs[0]);
                                                                                                        goto ps_output;
            } else if(!strcmp(Stat, "A&A?")) {  soutput(SendBuf, "\ttest %s,%s\n", Strs[0], Strs[j]);   goto ps_output;
            } else if(!strcmp(Stat, "A-A?")) {  soutput(SendBuf, "\tcmp %s,%s\n", Strs[0], Strs[j]);    goto ps_output;
            } else if(!strcmp(Stat, "A?")) {    soutput(SendBuf, "\ttest %s,%s\n", Strs[0], Strs[0]);   goto ps_output;
            } else if(!memcmp(Stat, "A+A", 3)) {    soutput(SendBuf, "\tadd %s,%s\n", Strs[0], Strs[j]);    sshift = 2;
            } else if(!memcmp(Stat, "A++A", 4)) {   soutput(SendBuf, "\tadc %s,%s\n", Strs[0], Strs[j]);    sshift = 3;
            } else if(!memcmp(Stat, "A-A", 3)) {    soutput(SendBuf, "\tsub %s,%s\n", Strs[0], Strs[j]);    sshift = 2;
            } else if(!memcmp(Stat, "A--A", 4)) {   soutput(SendBuf, "\tsbb %s,%s\n", Strs[0], Strs[j]);    sshift = 3;
            } else if(!memcmp(Stat, "A&A", 3)) {    soutput(SendBuf, "\tand %s,%s\n", Strs[0], Strs[j]);    sshift = 2;
            } else if(!memcmp(Stat, "A|A", 3)) {    soutput(SendBuf, "\tor %s,%s\n", Strs[0], Strs[j]);     sshift = 2;
            } else if(!memcmp(Stat, "A^A", 3)) {    soutput(SendBuf, "\txor %s,%s\n", Strs[0], Strs[j]);    sshift = 2;
            } else if(!memcmp(Stat, "A*A", 3)) {    soutput(SendBuf, "\timul %s,%s\n", Strs[0], Strs[j]);   sshift = 2;
            } else if(!memcmp(Stat, "A<A", 3)) {    soutput(SendBuf, "\tshl %s,%s\n", Strs[0], Strs[j]);    sshift = 2;
            } else if(!memcmp(Stat, "A>A", 3)) {    soutput(SendBuf, "\tshr %s,%s\n", Strs[0], Strs[j]);    sshift = 2;
            } else if(!memcmp(Stat, "A+->A", 5)) {  soutput(SendBuf, "\tsar %s,%s\n", Strs[0], Strs[j]);    sshift = 4;
            } else if(!memcmp(Stat, "A<<A", 4)) {   soutput(SendBuf, "\trol %s,%s\n", Strs[0], Strs[j]);    sshift = 3;
            } else if(!memcmp(Stat, "A>>A", 4)) {   soutput(SendBuf, "\tror %s,%s\n", Strs[0], Strs[j]);    sshift = 3;
            } else if(!memcmp(Stat, "A<<<A", 3)) {  soutput(SendBuf, "\trcl %s,%s\n", Strs[0], Strs[j]);    sshift = 4;
            } else if(!memcmp(Stat, "A>>>A", 3)) {  soutput(SendBuf, "\trcr %s,%s\n", Strs[0], Strs[j]);    sshift = 4;
            } else if(!memcmp(Stat, "A**A", 4)) {   soutput(SendBuf, "\tmul %s,%s\n", Strs[0], Strs[j]);    sshift = 3;
            } else if(!memcmp(Stat, "A/A", 3)) {    soutput(SendBuf, "\tidiv %s,%s\n", Strs[0], Strs[j]);   sshift = 2;
            } else if(!memcmp(Stat, "A//A", 4)) {   soutput(SendBuf, "\tdiv %s,%s\n", Strs[0], Strs[j]);    sshift = 3;
            } else {
                if(j == 1) goto ps_passthru;       // pass-through?
                else goto ps_error;
            }
        }
    } else switch(f_char) {                        // OTHER STATEMENTS (by char)
        case '!':
                 if(!strcmp(Stat, "!A;"))           soutput(SendBuf, "\tint %s\n", Strs[0]);
            else if(!strcmp(Stat, "!+;"))           soutput(SendBuf, "\tsti\n");
            else if(!strcmp(Stat, "!-;"))           soutput(SendBuf, "\tcli\n");
            else if(!strcmp(Stat, "!.=;"))          soutput(SendBuf, "\tiret\n");
            else if(!strcmp(Stat, "!+-;"))          soutput(SendBuf, "\tinto\n");
            else goto ps_error;
            break;

        case '\"':
                 if(!strcmp(Stat, "\"*;"))          soutput(SendBuf, "\taam\n");
            else if(!strcmp(Stat, "\"+;"))          soutput(SendBuf, "\taaa\n");
            else if(!strcmp(Stat, "\"-;"))          soutput(SendBuf, "\taas\n");
            else if(!strcmp(Stat, "\"/;"))          soutput(SendBuf, "\taad\n");
            else if(!strcmp(Stat, "\"*A;"))         soutput(SendBuf, "\tdb 0D4h, %s\n", Strs[0]);
            else if(!strcmp(Stat, "\"/A;"))         soutput(SendBuf, "\tdb 0D5h, %s\n", Strs[0]);
            else goto ps_error;
            break;

        case '&':
                 if(!strcmp(Stat, "&&A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "&&;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "&;"))            soutput(SendBuf, "\tclc\n");
            else if(!strcmp(Stat, "&A;"))           soutput(SendBuf, "\tsub %s,%s\n", Strs[0], Strs[0]);
            else goto ps_error;
            break;

        case '\'':
                 if(!strcmp(Stat, "\'+;"))          soutput(SendBuf, "\tdaa\n");
            else if(!strcmp(Stat, "\'-;"))          soutput(SendBuf, "\tdas\n");
            else goto ps_error;
            break;

        case '*':
                 if(!strcmp(Stat, "**A;"))          soutput(SendBuf, "\tmul %s\n", Strs[0]);
            else if(!strcmp(Stat, "*A;"))           soutput(SendBuf, "\timul %s\n", Strs[0]);
            else if(!strcmp(Stat, "*;"))            soutput(SendBuf, "\tcbw\n");
            else if(!strcmp(Stat, "**;"))           soutput(SendBuf, "\tcwd\n");
            else if(!strcmp(Stat, "****;"))         soutput(SendBuf, "\tcdq\n");
            else if(!strcmp(Stat, "*=;"))           soutput(SendBuf, "\tstosb\n");
            else if(!strcmp(Stat, "**=;"))          soutput(SendBuf, "\tstosw\n");
            else if(!strcmp(Stat, "****=;"))        soutput(SendBuf, "\tstosd\n");
            else if(!strcmp(Stat, "*=*;"))          soutput(SendBuf, "\tmovsb\n");
            else if(!strcmp(Stat, "**=**;"))        soutput(SendBuf, "\tmovsw\n");
            else if(!strcmp(Stat, "****=****;"))    soutput(SendBuf, "\tmovsd\n");
            else if(!strcmp(Stat, "*-?"))           soutput(SendBuf, "\tscasb\n");
            else if(!strcmp(Stat, "**-?"))          soutput(SendBuf, "\tscasw\n");
            else if(!strcmp(Stat, "****-?"))        soutput(SendBuf, "\tscasd\n");
            else if(!strcmp(Stat, "*-*?"))          soutput(SendBuf, "\tcmpsb\n");
            else if(!strcmp(Stat, "**-**?"))        soutput(SendBuf, "\tcmpsw\n");
            else if(!strcmp(Stat, "****-****?"))    soutput(SendBuf, "\tcmpsd\n");
            else if(!strcmp(Stat, "*=@;"))          soutput(SendBuf, "\tinsb\n");
            else if(!strcmp(Stat, "**=@@;"))        soutput(SendBuf, "\tinsw\n");
            else if(!strcmp(Stat, "****=@@@@;"))    soutput(SendBuf, "\tinsd\n");
            else if(!strcmp(Stat, "**=@;"))         soutput(SendBuf, "\tinsw\n");
            else if(!strcmp(Stat, "****=@;"))       soutput(SendBuf, "\tinsd\n");
            else if(!strcmp(Stat, "*A=;"))          soutput(SendBuf, "\tstos %s\n", Strs[0]);
            else if(!strcmp(Stat, "*A=*A;"))        soutput(SendBuf, "\tmovs %s,%s\n", Strs[0], Strs[1]);
            else if(!strcmp(Stat, "*A-?"))          soutput(SendBuf, "\tscas %s\n", Strs[0]);
            else if(!strcmp(Stat, "*A-*A?"))        soutput(SendBuf, "\tcmps %s,%s\n", Strs[0], Strs[1]);
            else if(!strcmp(Stat, "*A=@;"))         soutput(SendBuf, "\tins %s\n", Strs[0]);
            else goto ps_error;
            break;

        case '+':
                 if(!strcmp(Stat, "++A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "+-A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "++;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "+-;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "+;"))            soutput(SendBuf, "\tcld\n");
            else goto ps_error;
            break;

        case '-':
                 if(!strcmp(Stat, "--A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "-+A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "--;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "-+;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "-;"))            soutput(SendBuf, "\tstd\n");
            else if(!strcmp(Stat, "-A;"))           soutput(SendBuf, "\tneg %s\n", Strs[0]);
            else if(!strcmp(Stat, "-.A;"))          soutput(SendBuf, "\tloop %s\n", Strs[0]);
            else if(!strcmp(Stat, "-==A;"))         soutput(SendBuf, "\tloopz %s\n", Strs[0]);
            else if(!strcmp(Stat, "-<>A;"))         soutput(SendBuf, "\tloopnz %s\n", Strs[0]);
            else goto ps_error;
            break;

        case '.':
            if(!strcmp(Stat, ".A;") || !strcmp(Stat, "..A;")) {
                if(Strs[0][1] == 0 && isdigit(Strs[0][0])) {
                    j = Strs[0][0] - '0';           // break multi-level
                    for(i = 0; i < j; i++) {
                        if(--true_blk < 0) goto ps_noblk;
                        if(BlkType[true_blk] == 0) true_blk--;
                    }
                    soutput(SendBuf, "\tjmp %s?End%04X\n",
                        Stat[1] == '.' ? "" : "short ", BlkStack[true_blk]);
                } else {
                    soutput(SendBuf, "\tjmp %s%s\n",
                        Stat[1] == '.' ? "" : "short ", Strs[0]);
                }
            } else if(!strcmp(Stat, ".;") || !strcmp(Stat, "..;")) {
                if(true_blk < 0) goto ps_noblk;     // break out of block
                soutput(SendBuf, "\tjmp %s?End%04X\n",
                    Stat[1] == '.' ? "" : "short ", BlkStack[true_blk]);
            }
            else if(!strcmp(Stat, ".=;"))           soutput(SendBuf, "\tret\n");
            else if(!strcmp(Stat, ".=A;"))          soutput(SendBuf, "\tret %s\n", Strs[0]);
            else if(!strcmp(Stat, "...;"))          soutput(SendBuf, "\twait\n");
            else if(!strcmp(Stat, "....;"))         soutput(SendBuf, "\thlt\n");
            else if(strchr(Stat, 'A'))
                goto ps_parsejcc0;
            else goto ps_parsebrk0;
            break;

        case '/':
                 if(!strcmp(Stat, "//A;"))          soutput(SendBuf, "\tdiv %s\n", Strs[0]);
            else if(!strcmp(Stat, "/A;"))           soutput(SendBuf, "\tidiv %s\n", Strs[0]);
            else goto ps_error;
            break;

        case '<':
                 if(!strcmp(Stat, "<<A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "<<=A;"))         goto ps_parsejcc0;
            else if(!strcmp(Stat, "<A;"))           goto ps_parsejcc0;
            else if(!strcmp(Stat, "<=A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "<>A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "<<;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "<<=;"))          goto ps_parsebrk0;
            else if(!strcmp(Stat, "<;"))            goto ps_parsebrk0;
            else if(!strcmp(Stat, "<=;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "<>;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "<>=*;"))         soutput(SendBuf, "\trep lodsb\n");
            else if(!strcmp(Stat, "<>=**;"))        soutput(SendBuf, "\trep lodsw\n");
            else if(!strcmp(Stat, "<>=****;"))      soutput(SendBuf, "\trep lodsd\n");
            else if(!strcmp(Stat, "<>*=;"))         soutput(SendBuf, "\trep stosb\n");
            else if(!strcmp(Stat, "<>**=;"))        soutput(SendBuf, "\trep stosw\n");
            else if(!strcmp(Stat, "<>****=;"))      soutput(SendBuf, "\trep stosd\n");
            else if(!strcmp(Stat, "<>*=*;"))        soutput(SendBuf, "\trep movsb\n");
            else if(!strcmp(Stat, "<>**=**;"))      soutput(SendBuf, "\trep movsw\n");
            else if(!strcmp(Stat, "<>****=****;"))  soutput(SendBuf, "\trep movsd\n");
            else if(!strcmp(Stat, "<>*-?"))         soutput(SendBuf, "\trepne scasb\n");
            else if(!strcmp(Stat, "<>**-?"))        soutput(SendBuf, "\trepne scasw\n");
            else if(!strcmp(Stat, "<>****-?"))      soutput(SendBuf, "\trepne scasd\n");
            else if(!strcmp(Stat, "<>*-*?"))        soutput(SendBuf, "\trepne cmpsb\n");
            else if(!strcmp(Stat, "<>**-**?"))      soutput(SendBuf, "\trepne cmpsw\n");
            else if(!strcmp(Stat, "<>****-****?"))  soutput(SendBuf, "\trepne cmpsd\n");
            else if(!strcmp(Stat, "<>*=@;"))        soutput(SendBuf, "\trep insb\n");
            else if(!strcmp(Stat, "<>**=@@;"))      soutput(SendBuf, "\trep insw\n");
            else if(!strcmp(Stat, "<>****=@@@@;"))  soutput(SendBuf, "\trep insd\n");
            else if(!strcmp(Stat, "<>@=*;"))        soutput(SendBuf, "\trep outsb\n");
            else if(!strcmp(Stat, "<>@@=**;"))      soutput(SendBuf, "\trep outsw\n");
            else if(!strcmp(Stat, "<>@@@@=****;"))  soutput(SendBuf, "\trep outsd\n");
            else if(!strcmp(Stat, "<>=*A;"))        soutput(SendBuf, "\trep lods %s\n", Strs[0]);
            else if(!strcmp(Stat, "<>*A=;"))        soutput(SendBuf, "\trep stos %s\n", Strs[0]);
            else if(!strcmp(Stat, "<>*A=*A;"))      soutput(SendBuf, "\trep movs %s,%s\n", Strs[0], Strs[1]);
            else if(!strcmp(Stat, "<>*A-?"))        soutput(SendBuf, "\trepne scas %s\n", Strs[0]);
            else if(!strcmp(Stat, "<>*A-*A?"))      soutput(SendBuf, "\trepne cmps %s,%s\n", Strs[0], Strs[1]);
            else if(!strcmp(Stat, "<>*A=@;"))       soutput(SendBuf, "\trep ins %s\n", Strs[0]);
            else if(!strcmp(Stat, "<>@=*A;"))       soutput(SendBuf, "\trep outs %s\n", Strs[0]);
            else goto ps_error;
            break;

        case '=':
            if(!memcmp(Stat, "=A", 2)) {           // PUSH sequence
                i = 0; p = Stat;
                while(!memcmp(p, "=A", 2)) {
                    soutput(SendBuf, "\tpush %s\n", Strs[i]);
                    p += 2; i++;
                };
                if(*p != ';') goto ps_error;
            }
            else if(!strcmp(Stat, "==A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "=<<A;"))         goto ps_parsejcc0;
            else if(!strcmp(Stat, "=>>A;"))         goto ps_parsejcc0;
            else if(!strcmp(Stat, "=<A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "=>A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "==;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "=<<;"))          goto ps_parsebrk0;
            else if(!strcmp(Stat, "=<;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "=>>;"))          goto ps_parsebrk0;
            else if(!strcmp(Stat, "=>;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "=.A;"))          soutput(SendBuf, "\tcall %s\n", Strs[0]);
            else if(!strcmp(Stat, "=??;"))          soutput(SendBuf, "\tpushf\n");
            else if(!strcmp(Stat, "=????;"))        soutput(SendBuf, "\tpushfd\n");
            else if(!strcmp(Stat, "=?;"))           soutput(SendBuf, "\tlahf\n");
            else if(!strcmp(Stat, "=;"))            soutput(SendBuf, "\tdb 0D6h\n");
            else if(!strcmp(Stat, "=@;"))           soutput(SendBuf, "\tin al,dx\n");
            else if(!strcmp(Stat, "=@@;"))          soutput(SendBuf, "\tin ax,dx\n");
            else if(!strcmp(Stat, "=@@@@;"))        soutput(SendBuf, "\tin eax,dx\n");
            else if(!strcmp(Stat, "=@A;"))          soutput(SendBuf, "\tin al,%s\n", Strs[0]);
            else if(!strcmp(Stat, "=@@A;"))         soutput(SendBuf, "\tin ax,%s\n", Strs[0]);
            else if(!strcmp(Stat, "=@@@@A;"))       soutput(SendBuf, "\tin eax,%s\n", Strs[0]);
            else if(!strcmp(Stat, "=*;"))           soutput(SendBuf, "\tlodsb\n");
            else if(!strcmp(Stat, "=**;"))          soutput(SendBuf, "\tlodsw\n");
            else if(!strcmp(Stat, "=****;"))        soutput(SendBuf, "\tlodsd\n");
            else if(!strcmp(Stat, "=*A;"))          soutput(SendBuf, "\tlods %s\n", Strs[0]);
            else if(!strcmp(Stat, "==*-?"))         soutput(SendBuf, "\trepe scasb\n");
            else if(!strcmp(Stat, "==**-?"))        soutput(SendBuf, "\trepe scasw\n");
            else if(!strcmp(Stat, "==****-?"))      soutput(SendBuf, "\trepe scasd\n");
            else if(!strcmp(Stat, "==*-*?"))        soutput(SendBuf, "\trepe cmpsb\n");
            else if(!strcmp(Stat, "==**-**?"))      soutput(SendBuf, "\trepe cmpsw\n");
            else if(!strcmp(Stat, "==****-****?"))  soutput(SendBuf, "\trepe cmpsd\n");
            else if(!strcmp(Stat, "==*A-?"))        soutput(SendBuf, "\trepe scas %s\n", Strs[0]);
            else if(!strcmp(Stat, "==*A-*A?"))      soutput(SendBuf, "\trepe cmps %s,%s\n", Strs[0], Strs[1]);
            else goto ps_error;
            break;

        case '>':
                 if(!strcmp(Stat, ">>A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, ">>=A;"))         goto ps_parsejcc0;
            else if(!strcmp(Stat, ">A;"))           goto ps_parsejcc0;
            else if(!strcmp(Stat, ">=A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, ">>;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, ">>=;"))          goto ps_parsebrk0;
            else if(!strcmp(Stat, ">;"))            goto ps_parsebrk0;
            else if(!strcmp(Stat, ">=;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "><;"))           soutput(SendBuf, "\txlatb\n");
            else if(!strcmp(Stat, "><A;"))          soutput(SendBuf, "\txlat %s\n", Strs[0]);
            else goto ps_error;
            break;

        case '?':
            if(!strcmp(Stat, "?==;")) {             // Break on CXZ
                if(true_blk < 0) goto ps_noblk;
                soutput(SendBuf, "\tjcxz ?End%04X\n", BlkStack[true_blk]);
            }
            else if(!strcmp(Stat, "?=;"))           soutput(SendBuf, "\tsahf\n", Strs[1]);
            else if(!strcmp(Stat, "?==A;"))         soutput(SendBuf, "\tjcxz %s\n", Strs[0]);
            else if(!strcmp(Stat, "??=;"))          soutput(SendBuf, "\tpopf\n");
            else if(!strcmp(Stat, "????=;"))        soutput(SendBuf, "\tpopfd\n");
            else goto ps_error;
            break;

        case '@':
                 if(!strcmp(Stat, "@=;"))           soutput(SendBuf, "\tout dx,al\n");
            else if(!strcmp(Stat, "@@=;"))          soutput(SendBuf, "\tout dx,ax\n");
            else if(!strcmp(Stat, "@@@@=;"))        soutput(SendBuf, "\tout dx,eax\n");
            else if(!strcmp(Stat, "@A=;"))          soutput(SendBuf, "\tout %s,al\n", Strs[0]);
            else if(!strcmp(Stat, "@@A=;"))         soutput(SendBuf, "\tout %s,ax\n", Strs[0]);
            else if(!strcmp(Stat, "@@@@A=;"))       soutput(SendBuf, "\tout %s,eax\n", Strs[0]);
            else if(!strcmp(Stat, "@=*;"))          soutput(SendBuf, "\toutsb\n");
            else if(!strcmp(Stat, "@@=**;"))        soutput(SendBuf, "\toutsw\n");
            else if(!strcmp(Stat, "@@@@=****;"))    soutput(SendBuf, "\toutsd\n");
            else if(!strcmp(Stat, "@=*A;"))         soutput(SendBuf, "\touts %s\n", Strs[0]);
            else goto ps_error;
            break;

        case '^':
                 if(!strcmp(Stat, "^;"))            soutput(SendBuf, "\tcmc\n");
            else if(!strcmp(Stat, "^A;"))           soutput(SendBuf, "\tnot %s", Strs[0]);
            else goto ps_error;
            break;

        case '|':
                 if(!strcmp(Stat, "||A;"))          goto ps_parsejcc0;
            else if(!strcmp(Stat, "||;"))           goto ps_parsebrk0;
            else if(!strcmp(Stat, "|;"))            soutput(SendBuf, "\tstc\n");
            else goto ps_error;
            break;

        case ';':
            break;

        default:
            goto ps_error;
    }

////////////////////////////////////////////// OUTPUT SEQUENCE

ps_output:
    fputs(SendBuf, OutFile);                // send output buffer
    if(to_follow)
        fputs(FollowBuf, OutFile);          // send follow codes (if any)
    return 1;

////////////////////////////////////////////// CONDITIONAL JUMP

ps_parsejcc0:
    if(!FindCond(Stat, cond1))              // find the condition code
        goto ps_error;
    if(Strs[0][1] == 0 && isdigit(Strs[0][0])) {
        j = Strs[0][0] - '0';               // break multi-level
        for(i = 0; i < j; i++) {
            if(--true_blk < 0) goto ps_noblk;
            if(BlkType[true_blk] == 0) true_blk--;
        }
        sprintf(ibuf, "?End%04X", BlkStack[true_blk]);
    } else strcpy(ibuf, Strs[0]);
    if(Stat[0] == '.') {
        ParseCond(cond1, sbuf, 1);
        soutput(SendBuf, "\tj%s ?End%04X\n", sbuf, NextBlk);
        soutput(SendBuf, "\tjmp %s\n", ibuf);
        soutput(SendBuf, "?End%04X:\n", NextBlk++);
    } else {
        ParseCond(cond1, sbuf, 0);
        soutput(SendBuf, "\tj%s %s\n", sbuf, ibuf);
    }
    goto ps_output;

////////////////////////////////////////////// CONDITIONAL BREAK

ps_parsebrk0:
    if(true_blk < 0) goto ps_noblk;
    FindCond(Stat, cond1);
    if(Stat[0] == '.') {
        ParseCond(cond1, sbuf, 1);
        soutput(SendBuf, "\tj%s ?End%04X\n", sbuf, NextBlk);
        soutput(SendBuf, "\tjmp ?End%04X\n", BlkStack[true_blk]);
        soutput(SendBuf, "?End%04X:\n", NextBlk++);
    } else {
        ParseCond(cond1, sbuf, 0);
        soutput(SendBuf, "\tj%s ?End%04X\n", sbuf, BlkStack[true_blk]);
    }
    goto ps_output;

////////////////////////////////////////////// ERROR CONDITIONS

ps_error:
    sprintf(ErrorBuf, "syntax error at or near line %ld", LineCtr);
    xerror(2);

ps_nesterr:
    sprintf(ErrorBuf, "nesting too deep at or near line %ld", LineCtr);
    xerror(5);

ps_blkerr:
    sprintf(ErrorBuf, "block syntax error at or near line %ld", LineCtr);
    xerror(6);

ps_noblk:
    sprintf(ErrorBuf, "block does not exist at or near line %ld", LineCtr);
    xerror(7);

////////////////////////////////////////////// PASS-THROUGH CODE

ps_passthru:
    p = Stat; i = 0;                        // p = pointer, i = string ctr.
    soutput(SendBuf, "\t");
    if(*p == 0) return 1;                   // empty statement?
    while(*(p+1) != 0) {                    // send each piece
        if(*p == 'A') soutput(SendBuf, " %s", Strs[i++]);
        else soutput(SendBuf, " %c", *p);
        p++;
    }
    fprintf(OutFile, "%s\n", SendBuf);      // send output buffer
    return 1;
}

/********************************************************************/
/* Parse pointer bumping (if any)                                   */
/********************************************************************/

int ParseBumping(void)
{
    int i, j, n, d, l, r = 0;
    char *s, *p, a[128];

    for(i = n = 0; i < strlen(Stat); i++)           // count the strings
        if(Stat[i] == 'A') n++;

    for(n--; n >= 0; n--) {
        s = Strs[n];
        do {
            d = 0; l = strlen(s);
            if((p = strchr(s, '[')) != NULL) {      // look for '[+' or '[-'
                if(*(p+1) == '+' || *(p+1) == '-') {
                    i = (p+2) - s;                  // extract alpha string
                    for(; i < l; i++) if(isalnum(s[i])) break;
                    for(j = 0; i+j < l; j++) {
                        if(!isalnum(s[i+j])) break;
                        a[j] = s[i+j];
                    }
                    a[j] = 0;                       // finish up
                    soutput(SendBuf, "\t%s %s\n",
                        *(p+1) == '+' ? "inc" : "dec", a);
                    memmove(p+1, p+2, 126 - (p - s)); d = 1;
                }
            }
            if((p = strchr(s+1, ']')) != NULL) {    // look for '+]' or '-]'
                if(*(p-1) == '+' || *(p-1) == '-') {
                    i = (p-2) - s;                  // extract alpha string
                    for(; i >= 0; i--) if(isalnum(s[i])) break;
                    a[0] = 0;
                    for(; i >= 0; i--) {
                        if(!isalnum(s[i])) break;
                        memmove(a+1, a, 127);
                        a[0] = s[i];
                    }
                    soutput(FollowBuf, "\t%s %s\n", // finish up
                        *(p-1) == '+' ? "inc" : "dec", a);
                    memmove(p-1, p, 128 - (p - s)); d = r = 1;
                }
            }
        } while(d);
    }

    return(r);
}

/********************************************************************/
/* Parse a conditional string                                       */
/********************************************************************/

int ParseCond(char *in, char *out, int flip)
{
    static char *outs[16] = {               // output strings
        "o", "no", "b", "ae", "z", "nz", "be", "a",
        "s", "ns", "pe", "po", "l", "ge", "le", "g" };
    static char *ins[20] = {                // input strings
        "+-", "-+", "<<", ">>=", "=>>", "==", "<>", "<<=", "=<<", ">>",
        "--", "++", "&&", "||", "<", ">=", "=>", "<=", "=<", ">" };
    static int inv[20] = {                  // string values
        0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8,
        9, 10, 11, 12, 13, 13, 14, 14, 15 };

    int i, n = -1;

    for(i = 0; i < 20; i++)                 // find the string
        if(!strcmp(in, ins[i]))
            n = inv[i];
    if(n == -1) {                           // not a condition?
        *out = 0;                           // return a blank string
        return 0;
    }
    if(flip) n ^= 1;                        // opposite condition if needed
    strcpy(out, outs[n]);                   // return the condition string
    return 1;
}

/********************************************************************/
/* Find a conditional in a statement                                */
/********************************************************************/

int FindCond(char *in, char *out)
{
    static char *conds[20] = {                  // List of conditions
        ">>=", "=>>", "<<=", "=<<", ">=", "=>", "<=", "=<", "+-", "-+",
        "--", "++", "<>", "<<", ">>", "<", ">", "==", "&&", "||" };

    int i, j, l = strlen(in), m;

    for(i = 0; i < l; i++) {
        for(j = 0; j < 20; j++) {
            m = strlen(conds[j]);               // get length of string
            if(!memcmp(in + i, conds[j], m)) {  // found a condition?
                strcpy(out, conds[j]);          // copy it into the output buffer
                memmove(in + i + 1, in + i + m, l - i - m + 1);
                in[i] = 'C';                    // replace it by a 'C' in the main string
                return 1;
            }
        }
    }

    out[0] = 0;                                 // not found, return a blank string
    return 0;
}

/********************************************************************/
/* Get one statement                                                */
/********************************************************************/

int GetStatement(void)
{
    static char buf[300];
    char *p = Stat, oc, c = -1;
    int stn = 0;

    do {
        oc = c;                                 // oc = prev. char
        if(!GetToken(buf, p - Stat)) return 0;  // get a token
        if(isalnum(*buf) || strlen(buf) > 1) {  // if alphanumeric or string,
            strcpy(Strs[stn++], buf);           // then add it to string the list
            *p++ = c = 'A';                     // and code it as an 'A'
        } else *p++ = c = *buf;                 // otherwise, code it as itself
        if(p - Stat > 60 || stn > 30) {         // out of space?
            sprintf(ErrorBuf, "statement too big at or near line %d", LineCtr);
            xerror(3);
        }
    } while(c != ';' && c != '{' &&             // terminators are ';' and '{', sometines '?'.
            (c != '?' || !strchr("A-*", oc)));  // '?' is terminator only if after 'A', '-', or '*'.
    *p++ = 0;
    return 1;
}

/********************************************************************/
/* Init token parsing                                               */
/********************************************************************/

void InitToken(void)
{
    GetLine(LineBuf);
    LinePos = 0;
}

/********************************************************************/
/* Return one token                                                 */
/********************************************************************/

int GetToken(char *buf, int flg)
{
    int i, j, c, e;
    char deli[5] = "[\'\"";

    if(!flg) deli[1] = 0;                       // strings OK?

    while(1) {
        c = LineBuf[LinePos++];                 // get char
        if(c == 0) {                            // end of line?
            if(!GetLine(LineBuf)) return 0;     // get line
            LinePos = 0;                        // reset line pos
            continue;
        } else if(isspace(c)) {                 // skip spaces
            continue;
        } else {
            buf[0] = c;                         // set first char
            if(!isalnum(c)) {                   // alphanumeric?
                if(c == '(') {                  // parenthesis?
                    j = 1; i = -1;              // depth 1, position -1
                    do {
                        buf[++i] = LineBuf[LinePos++];  // add char to string
                        if(i > 100) {                   // string too long?
                            sprintf(ErrorBuf, "Expression too long at or near line %d", LineCtr);
                            xerror(4);
                        } else if(buf[i] == 0) {        // end of line?
                            if(!GetLine(LineBuf)) return 0;
                            LinePos = 0; --i; continue;
                        }
                        if(buf[i] == ')') j--;          // right parenthesis = reduce depth
                        if(buf[i] == '(') j++;          // left parenthesis = increase depth
                    } while(j > 0);                     // loop while depth > 0
                    buf[i] = 0;                 // add null terminator
                } else if(strchr(deli, c)) {    // delimiter?
                    if(c == '[') e = ']';       // e = end delimiter
                    else e = c;
                    j = 1; i = 0;               // depth 1, position 0
                    do {
                        buf[++i] = LineBuf[LinePos++];  // add char to string
                        if(i > 100) {                   // string too long?
                            sprintf(ErrorBuf, "String or address too long at or near line %d", LineCtr);
                            xerror(8);
                        } else if(buf[i] == 0) {        // end of line?
                            sprintf(ErrorBuf, "String or address spans lines at or near line %d", LineCtr);
                            xerror(9);
                        }
                        if(buf[i] == e)      j--;       // ending delimiter = reduce depth
                        else if(buf[i] == c) j++;       // starting delimiter = increase depth
                    } while(j > 0);                     // loop while depth > 0
                    buf[++i] = 0;               // add null terminator
                } else {
                    buf[1] = 0;                 // not delimiter, return the character
                }
                return 1;
            } else {                            // alphanumeric string
                i = j = 0;                      // position = 0, depth = 0
                do {
                    buf[++i] = LineBuf[LinePos++];  // add char to string
                    if(i > 30) {                    // string too long?
                        sprintf(ErrorBuf, "identifier too long at or near line %d", LineCtr);
                        xerror(3);
                    } else if(buf[i] == 0 && j > 0) {
                        sprintf(ErrorBuf, "part of identifier spans lines at or near line %d", LineCtr);
                        xerror(10);
                    }
                    if(buf[i] == ' ' && buf[i-1] == ' ') i--;   // compress spaces
                    if(buf[i] == '[') j++;              // process brackets (for address expressions)
                    if(buf[i-1] == ']') j--;
                    if(flg) {
                        if(buf[i] == '(') j++;          // process parentheses only if strings OK
                        if(buf[i-1] == ')') j--;
                    }
                } while(j > 0 || strchr(":$_ \t", buf[i]) || isalnum(buf[i]));
                LinePos--; buf[i] = 0;                  // add null terminator
                while(isspace(buf[--i])) buf[i] = 0;    // strip trailing white space
                return 1;
            }
        }
    }
}

/********************************************************************/
/* Return the next token without removing it from the stream        */
/********************************************************************/

int PeekToken(char *p)
{
    int c, lp = LinePos;

    while(1) {
        c = LineBuf[lp++];                      // get char
        if(c == 0) {                            // end of line?
            if(!GetLine(LineBuf)) return 0;     // get line
            LinePos = lp = 0;                   // reset line pos
            continue;
        } else if(isspace(c)) {                 // skip spaces
            continue;
        } else {
            if(isalnum(c) || strchr("([", c))   // identifier, expression,
                *p = 'A';                       // or address = return an 'A'
            else *p = c;                        // otherwise, return the char
            return 1;
        }
    }
}

/********************************************************************/
/* Get line with full pass-through, stripping comments, etc.        */
/********************************************************************/

int GetLine(char *buf)
{
    int i;

    do {
        if(!ReadLine(buf)) return 0;                    // read a line
        if(!isspace(buf[0])) {
            i = 0;                                      // statement starts line, label, etc.
            if(strchr(buf, ':')) {                      // if there's a ':', then
                while(isalnum(buf[i]) ||                // check for a label
                      strchr("$_", buf[i])) i++;
                if(buf[i] == ':') {                     // If a label, then pass it through:
                    i = 0;
                    while(1) {
                        fputc(buf[i], OutFile);         // output one char
                        if(buf[i] == ':') break;        // char is ':', break
                        i++;
                    }
                }
            }
            if(buf[i] != ':') {                         // if no label, then
                i = 0;
                while(buf[i] != ';') {                  // while no semicolon,
                    if(buf[i] == 0) {                   // if end of line, then
                        if(!ReadLine(buf)) return 0;    // read a line, reset the counter
                        i = 0; continue;                // and continue
                    }
                    fputc(buf[i++], OutFile);           // output the char
                }
            }
            fputs("\n", OutFile);                       // output newline
            memmove(buf, buf + i + 1, 300 - i - 1);     // shift over buffer
        }
    } while(IsEmpty(buf));
    return 1;
}

/********************************************************************/
/* Read a line, passing delimited lines                             */
/********************************************************************/

int ReadLine(char *buf)
{
    char *p;

    while(1) {
        if(!fgets(buf, 300, InFile)) return 0;          // read one line
        if(!(LineCtr++ & 0xFF)) fprintf(stderr, ".");   // increment counter, put dot every 256 lines
        if(buf[0] == '!') {                             // is it a pass-through line?
            fputs(buf + 1, OutFile);                    // if so, then send it, and loop back for another
            continue;                                   // make sure string is empty...
        }
        if((p = strchr(buf, 0x0D)) != NULL) *p = 0;     // strip off CR and/or LF at the end
        if((p = strchr(buf, 0x0A)) != NULL) *p = 0;
        StripComments(buf);                             // strip off comments (if any)
        if(!IsEmpty(buf)) return 1;
    }
}

/********************************************************************/
/* Strip comments from end of line                                  */
/********************************************************************/

void StripComments(char *buf)
{
    char c, e = 0;
    int i = -1;

    while((c = buf[++i]) != 0) {            // while not null, if in a string ignore
        if(e) {                             // everything except the delimiter char;
            if(c == e) e = 0;               // otherwise cut the string if '\' found.
        } else {
            if(c == '\'' || c == '\"') {
                e = c;
            } else if(c == '\\') {
                buf[i] = 0;
                return;
            }
        }
    }
}

/********************************************************************/
/* Check whether line is empty                                      */
/********************************************************************/

int IsEmpty(char *buf)
{
    int i = 0;

    while(buf[i] != 0) {
        if(!isspace(buf[i++]))
            return 0;
    }
    return 1;
}

/********************************************************************/
/* Error handling                                                   */
/********************************************************************/

void xerror(int en)
{
    fprintf(stderr, "\nerror %02x: %s\n\n", en, ErrorBuf);
    if(InFile != NULL) fclose(InFile);
    if(OutFile != NULL) fclose(OutFile);
    exit(en);
}

/********************************************************************/
/* String printf + concatenation                                    */
/********************************************************************/

void soutput(char *s, char *fmt, ...)
{
    va_list ap;

    va_start(ap, fmt);
    vsprintf(s + strlen(s), fmt, ap);
    va_end(ap);
}
