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} /