/*
    module: engine.cc
  synopsis: kernal module developed for the Genesys source code engine

      call: status = Engine(struct EngineRec *E);

        struct  EngineRec {
            GSString255ptr  sName;  GS/OS class 1 filename of the source file
            int             resID,  Resource Manager file ID of resource file
                            memID;  Memory Manager caller application ID
            long            pLang;  Principle language (equates if REZ src)
            word            rType;  Resource Type to generate (NULL for all)
            int             flag;   Modifier flags
        };
                                
        flag bits: 0..14    reserved
                      15    generate equates for resource based programs.

    author: Marc Wolfgram
      date: 05Jul89
   revised: 24Sep89  All major functions in place... debugging started.
            14Oct89  Implemented LCMP calls to work around long library bugs.
            18Oct89  Restored long compare code and asm left shift code.
            21Oct89  Released stand-alone test version to Kevin and Mike.
            22Oct89  Removed update function from EngineRec structure.
            08Nov89  Separated main, update and usage functions into the two
                     application dependent ancillary files SCG.CC and ICG.CC.
            15Nov89  Added null update call in parse_res, stop is now global.
            11Jan90  Added CTRL command (initially for Glen Bredon's ascii!)
            02Feb90  Revised update, user abort and error handling.
            02Mar90  Fixed get_val() and removed nested case stuff.
            12Mar90  Added MUL, DIV and MOD and CTRL 5-8.
            13Mar90  Added IF OR/AND/, BIT SL/SR and empty lines in templates.
                     Templates are no longer case sensitive.
         B3 28Mar90  Changed equate processing to benefit unnamed items.
                     Added INDEX, PTEXT and TITLE commands and add_header().
         B4 04Apr90  Changed data flag for string reads, now R17 set to 2 if
                     target has character pointer with data pending.
         B5 09Apr90  Removed resname data handler code and added get_label()
                     as an external function (rname.cc).
       1.01 22May90  Replaced ualloc and ufree calls with real memory calls.
       1.2  18Sep90  Segmented engine, added res attr ref in R19.
            04Oct90  Added nested if/else support via CTRL 9 > 0.
            10Oct90  Final 1.2 engine... MWW

            Copyright (c) 1989, 1990
            Simple Software Systems International, Inc.

            ALL RIGHTS RESERVED
*/

segment "M_WOLFGRAM";

#include "genesys.h"
#include <ctype.h>
#include <gsos.h>
#include <memory.h>
#include <orca.h>
#include <resources.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "engine.h"

void  add_header(int);
struct lData *add_template(struct lData *, int, char *);
int   checkDisk(pointer, int);
int   Engine(struct EngineRec *);
int   err(int);
void  free_templates(void);
char *get_label(word, long, int);
char *get_name(word, int);
long  get_str(long);
long  get_val(int);
long  htol(char *);
int   load_templates(void);
char *outsource(char *);
void  parse_res(void);
char *scan(char *);
char *set_template(word);
long  word2long(word);
int   update(word, long, int, int, int, int, int, long*, long);

char rStr[256], *tPtr, *rPtr, *title, part[4][16], OutStr[256], Omask, Amask;

char *keys[] = {
    "AND",  "BIT",  "BYTE", "CONT", "CSTR", "CTRL", "DEC",  "DONE",
    "ELSE", "END",  "EQ",   "GE",   "GSOS", "GT",   "IF",   "INC",
    "LE",   "LOAD", "LONG", "LOOP", "LT",   "NAME", "NE",   "NOR",
    "NSTR", "OR",   "PSTR", "READ", "TYPE", "WORD", "WRITE","XOR",
    "#",    "MUL",  "DIV",  "MOD",  "SL",   "SR",   "PTEXT","INDEX",
    "TITLE"};

int     cmd_key, cmd_mod, cmd_reg, test, stop, class;

