leo@marco.UUCP (Matthias Pfaller) (03/13/90)
Do you like infocom adventures? This is an interpreter for infocom interactive
fiction games (NOT interactive fiction plus). So it should be possible to
play most of infocom's games with minix. I have tried this interpreter only
with the games I own (starcross, deadline and suspended). If the interpreter
fails with a game, please send me a bug report.
leo@marco.uucp
# ----------------------- cut -----------------------
echo x - zmachine.doc
sed '/^X/s///' > zmachine.doc << '/'
XWith zmachine you can play Interactive Fiction games of Infocom
X(NOT Interactive Fiction Plus!!!)
X
XUsage: zmachine [ -p protocolfile ] [ -w protocol linewith ]
X [ -s storyfile ] [ -r restorefile ] [ <storyfile>.dat ]
X [ <restorefile>.sav ]
X
X-p protocolfile : send output after script command to protocolfile
X-w protocol linewith: set protocol linewith to linewith
X-s storyfile : use storyfile (only necessary if storyfile ends
X not with .dat)
X-r restorefile : restore savefile restorefile before starting game
X (only necessary if restorefile ends not with .sav)
X<storyfile>.dat : use storyfile.dat
X<restorefile>.sav : restore restorefile.dat
X
XIf you call zmachine without arguments, it trys to open the file
Xstory.dat on the current working directory. If you call zmachine with a
Xsavefile argument only, it first tries to open a file with the name
Xsavefile.dat, then a file with the name story.dat.
X
XEditing keys:
XIf the TERMCAP-Entry for your terminal is complete, you can use the
Xcorresponding special keys on your keyboard;
X
XCursor left : ^B
XCursor right : ^F
XUp in history : ^P
XDown in history : ^N
XDelete left : 0x7f, 0x08
XDelete right : ^D
XStart of line : ^A
XEnd of line : ^E
XKill to end of line : ^K
XKill to start of line : ^X
XProgram function key : ^I (TAB)
XUndo : ^Y
XFunction key 0-9 : ^X0 - ^X9
X
X
XBug reports to leo@marco.UUCP
/
echo x - zmachine.c
sed '/^X/s///' > zmachine.c << '/'
X/*
X* @(#)zmachine.c 2.24
X*/
X
X# include "zmachine.h"
X
Xchar *story_name;
Xstruct dev *printer;
X
Xjmp_buf restart_buf;
X
X/************************************************************************/
Xint release;
Xint status_type;
X
XBYTE *ff0;
XBYTE *fec;
Xchar *terms, *my_terms; /* word terminators */
XBYTE *first_word, *last_word;
XUWORD word_len, word_num;
X
Xchar *line; /* line buffer during status */
Xint use_line = 0; /* line output */
XUWORD *stack, *framep, *sp; /* stack management */
X
X#ifdef DEBUG
XUWORD ops_ix = 0;
Xstruct {
X BYTE c_op;
X struct address c_pc;
X UWORD c_sp;
X UWORD c_fp;
X} ops[256];
X#endif
X
XBYTE *short_cuts;
Xint save_len;
Xint status_len; /* number of status lines */
X
Xint line_cnt;
X
Xint in_status = 1;
X
X/************************************************************************/
X
XWORD l116b2(d0)
Xregister WORD d0;
X{
X WORD l116ca();
X
X if (--d0 < 0)
X return(fetchw_op());
X else if (d0 == 0)
X return(fetchb_op());
X else if ((d0 = fetchb_op()) != 0)
X return(l116ca(d0));
X else
X return(*(sp++));
X}
X
XWORD l116c2(d0)
XWORD d0;
X{
X WORD l116ca();
X
X if (!d0)
X return(*sp);
X else
X return(l116ca(d0));
X}
X
XWORD l116ca(d0)
XWORD d0;
X{
X if (d0 < 0x10)
X return(framep[- d0 + 1]);
X else
X return(word_get(&fec[(d0 - 0x10)*2]));
X}
X
Xvoid l116ee(d0, d1)
XWORD d0, d1;
X{
X if (!d0)
X *sp = d1;
X else
X if (d0 < 0x10)
X framep[- d0 + 1] = d1;
X else
X word_put(&fec[(d0 - 0x10)*2], d1);
X}
X
Xvoid l11720(d0)
XWORD d0;
X{
X WORD d1;
X
X if (d1 = fetchb_op())
X l116ee(d1, d0);
X else
X *(--sp) = d0;
X}
X
Xvoid l1171c(d0)
XWORD d0;
X{
X l11720(d0 & 0xff);
X}
X
X#ifdef DEBUG
Xvoid zcore_dump()
X{
X int i, ix;
X FILE *fp;
X
X fp = fopen("zcore", "w");
X if (!fp)
X fp = stderr;
X
X fprintf(fp, "sp = %04x fp = %04x\n", sp-stack, framep-stack);
X
X fprintf(fp, "Stack:");
X for (i = 0; i < 256; i++)
X {
X if (!(i % 8))
X fprintf(fp, "\n%04x: ", i);
X
X fprintf(fp, "%04x ", stack[i]);
X }
X
X fprintf(fp, "\nLast instructions(fp:sp:addr:op):\n");
X for (i = 256; i; i--)
X {
X ix = (ops_ix++ & 0xff);
X if (!(i % 4))
X fprintf(fp, "\n");
X
X fprintf(fp, "%02x:%02x:%05lx:%02x ", ops[ix].c_fp, ops[ix].c_sp,
X (long)ops[ix].c_pc.offset|((long)ops[ix].c_pc.segment<<9),
X ops[ix].c_op);
X }
X fprintf(fp, "\n");
X unlink("zcore.sav");
X save_gf("zcore.sav");
X}
X#endif
X
X/************************************************************************/
X/* op-codes */
X
X/************************************************************************/
X/* op-code utilities */
X
X# define dojmp() jump(0)
X# define dontjmp() jump(1)
X
Xvoid jump(d1)
Xregister WORD d1;
X{
X register WORD d0;
X void ret_0(), ret_1();
X
X d0 = fetchb_op();
X if(d0 & 0x80)
X d1++;
X
X d0 &= ~0x80;
X
X if (!(d0 & 0x40))
X {
X d0 &= ~0x40;
X d0 <<= 8;
X d0 |= fetchb_op();
X if (d0 & 0x2000)
X d0 |= ~0x3fff;
X }
X else
X d0 &= ~0x40;
X
X if (--d1)
X {
X if (!d0)
X ret_0();
X else if (!(--d0))
X ret_1();
X else
X {
X pc.offset += (d0 - 1);
X load_code();
X }
X }
X}
X
XBYTE *l11846(d0)
XWORD d0;
X{
X return(ff0+9*d0+0x35);
X}
X
XBYTE *l11856(a0)
XBYTE *a0;
X{
X register BYTE *p;
X register WORD d;
X
X p = main_p + word_get(a0+7);
X d = *(p++)*2;
X return(p + d);
X}
X
XBYTE *l11870(a0)
XBYTE *a0;
X{
X register WORD d;
X
X d = (*(a0++)>>5) + 1;
X return(a0 + d);
X}
X
Xvoid illegal()
X{
X#ifdef DEBUG
X zcore_dump();
X#endif
X fatal("Bad operation", story_name);
X}
X
X/************************************************************************/
X/* class 0 */
X
Xvoid ret_1()
X{
X void ret();
X ret(1);
X}
X
Xvoid ret_0()
X{
X void ret();
X ret(0);
X}
X
Xvoid print_im()
X{
X decode(&pc);
X load_code();
X}
X
Xvoid printcr_im()
X{
X void newline();
X print_im();
X newline();
X ret_1();
X}
X
Xvoid nop()
X{
X}
X
Xvoid save_g()
X{
X char *name;
X int save_gf();
X
X if (!(name = read_sname()))
X dojmp();
X else
X jump(save_gf(name));
X}
X
Xint save_gf(p)
Xchar *p;
X{
X extern char *iovers;
X UWORD *csp;
X
X if (open_save_w(p))
X return(0);
X
X csp = sp;
X *(--sp) = pc.segment;
X *(--sp) = pc.offset;
X#ifdef GEMDOS
X /* sorry, its awful but compatibel to infocom's interpreter */
X *(--((long *)sp)) = (BYTE *)framep - (BYTE *)stack;
X *(--sp) = release;
X *(((long *)stack)) = (BYTE *)sp - (BYTE *)stack;
X *(stack + sizeof(long)/sizeof(UWORD)) = atoi(iovers);
X#else
X *(--sp) = framep - stack;
X *(--sp) = release;
X *stack = sp - stack;
X *(stack + 1) = atoi(iovers);
X#endif
X sp = csp;
X
X if (write_save(0, 1, stack))
X return(0);
X
X if (write_save(1, save_len, main_p))
X return(0);
X
X if (save_keys())
X return(0);
X
X if (close_save())
X return(0);
X
X return(1);
X}
X
Xvoid restore_g()
X{
X char *name;
X int restore_gf();
X
X if (!(name = read_rname()))
X dojmp();
X else
X jump(restore_gf(name));
X}
X
Xint restore_gf(p)
Xchar *p;
X{
X extern char *iovers;
X int sv;
X
X if (open_save_r(p))
X return(0);
X
X if (read_save(0, 1, stack))
X fatal("Save file read error", p);
X
X#ifdef GEMDOS
X sp = (UWORD *)((BYTE *)stack + *((long *)stack));
X#else
X sp = stack + *stack;
X#endif
X
X if (*sp++ != release)
X fatal("Wrong save file version", p);
X
X#ifdef GEMDOS
X framep = (BYTE *)stack + *(((long *)sp)++);
X#else
X framep = stack + *sp++;
X#endif
X pc.offset = *(sp++);
X pc.segment = *(sp++);
X
X sv = word_get(main_h->reserved5);
X if (read_save(1, save_len, main_p))
X fatal("Save file read error", p);
X word_put(main_h->reserved5, sv);
X
X#ifdef GEMDOS
X if (*(stack + sizeof(long)/sizeof(UWORD)) == atoi(iovers))
X#else
X if (*(stack + 1) == atoi(iovers))
X#endif
X if (restore_keys())
X fatal("Save file read error", p);
X
X close_save();
X load_code();
X
X return(1);
X}
X
Xvoid restart_g()
X{
X line_cnt = 0;
X output_chr('\n');
X longjmp(restart_buf);
X}
X
Xvoid ret_sp()
X{
X void ret();
X ret(*(sp++));
X}
X
Xvoid inc_sp()
X{
X sp++;
X}
X
Xvoid halt()
X{
X clean_up();
X exit(userexit(0));
X}
X
Xvoid newline()
X{
X output_chr('\n');
X}
X
Xvoid status()
X{
X char room[256];
X char *score;
X char am_pm;
X int hour;
X
X void write_num();
X void l11e24();
X
X use_line = 1;
X line = room;
X
X *line++ = ' ';
X l11e24(l116c2(0x10));
X *(line++) = '\0';
X score = line;
X if (!status_type)
X {
X strcpy(line, "Score: ");
X write_num(l116c2(0x11));
X *line++ = '/';
X write_num(l116c2(0x12));
X }
X else
X {
X if ((hour = l116c2(0x11)) > 12)
X {
X hour -= 12;
X am_pm = 'p';
X }
X else
X am_pm = 'a';
X
X if (hour == 0)
X hour = 12;
X
X/*
X sprintf(line, "Time: %2d:%02d %cm", hour, l116c2(0x12), am_pm);
X*/
X strcpy(line, "Time: ");
X line += strlen(line);
X if (hour < 10)
X *line++ = ' ';
X write_num(hour);
X *line++ = ':';
X if (l116c2(0x12) < 10)
X *line++ = '0';
X write_num(l116c2(0x12));
X *line++ = ' ';
X *line++ = am_pm;
X *line++ = 'm';
X }
X *line++ = ' ';
X *line++ = '\0';
X output_status(room, score);
X use_line = 0;
X}
X
Xvoid verify()
X{
X struct address a, end;
X int m_len;
X long sum;
X
X output_str("Z-Code 3 interpreter V2.24 for ");
X output_str(sysname);
X output_str("\n");
X
X m_len = main_l;
X main_l = 0;
X
X a.segment = 0;
X a.offset = 0x40;
X sum = 0;
X waddr_to_vaddr(&end, word_get(main_h->len));
X
X while(a.segment != end.segment || a.offset != end.offset)
X sum += fetchb_data(&a);
X
X main_l = m_len;
X
X jump((UWORD)sum == word_get(main_h->checksum));
X}
X
X/************************************************************************/
X/* class 1 */
X
Xvoid bne(d0)
XWORD d0;
X{
X if(d0)
X dojmp();
X else
X dontjmp();
X}
X
Xvoid l1190c(d0)
XWORD d0;
X{
X register BYTE d1;
X
X l11720(d1 = l11846(d0)[5]);
X
X if (d1)
X dontjmp();
X else
X dojmp();
X}
X
Xvoid l118f2(d0)
XWORD d0;
X{
X register BYTE d1;
X
X l11720(d1 = l11846(d0)[6]);
X
X if (d1)
X dontjmp();
X else
X dojmp();
X}
X
Xvoid l118e4(d0)
XWORD d0;
X{
X l11720(l11846(d0)[4]);
X}
X
Xvoid l11a7e(d0)
XWORD d0;
X{
X l11720((main_p[d0-1]>>5)+1);
X}
X
Xvoid l11aac(d0)
XWORD d0;
X{
X l116ee(d0,l116c2(d0)+1);
X}
X
Xvoid l11aba(d0)
XWORD d0;
X{
X l116ee(d0,l116c2(d0)-1);
X}
X
Xvoid print_near(d0)
XWORD d0;
X{
X struct address a;
X baddr_to_vaddr(&a,d0);
X decode(&a);
X}
X
Xvoid l118a2(d1)
XWORD d1;
X{
X register BYTE *a0, *a1;
X register UWORD d0;
X
X a1 = l11846(d1);
X if (d0 = a1[4])
X {
X d0 = (a0 = l11846(d0))[6];
X if (d1 == d0)
X a0[6] = a1[5];
X else
X {
X while ((d0 = (a0 = l11846(d0))[5]) != d1)
X ;
X a0[5] = a1[5];
X }
X a1[4] = a1[5] = 0;
X }
X}
X
Xvoid l11e24(d0)
XWORD d0;
X{
X struct address a;
X
X baddr_to_vaddr(&a, word_get(l11846(d0)+7) + 1);
X decode(&a);
X}
X
Xvoid ret(d0)
XWORD d0;
X{
X sp = framep + 1;
X#ifdef GEMDOS
X framep = (BYTE *)stack + *(((long *)sp)++);
X#else
X framep = stack + *sp++;
X#endif
X pc.offset = *(sp++);
X pc.segment = *(sp++);
X load_code();
X l11720(d0);
X}
X
Xvoid bra(d0)
XWORD d0;
X{
X pc.offset += (d0 - 2);
X load_code();
X}
X
Xvoid print_far(d0)
XWORD d0;
X{
X struct address a;
X waddr_to_vaddr(&a, d0);
X decode(&a);
X}
X
Xvoid l11a96(d0)
XWORD d0;
X{
X l11720(l116c2(d0));
X}
X
Xvoid not(d0)
XWORD d0;
X{
X l11720(~d0);
X}
X
X/************************************************************************/
X/* class 2 */
X
Xvoid l11824(a0)
Xregister WORD *a0;
X{
X register WORD d0, d1;
X
X d1 = *(a0++) - 1;
X d0 = *(a0++);
X
X for(;;)
X if(*(a0++) == d0)
X {
X dontjmp();
X break;
X }
X else if (!--d1)
X {
X dojmp();
X break;
X }
X}
X
Xvoid cbge(d0, d1)
XWORD d0, d1;
X{
X jump(d0 < d1);
X}
X
Xvoid cble(d0, d1)
XWORD d0, d1;
X{
X jump(d0 > d1);
X}
X
Xvoid dcbge(d0, d1) /* decrement, compare and jump greater equal */
XWORD d0, d1; /* dcbge */
X{
X register WORD d2;
X
X d2 = l116c2(d0) - 1;
X l116ee(d0,d2);
X if (d2 < d1)
X dontjmp();
X else
X dojmp();
X}
X
Xvoid icble(d0, d1) /* increment, compare and jump less equal */
XWORD d0, d1; /* icble */
X{
X register WORD d2;
X
X d2 = l116c2(d0) + 1;
X l116ee(d0,d2);
X if (d2 > d1)
X dontjmp();
X else
X dojmp();
X}
X
Xvoid l11926(d0, d1)
XWORD d0, d1;
X{
X if (l11846(d0)[4] == d1)
X dontjmp();
X else
X dojmp();
X}
X
Xvoid l11806(d0, d1)
XWORD d0, d1;
X{
X jump((d1 & ~d0) == 0);
X}
X
Xvoid or(d0, d1)
XWORD d0, d1;
X{
X l11720(d0 | d1);
X}
X
Xvoid and(d0, d1)
X{
X l11720(d0 & d1);
X}
X
X#define tst(a0, d0) (a0[d0 >> 3] & (1<<((~d0)&7)))
X#define clr(a0, d0) (a0[d0 >> 3] &= ~(1<<((~d0)&7)))
X#define set(a0, d0) (a0[d0 >> 3] |= (1<<((~d0)&7)))
X
Xvoid tstbit_jz(d0, d1)
XWORD d0, d1;
X{
X if (tst(l11846(d0), d1))
X dontjmp();
X else
X dojmp();
X}
X
Xvoid setbit(d0, d1)
XWORD d0, d1;
X{
X set(l11846(d0), d1);
X}
X
Xvoid clrbit(d0, d1)
XWORD d0, d1;
X{
X clr(l11846(d0), d1);
X}
X
Xvoid l11a9e(d0, d1)
XWORD d0, d1;
X{
X l116ee(d0, d1);
X}
X
Xvoid l1187c(d0, d1)
XWORD d0, d1;
X{
X register BYTE *a0, *a1;
X void l118a2();
X
X l118a2(d0);
X a1 = l11846(d1);
X a0 = l11846(d0);
X
X a0[5] = a1[6];
X a0[4] = d1;
X a1[6] = d0;
X}
X
Xvoid l11a16(d0, d1)
XWORD d0, d1;
X{
X struct address a;
X baddr_to_vaddr(&a, d0 + d1 * 2);
X l11720(fetchw_data(&a));
X}
X
Xvoid l11a28(d0, d1)
XWORD d0, d1;
X{
X struct address a;
X baddr_to_vaddr(&a, d0 + d1);
X l1171c(fetchb_data(&a));
X}
X
Xvoid l11936(d0, d1)
Xregister WORD d0, d1;
X{
X register BYTE *a0;
X
X for (a0 = l11856(l11846(d0)); d1 < (d0 = *a0 & 0x1f); a0 = l11870(a0))
X ;
X
X if (d1 == d0)
X {
X if (!(*(a0++) & 0x20))
X l1171c(*a0);
X else
X l11720(word_get(a0));
X }
X else
X l11720(word_get(&ff0[(d1-1) * 2]));
X
X}
X
Xvoid l11a54(d0, d1)
Xregister WORD d0, d1;
X{
X register BYTE *a0;
X
X for (a0 = l11856(l11846(d0)); d1 < (d0 = *a0 & 0x1f); a0 = l11870(a0))
X ;
X
X if (d1 != d0)
X l11720(0);
X else
X l11720(++a0 - main_p);
X}
X
Xvoid l119a4(d0, d1)
Xregister WORD d0, d1;
X{
X register BYTE *a0;
X
X a0 = l11856(l11846(d0));
X
X if (d1)
X {
X for(; d1 < (d0 = *a0 & 0x1f); a0 = l11870(a0))
X ;
X if (d1 != d0)
X fatal("Non-existant next property", story_name);
X else
X a0 = l11870(a0);
X }
X l11720(*a0 & 0x1f);
X}
X
Xvoid add(d0, d1)
XWORD d0, d1;
X{
X l11720(d0 + d1);
X}
X
Xvoid sub(d0, d1)
XWORD d0, d1;
X{
X l11720(d0 - d1);
X}
X
Xvoid mul(d0, d1)
XWORD d0, d1;
X{
X l11720(d0 * d1);
X}
X
Xvoid div(d0, d1)
XWORD d0, d1;
X{
X l11720(d0 / d1);
X}
X
Xvoid mod(d0, d1)
XWORD d0, d1;
X{
X l11720(d0 % d1);
X}
X
Xvoid call(a0)
Xregister WORD *a0;
X{
X register WORD d0, d1, d2;
X
X d2 = *(a0++);
X if ((d0 = *(a0++)) == 0)
X l11720(0);
X else
X {
X d2--;
X *(--sp) = pc.segment;
X *(--sp) = pc.offset;
X#ifdef GEMDOS
X *(--((long *)sp)) = (BYTE *)framep - (BYTE *)stack;
X#else
X *(--sp) = framep - stack;
X#endif
X waddr_to_vaddr(&pc,d0);
X load_code();
X framep = sp - 1;
X if (d1 = fetchb_op())
X {
X do
X {
X d0 = fetchw_op();
X if (--d2 >= 0)
X d0 = *(a0++);
X *(--sp) = d0;
X }
X while (--d1 > 0);
X }
X }
X}
X
Xvoid l11a38(d0, d1, d2)
XWORD d0, d1, d2;
X{
X word_put(&main_p[d0 + d1 * 2], d2);
X}
X
X
Xvoid l11a48(d0, d1, d2)
XWORD d0, d1; WORD d2;
X{
X main_p[d0+d1] = (BYTE)d2;
X}
X
Xvoid l1196e(d0, d1, d2)
Xregister WORD d0, d1; WORD d2;
X{
X register BYTE *a0;
X
X for (a0 = l11856(l11846(d0)); d1 < (d0 = *a0 & 0x1f); a0 = l11870(a0))
X ;
X
X if (d1 != d0)
X fatal("Non-existant put property", story_name);
X
X if (*(a0++) & 0x20)
X word_put(a0, d2);
X else
X *a0 = d2;
X}
X
XUWORD findword(a0)
XUWORD *a0;
X{
X register UWORD d0, step, code0, code1;
X register BYTE *word;
X
X d0 = word_num;
X step = word_len;
X
X code0 = *a0;
X code1 = *(a0+1);
X
X for ( d0 >>= 1, step <<= 1; d0 >>= 1; step <<= 1)
X ;
X
X word = &first_word[step - word_len];
X
X do
X {
X step >>= 1;
X d0 = word_get(word);
X if (code0 <= d0)
X {
X if (code0 != d0)
X word -= step;
X else
X {
X d0 = word_get(word+2);
X if (code1 <= d0)
X {
X if (code1 != d0)
X word -= step;
X else
X return(word - main_p);
X }
X else
X {
X word += step;
X if (last_word < word)
X word = last_word;
X }
X }
X }
X else
X {
X word += step;
X if (last_word < word)
X word = last_word;
X }
X }
X while(word_len <= step);
X return(0);
X}
X
Xchar *wordend(c)
Xregister char c;
X{
X register char *p;
X
X for(p = terms; *p; p++)
X if (*p == c)
X return (p);
X return(NULL);
X}
X
Xvoid input(a0)
XWORD *a0;
X{
X char c;
X char *inp; /* input field */
X char *end; /* end of input line */
X BYTE *out; /* output field */
X BYTE *outp; /* pointer into output field */
X char src_word[8]; /* source word */
X UWORD dst_word[4]; /* word coded in z-code */
X register char *lp; /* pointer into line */
X register char *cp; /* pointer into src_word */
X char *ws; /* pointer into line (start of line) */
X register int wc; /* word count */
X register char *p; /* scratch */
X static struct hist_buf history = {I_HIST_LEN, NULL, NULL};
X /* history buffer */
X
X if (!history.hb)
X {
X if (!(history.undo = history.hb = malloc(history.len)))
X no_mem_error();
X else
X history.hb[0] = history.hb[1] = '\0';
X }
X
X inp = (char *)&main_p[a0[1]];
X out = &main_p[a0[2]];
X
X status();
X output_chr(FLUSH);
X
X end = read_str(inp, &history);
X
X lp = inp + 1;
X outp = out + 2;
X wc = 0;
X
X for(;;)
X {
X cp = src_word;
X ws = lp;
X while (lp < end)
X {
X if (p = wordend(c = *lp++))
X break;
X else
X if (cp < src_word + 6)
X *cp++ = c;
X }
X
X if (lp < end)
X {
X if (cp != src_word)
X lp--;
X else if (p < my_terms)
X *cp++ = c;
X else
X continue;
X }
X else
X {
X if (src_word == cp)
X break;
X }
X
X if (++wc >= out[0])
X {
X output_chr('\n');
X output_str("too many words for internal buffer\n");
X out[1] = 0;
X return;
X }
X
X outp[2] = (char)(lp - ws);
X outp[3] = (char)(ws - inp);
X *cp = '\0';
X encode(dst_word, src_word);
X word_put(outp, findword(dst_word));
X outp += 4;
X }
X out[1] = wc;
X
X if (word_get(main_h->reserved5) & 1)
X {
X for (p = inp; *++p; )
X putc_dev(*p, printer);
X putc_dev('\n', printer);
X }
X}
X
Xvoid l11dcc(d0)
XWORD d0;
X{
X output_chr(d0);
X}
X
Xvoid write_num(d0)
XWORD d0;
X{
X char buf[16];
X register char *p;
X int flag;
X
X flag = 0;
X if (d0 < 0)
X {
X flag = 1;
X d0 = -d0;
X }
X
X p = buf + 15;
X *p = '\0';
X do
X *--p = d0 % 10 + '0';
X while(d0 /= 10);
X
X if (flag)
X *--p = '-';
X
X output_str(p);
X}
X
Xvoid rnd(d0)
XWORD d0;
X{
X extern long zrandom();
X/*
X static UWORD rnd0 = 0xffff, rnd1 = 0;
X register long d1;
X
X if (rnd0 == 0xffff)
X {
X d1 = Random();
X rnd0 = d1 >> 16;
X rnd1 = d1;
X }
X
X d1 = rnd0;
X d1 |= (rnd0 = rnd1) << 16;
X d1 >>= 1;
X d1 = (rnd1 ^= d1);
X d1 &= 0x7fff;
X l11720(((UWORD)d1 % d0) + 1);
X*/
X l11720((((UWORD)zrandom() & (UWORD)0x7fff) % d0) + 1);
X}
X
Xvoid l11aa2(d0)
XWORD d0;
X{
X *(--sp) = d0;
X}
X
Xvoid l11aa6(d0)
XWORD d0;
X{
X l116ee(d0, *(sp++));
X}
X
Xvoid l11e5e(d0)
XWORD d0;
X{
X status_len = d0;
X}
X
Xvoid l11e7e(d0)
XWORD d0;
X{
X static int x,y;
X
X if (d0 == 0)
X {
X if (in_status)
X gotoXY(x,y);
X
X in_status = 0;
X }
X else if(d0 == 1)
X {
X if (!in_status)
X storeXY(&x, &y);
X
X in_status = 1;
X gotoXY(0,1);
X }
X}
X
X/************************************************************************/
X/* main interpreter loop */
X
Xvoid (*class0[])() = {
X ret_1, /* op 0xb0 */
X ret_0, /* op 0xb1 */
X print_im, /* op 0xb2 */
X printcr_im, /* op 0xb3 */
X nop, /* op 0xb4 */
X save_g, /* op 0xb5 */
X restore_g, /* op 0xb6 */
X restart_g, /* op 0xb7 */
X ret_sp, /* op 0xb8 */
X inc_sp, /* op 0xb9 */
X halt, /* op 0xba */
X newline, /* op 0xbb */
X status, /* op 0xbc */
X verify, /* op 0xbd */
X illegal, /* op 0xbe */
X illegal /* op 0xbf */
X};
X
Xvoid (*class1[])() = {
X bne,
X l1190c,
X l118f2,
X l118e4,
X l11a7e,
X l11aac,
X l11aba,
X print_near,
X illegal,
X l118a2,
X l11e24,
X ret,
X bra,
X print_far,
X l11a96,
X not
X};
X
Xstruct {
X void (*func)();
X BYTE flag;
X } class2[] = {
X { illegal, 1 }, /* 00 */
X { l11824, 0 }, /* 01 */
X { cbge, 1 }, /* 02 */
X { cble, 1 }, /* 03 */
X { dcbge, 1 }, /* 04 */
X { icble, 1 }, /* 05 */
X { l11926, 1 }, /* 06 */
X { l11806, 1 }, /* 07 */
X { or, 1 }, /* 08 */
X { and, 1 }, /* 09 */
X { tstbit_jz, 1 }, /* 0a */
X { setbit, 1 }, /* 0b */
X { clrbit, 1 }, /* 0c */
X { l11a9e, 1 }, /* 0d */
X { l1187c, 1 }, /* 0e */
X { l11a16, 1 }, /* 0f */
X { l11a28, 1 }, /* 10 */
X { l11936, 1 }, /* 11 */
X { l11a54, 1 }, /* 12 */
X { l119a4, 1 }, /* 13 */
X { add, 1 }, /* 14 */
X { sub, 1 }, /* 15 */
X { mul, 1 }, /* 16 */
X { div, 1 }, /* 17 */
X { mod, 1 }, /* 18 */
X { illegal, 1 }, /* 19 */
X { illegal, 1 }, /* 1a */
X { illegal, 1 }, /* 1b */
X { illegal, 1 }, /* 1c */
X { illegal, 1 }, /* 1d */
X { illegal, 1 }, /* 1e */
X { illegal, 1 }, /* 1f */
X { call, 0 }, /* 20 */
X { l11a38, 1 }, /* 21 */
X { l11a48, 1 }, /* 22 */
X { l1196e, 1 }, /* 23 */
X { input, 0 }, /* 24 */
X { l11dcc, 1 }, /* 25 */
X { write_num, 1 }, /* 26 */
X { rnd, 1 }, /* 27 */
X { l11aa2, 1 }, /* 28 */
X { l11aa6, 1 }, /* 29 */
X { l11e5e, 1 }, /* 2a */
X { l11e7e, 1 }, /* 2b */
X { illegal, 1 }, /* 2c */
X { illegal, 1 }, /* 2d */
X { illegal, 1 }, /* 2e */
X { illegal, 1 }, /* 2f */
X { illegal, 1 }, /* 30 */
X { illegal, 1 }, /* 31 */
X { illegal, 1 }, /* 32 */
X { illegal, 1 }, /* 33 */
X { illegal, 1 }, /* 34 */
X { illegal, 1 }, /* 35 */
X { illegal, 1 }, /* 36 */
X { illegal, 1 }, /* 37 */
X { illegal, 1 }, /* 38 */
X { illegal, 1 }, /* 39 */
X { illegal, 1 }, /* 3a */
X { illegal, 1 }, /* 3b */
X { illegal, 1 }, /* 3c */
X { illegal, 1 }, /* 3d */
X { illegal, 1 }, /* 3e */
X { illegal, 1 } /* 3f */
X};
X
Xint oc;
Xint o1;
X
Xvoid run()
X{
X register UWORD d0, d1, d2, d3, d4;
X register UWORD *argp, *bp;
X UWORD args[6];
X UWORD bits[4];
X
X argp = args;
X for(;;)
X {
X#ifdef DEBUG
X ops[ops_ix].c_pc.offset = pc.offset;
X ops[ops_ix].c_pc.segment = pc.segment;
X ops[ops_ix].c_sp = sp - stack;
X ops[ops_ix].c_fp = framep - stack;
X ops[ops_ix].c_op = d0 = fetchb_op();
X ops_ix = (ops_ix + 1) & 0xff;
X#else
X d0 = fetchb_op();
X#endif
X if (d0 < 0x80)
X {
X d1 = l116b2((d0 & 0x40)?2:1);
X d2 = l116b2((d0 & 0x20)?2:1);
X
X if (class2[d0 & 0x1f].flag)
X (*class2[d0 & 0x1f].func)(d1, d2);
X else
X {
X argp[0] = 2;
X argp[1] = d1;
X argp[2] = d2;
X (*class2[d0 & 0x1f].func)(argp);
X }
X }
X else if (d0 < 0xb0)
X (*class1[d0 & 0x0f])(l116b2((d0 >> 4) & 0x03));
X else if (d0 < 0xc0)
X (*class0[d0 & 0x0f])();
X else
X {
X d2 = d0;
X bp = &bits[4];
X d0 = fetchb_op();
X *(--bp) = d0;
X *(--bp) = (d0 >>= 2);
X *(--bp) = (d0 >>= 2);
X *(--bp) = (d0 >>= 2);
X for (d4 = 0, d3 = 4; d3; d3--)
X {
X if ((d0 = *(bp++) & 3) == 3)
X break;
X argp[++d4] = l116b2(d0);
X }
X argp[0] = d4;
X
X if (class2[d2 & 0x3f].flag)
X (*class2[d2 & 0x3f].func)
X (argp[1],argp[2],argp[3],argp[4]);
X else
X (*class2[d2 & 0x3f].func)(argp);
X }
X }
X}
X
X/************************************************************************/
X/* initializiation */
Xno_mem_error()
X{
X fatal("Out of memory", NULL);
X}
X
Xmain(argc,argv)
Xint argc; char **argv;
X{
X struct header h;
X register BYTE *p, *q, *r;
X register int i;
X
X char space[128];
X extern int optind;
X extern char *optarg;
X char *usage;
X
X int c, sflag, rflag;
X char *restore_name;
X char *strchr(), *strrchr();
X
X# if defined(OSK)
X usage = "\15\12Usage:\tzmachine [ -p protocolfile ] [ -w protocol linewith ]\
X\15\12\t[ -s storyfile ] [ -r restorefile ] [ <storyfile>.dat ]\
X\15\12\t[ <restorefile>.sav ]\15\12";
X# else
X usage = "\r\nUsage:\tzmachine [ -p protocolfile ] [ -w protocol linewith ]\
X\r\n\t[ -s storyfile ] [ -r restorefile ] [ <storyfile>.dat ]\
X\r\n\t[ <restorefile>.sav ]\r\n";
X# endif
X
X story_name = restore_name = NULL;
X
X sflag = rflag = 0;
X while ((c = getopt(argc, argv, "s:p:r:w:")) != EOF)
X {
X switch(c)
X {
X case 's':
X story_name = optarg;
X sflag++;
X break;
X
X case 'p':
X print_name = optarg;
X break;
X
X case 'r':
X restore_name = optarg;
X rflag++;
X break;
X
X case 'w':
X if (!(printer_width = atoi(optarg)))
X printer_width = 80;
X break;
X
X default:
X write(2, usage, strlen(usage));
X exit(userexit(1));
X }
X }
X
X while (optind < argc)
X {
X char *tmp;
X
X if ((tmp = strrchr(argv[optind],'.')) &&
X (!strcmp(tmp,".dat") || !strcmp(tmp, ".DAT")))
X story_name = argv[optind];
X else if (tmp && (!strcmp(tmp, ".sav") || !strcmp(tmp, ".SAV")))
X {
X rflag++;
X restore_name = argv[optind];
X }
X else
X {
X fatal("Unknown filetype", argv[optind]);
X }
X optind++;
X }
X
X
X if (!story_name && restore_name && strchr(restore_name,'.'))
X {
X strcpy(space, restore_name);
X strcpy(strchr(space, '.'), ".dat");
X if (access(space,0))
X story_name = "story.dat";
X else
X story_name = space;
X }
X
X if (!story_name)
X story_name = "story.dat";
X
X if (open_story())
X fatal("Can't open", story_name);
X
X read_header(&h);
X
X if (h.zmachine != 3)
X fatal("Wrong Z-machine version", story_name);
X
X mmu_init(&h);
X
X read_story(0, main_l, main_p);
X
X in_status = 0;
X
X release = word_get(h.release);
X
X status_type = (h.flags & 2) >> 1;
X
X ff0 = main_p + word_get(h.reserved2);
X fec = main_p + word_get(h.reserved3);
X short_cuts = main_p + word_get(h.short_cuts);
X save_len = btop((long)word_get(h.save_len));
X if (!(stack = (UWORD *)malloc(0x100 * sizeof(WORD))))
X no_mem_error();
X
X printer = init_dev(prot_write, prot_crlf, printer_width, 0, 0);
X
X if (!(terms = malloc(0x40)))
X no_mem_error();
X
X q = main_p + word_get(h.vocabulary);
X for(p = (BYTE *)terms, i = *(q++); i; i--)
X *p++ = *q++; /* copy terminators */
X
X my_terms = (char *)p;
X for(r = (BYTE *)" \011\015.,?"; *p++ = *r++;)
X ; /* copy my terminators */
X
X word_len = *q++; /* word-len */
X word_num = word_get(q); /* number of words */
X q += 2;
X
X first_word = q; /* start of words */
X last_word = (word_num - 1) * word_len + first_word;
X /* end of words */
X
X if (setjmp(restart_buf))
X {
X rflag = 0;
X i = word_get(main_h->reserved5);
X read_story(0, save_len, main_p);
X word_put(main_h->reserved5, i);
X }
X
X printer->bp = printer->buffer;
X printer->count = 0;
X main_h->flags |= 32;
X
X status_len = 1;
X init_con();
X if (!rflag)
X {
X sp = &stack[0x100];
X framep = sp - 1;
X baddr_to_vaddr(&pc, word_get(h.initial_pc));
X load_code();
X }
X else
X {
X if (restore_gf(restore_name))
X jump(1);
X else
X fatal("Cannot load restore file", restore_name);
X }
X run();
X}
/
echo x - zmachine.h
sed '/^X/s///' > zmachine.h << '/'
X/*
X* @(#)zmachine.h 2.24
X*/
X
X# include <stdio.h>
X# include <setjmp.h>
X# include "config.h"
X
Xchar *lmalloc(), *malloc(), *realloc();
X
Xstruct dev {
X char *buffer;
X int width;
X int height;
X char *bp;
X int count;
X void (*out_f)();
X int wrap;
X void (*crlf_f)();
X };
X
Xstruct hist_buf {
X int len;
X char *hb;
X char *undo;
X };
X
Xstruct header
X {
X BYTE zmachine; /* 00 */
X BYTE flags; /* 01 */
X ZWORD release; /* 02 */
X ZWORD minmem; /* 04 */
X ZWORD initial_pc; /* 06 */
X ZWORD vocabulary; /* 08 */
X ZWORD reserved2; /* 0a ? */
X ZWORD reserved3; /* 0c ? */
X ZWORD save_len; /* 0e */
X ZWORD reserved5; /* 10 zero */
X BYTE serial[6]; /* 12 */
X ZWORD short_cuts; /* 18 */
X ZWORD len; /* 1a */
X ZWORD checksum; /* 1c */
X ZWORD reserved8; /* 1e zero */
X ZWORD reserved9[16]; /* 20 zero */
X };
X
X
Xstruct virt_page {
X WORD page;
X unsigned long lru;
X BYTE *paddr;
X };
X
Xstruct address
X {
X WORD segment;
X WORD offset; /* MUST be signed */
X };
X
X# define FLUSH 0x7f /* Flush output buffer */
X
Xextern char *story_name; /* Name of story file */
Xextern char *print_name; /* Name of protocol file */
Xextern int printer_width; /* Character-width of protocol
X file */
Xextern struct dev *screen, *printer; /* screen and protocol device
X structures */
X
Xextern int main_l; /* len of main memory in pages */
Xextern BYTE *main_p; /* start of main memory */
Xextern struct header *main_h; /* pointer to header structure
X in main memory */
Xextern struct address pc; /* programcounter */
Xextern struct virt_page *pc_page; /* currently executed page */
X
Xextern char *sysname; /* system name */
X
X/* from io.c */
X
Xstruct dev *init_dev();
Xvoid scr_write();
Xvoid prot_write();
Xvoid prot_crlf();
Xvoid output_status();
Xvoid output_chr();
Xvoid output_str();
Xchar *read_str();
Xint save_keys();
Xint restore_keys();
X
Xextern int pfile;
X
X/* from zbios.c */
X
Xlong zrandom();
Xvoid con_flush();
Xvoid con_chr();
Xvoid con_str1();
Xvoid con_str2();
Xvoid con_crlf();
Xvoid reverseON();
Xvoid reverseOFF();
Xvoid cursorON();
Xvoid cursorOFF();
Xvoid gotoXY();
Xvoid storeXY();
Xint con_getc();
Xvoid init_con();
X
Xint open_story();
Xint close_story();
Xvoid read_story();
Xvoid read_header();
X
Xchar *read_sname();
Xchar *read_rname();
Xint open_save_w();
Xint open_save_r();
Xint close_save();
Xint write_save();
Xint read_save();
Xvoid fatal();
Xvoid clean_up();
Xint userexit();
X
X/* from mem.c */
X
Xvoid load_code();
XBYTE *load_page();
Xlong ptob();
XUWORD btob();
XUWORD word_get();
Xvoid word_put();
XBYTE fetchb_data();
XUWORD fetchw_data();
XBYTE fetchb_op();
XUWORD fetchw_op();
Xvoid baddr_to_vaddr();
Xvoid waddr_to_vaddr();
X
X/* from code.c */
X
Xvoid decode();
Xvoid encode();
/
echo x - mem.c
sed '/^X/s///' > mem.c << '/'
X/*
X* @(#)mem.c 2.24
X*/
X
X# include "zmachine.h"
X
X# define LOCK 0xffffffffL
X
XBYTE *main_p; /* main memory pointer */
Xint main_l; /* main memory len in pages */
Xstruct header *main_h; /* header pointer */
X
Xstruct virt_page *pc_page; /* page of current pc */
Xstruct virt_page main_page; /* page control for main memory */
Xstruct virt_page *c_page; /* current active page */
Xstruct virt_page *virt_p; /* the virtual memory array */
Xstruct address pc; /* program counter */
X
Xunsigned long lruc = 0; /* LRU counter */
X
X/************************************************************************/
X
X/* find free page */
X
Xstruct virt_page *find_page()
X{
X register struct virt_page *u, *v;
X register long t;
X
X for(v = virt_p, t = LOCK; v->page != -1; v++)
X if (v->lru < t)
X {
X t = v->lru;
X u = v;
X }
X
X return(u);
X}
X
X
X/* load a page */
X
XBYTE *load_page(page)
Xregister WORD page;
X{
X register struct virt_page *v;
X
X if (page != c_page->page)
X {
X lruc++;
X for (v = virt_p; v->page != -1; v++)
X {
X if (v->page == page)
X {
X if (pc_page->page != page)
X v->lru = lruc;
X
X c_page = v;
X return(c_page->paddr);
X }
X }
X c_page = find_page();
X c_page->page = page;
X c_page->lru = lruc;
X read_story(c_page->page, 1, c_page->paddr);
X }
X return(c_page->paddr);
X}
X
X
X/* load page for current pc */
X
Xvoid load_code()
X{
X pc.segment += pc.offset >> 9; /* pc.offset may be negative */
X pc.offset &= 0x1ff;
X if (pc.segment != pc_page->page)
X {
X pc_page->lru = lruc;
X
X if (pc.segment >= main_l)
X {
X load_page(pc.segment);
X pc_page = c_page;
X pc_page->lru = LOCK;
X }
X else
X {
X main_page.page = pc.segment;
X main_page.paddr = main_p+ptob(pc.segment);
X pc_page = &main_page;
X }
X }
X}
X
X
X/* page to byte address */
X
Xlong ptob(page)
XUWORD page;
X{
X return(((long)page << (long)9) & 0x1ffffL);
X}
X
X
X/* byte to page address */
X
XUWORD btop(byte)
Xlong byte;
X{
X return((byte & 0x1ff ? (byte >> 9)+1:(byte >> 9)) & 0xff);
X}
X
X
X/* get word */
X
XUWORD word_get(p)
XBYTE *p;
X{
X register UWORD i;
X i = *p++ << 8;
X return(i | *p);
X}
X
X
X/* put word */
X
Xvoid word_put(p, d)
XBYTE *p; register UWORD d;
X{
X p[1] = d & 0xff;
X p[0] = d >> 8;
X}
X
X
X/* fetch byte data */
X
XBYTE fetchb_data(a)
Xregister struct address *a;
X{
X BYTE *load_page();
X register BYTE r;
X
X if (a->segment < main_l)
X r = main_p[ptob(a->segment) | a->offset];
X else
X r = load_page(a->segment)[a->offset];
X
X if (++(a->offset) == 0x200)
X {
X a->offset = 0;
X (a->segment)++;
X }
X return(r);
X}
X
X
X/* fetch word data */
X
XUWORD fetchw_data(a)
Xstruct address *a;
X{
X register UWORD r;
X r = fetchb_data(a);
X return((r << 8) | fetchb_data(a));
X}
X
X
X/* fetch next byte from pc */
X
XBYTE fetchb_op()
X{
X register BYTE r;
X
X r = pc_page->paddr[pc.offset];
X if (++pc.offset >= 0x200)
X load_code();
X
X return(r);
X}
X
X
X/* fetch next word from pc */
X
XUWORD fetchw_op()
X{
X register UWORD r;
X
X r = fetchb_op() << 8;
X return(r | fetchb_op());
X}
X
X
X/* byte address to virtual address */
X
Xvoid baddr_to_vaddr(vaddress, baddress)
Xregister struct address *vaddress; register UWORD baddress;
X{
X vaddress->segment = (baddress >> 9) & 0xff;
X vaddress->offset = baddress & 0x1ff;
X}
X
X
X/* word address to virtual address */
X
Xvoid waddr_to_vaddr(vaddress, waddress)
Xstruct address *vaddress; register UWORD waddress;
X{
X vaddress->segment = (waddress >> 8) & 0xff;
X vaddress->offset = (waddress & 0xff) * 2;
X}
X
X
X/* initialize mmu */
X
X#if defined(minix)
Xextern int brk();
Xextern char *sbrk();
Xstatic char *oldbrk = NULL;
Xstatic char *lmalloc(s)
Xlong s;
X{
X char *m;
X if (!oldbrk)
X oldbrk = sbrk(0);
X
X m = sbrk(0);
X if (brk(m + s + 0x2000) != 0)
X {
X brk(m);
X return(NULL);
X }
X else
X {
X brk(m + s);
X return(m);
X }
X}
X
Xstatic free(p)
Xchar *p;
X{
X brk(oldbrk);
X}
X#endif
X
Xvoid mmu_init(h)
Xstruct header *h;
X{
X long virt_l;
X int i;
X
X if (main_p = (BYTE *)lmalloc(btop((long)word_get(h->len) * 2L) * 512L))
X {
X main_l = btop((long)word_get(h->len) * 2L);
X virt_l = 2;
X if (!(virt_p = (struct virt_page *)lmalloc((0x200L +
X sizeof(struct virt_page)) * 2 +
X sizeof(struct virt_page))))
X {
X free(main_p);
X goto low_mem;
X }
X }
X else
X {
Xlow_mem:
X if ((main_p = (BYTE *)
X lmalloc((long)btop((long)word_get(h->minmem)) * 512L))
X == NULL)
X no_mem_error();
X
X main_l = btop((long)word_get(h->minmem));
X
X for (virt_l = btop((long)word_get(h->len) * 2L -
X (long)word_get(h->minmem));
X virt_l >= 2 && virt_p == NULL; virt_l--)
X virt_p = (struct virt_page *)lmalloc((long)
X (virt_l * (0x200L +
X sizeof(struct virt_page)) +
X sizeof(struct virt_page)));
X
X if (virt_l < 2)
X no_mem_error();
X }
X
X for (i = 0; i < virt_l; i++)
X {
X virt_p[i].page = -2;
X virt_p[i].lru = 0L;
X virt_p[i].paddr = (BYTE *)virt_p +
X (long)(sizeof(struct virt_page) *
X (virt_l + 1L) + 0x200L * (long)i);
X }
X virt_p[i].page = -1;
X
X pc_page = c_page = virt_p;
X main_h = (struct header *)main_p;
X}
/