advint: interpreter for Advsys games
authorAlan Cox <alan@linux.intel.com>
Tue, 15 May 2018 22:44:21 +0000 (23:44 +0100)
committerAlan Cox <alan@linux.intel.com>
Tue, 15 May 2018 22:44:21 +0000 (23:44 +0100)
Chopped down a little from https://github.com/dbetz/advsys

A lot more cleanup is possible, especially on the tty side

Applications/games/Makefile.z80
Applications/games/advint.c [new file with mode: 0644]
Applications/games/advint.h [new file with mode: 0644]

index fedd18d..6c3ccb0 100644 (file)
@@ -13,7 +13,7 @@ BINMAN = ../../Library/tools/binman
 
 .SUFFIXES: .c .rel
 
-SRCSNS = qrun.c fortune.c fweep.c
+SRCSNS = advint.c fortune.c fweep.c qrun.c
 
 SRCS  = adv01.c adv02.c adv03.c adv04.c adv05.c adv06.c adv07.c \
         adv08.c adv09.c adv10.c adv11.c adv12.c adv13.c adv14a.c adv14b.c \
diff --git a/Applications/games/advint.c b/Applications/games/advint.c
new file mode 100644 (file)
index 0000000..fd602ed
--- /dev/null
@@ -0,0 +1,1551 @@
+/*
+
+       Copyright (c) 1993, by David Michael Betz
+       All rights reserved
+       
+MIT License
+
+Copyright (c) 2017 dbetz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+    Modified from the original quite a bit in order to bash it into a single
+    file and shrink it down for Fuzix
+
+    https://github.com/dbetz/advsys
+
+*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "advint.h"
+
+/* useful definitions */
+#define EOS     '\0'
+#define LINEMAX 200
+#define WORDMAX 100
+
+/* global variables */
+char line[LINEMAX + 1];
+
+/* local variables */
+static int col, maxcol, row, maxrow;
+static int scnt, wordcnt;
+static char theWord[WORDMAX + 1], *wordptr;
+
+jmp_buf restart;
+
+int pc, opcode, p2, p3, sts;
+int stack[STKSIZE], *sp, *fp, *top;
+long rseed = 1L;
+
+int h_init;                    /* initialization code */
+int h_update;                  /* update code */
+int h_before;                  /* before handler code */
+int h_after;                   /* after handler code */
+int h_error;                   /* error handling code */
+
+int datafd;                    /* data file descriptor */
+
+/* table base addresses */
+char *wtable;                  /* word table */
+char *wtypes;                  /* word type table */
+int wcount;                    /* number of words */
+char *otable;                  /* object table */
+int ocount;                    /* number of objects */
+char *atable;                  /* action table */
+int acount;                    /* number of actions */
+char *vtable;                  /* variable table */
+int vcount;                    /* number of variables */
+char *data;                    /* base of data tables */
+char *base;                    /* current base address */
+char *dbase;                   /* base of the data space */
+char *cbase;                   /* base of the code space */
+int length;                    /* length of resident data structures */
+
+/* data file header */
+static char hdr[HDR_SIZE];
+
+/* save parameters */
+static long saveoff;           /* save data file offset */
+static char *save;             /* save area base address */
+static int slen;               /* save area length */
+
+/* parser result variables */
+int nouns[20];
+int *adjectives[20];
+static int actor, action, dobject, ndobjects, iobject;
+static int flag;
+
+/* local variables */
+static char *lptr;             /* line pointer */
+static int words[100];         /* word table */
+static char *wtext[100];       /* word text table */
+static int *wptr;              /* word pointer */
+static int wcnt;               /* word count */
+
+static int verbs[3];           /* words in the verb phrase */
+static int nnums[20];          /* noun word numbers */
+static int nptr;               /* noun pointer (actually, an index) */
+static int adjs[100];          /* adjective lists */
+static int anums[100];         /* adjective word numbers */
+static int aptr;               /* adjective pointer (actually, an index) */
+
+/* cache size */
+#define CSIZE  8
+
+/* message block cache */
+static char *mbuffer[CSIZE];   /* message text block cache buffers */
+static int mblock[CSIZE];      /* message text block cache block numbers */
+static int mnext[CSIZE];       /* next most recently used block */
+static int mhead, mtail;       /* head and tail of lru list */
+
+static char mbufdata[CSIZE * 512];
+
+/* message file variables */
+static int mbase;              /* message base block */
+static int mfd;
+
+/* current message variables */
+static int mblk;               /* current block */
+static char *mbuf;             /* current buffer */
+static int moff;               /* current buffer offset */
+
+
+static void writes(const char *p)
+{
+       write(1, p, strlen(p));
+}
+
+/* execute - execute adventure code */
+int execute(int code)
+{
+       /* setup initial program counter */
+       if ((pc = code) == NIL)
+               return (CHAIN);
+
+       /* initialize */
+       sp = fp = top = stack + STKSIZE;
+
+       /* execute the code */
+       for (sts = 0; sts == 0;)
+               exe_one();
+
+       return (sts);
+}
+
+/* exe_one - execute one instruction */
+void exe_one(void)
+{
+       /* get the opcode */
+       opcode = getcbyte(pc);
+       pc++;
+
+       /* execute the instruction */
+       switch (opcode) {
+       case OP_CALL:
+               *--sp = getboperand();
+               *--sp = pc;
+               *--sp = (int) (top - fp);
+               fp = sp;
+               pc = getafield(fp[fp[2] + 3], A_CODE);
+               break;
+       case OP_SEND:
+               *--sp = getboperand();
+               *--sp = pc;
+               *--sp = (int) (top - fp);
+               fp = sp;
+               if (p2 = fp[fp[2] + 3])
+                       p2 = getofield(p2, O_CLASS);
+               else
+                       p2 = fp[fp[2] + 2];
+               if (p2 && (p2 = getp(p2, fp[fp[2] + 1]))) {
+                       pc = getafield(p2, A_CODE);
+                       break;
+               }
+               *sp = NIL;
+               /* return NIL if there is no method for this message */
+       case OP_RETURN:
+               if (fp == top)
+                       sts = CHAIN;
+               else {
+                       p2 = *sp;
+                       sp = fp;
+                       fp = top - *sp++;
+                       pc = *sp++;
+                       p3 = *sp++;
+                       sp += p3;
+                       *sp = p2;
+               }
+               break;
+       case OP_TSPACE:
+               sp -= getboperand();
+               break;
+       case OP_TMP:
+               p2 = getboperand();
+               *sp = fp[-p2 - 1];
+               break;
+       case OP_TSET:
+               p2 = getboperand();
+               fp[-p2 - 1] = *sp;
+               break;
+       case OP_ARG:
+               p2 = getboperand();
+               if (p2 >= fp[2])
+                       error("too few arguments");
+               *sp = fp[p2 + 3];
+               break;
+       case OP_ASET:
+               p2 = getboperand();
+               if (p2 >= fp[2])
+                       error("too few arguments");
+               fp[p2 + 3] = *sp;
+               break;
+       case OP_BRT:
+               pc = (*sp ? getwoperand() : pc + 2);
+               break;
+       case OP_BRF:
+               pc = (*sp ? pc + 2 : getwoperand());
+               break;
+       case OP_BR:
+               pc = getwoperand();
+               break;
+       case OP_T:
+               *sp = T;
+               break;
+       case OP_NIL:
+               *sp = NIL;
+               break;
+       case OP_PUSH:
+               *--sp = NIL;
+               break;
+       case OP_NOT:
+               *sp = (*sp ? NIL : T);
+               break;
+       case OP_ADD:
+               p2 = *sp++;
+               *sp += p2;
+               break;
+       case OP_SUB:
+               p2 = *sp++;
+               *sp -= p2;
+               break;
+       case OP_MUL:
+               p2 = *sp++;
+               *sp *= p2;
+               break;
+       case OP_DIV:
+               p2 = *sp++;
+               *sp = (p2 == 0 ? 0 : *sp / p2);
+               break;
+       case OP_REM:
+               p2 = *sp++;
+               *sp = (p2 == 0 ? 0 : *sp % p2);
+               break;
+       case OP_BAND:
+               p2 = *sp++;
+               *sp &= p2;
+               break;
+       case OP_BOR:
+               p2 = *sp++;
+               *sp |= p2;
+               break;
+       case OP_BNOT:
+               *sp = ~*sp;
+               break;
+       case OP_LT:
+               p2 = *sp++;
+               *sp = (*sp < p2 ? T : NIL);
+               break;
+       case OP_EQ:
+               p2 = *sp++;
+               *sp = (*sp == p2 ? T : NIL);
+               break;
+       case OP_GT:
+               p2 = *sp++;
+               *sp = (*sp > p2 ? T : NIL);
+               break;
+       case OP_LIT:
+               *sp = getwoperand();
+               break;
+       case OP_SPLIT:
+               *sp = getboperand();
+               break;
+       case OP_SNLIT:
+               *sp = -getboperand();
+               break;
+       case OP_VAR:
+               *sp = getvalue(getwoperand());
+               break;
+       case OP_SVAR:
+               *sp = getvalue(getboperand());
+               break;
+       case OP_SET:
+               setvalue(getwoperand(), *sp);
+               break;
+       case OP_SSET:
+               setvalue(getboperand(), *sp);
+               break;
+       case OP_GETP:
+               p2 = *sp++;
+               *sp = getp(*sp, p2);
+               break;
+       case OP_SETP:
+               p3 = *sp++;
+               p2 = *sp++;
+               *sp = setp(*sp, p2, p3);
+               break;
+       case OP_PRINT:
+               print(*sp);
+               break;
+       case OP_PNUMBER:
+               pnumber(*sp);
+               break;
+       case OP_PNOUN:
+               show_noun(*sp);
+               break;
+       case OP_TERPRI:
+               trm_chr('\n');
+               break;
+       case OP_FINISH:
+               sts = FINISH;
+               break;
+       case OP_CHAIN:
+               sts = CHAIN;
+               break;
+       case OP_ABORT:
+               sts = ABORT;
+               break;
+       case OP_EXIT:
+               trm_done();
+               exit(1);
+               break;
+       case OP_YORN:
+               trm_get(line);
+               *sp = (line[0] == 'Y' || line[0] == 'y' ? T : NIL);
+               break;
+       case OP_CLASS:
+               *sp = getofield(*sp, O_CLASS);
+               break;
+       case OP_MATCH:
+               p2 = *sp++;
+               *sp = (match(*sp, nouns[p2 - 1], adjectives[p2 - 1]) ? T : NIL);
+               break;
+       case OP_SAVE:
+               *sp = db_save();
+               break;
+       case OP_RESTORE:
+               *sp = db_restore();
+               break;
+       case OP_RESTART:
+               *sp = db_restart();
+               break;
+       case OP_RAND:
+               *sp = getrand(*sp);
+               break;
+       case OP_RNDMIZE:
+               setrand(time(0L));
+               *sp = NIL;
+               break;
+       default:
+               if (opcode >= OP_XVAR && opcode < OP_XSET)
+                       *sp = getvalue(opcode - OP_XVAR);
+               else if (opcode >= OP_XSET && opcode < OP_XPLIT)
+                       setvalue(opcode - OP_XSET, *sp);
+               else if (opcode >= OP_XPLIT && opcode < OP_XNLIT)
+                       *sp = opcode - OP_XPLIT;
+               else if (opcode >= OP_XNLIT && opcode < 256)
+                       *sp = OP_XNLIT - opcode;
+               else
+                       trm_str("Bad opcode\n");
+               break;
+       }
+}
+
+/* getboperand - get data byte */
+int getboperand(void)
+{
+       int data;
+       data = getcbyte(pc);
+       pc += 1;
+       return (data);
+}
+
+/* getwoperand - get data word */
+int getwoperand(void)
+{
+       int data;
+       data = getcword(pc);
+       pc += 2;
+       return (data);
+}
+
+/* print - print a message */
+void print(int msg)
+{
+       int ch;
+
+       msg_open(msg);
+       while (ch = msg_byte())
+               trm_chr(ch);
+}
+
+/* pnumber - print a number */
+void pnumber(int n)
+{
+       char buf[10];
+#ifdef __linux__
+       sprintf(buf, "%d", n);
+       trm_str(buf);
+#else
+       trm_str(_itoa(n));
+#endif
+}
+
+/* getrand - get a random number between 0 and n-1 */
+int getrand(int n)
+{
+       long k1;
+
+       /* make sure we don't get stuck at zero */
+       if (rseed == 0L)
+               rseed = 1L;
+
+       /* algorithm taken from Dr. Dobbs Journal, November 1985, page 91 */
+       k1 = rseed / 127773L;
+       if ((rseed = 16807L * (rseed - k1 * 127773L) - k1 * 2836L) < 0L)
+               rseed += 2147483647L;
+
+       /* return a random number between 0 and n-1 */
+       return ((int) (rseed % (long) n));
+}
+
+/* setrand - set the random number seed */
+void setrand(long n)
+{
+       rseed = n;
+}
+
+long _seed = 1L;
+
+int advsave(char *hdr, int hlen, char *save, int slen)
+{
+       char fname[50];
+       int fd;
+
+       trm_str("File name? ");
+       trm_get(fname);
+
+       /* add the extension */
+       strcat(fname, ".sav");
+
+       /* create the data file */
+       if ((fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0600)) == 0)
+               return (0);
+
+       if (write(fd, hdr, hlen) != hlen) {
+               close(fd);
+               return (0);
+       }
+
+       if (write(fd, save, slen) != slen) {
+               close(fd);
+               return (0);
+       }
+
+       /* close the file and return successfully */
+       close(fd);
+       return (1);
+}
+
+int advrestore(char *hdr, int hlen, char *save, int slen)
+{
+       char fname[50], hbuf[50], *p;
+       int fd;
+
+       if (hlen > 50)
+               error("save file header buffer too small");
+
+       trm_str("File name? ");
+       trm_get(fname);
+
+       /* add the extension */
+       strcat(fname, ".sav");
+
+       /* create the data file */
+       if ((fd = open(fname, O_RDONLY)) == 0)
+               return (0);
+
+       /* read the header */
+       if (read(fd, hbuf, hlen) != hlen) {
+               close(fd);
+               return (0);
+       }
+
+       /* compare the headers */
+       for (p = hbuf; hlen--;)
+               if (*hdr++ != *p++) {
+                       trm_str("This save file does not match the adventure!\n");
+                       close(fd);
+                       return (0);
+               }
+
+       /* read the data */
+       if (read(fd, save, slen) != slen) {
+               close(fd);
+               return (0);
+       }
+
+       /* close the file and return successfully */
+       close(fd);
+       return (1);
+}
+
+/* main - the main routine */
+void main(int argc, char *argv[])
+{
+       char *fname, *lname;
+       int rows, cols, i;
+
+       writes("ADVINT v1.2 - Copyright (c) 1986, by David Betz\n");
+       writes("ANSI-fied by Matt Ackeret (unknown@apple.com or unknown@ucscb.ucsc.edu).\n");
+       writes("ANSI-compliant source is available on ftp.gmd.de (mirrored on\nwuarchive.wustl.edu). " "Other games are available there also.\n");
+
+       fname = NULL;
+       lname = NULL;
+       rows = 24;
+       cols = 80;
+
+       /* parse the command line */
+       for (i = 1; i < argc; i++)
+               if (argv[i][0] == '-')
+                       switch (argv[i][1]) {
+                       case 'r':
+                       case 'R':
+                               rows = atoi(&argv[i][2]);
+                               break;
+                       case 'c':
+                       case 'C':
+                               cols = atoi(&argv[i][2]);
+                               break;
+                       case 'l':
+                       case 'L':
+                               lname = &argv[i][2];
+                               break;
+               } else
+                       fname = argv[i];
+       if (fname == NULL) {
+               writes("usage: advint [-r<rows>] [-c<columns>] [-l<log-file>] <file>\n");
+               exit(1);
+       }
+
+       /* initialize terminal i/o */
+       trm_init(rows, cols, lname);
+
+       /* initialize the database */
+       db_init(fname);
+
+       /* play the game */
+       play();
+}
+
+/* play - the main loop */
+void play(void)
+{
+       /* establish the restart point */
+       setjmp(restart);
+
+       /* execute the initialization code */
+       execute(h_init);
+
+       /* turn handling loop */
+       for (;;) {
+
+               /* execute the update code */
+               execute(h_update);
+
+               /* parse the next input command */
+               if (parse()) {
+                       if (single())
+                               while (next() && single());
+               }
+
+               /* parse error, call the error handling code */
+               else
+                       execute(h_error);
+       }
+}
+
+/* single - handle a single action */
+int single(void)
+{
+       /* execute the before code */
+       switch (execute(h_before)) {
+       case ABORT:             /* before handler aborted sequence */
+               return (FALSE);
+       case CHAIN:             /* execute the action handler */
+               if (execute(getafield(getvalue(V_ACTION), A_CODE)) == ABORT)
+                       return (FALSE);
+       case FINISH:            /* execute the after code */
+               if (execute(h_after) == ABORT)
+                       return (FALSE);
+               break;
+       }
+       return (TRUE);
+}
+
+/* error - print an error message and exit */
+void error(char *msg)
+{
+       trm_str(msg);
+       trm_chr('\n');
+       exit(1);
+}
+
+/* db_init - read and decode the data file header */
+void db_init(char *name)
+{
+       int woff, ooff, aoff, voff, n;
+       char fname[50];
+       long TMPLONG;
+       int TMPINT;
+
+       /* get the data file name */
+       strcpy(fname, name);
+       strcat(fname, ".dat");
+
+       /* open the data file */
+       datafd = open(fname, O_RDONLY);
+       if (datafd == -1)
+               error("can't open data file");
+
+       /* read the header */
+
+       TMPINT = read(datafd, hdr, HDR_SIZE);
+       if (TMPINT != HDR_SIZE)
+               error("bad data file");
+
+       complement(hdr, HDR_SIZE);
+       base = hdr;
+
+       /* check the magic information */
+       if (strncmp(&hdr[HDR_MAGIC], "ADVSYS", 6) != 0)
+               error("not an adventure data file");
+
+       /* check the version number */
+
+       if ((n = getword(HDR_VERSION)) < 101 || n > VERSION)
+               error("wrong version number");
+
+       /* decode the resident data length header field */
+       length = getword(HDR_LENGTH);
+
+       /* allocate space for the resident data structure */
+       if ((data = sbrk(length)) == ((void *)-1))
+               error("insufficient memory");
+
+       /* compute the offset to the data */
+       saveoff = (long) getword(HDR_DATBLK) * 512L;
+
+       /* read the resident data structure */
+       TMPLONG = lseek(datafd, saveoff, 0);
+       TMPINT = read(datafd, data, (int) length);
+       if (TMPINT != (int) length)
+               error("bad data file");
+       complement(data, length);
+
+       /* get the table base addresses */
+       wtable = data + (woff = getword(HDR_WTABLE));
+       wtypes = data + getword(HDR_WTYPES) - 1;
+       otable = data + (ooff = getword(HDR_OTABLE));
+       atable = data + (aoff = getword(HDR_ATABLE));
+       vtable = data + (voff = getword(HDR_VTABLE));
+
+       /* get the save data area */
+       saveoff += (long) getword(HDR_SAVE);
+       save = data + getword(HDR_SAVE);
+       slen = getword(HDR_SLEN);
+
+       /* get the base of the data and code spaces */
+       dbase = data + getword(HDR_DBASE);
+       cbase = data + getword(HDR_CBASE);
+
+       /* initialize the message routines */
+       msg_init(datafd, getword(HDR_MSGBLK));
+
+       /* get the code pointers */
+       h_init = getword(HDR_INIT);
+       h_update = getword(HDR_UPDATE);
+       h_before = getword(HDR_BEFORE);
+       h_after = getword(HDR_AFTER);
+       h_error = getword(HDR_ERROR);
+
+       /* get the table lengths */
+       base = data;
+       wcount = getword(woff);
+       ocount = getword(ooff);
+       acount = getword(aoff);
+       vcount = getword(voff);
+
+       /* setup the base of the resident data */
+       base = dbase;
+
+       /* set the object count */
+       setvalue(V_OCOUNT, ocount);
+}
+
+/* db_save - save the current database */
+int db_save(void)
+{
+       return (advsave(&hdr[HDR_ANAME], 20, save, slen) ? T : NIL);
+}
+
+/* db_restore - restore a saved database */
+int db_restore(void)
+{
+       return (advrestore(&hdr[HDR_ANAME], 20, save, slen) ? T : NIL);
+}
+
+/* db_restart - restart the current game */
+int db_restart(void)
+{
+       lseek(datafd, saveoff, 0);
+       if (read(datafd, save, slen) != slen)
+               return (NIL);
+       complement(save, slen);
+       setvalue(V_OCOUNT, ocount);
+       longjmp(restart, 1);
+}
+
+/* complement - complement a block of memory */
+void complement(char *adr, int len)
+{
+       for (; len--; adr++)
+               *adr = ~(*adr + 30);
+}
+
+/* findword - find a word in the dictionary */
+int findword(char *word)
+{
+       char sword[WRDSIZE + 1];
+       int wrd, i;
+
+       /* shorten the word */
+       strncpy(sword, word, WRDSIZE);
+       sword[WRDSIZE] = 0;
+
+       /* look up the word */
+       for (i = 1; i <= wcount; i++) {
+               wrd = getwloc(i);
+               if (strcmp(base + wrd + 2, sword) == 0)
+                       return (getword(wrd));
+       }
+       return (NIL);
+}
+
+/* wtype - return the type of a word */
+int wtype(int wrd)
+{
+       return (wtypes[wrd]);
+}
+
+/* match - match an object against a name and list of adjectives */
+int match(int obj, int noun, int *adjs)
+{
+       int *aptr;
+
+       if (!hasnoun(obj, noun))
+               return (FALSE);
+       for (aptr = adjs; *aptr != NIL; aptr++)
+               if (!hasadjective(obj, *aptr))
+                       return (FALSE);
+       return (TRUE);
+}
+
+/* checkverb - check to see if this is a valid verb */
+int checkverb(int *verbs)
+{
+       int act;
+
+       /* look up the action */
+       for (act = 1; act <= acount; act++)
+               if (hasverb(act, verbs))
+                       return (act);
+       return (NIL);
+}
+
+/* findaction - find an action matching a description */
+int findaction(int *verbs, int preposition, int flag)
+{
+       int act, mask;
+
+       /* look up the action */
+       for (act = 1; act <= acount; act++) {
+               if (preposition && !haspreposition(act, preposition))
+                       continue;
+               if (!hasverb(act, verbs))
+                       continue;
+               mask = ~getabyte(act, A_MASK);
+               if ((flag & mask) == (getabyte(act, A_FLAG) & mask))
+                       return (act);
+       }
+       return (NIL);
+}
+
+/* getp - get the value of an object property */
+int getp(int obj, int prop)
+{
+       int p;
+
+       for (; obj; obj = getofield(obj, O_CLASS))
+               if (p = findprop(obj, prop))
+                       return (getofield(obj, p));
+       return (NIL);
+}
+
+/* setp - set the value of an object property */
+int setp(int obj, int prop, int val)
+{
+       int p;
+
+       for (; obj; obj = getofield(obj, O_CLASS))
+               if (p = findprop(obj, prop))
+                       return (putofield(obj, p, val));
+       return (NIL);
+}
+
+/* findprop - find a property */
+int findprop(int obj, int prop)
+{
+       int n, i, p;
+
+       n = getofield(obj, O_NPROPERTIES);
+       for (i = p = 0; i < n; i++, p += 4)
+               if ((getofield(obj, O_PROPERTIES + p) & ~P_CLASS) == prop)
+                       return (O_PROPERTIES + p + 2);
+       return (NIL);
+}
+
+/* hasnoun - check to see if an object has a specified noun */
+int hasnoun(int obj, int noun)
+{
+       while (obj) {
+               if (inlist(getofield(obj, O_NOUNS), noun))
+                       return (TRUE);
+               obj = getofield(obj, O_CLASS);
+       }
+       return (FALSE);
+}
+
+/* hasadjective - check to see if an object has a specified adjective */
+int hasadjective(int obj, int adjective)
+{
+       while (obj) {
+               if (inlist(getofield(obj, O_ADJECTIVES), adjective))
+                       return (TRUE);
+               obj = getofield(obj, O_CLASS);
+       }
+       return (FALSE);
+}
+
+/* hasverb - check to see if this action has this verb */
+int hasverb(int act, int *verbs)
+{
+       int link, word, *verb;
+
+       /* get the list of verbs */
+       link = getafield(act, A_VERBS);
+
+       /* look for this verb */
+       while (link != NIL) {
+               verb = verbs;
+               word = getword(link + L_DATA);
+               while (*verb != NIL && word != NIL) {
+                       if (*verb != getword(word + L_DATA))
+                               break;
+                       verb++;
+                       word = getword(word + L_NEXT);
+               }
+               if (*verb == NIL && word == NIL)
+                       return (TRUE);
+               link = getword(link + L_NEXT);
+       }
+       return (FALSE);
+}
+
+/* haspreposition - check to see if an action has a specified preposition */
+int haspreposition(int act, int preposition)
+{
+       return (inlist(getafield(act, A_PREPOSITIONS), preposition));
+}
+
+/* inlist - check to see if a word is an element of a list */
+int inlist(int link, int word)
+{
+       while (link != NIL) {
+               if (word == getword(link + L_DATA))
+                       return (TRUE);
+               link = getword(link + L_NEXT);
+       }
+       return (FALSE);
+}
+
+/* getofield - get a field from an object */
+int getofield(int obj, int off)
+{
+       return (getword(getoloc(obj) + off));
+}
+
+/* putofield - put a field into an object */
+int putofield(int obj, int off, int val)
+{
+       return (putword(getoloc(obj) + off, val));
+}
+
+/* getafield - get a field from an action */
+int getafield(int act, int off)
+{
+       return (getword(getaloc(act) + off));
+}
+
+/* getabyte - get a byte field from an action */
+int getabyte(int act, int off)
+{
+       return (getbyte(getaloc(act) + off));
+}
+
+/* getoloc - get an object from the object table */
+int getoloc(int n)
+{
+       if (n < 1 || n > ocount)
+               range("object", n);
+       return (getdword(otable + n + n));
+}
+
+/* getaloc - get an action from the action table */
+int getaloc(int n)
+{
+       if (n < 1 || n > acount)
+               range("action", n);
+       return (getdword(atable + n + n));
+}
+
+/* getvalue - get the value of a variable from the variable table */
+int getvalue(int n)
+{
+       if (n < 1 || n > vcount)
+               range("variable", n);
+       return (getdword(vtable + n + n));
+}
+
+/* setvalue - set the value of a variable in the variable table */
+int setvalue(int n, int v)
+{
+       if (n < 1 || n > vcount)
+               range("variable", n);
+       return (putdword(vtable + n + n, v));
+}
+
+/* getwloc - get a word from the word table */
+int getwloc(int n)
+{
+       if (n < 1 || n > wcount)
+               range("word", n);
+       return (getdword(wtable + n + n));
+}
+
+/* getword - get a word from the data array */
+int getword(int n)
+{
+       return (getdword(base + n));
+}
+
+/* putword - put a word into the data array */
+int putword(int n, int w)
+{
+       return (putdword(base + n, w));
+}
+
+/* getbyte - get a byte from the data array */
+int getbyte(int n)
+{
+       return (*(base + n) & 0xFF);
+}
+
+/* getcbyte - get a code byte */
+int getcbyte(int n)
+{
+       return (*(cbase + n) & 0xFF);
+}
+
+/* getcword - get a code word */
+int getcword(int n)
+{
+       return (getdword(cbase + n));
+}
+
+/* getdword - get a word from the data array */
+int getdword(char *p)
+{
+       return (((*p & 0xFF) | (*(p + 1) << 8)) & 0xFFFF);
+}
+
+/* putdword - put a word into the data array */
+int putdword(char *p, int w)
+{
+       *p = w;
+       *(p + 1) = w >> 8;
+       return (w);
+}
+
+/* range - handle errors with numeric arguments */
+void range(char *what, int n)
+{
+       error(what);
+       error(" out of range: ");
+       pnumber(n);
+       error(".\n");
+}
+
+/* parse - read and parse an input line */
+int parse(void)
+{
+       if (!parse1())
+               return (FALSE);
+       setvalue(V_ACTOR, actor);
+       setvalue(V_ACTION, action);
+       setvalue(V_DOBJECT, dobject);
+       setvalue(V_NDOBJECTS, ndobjects);
+       setvalue(V_IOBJECT, iobject);
+       return (TRUE);
+}
+
+/* next - get the next command (next direct object) */
+int next(void)
+{
+       if (getvalue(V_NDOBJECTS) > 1) {
+               setvalue(V_ACTOR, actor);
+               setvalue(V_ACTION, action);
+               setvalue(V_DOBJECT, getvalue(V_DOBJECT) + 1);
+               setvalue(V_NDOBJECTS, getvalue(V_NDOBJECTS) - 1);
+               setvalue(V_IOBJECT, iobject);
+               return (TRUE);
+       } else
+               return (FALSE);
+}
+
+/* parse1 - the main parser */
+int parse1(void)
+{
+       int noun1, cnt1, noun2, cnt2;
+       int preposition, flag;
+
+       /* initialize */
+       noun1 = noun2 = NIL;
+       cnt1 = cnt2 = 0;
+       nptr = aptr = 0;
+       preposition = 0;
+       flag = 0;
+
+       /* initialize the parser result variables */
+       actor = action = dobject = iobject = NIL;
+       ndobjects = 0;
+
+       /* get an input line */
+       if (!get_line())
+               return (FALSE);
+
+       /* check for actor */
+       if (wtype(*wptr) == WT_ADJECTIVE || wtype(*wptr) == WT_NOUN) {
+               if ((actor = getnoun()) == NIL)
+                       return (FALSE);
+               flag |= A_ACTOR;
+       }
+
+       /* get verb phrase */
+       if (!getverb())
+               return (FALSE);
+
+       /* direct object, preposition and indirect object */
+       if (*wptr) {
+
+               /* get the first set of noun phrases (direct objects) */
+               noun1 = nptr + 1;
+               for (;;) {
+
+                       /* get the next direct object */
+                       if (getnoun() == NIL)
+                               return (FALSE);
+                       ++cnt1;
+
+                       /* check for more direct objects */
+                       if (*wptr == NIL || wtype(*wptr) != WT_CONJUNCTION)
+                               break;
+                       wptr++;
+               }
+
+               /* get the preposition and indirect object */
+               if (*wptr) {
+
+                       /* get the preposition */
+                       if (wtype(*wptr) == WT_PREPOSITION)
+                               preposition = *wptr++;
+
+                       /* get the second set of noun phrases (indirect object) */
+                       noun2 = nptr + 1;
+                       for (;;) {
+
+                               /* get the next direct object */
+                               if (getnoun() == NIL)
+                                       return (FALSE);
+                               ++cnt2;
+
+                               /* check for more direct objects */
+                               if (*wptr == NIL || wtype(*wptr) != WT_CONJUNCTION)
+                                       break;
+                               wptr++;
+                       }
+               }
+
+               /* make sure this is the end of the sentence */
+               if (*wptr) {
+                       parse_error();
+                       return (FALSE);
+               }
+       }
+
+       /* setup the direct and indirect objects */
+       if (preposition) {
+               if (cnt2 > 1) {
+                       parse_error();
+                       return (FALSE);
+               }
+               dobject = noun1;
+               ndobjects = cnt1;
+               iobject = noun2;
+       } else if (noun2) {
+               if (cnt1 > 1) {
+                       parse_error();
+                       return (FALSE);
+               }
+               preposition = findword("to");
+               dobject = noun2;
+               ndobjects = cnt2;
+               iobject = noun1;
+       } else {
+               dobject = noun1;
+               ndobjects = cnt1;
+       }
+
+       /* setup the flags for the action lookup */
+       if (dobject)
+               flag |= A_DOBJECT;
+       if (iobject)
+               flag |= A_IOBJECT;
+
+       /* find the action */
+       if ((action = findaction(verbs, preposition, flag)) == NIL) {
+               parse_error();
+               return (FALSE);
+       }
+
+       /* return successfully */
+       return (TRUE);
+}
+
+/* getverb - get a verb phrase and return the action it refers to */
+int getverb(void)
+{
+       /* get the verb */
+       if (*wptr == NIL || wtype(*wptr) != WT_VERB) {
+               parse_error();
+               return (NIL);
+       }
+       verbs[0] = *wptr++;
+       verbs[1] = NIL;
+
+       /* check for a word following the verb */
+       if (*wptr) {
+               verbs[1] = *wptr;
+               verbs[2] = NIL;
+               if (checkverb(verbs))
+                       wptr++;
+               else {
+                       verbs[1] = words[wcnt - 1];
+                       if (checkverb(verbs))
+                               words[--wcnt] = NIL;
+                       else {
+                               verbs[1] = NIL;
+                               if (!checkverb(verbs)) {
+                                       parse_error();
+                                       return (NIL);
+                               }
+                       }
+               }
+       }
+       return (T);
+}
+
+/* getnoun - get a noun phrase and return the object it refers to */
+int getnoun(void)
+{
+       /* initialize the adjective list pointer */
+       adjectives[nptr] = adjs + aptr;
+
+       /* get the optional article */
+       if (*wptr != NIL && wtype(*wptr) == WT_ARTICLE)
+               wptr++;
+
+       /* get optional adjectives */
+       while (*wptr != NIL && wtype(*wptr) == WT_ADJECTIVE) {
+               adjs[aptr] = *wptr++;
+               anums[aptr] = wptr - words - 1;
+               aptr++;
+       }
+       adjs[aptr++] = 0;
+
+       /* get the noun itself */
+       if (*wptr == NIL || wtype(*wptr) != WT_NOUN) {
+               parse_error();
+               return (NIL);
+       }
+
+       /* save the noun */
+       nouns[nptr] = *wptr++;
+       nnums[nptr] = wptr - words - 1;
+       return (++nptr);
+}
+
+/* get_line - get the input line and lookup each word */
+int get_line(void)
+{
+       /* read an input line */
+       trm_chr(':');
+       if ((lptr = trm_get(line)) == NULL) {
+               trm_str("Speak up!  I can't hear you!\n");
+               return (FALSE);
+       }
+
+       /* get each word on the line */
+       for (wcnt = 0; skip_spaces(); wcnt++)
+               if (get_word() == NIL)
+                       return (FALSE);
+       words[wcnt] = NIL;
+
+       /* check for a blank line */
+       if (wcnt == 0) {
+               trm_str("Speak up!  I can't hear you!\n");
+               return (FALSE);
+       }
+
+       /* point to the first word and return successfully */
+       wptr = words;
+       return (TRUE);
+}
+
+/* skip_spaces - skip leading spaces */
+int skip_spaces(void)
+{
+       while (spacep(*lptr))
+               lptr++;
+       return (*lptr != EOS);
+}
+
+/* show_noun - show a noun phrase */
+void show_noun(int n)
+{
+       int adj, *p;
+
+       /* print the adjectives */
+       for (p = adjectives[n - 1], adj = FALSE; *p; p++, adj = TRUE) {
+               if (adj)
+                       trm_chr(' ');
+               trm_str(wtext[anums[p - adjs]]);
+       }
+
+       /* print the noun */
+       if (adj)
+               trm_chr(' ');
+       trm_str(wtext[nnums[n - 1]]);
+}
+
+/* get_word - get the next word */
+int get_word(void)
+{
+       int ch;
+
+       /* get the next word */
+       for (wtext[wcnt] = lptr; (ch = *lptr) != EOS && !spacep(ch);)
+               *lptr++ = (isupper(ch) ? tolower(ch) : ch);
+       if (*lptr != EOS)
+               *lptr++ = EOS;
+
+       /* look up the word */
+       if (words[wcnt] = findword(wtext[wcnt]))
+               return (words[wcnt]);
+       else {
+               trm_str("I don't know the word \"");
+               trm_str(wtext[wcnt]);
+               trm_str("\".\n");
+               return (NIL);
+       }
+}
+
+/* spacep - is this character a space? */
+int spacep(int ch)
+{
+       return (ch == ' ' || ch == ',' || ch == '.');
+}
+
+/* parse_error - announce a parsing error */
+void parse_error(void)
+{
+       trm_str("I don't understand.\n");
+}
+
+/* trm_init - initialize the terminal module */
+void trm_init(int rows, int cols, char *name)
+{
+       /* initialize the terminal i/o variables */
+       maxcol = cols - 1;
+       col = 0;
+       maxrow = rows - 1;
+       row = 0;
+       wordptr = theWord;
+       wordcnt = 0;
+       scnt = 0;
+
+}
+
+/* trm_done - finish terminal i/o */
+void trm_done(void)
+{
+       if (wordcnt)
+               trm_word();
+}
+
+/* trm_get - get a line */
+char *trm_get(char *line)
+{
+       if (wordcnt)
+               trm_word();
+       while (scnt--)
+               putchr(' ');
+       row = col = scnt = 0;
+       return (trm_line(line));
+}
+
+/* trm_str - output a string */
+void trm_str(char *str)
+{
+       while (*str)
+               trm_chr(*str++);
+}
+
+/* trm_xstr - output a string without logging or word wrap */
+void trm_xstr(char *str)
+{
+       writes(str);
+}
+
+/* trm_chr - output a character */
+void trm_chr(int ch)
+{
+       switch (ch) {
+       case ' ':
+               if (wordcnt)
+                       trm_word();
+               scnt++;
+               break;
+       case '\t':
+               if (wordcnt)
+                       trm_word();
+               scnt = (col + 8) & ~7;
+               break;
+       case '\n':
+               if (wordcnt)
+                       trm_word();
+               trm_eol();
+               scnt = 0;
+               break;
+       default:
+               if (wordcnt < WORDMAX) {
+                       *wordptr++ = ch;
+                       wordcnt++;
+               }
+               break;
+       }
+}
+
+/* trm_word - output the current word */
+void trm_word(void)
+{
+       if (col + scnt + wordcnt > maxcol)
+               trm_eol();
+       else
+               while (scnt--) {
+                       putchr(' ');
+                       col++;
+               }
+       col += write(1, theWord, wordcnt);
+       wordptr = theWord;
+       wordcnt = 0;
+       scnt = 0;
+}
+
+/* trm_eol - end the current line */
+void trm_eol(void)
+{
+       putchr('\n');
+       if (++row >= maxrow) {
+               trm_wait();
+               row = 0;
+       }
+       col = 0;
+}
+
+/* trm_wait - wait for the user to type return */
+void trm_wait(void)
+{
+       trm_xstr("  << MORE >>");
+       while (getchr() != '\n');
+       trm_xstr("            \r");
+}
+
+/* trm_line - get an input line */
+char *trm_line(char *line)
+{
+       char *p;
+       int ch;
+
+       p = line;
+       while ((ch = getchr()) != EOF && ch != '\n')
+               if ((p - line) < LINEMAX)
+                       *p++ = ch;
+       *p = 0;
+       return (ch == EOF ? NULL : line);
+}
+
+/* getchr - input a single character */
+int getchr(void)
+{
+       uint8_t c;
+       if (read(0, &c, 1) == 0)
+               exit(1);
+       return c;
+}
+
+/* putchr - output a single character */
+void putchr(char ch)
+{
+       write(1, &ch, 1);       /* FIXME buffering */
+}
+
+/* msg_init - initialize the message routines */
+void msg_init(int fd, int base)
+{
+       char *p;
+       int i;
+
+       /* remember the message file descriptor and base */
+       mbase = base;
+       mfd = fd;
+       p = mbufdata;
+       for (i = 0; i < CSIZE; i++) {
+               mbuffer[i] = p;
+               p += 512;
+               mblock[i] = -1;
+               mnext[i] = i + 1;
+       }
+       mhead = 0;
+       mtail = CSIZE - 1;
+       mnext[mtail] = -1;
+}
+
+/* msg_open - open a message */
+void msg_open(unsigned int msg)
+{
+       /* save the current message block */
+       mblk = msg >> 7;
+
+       /* make sure the first block is in a buffer */
+       get_block(mblk);
+
+       /* setup the initial offset into the block */
+       moff = (msg & 0x7F) << 2;
+}
+
+/* msg_byte - get a byte from a message */
+int msg_byte(void)
+{
+       /* check for end of block and get next block */
+       if (moff >= 512) {
+               get_block(++mblk);
+               moff = 0;
+       }
+
+       /* return the next message byte */
+       return (decode(mbuf[moff++]));
+}
+
+/* decode - decode a character */
+int decode(int ch)
+{
+       return ((ch + 30) & 0xFF);
+}
+
+/* get_block - get a block of message text */
+void get_block(unsigned int blk)
+{
+       int last, n;
+       long loff;
+
+       /* first check the cache */
+       for (n = mhead; n != -1; last = n, n = mnext[n])
+               if (blk == mblock[n]) {
+                       if (n != mhead) {
+                               if ((mnext[last] = mnext[n]) == -1)
+                                       mtail = last;
+                               mnext[n] = mhead;
+                               mhead = n;
+                       }
+                       mbuf = mbuffer[n];
+                       return;
+               }
+
+       /* overwrite the least recently used buffer */
+       mblock[mtail] = blk;
+       loff = ((long) mbase + (long) blk) << 9;
+
+       lseek(mfd, loff, 0);
+       if (read(mfd, mbuffer[mtail], 512) != 512)
+               error("error reading message text");
+
+       /* get the block */
+       get_block(blk);
+}
diff --git a/Applications/games/advint.h b/Applications/games/advint.h
new file mode 100644 (file)
index 0000000..a664d4e
--- /dev/null
@@ -0,0 +1,234 @@
+int advrestore(char *hdr, int hlen, char *save, int slen);
+int advsave(char *hdr, int hlen, char *save, int slen);
+int checkverb(int *verbs);
+void complement(char *adr, int len);
+void db_init(char *name);
+int db_restart(void);
+int db_restore(void);
+int db_save(void);
+int decode(int ch);
+void display_picture(void);
+void error(char *msg);
+int execute(int code);
+void exe_one(void);
+int fill_action_array(void);
+int findaction(int *verbs, int preposition, int flag);
+int findprop(int obj, int prop);
+int findword(char *word);
+int getabyte(int act, int off);
+int getafield(int act, int off);
+int getaloc(int n);
+int getboperand(void);
+int getbyte(int n);
+int getcbyte(int n);
+int getchr(void);
+int getch(void);
+int getcword(int n);
+int getdword(char *p);
+void get_block(unsigned int blk);
+int get_line(void);
+int getnoun(void);
+int getofield(int obj, int off);
+int getoloc(int n);
+int getp(int obj, int prop);
+int getrand(int n);
+int getvalue(int n);
+int getverb(void);
+int getwloc(int n);
+int getwoperand(void);
+int getword(int n);
+int get_word(void);
+int hasadjective(int obj, int adjective);
+int hasnoun(int obj, int noun);
+int haspreposition(int act, int preposition);
+int hasverb(int act, int *verbs);
+int inlist(int link, int word);
+int match(int obj, int noun, int *adjs);
+int msg_byte(void);
+void msg_init(int fd, int base);
+void msg_open(unsigned int msg);
+int next(void);
+void parse_error(void);
+int parse1(void);
+int parse(void);
+void play(void);
+void pnumber(int n);
+void print(int msg);
+void putch(int ch, FILE * fp);
+void putchr(char ch);
+int putdword(char *p, int w);
+int putofield(int obj, int off, int val);
+int putword(int n, int w);
+int rand(void);
+void range(char *what, int n);
+int setp(int obj, int prop, int val);
+void setrand(long n);
+int setvalue(int n, int v);
+void show_noun(int n);
+int single(void);
+int skip_spaces(void);
+int spacep(int ch);
+void trm_chr(int ch);
+void trm_done(void);
+void trm_eol(void);
+char *trm_get(char *line);
+void trm_init(int rows, int cols, char *name);
+void trm_shutdown(char *textport);
+void trm_str(char *str);
+void trm_wait(void);
+void trm_word(void);
+void trm_xstr(char *str);
+char *trm_line(char *line);
+int vowel(int msg);
+void waitch(void);
+int wtype(int wrd);
+
+/* useful definitions */
+#define TRUE           (int)1
+#define FALSE          (int)0
+#define EOS            '\0'
+
+/* program limits */
+#define STKSIZE                (int)500
+
+/* code completion codes */
+#define FINISH         (int)1
+#define CHAIN          (int)2
+#define ABORT          (int)3
+
+/* useful constants */
+#define T      -1L
+#define NIL    0L
+#define WRDSIZE        (int)6
+
+/* data structure version number */
+#define VERSION                ((int)102)
+
+/* file header offsets */
+#define HDR_LENGTH     ((int)0)        /* length of header in bytes */
+#define HDR_MAGIC      ((int)2)        /* magic information (6 bytes) */
+#define HDR_VERSION    ((int)8)        /* data structure version number */
+#define HDR_ANAME      ((int)10)       /* adventure name (18 bytes) */
+#define HDR_AVERSION   ((int)28)       /* adventure version number */
+#define HDR_WTABLE     ((int)30)       /* offset to word table */
+#define HDR_WTYPES     ((int)32)       /* offset to word type table */
+#define HDR_OTABLE     ((int)34)       /* offset to object table */
+#define HDR_ATABLE     ((int)36)       /* offset to action table */
+#define HDR_VTABLE     ((int)38)       /* offset to variable table */
+#define HDR_DBASE      ((int)40)       /* offset to base of data space */
+#define HDR_CBASE      ((int)42)       /* offset to base of code space */
+#define HDR_DATBLK     ((int)44)       /* first data block */
+#define HDR_MSGBLK     ((int)46)       /* first message text block */
+#define HDR_INIT       ((int)48)       /* initialization code */
+#define HDR_UPDATE     ((int)50)       /* update code */
+#define HDR_BEFORE     ((int)52)       /* code to execute before verb handler */
+#define HDR_AFTER      ((int)54)       /* code to execute after verb handler */
+#define HDR_ERROR      ((int)56)       /* error handler code */
+#define HDR_SAVE       ((int)58)       /* save area offset */
+#define HDR_SLEN       ((int)60)       /* save area length */
+#define HDR_SIZE       ((int)62)       /* size of header */
+
+/* word types */
+#define WT_UNKNOWN     ((int)0)
+#define WT_VERB                ((int)1)
+#define WT_NOUN                ((int)2)
+#define WT_ADJECTIVE   ((int)3)
+#define WT_PREPOSITION ((int)4)
+#define WT_CONJUNCTION ((int)5)
+#define WT_ARTICLE     ((int)6)
+
+/* object fields */
+#define O_CLASS                ((int)0)
+#define O_NOUNS                ((int)2)
+#define O_ADJECTIVES   ((int)4)
+#define O_NPROPERTIES  ((int)6)
+#define O_PROPERTIES   ((int)8)
+#define O_SIZE         ((int)8)
+
+/* action fields */
+#define A_VERBS                ((int)0)
+#define A_PREPOSITIONS ((int)2)
+#define A_FLAG         ((int)4)
+#define A_MASK         ((int)5)
+#define A_CODE         ((int)6)
+#define A_SIZE         ((int)8)
+
+/* link fields */
+#define L_DATA         ((int)0)
+#define L_NEXT         ((int)2)
+#define L_SIZE         ((int)4)
+
+/* property flags */
+#define P_CLASS                (int)0x8000     /* class property */
+
+/* action flags */
+#define A_ACTOR                (int)0x01       /* actor */
+#define A_DOBJECT      (int)0x02       /* direct object */
+#define A_IOBJECT       (int)0x04      /* indirect object */
+
+/* opcodes */
+#define OP_BRT         (int)0x01       /* branch on true */
+#define OP_BRF         (int)0x02       /* branch on false */
+#define OP_BR          (int)0x03       /* branch unconditionally */
+#define OP_T           (int)0x04       /* load top of stack with t */
+#define OP_NIL         (int)0x05       /* load top of stack with nil */
+#define OP_PUSH                (int)0x06       /* push nil onto stack */
+#define OP_NOT         (int)0x07       /* logical negate top of stack */
+#define OP_ADD         (int)0x08       /* add two numeric expressions */
+#define OP_SUB         (int)0x09       /* subtract two numeric expressions */
+#define OP_MUL         (int)0x0A       /* multiply two numeric expressions */
+#define OP_DIV         (int)0x0B       /* divide two numeric expressions */
+#define OP_REM         (int)0x0C       /* remainder of two numeric expressions */
+#define OP_BAND                (int)0x0D       /* bitwise and of two numeric expressions */
+#define OP_BOR         (int)0x0E       /* bitwise or of two numeric expressions */
+#define OP_BNOT                (int)0x0F       /* bitwise not of two numeric expressions */
+#define OP_LT          (int)0x10       /* less than */
+#define OP_EQ          (int)0x11       /* equal to */
+#define OP_GT          (int)0x12       /* greater than */
+#define OP_LIT         (int)0x13       /* load literal */
+#define OP_VAR         (int)0x14       /* load a variable value */
+#define OP_GETP                (int)0x15       /* get the value of an object property */
+#define OP_SETP                (int)0x16       /* set the value of an object property */
+#define OP_SET         (int)0x17       /* set the value of a variable */
+#define OP_PRINT       (int)0x18       /* print messages */
+#define OP_TERPRI      (int)0x19       /* terminate the print line */
+#define OP_PNUMBER     (int)0x1A       /* print a number */
+#define OP_FINISH      (int)0x1B       /* finish handling this command */
+#define OP_CHAIN       (int)0x1C       /* chain to the next handler */
+#define OP_ABORT       (int)0x1D       /* abort this command */
+#define OP_EXIT                (int)0x1E       /* exit the program */
+#define OP_RETURN      (int)0x1F       /* return from interpreter */
+#define OP_CALL                (int)0x20       /* call a function */
+#define OP_SVAR                (int)0x21       /* int load a variable */
+#define OP_SSET                (int)0x22       /* short set a variable */
+#define OP_SPLIT       (int)0x23       /* short load a positive literal */
+#define OP_SNLIT       (int)0x24       /* short load a negative literal */
+#define OP_YORN                (int)0x25       /* yes-or-no predicate */
+#define OP_SAVE                (int)0x26       /* save data structures */
+#define OP_RESTORE     (int)0x27       /* restore data structures */
+#define OP_ARG         (int)0x28       /* load an argument value */
+#define OP_ASET                (int)0x29       /* set an argument value */
+#define OP_TMP         (int)0x2A       /* load a temporary variable value */
+#define OP_TSET                (int)0x2B       /* set a temporary variable */
+#define OP_TSPACE      (int)0x2C       /* allocate temporary variable space */
+#define OP_CLASS       (int)0x2D       /* get the class of an object */
+#define OP_MATCH       (int)0x2E       /* match a noun phrase with an object */
+#define OP_PNOUN       (int)0x2F       /* print a noun phrase */
+#define OP_RESTART     (int)0x30       /* restart the current game */
+#define OP_RAND                (int)0x31       /* generate a random number */
+#define OP_RNDMIZE     (int)0x32       /* seed the random number generator */
+#define OP_SEND                (int)0x33       /* send a message to an object */
+#define OP_VOWEL       (int)0x34       /* check for vowel beginning string */
+
+#define OP_XVAR                (int)0x40       /* extra int load a variable */
+#define OP_XSET                (int)0x60       /* extra short set a variable */
+#define OP_XPLIT       (int)0x80       /* extra short load a positive literal */
+#define OP_XNLIT       (int)0xC0       /* extra short load a negative literal */
+
+/* builtin variables */
+#define V_ACTOR                (int)1  /* actor noun phrase number */
+#define V_ACTION       (int)2  /* action from parse */
+#define V_DOBJECT      (int)3  /* first direct object noun phrase number */
+#define V_NDOBJECTS    (int)4  /* number of direct object noun phrases */
+#define V_IOBJECT      (int)5  /* indirect object noun phrase number */
+#define V_OCOUNT       (int)6  /* total object count */