/*  form:
 -1 no args possible because this is a modifier
  0 no num args possible
  1 single arg to be interpreted as a num|reg
  2 two args w/second as num|reg
  3 three args w/third as num|reg                 */

int form[] = {
     3,      3,     -1,      0,     -1,      2,      2,      1,
     0,      0,     -1,     -1,     -1,     -1,      3,      2,
    -1,      2,     -1,      0,     -1,      2,     -1,     -1,
    -1,      3,     -1,      3,      1,     -1,      0,     -1,
     0,      2,      2,      2,     -1,     -1,      0,      2,
     0};

long    cmd_arg, r_table[256], XQUOTE, NPCHAR;
struct  lData *tROOT;
struct  EngineRec *Ep;

CreateRecGS pCreate = { 4, 0L, 0xc3, 0xb0 };

NameRecGS pDelete = { 1, 0L };

OpenRecGS pSource = { 2, 0, 0L };
OpenRecGS pOpenDir = { 2, 0 };
OpenRecGS pOpenTmpl = { 12, 0 };

IORecGS   pOutput = { 4, 0, OutStr, 0L };
IORecGS   pRead = { 4 };

DirEntryRecGS pEntry = { 13, 0, 0, 1, 1 };

GSString255Ptr sName;
GSString32  pIName = { 2, "5:" };
ResultBuf32 pOName;

/*****************************************************************************
 *                                                                           *
 *  T H E   E N G I N E  - Genesys Source Code Generator, Marc Wolfgram 1989 *
 *                                                                           *
 *****************************************************************************/

int Engine(struct EngineRec *EngineData)
{
    int i, j, rdpth, typeCount, itemCount, rAttr;
    int (*proc)();
    word rType;
    long rID;
    char *template, **rHandle;

    Ep = EngineData;

    Omask = 0x00;
    Amask = 0x7f;

    XQUOTE = 0x22L;     /* dquote */
    NPCHAR = 0x2eL;     /* period */

    class = 0;                          /* set default language to v1.0 */

    SetCurResourceFile(Ep->resID);      /* insure proper resource access */
    rdpth = SetResourceFileDepth(1);

    i = load_templates();
    if (i != 0) {
        update(0, 0L, 0, 0, 0, 0, 4, r_table, (long) rPtr);
        rdpth = SetResourceFileDepth(rdpth);
        if (i != -1) {
            free_templates();
            return i;
        }
        return 0;
    }

    sName = Ep->sName;
    pSource.pathname = sName;
    pCreate.pathname = sName;
    pDelete.pathname = sName;

    DestroyGS(&pDelete);

    if (Ep->pLang > 0x7fl) {        /* general text output */
        pCreate.fileType = 0x04;        /* text */
        pCreate.auxType = 0l;           /* null */
        if (Ep->pLang == 0xffl) {   /* MERLIN BULLSHIT */
            Omask = 0x80;               /* msb1 */
            Amask = 0xff;
        }
    }
    else {                          /* THE REAL WORLD! */
        pCreate.fileType = 0xb0;        /* src  */
        pCreate.auxType = Ep->pLang;    /* lang */
    }

    CreateGS(&pCreate);
    i = toolerror();
    if (i != 0) {
        update(0, 0L, 0, 0, 0, 0, 4, r_table, (long) rPtr);
        free_templates();
        rdpth = SetResourceFileDepth(rdpth);
        return i;
    }
    pSource.pCount = 2;
    OpenGS(&pSource);
    i = toolerror();
    if (i != 0) {
        update(0, 0L, 0, 0, 0, 0, 4, r_table, (long) rPtr);
        free_templates();
        rdpth = SetResourceFileDepth(rdpth);
        return i;
    }
    pOutput.refNum = pSource.refNum;

    typeCount = CountTypes();

    update(0, 0L, 0, 0, 0, 0, 0, r_table, (long) rPtr);

    add_header(1);

    for(i = 1, stop = 0; i <= typeCount && -2 < stop; i++) {
        rType = GetIndType(i);

        if (rType == RNAME_TYPE) continue;

        update(rType, 0L, typeCount, 0, i, 0, 1, r_table, (long) rPtr);

        template = set_template((Ep->flag & 0x8000) ? RNAME_TYPE : rType);

        itemCount = (int) CountResources(rType);

        for(j = 1; j <= itemCount && -1 < stop; j++) {

            rID = GetIndResource(rType, (long) j);

            update(rType, rID, typeCount, itemCount, i, j, 2, r_table,
                    (long) rPtr);
            rAttr = GetResourceAttr(rType, rID);
            SetResourceAttr(0x000c, rType, rID);
            rHandle = (char **) LoadResource(rType, rID);
            HLock(rHandle);
            SetResourceAttr(rAttr, rType, rID);
            tPtr = template;
            rPtr = (char *) *rHandle;
            r_table[10] = (long) tPtr;
            r_table[11] = (long) rPtr;
            r_table[12] = GetHandleSize(rHandle);
            r_table[13] = (long) rType;
            r_table[14] = rID;
            r_table[15] = (long) j;
            r_table[17] = r_table[16] = 0L;
            r_table[18] = (long)itemCount;
            r_table[19] = word2long(rAttr);
            parse_res();
            if  (stop == 0)
                update(rType, rID, typeCount, itemCount, i, j, 3, r_table,
                        (long) rPtr);
            else if (stop == -1)
                stop = update(rType, rID, typeCount, itemCount, i, j, 5,
                        r_table, (long) rPtr);
            else if (stop > 0)
                stop = update(rType, rID, typeCount, itemCount, i, j, 6,
                        r_table, (long) rPtr);

            ReleaseResource(-1, rType, rID);
        }
    }

    add_header(2);

    update(rType, 0L, typeCount, itemCount, i, j, 4, r_table, (long) rPtr);

    pSource.pCount = 1;
    CloseGS(&pSource);
    free_templates();
    rdpth = SetResourceFileDepth(rdpth);
    return 0;
}

/****************************************************************************
 *  add_header enables the title template to handle head and tail in output
 *  MWW 31Mar90
 */
void add_header(int subtype) /* 1 head, 2 tail */
{
    update(0, 0L, 0, 0, 0, 0, subtype + 8, r_table, (long) rPtr);
    if (Ep->flag & 0x8000)
        subtype += 2;        /* 3 & 4 for equates */
    if (title != NULL) {
        tPtr = title;
        r_table[17] = (long) subtype;
        parse_res();
        r_table[17] = (long) NULL;
    }
}

/****************************************************************************
 *  add_template is the load_template, recursive, linked list function
 *  MWW 10Aug89 validated 7Oct89
 */
struct lData *add_template(struct lData *t, int rType, char *data)
{
    Handle  h;
    if (t == 0L) {
        h = NewHandle(sizeof (struct lData), Ep->memID, 0xc018, 0L);
        HLock(h);
        t = (struct lData *) *h;
        t->rType = rType;
        t->rule = data;
        t->next = 0L;
    }
    else if (t->rType != rType)
         t->next = add_template(t->next, rType, data);
    return t;
}

/****************************************************************************
 *  err places template error information in the output file
 *  MWW 21Aug89
 *  MWW 15Nov89 - no longer bogus
 */
int err(int e)
{
    char *eStr;
    switch(e) {
    case F_BADBITTOK:
        eStr = "BAD BIT COMMAND MODIFIER";
        break;

    case F_BADRESTOK:
        eStr = "BAD READ COMMAND MODIFIER";
        break;

    case F_BADCMDTOK:
        eStr = "UNDEFINED COMMAND";
        break;

    case F_BADCTLTOK:
        eStr = "UNASSIGNED CTRL COMMAND CODE";
        break;

    case F_DIVBYZERO:
        eStr = "DIV/MOD BY ZERO";
        break;

    case F_BADIFTTOK:
        eStr = "AND/OR COMMAND WITHOUT ADJACENT IF";
        break;

    default:
        eStr = "UNKNOWN";
        break;
    }

    pOutput.requestCount = (long) sprintf(pOutput.dataBuffer,
        "\r<<< TEMPLATE ERROR - %s >>>\r\r", eStr);
    WriteGS(&pOutput);
    return e;
}

/****************************************************************************
 *  free_templates deallocates all template memory.
 *  MWW 7Oct89
 */
void free_templates(void)
{
    struct lData *p, *n;

    p = tROOT;
    while (p) {
        n = p->next;
        DisposeHandle((Handle) FindHandle((Pointer) p));
        p = n;
    }
}

/****************************************************************************
 * get_str parses a string based upon a passed length value.
 * MWW  8Oct89
 * MWW 21Oct89 revised to return string pointer or nonprinting byte.
 */
long get_str(long len)
{
    char *s;
    long cnt;

    for (cnt = 0L, s = rStr;; cnt++) {
        if (isprint(*rPtr) && cnt < len && *rPtr != (char) XQUOTE)
            *s++ = *rPtr++;
        else if (cnt > 0) {
            *s = 0x00;
            r_table[16] = cnt;
            r_table[17] = (cnt == len) ? 0L : 2L;
            return (long) rStr;
        }
        else {
            r_table[16] = 1L;
            r_table[17] = 1L;
            return get_val(1);
        }
    }
}

/****************************************************************************
 * get_val parses a byte, word or long value from the resource
 * MWW 23Sep89
 *     28Feb90 fixed non-null bytes on char and int return
 */
long get_val(int size)
{
    long rVal, *lPtr;

asm {   /* bug fix until 1.1 final... lPtr = rPtr; rPtr += size; */
    pha
    clc
    lda rPtr
    sta lPtr
    adc size
    sta rPtr
    lda rPtr+2
    sta lPtr+2
    adc #0
    sta rPtr+2
    pla
}

    rVal = *lPtr;

    if (size == 1)      /* if one byte null the top three */
        rVal &= 0xffL;

    else if (size == 2) /* if two bytes null the top two */
        rVal &= 0xffffL;

    return rVal;        /* regardless, return four bytes  */
}

/****************************************************************************
 *  htol returns a long value derived from a hex string.
 *  MWW 17Sep89 validated 5Oct89
 */
long htol(char *xstr)
{
    long xval = 0L;
    while(isxdigit(*xstr)) {
        xval <<= 4;
        xval += (*xstr > '9') ? toupper(*xstr) - 'A' + 10 : *xstr - '0';
        xstr++;
    }
    return xval;
}

/****************************************************************************
 *  load_templates places the templates into a single-linked list of resource
 *  language rules.
 *  MWW 10Aug89 validated 5Oct89
 */
int load_templates(void)
{
    char *tEnd;
    int terr;
    Handle  h;

    if (checkDisk((pointer) &pIName, 1) != 0) { /* this puppy allows cancel */
        checkDisk((pointer) Ep->sName, 0);      /* ... but this one doesn't */
        return -1;
    }

    tROOT = 0L;
    pOpenDir.pathname = (GSString255Ptr) &pIName;
    pOpenTmpl.pathname = (GSString255Ptr) &pOName;
    pEntry.name = (ResultBuf255Ptr) &pOName;
    OpenGS(&pOpenDir);
    pEntry.refNum = pOpenDir.refNum;
    title = NULL;

    for (;;) {
        pOName.bufSize = 32;
        GetDirEntryGS(&pEntry);
        if (terr = toolerror()) {
           if (terr == 0x61)        /* end of dir - only acceptable error */
               terr = 0x00;         /* clear same... */
           break;
        }

        if (pEntry.fileType == 0xb0 && pEntry.auxType == Ep->pLang) {
            pOName.bufSize = pOName.bufString.length + 2;
            pOName.bufString.length = 0x3a35;  /* in memory this is "5:" */

            OpenGS(&pOpenTmpl);
            pRead.pCount = 4;
            pRead.refNum = pOpenTmpl.refNum;
            h = NewHandle(pOpenTmpl.eof, Ep->memID, 0xc018, 0L);
            HLock(h);
            pRead.dataBuffer = (Pointer) *h;
            pRead.requestCount = pOpenTmpl.eof;
            ReadGS(&pRead);
            pRead.pCount = 1;
            CloseGS(&pRead);

            tPtr = (char *) pRead.dataBuffer;
            tEnd = tPtr + pOpenTmpl.eof;
            do {
                tPtr = (char *) scan(tPtr);
                if (cmd_key == T_TYPE) {
                    update((int) cmd_arg, 0L, 0, 0, 0, 0, 1, r_table,
                            (long) rPtr);
                    tROOT = add_template(tROOT, (int) cmd_arg, tPtr);
                }
                else if (cmd_key == T_TITLE)
                    title = tPtr;
                else if (cmd_key == T_PTEXT || cmd_key == T_WRITE)
                    tPtr = (char *) scan(tPtr);
            } while(tPtr && tPtr < tEnd);
        }
    }
    pRead.pCount = 1;
    pRead.refNum = pEntry.refNum;
    CloseGS(&pRead);

    if (tROOT == 0L)
        terr = 0x46;    /* file not found make sense here */

    checkDisk((pointer) Ep->sName, 0);      /* ... so this doesn't too! */

    return terr;
}

char wformat[256];

/****************************************************************************
 *  outsource constructs a variable argument fprintf call based on r_table[0]
 *  count of r_table[1..9] arguments.  This SEX replaced ten redundant and
 *  obscene fprintfs...
 *
 *  D O   N O T   C H A N G E   A N Y T H I N G   I N   A S S E M B L E R !
 *
 *  MWW 18Sep89 validated 11Oct89
 *  MWW 12Nov89 replaced file IO (fprintf) with GS/OS IO, "\n" and "\r" now
 *              generate '\r'.
 *  MWW 21Oct90 \xnn now generates hex data escape sequences.
 */
char *outsource(char *t)
{
    char c, *f;

    f = wformat;
    r_table[0] = 0L;

    while ((c = *t++) != '\r') {
        if (c == '%')
            r_table[0]++;
        else if (c == '\\') {
            if ((c = tolower(*t++)) == 'n' || c == 'r')
                 c = '\r';
            else if (c == 't')
                c = '\t';
            else if (c == 'x') {
                c = (*t > '9') ? (toupper(*t)-'A'+10)<<4 : (*t-'0')<<4;
                t++;
                c += (*t > '9') ? toupper(*t)-'A'+10 : *t-'0';
                t++;
            }
        }
        *f++ = c;
    }
    *f = 0;

    asm {
        lda r_table     /* change argument count into a long index */
        beq skip_       /* if true hits a bne below which is false */
        asl a
        asl a
        tax
        inx             /* really start index at the long's h_word */
        inx
next_:  lda r_table,x   /* R->L and hi->lo push arg words on stack */
        pha
        dex
        dex
        cpx #2          /* are we done? (don't process r_table[0]) */
skip_:  bne next_       /* no.. (tricky truth reliance from skip_) */
    }

    pOutput.requestCount = (long) sprintf(pOutput.dataBuffer, wformat);
    for(f = pOutput.dataBuffer; *f; f++) {
        *f |= Omask;
        *f &= Amask;
    }
    WriteGS(&pOutput);

    return t;
}

char nulstr[] = { "0" };
char cvtstr[] = { "SSSi" };

/****************************************************************************
 *  parse_res processes a single resource, calling scan.
 *  MWW 07Oct89
 *  MWW 09Oct89 integrated interp() code with parse.
 *  MWW 15Oct89 added update call for dialog control handling
 *  MWW 13Mar90 added shift, if or/and mult/div/mod
 */
void parse_res(void)
{
    int  skip, i, ifNest;
    long ltemp;
    char *ptemp;

    while(tPtr) {

        if (stop == 0)
            stop = update(0, 0L, 0, 0, 0, 0, 7, r_table, (long) rPtr);
        tPtr = scan(tPtr);

        switch (cmd_key) {

        case COMMENT:
            break;

        case T_BIT:     /* BIT {reg} {AND|NOR|OR|XOR} {num|reg} */
            if (cmd_mod == T_AND)
                r_table[cmd_reg] &= cmd_arg;
            else if (cmd_mod == T_NOR) {
                r_table[cmd_reg] |= cmd_arg;
                r_table[cmd_reg] ^= -1L;
            }
            else if (cmd_mod == T_OR)
                r_table[cmd_reg] |= cmd_arg;
            else if (cmd_mod == T_XOR)
                r_table[cmd_reg] ^= cmd_arg;
            else if (cmd_mod == T_SL)
                r_table[cmd_reg] <<= (cmd_arg & 0x1fL);
            else if (cmd_mod == T_SR)
                r_table[cmd_reg] >>= (cmd_arg & 0x1fL);
            else
                stop = err(F_BADBITTOK);
            break;

        case T_CONT:    /* CONT {reg} */
            tPtr = (char *) r_table[cmd_reg];
            break;

        case T_CTRL:    /* CTRL {reg} {num|reg} */
            switch((int) cmd_arg) {
                case 1:
                    Omask = 0x80;
                    Amask = 0xff;
                    r_table[cmd_reg] = cmd_arg;
                    break;

                case 2:
                    Omask = 0x00;
                    Amask = 0x7f;
                    r_table[cmd_reg] = cmd_arg;
                    break;

                case 3:
                    if (r_table[cmd_reg] == 0L)
                        return;
                    break;

                case 4:
                    if (r_table[cmd_reg] != 0L)
                        return;
                    break;

                case 5:
                    r_table[17] = 1L;
                    if (r_table[cmd_reg] > 0x1fL && r_table[cmd_reg] < 0x7fL &&
                        r_table[cmd_reg] != XQUOTE) {
                            strcpy(&cvtstr[0], (char *) &r_table[cmd_reg]);
                            r_table[cmd_reg] = (long) &cvtstr[0];
                            r_table[17] = 0L;
                    }
                    break;

                case 6:
                    if (r_table[cmd_reg] < 0x20L || r_table[cmd_reg] > 0x7fL)
                        r_table[cmd_reg] = NPCHAR;
                    strcpy(&cvtstr[0], (char *) &r_table[cmd_reg]);
                    r_table[cmd_reg] = (long) &cvtstr[0];
                    break;

                case 7:
                    if (r_table[cmd_reg] > 0x1f)
                        XQUOTE = r_table[cmd_reg] & 0X7fL;
                    break;

                case 8:
                    if (r_table[cmd_reg] > 0x1f)
                        NPCHAR = r_table[cmd_reg] & 0x7fL;
                    break;
/*
                case 9:
                    class = r_table[cmd_reg];
                    break;
*/
                default:
                    stop = err(F_BADCTLTOK);
                    break;
            }
            break;

        case T_DEC:     /* DEC {reg} {num|reg} */
            r_table[cmd_reg] -= cmd_arg;
            break;
        
        case T_DONE:
            if (cmd_arg == 0L || cmd_arg == r_table[13])
                return;

        case T_ELSE:    /* ELSE (falls int T_END code below)*/
            /*
            for (ifNest = 1; ifNest > 0;) {
                tPtr = scan(tPtr);
                if (cmd_key == T_IF && class != 0) ifNest++;
                else if (cmd_key == T_END) ifNest--;
            }
            */
            while (cmd_key != T_END)
                tPtr = scan(tPtr);

        case T_END:
            break;

        case T_IF:      /* IF {reg} {EQ|NE|GE|LE|GT|LT} {num|reg} */
            test = TRUE;
            do {
                ptemp = tPtr;
                skip = FALSE;
                ltemp = r_table[cmd_reg];
                if (cmd_key == T_OR) {
                    if (test == FALSE)
                        test = TRUE;
                    else
                        skip = TRUE;
                }
                if (skip == FALSE &&
                    ((cmd_mod == T_EQ && ltemp != cmd_arg) ||
                     (cmd_mod == T_GE && ltemp <  cmd_arg) ||
                     (cmd_mod == T_GT && ltemp <= cmd_arg) ||
                     (cmd_mod == T_LE && ltemp >  cmd_arg) ||
                     (cmd_mod == T_LT && ltemp >= cmd_arg) ||
                     (cmd_mod == T_NE && ltemp == cmd_arg)))
                    test = FALSE;
                tPtr = scan(tPtr);
            } while (cmd_key == T_AND || cmd_key == T_OR);

            if (test == FALSE) {
                /*
                for (ifNest = 1; ifNest > 0;) {
                    tPtr = scan(tPtr);
                    if (cmd_key == T_IF && class != 0) ifNest++;
                    else if ((cmd_key == T_ELSE && ifNest == 1) ||
                             (cmd_key == T_END)) ifNest--;
                }
                */
                while (cmd_key != T_END && cmd_key != T_ELSE)
                    tPtr = scan(tPtr);
            }
            else
                tPtr = ptemp;
            break;

        case T_INC:     /* INC {reg} {num|reg} */
            r_table[cmd_reg] += cmd_arg;
            break;

        case T_INDEX:   /* INDEX {reg} {num|reg} */
            r_table[cmd_reg] = r_table[cmd_arg];
            break;

        case T_LOAD:    /* LOAD {reg} {num|reg} */
            r_table[cmd_reg] = cmd_arg;
            break;
        
        case T_LOOP:    /* LOOP {reg} */
            r_table[cmd_reg] = (long) tPtr;
            break;
        
        case T_NAME:    /* NAME reg num */
            if (cmd_arg == 0)
                r_table[cmd_reg] =
                (long) get_label((word) r_table[13], r_table[14], 0x5f);
            else if (r_table[cmd_reg] == 0L)
                r_table[cmd_reg] = (long) nulstr;
            else
                r_table[cmd_reg] =
                (long) get_label((word) cmd_arg, r_table[cmd_reg], 0x5f);
            break;

        case T_PTEXT:   /* PTEXT <cr> {~literal} */
            r_table[cmd_reg] = (long) tPtr;
            i = 1;
            while(tPtr[i++] != '\r');
            tPtr[0] = (char) i - 2;
            tPtr += i;
            break;

        case T_READ:    /* READ {reg} {BYTE|CSTR|GSOS|LONG|PSTR|WORD} */
            if (cmd_mod == T_BYTE)
                r_table[cmd_reg] = get_val(1);
            else if (cmd_mod == T_CSTR)
                r_table[cmd_reg] = get_str((long) strlen(rPtr));
            else if (cmd_mod == T_GSOS)
                r_table[cmd_reg] = get_str(get_val(2));
            else if (cmd_mod == T_LONG)
                r_table[cmd_reg] = get_val(4);
            else if (cmd_mod == T_NSTR)
                r_table[cmd_reg] = get_str(cmd_arg);
            else if (cmd_mod == T_PSTR)
                r_table[cmd_reg] = get_str(get_val(1));
            else if (cmd_mod == T_WORD)
                r_table[cmd_reg] = get_val(2);
            else
                stop = err(F_BADRESTOK);
            update(0, 0L, 0, 0, 0, (int) r_table[15], 8, r_table, (long) rPtr);
            break;

        case T_TYPE:
            break;

        case T_WRITE:   /* WRITE <cr> {format} */
            tPtr = outsource(tPtr);
            break;

        case M_MUL:
            r_table[cmd_reg] *= cmd_arg;
            break;

        case M_DIV:
            r_table[17] = 0L;
            if (cmd_arg)
                r_table[cmd_reg] /= cmd_arg;
            else
                stop = err(F_DIVBYZERO);
            break;

        case M_MOD:
            r_table[17] = 0L;
            if (cmd_arg)
                r_table[cmd_reg] %= cmd_arg;
            else
                stop = err(F_DIVBYZERO);
            break;

        case T_AND:
        case T_OR:
            stop = err(F_BADIFTTOK);
            break;

        default:
            stop = err(F_BADCMDTOK);
            break;
        }
    }
    return;
}

/****************************************************************************
 *  scan fills the cmd structure from a single template line
 *  MWW 17Sep89 validated 5Oct89
 */
char *scan(char *line)
{
    int t, n, p, wspc, exit;
    wspc = TRUE;
    exit = FALSE;

/*    do {    */
        for(t = n = p = 0;;t++) {
            if (line[t] == '\r' || line[t] == 0) {
                part[n][p] = 0;
                if (line[t] == 0)
                    exit = TRUE;
                break;
            }
            if (line[t] == ' ' || line[t] == '\t') {
                if (wspc == FALSE) {
                    part[n++][p] = 0;
                    p = 0;
                    wspc = TRUE;
                }
                if (n > 3) {
                    while (line[t] != '\r' && line[t] != 0)
                        t++;
                    if (line[t] == 0)
                        exit = TRUE;
                    break;
                }
                continue;
            }
            wspc = FALSE;
            part[n][p++] = toupper(line[t]);
        }
        while (n < 3)
            part[++n][0] = 0;

/*    } while (part[0][0] == 0 && exit == FALSE);  */

    /* resolve command and possible modifier */

    for (cmd_key = cmd_mod = -1, n = 0; n < CMDMAX; n++) {
        if (strcmp(part[0], keys[n]) == 0)
            cmd_key = n;
        if (strcmp(part[2], keys[n]) == 0)
            cmd_mod = n;
    }

    /* resolve possible target register */

    if (part[1][0] == 'R')
        cmd_reg = atoi(&part[1][1]);

    /* resolve possible source/secondary register or numeric */

    n = form[cmd_key];
    if (n > 0) {
        if (part[n][0] == 'R')
            cmd_arg = r_table[atoi(&part[n][1])];
        else if (part[n][0] == '$')
            cmd_arg = htol(&part[n][1]);
        else if (part[n][0] == '0' && part[n][1] == 'X')
            cmd_arg = htol(&part[n][2]);
        else
            cmd_arg = atol(&part[n][0]);
    }
    return (exit == FALSE) ? line + t + 1 : (char *) 0L;
}

/****************************************************************************
 *  set_template returns a pointer to the specified resource's template if
 *  one exists, or a pointer to Generic template 0000 if it exists (or NULL).
 *  MWW 11Aug89 validated 5Oct89
 */
char *set_template(word rType)
{
    struct lData *p;

    p = tROOT;
    while (p->rType != rType) {
        if (p->next)
            p = p->next;
        else if (rType != 0) {
            p = tROOT;
            rType = 0;
        }
        else
            return (char *) 0L;
    }
    return (char *) p->rule;
}

/****************************************************************************
 *  word2long make sure that some seemingly negative integer values don't
 *  force 0xffff into the high word of a long.  This is important for long
 *  access required in source generation.
 *  MWW 29Sep90 validated
 */
long word2long(word uint)
{
    long rval;

    asm {
        lda uint
        sta rval
        stz rval+2
    }

    return rval;
}
