fweep: work in progress
authorAlan Cox <alan@linux.intel.com>
Sun, 25 Feb 2018 00:37:02 +0000 (00:37 +0000)
committerAlan Cox <alan@linux.intel.com>
Sun, 25 Feb 2018 00:37:02 +0000 (00:37 +0000)
Needs a lot more thinning down to be useful anywhere but 68000

Applications/games/fweep.c [new file with mode: 0644]

diff --git a/Applications/games/fweep.c b/Applications/games/fweep.c
new file mode 100644 (file)
index 0000000..966bb13
--- /dev/null
@@ -0,0 +1,1491 @@
+/*\r
+  Fweeplet -- a Z-machine interpreter for versions 1 to 5.\r
+  This program is license under GNU GPL v3 or later version.\r
+  \r
+  Cut down from 'fweep'\r
+*/\r
+\r
+#include <stdio.h>\r
+#include <stdint.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <time.h>\r
+#include <unistd.h>\r
+\r
+#define IVERSION "fe0.01"\r
+\r
+#define VERSION        3\r
+\r
+#if (VERSION == 8)\r
+#define PACKED_SHIFT   3\r
+typedef uint16_t       obj_t;\r
+#elif (VERSION > 3)\r
+#define PACKED_SHIFT   2\r
+typedef uin16_t                obj_t;\r
+#else\r
+#define PACKED_SHIFT   1\r
+typedef uint8_t                obj_t;\r
+#endif\r
+\r
+typedef char boolean;\r
+typedef uint8_t byte;\r
+\r
+typedef struct {\r
+  uint32_t pc;\r
+  uint16_t start;\r
+  uint8_t argc;\r
+  boolean stored;\r
+} StackFrame;\r
+\r
+const char zscii_conv_1[128]={\r
+  [155-128]=\r
+  'a','o','u','A','O','U','s','>','<','e','i','y','E','I','a','e','i','o','u','y','A','E','I','O','U','Y',\r
+  'a','e','i','o','u','A','E','I','O','U','a','e','i','o','u','A','E','I','O','U','a','A','o','O','a','n',\r
+  'o','A','N','O','a','A','c','C','t','t','T','T','L','o','O','!','?'\r
+};\r
+\r
+const char zscii_conv_2[128]={\r
+  [155-128]='e','e','e', [161-128]='s','>','<', [211-128]='e','E', [215-128]='h','h','h','h', [220-128]='e','E'\r
+};\r
+\r
+const char v1alpha[78]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789.,!?_#'\"/\\<-:()";\r
+const char v2alpha[78]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ^0123456789.,!?_#'\"/\\-:()";\r
+\r
+char*story_name;\r
+FILE*story;\r
+byte auxname[11];\r
+boolean original=1;\r
+boolean verified=0;\r
+boolean tandy = 0;\r
+boolean qtospace = 0;\r
+int sc_rows=25;\r
+int sc_columns=80;\r
+\r
+uint32_t routine_start;\r
+uint32_t text_start;\r
+\r
+uint32_t object_table;\r
+uint32_t dictionary_table;\r
+uint32_t restart_address;\r
+uint32_t synonym_table;\r
+uint32_t alphabet_table;\r
+uint32_t static_start;\r
+uint32_t global_table;\r
+\r
+uint8_t memory[0x20000];\r
+uint32_t program_counter;\r
+\r
+#define STACKSIZE 256\r
+#define FRAMESIZE 32\r
+StackFrame frames[32];\r
+int framemax;\r
+uint16_t stack[256];\r
+int frameptr;\r
+int stackptr;\r
+int stackmax;\r
+\r
+uint16_t stream3addr[16];\r
+uint16_t *stream3ptr = stream3addr - 1;\r
+boolean texting=1;\r
+boolean window=0;\r
+boolean buffering=1;\r
+int cur_row=2;\r
+int cur_column;\r
+int lmargin=0;\r
+int rmargin=0;\r
+\r
+uint16_t inst_args[8];\r
+#define inst_sargs ((int16_t*)inst_args)\r
+char text_buffer[1024];\r
+int textptr;\r
+uint8_t cur_prop_size;\r
+\r
+int zch_shift;\r
+int zch_shiftlock;\r
+int zch_code;\r
+\r
+/*\r
+ *     Memory management\r
+ */\r
+\r
+uint8_t pc(void)\r
+{\r
+  return memory[program_counter++];\r
+}\r
+\r
+uint16_t randv = 7;\r
+boolean predictable;\r
+\r
+int16_t get_random(int16_t max) {\r
+  int16_t tmp;\r
+  randv += 0xAA55;\r
+  tmp  = randv & 0x7FFF;\r
+  randv = (randv << 8) | (randv >> 8);\r
+  return (tmp & max) + 1;\r
+}\r
+\r
+void randomize(uint16_t seed) {\r
+  if (seed) {\r
+    randv = seed;\r
+    predictable = 1;\r
+  } else {\r
+    predictable = 0;\r
+    randv = time(NULL) ^ get_random(32767);\r
+  }\r
+}\r
+\r
+uint16_t read16low(uint8_t address) {\r
+  return (memory[address]<<8)|memory[address+1];\r
+}\r
+\r
+uint16_t read16(uint32_t address) {\r
+  return (memory[address]<<8)|memory[address+1];\r
+}\r
+\r
+void write16low(uint8_t address,uint16_t value) {\r
+  memory[address]=value>>8;\r
+  memory[address+1]=value&255;\r
+}\r
+\r
+/* Can be uint16 except when debugging */\r
+void write16(uint32_t address,uint16_t value) {\r
+  memory[address]=value>>8;\r
+  memory[address+1]=value&255;\r
+}\r
+\r
+uint8_t read8low(uint8_t address) {\r
+  return memory[address];\r
+}\r
+\r
+uint8_t read8(uint32_t address) {\r
+  return memory[address];\r
+}\r
+\r
+uint8_t write8low(uint8_t address, uint8_t value) {\r
+  memory[address] = value;\r
+}\r
+\r
+uint8_t write8(uint32_t address, uint8_t value) {\r
+  memory[address] = value;\r
+}\r
+\r
+\r
+void text_flush(void) {\r
+  int c;\r
+  text_buffer[textptr]=0;\r
+  if(textptr+cur_column>=sc_columns-rmargin) {\r
+    putchar('\n');\r
+    cur_row++;\r
+    cur_column=0;\r
+    while(cur_column<lmargin) {\r
+      putchar(32);\r
+      cur_column++;\r
+    }\r
+  }\r
+  if(cur_row>=sc_rows && sc_rows!=255) {\r
+    printf("[MORE]");\r
+    fflush(stdout);\r
+    while((c = fgetc(stdin)) != '\n')\r
+      if (c == EOF)\r
+        return;\r
+    cur_row=2;\r
+  }\r
+  fputs(text_buffer,stdout);\r
+  cur_column+=textptr;\r
+  fflush(stdout);\r
+  textptr=0;\r
+}\r
+\r
+void char_print(uint8_t zscii) {\r
+  if(!zscii) return;\r
+  if(stream3ptr != stream3addr - 1) {\r
+    uint16_t w=read16(*stream3ptr);\r
+    write8(*stream3ptr+2+w, zscii);\r
+    write16(*stream3ptr,w+1);\r
+    return;\r
+  }\r
+  if((read8low(0x11)&1) && !window) {\r
+    write8low(0x10, read8low(0x10) | 4);\r
+  }\r
+  if(texting && !window) {\r
+    if(zscii&0x80) {\r
+      text_buffer[textptr++]=zscii_conv_1[zscii&0x7F];\r
+      if(zscii_conv_2[zscii&0x7F]) text_buffer[textptr++]=zscii_conv_2[zscii&0x7F];\r
+    } else if(zscii&0x6F) {\r
+      text_buffer[textptr++]=zscii;\r
+    }\r
+    if(zscii<=32 || textptr>1000 || !buffering) text_flush();\r
+    if(zscii==13) {\r
+      putchar('\n');\r
+      cur_row++;\r
+      cur_column=0;\r
+      while(cur_column<lmargin) {\r
+        putchar(32);\r
+        cur_column++;\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+boolean verify_checksum(void) {\r
+  return 1;\r
+}\r
+\r
+uint32_t text_print(uint32_t address);\r
+\r
+void zch_print(int z) {\r
+  int zsl;\r
+  if(zch_shift==3) {\r
+    zch_code=z<<5;\r
+    zch_shift=4;\r
+  } else if(zch_shift==4) {\r
+    zch_code|=z;\r
+    char_print(zch_code);\r
+    zch_shift=zch_shiftlock;\r
+  } else if(zch_shift>=5) {\r
+    zsl=zch_shiftlock;\r
+    text_print(read16(synonym_table+(z<<1)+((zch_shift-5)<<6))<<1);\r
+    zch_shift=zch_shiftlock=zsl;\r
+  } else if(z==0) {\r
+    char_print(32);\r
+    zch_shift=zch_shiftlock;\r
+  } else if(z==1 && VERSION==1) {\r
+    char_print(13);\r
+    zch_shift=zch_shiftlock;\r
+  } else if(z==1) {\r
+    zch_shift=5;\r
+  } else if((z==4 || z==5) && VERSION>2 && (zch_shift==1 || zch_shift==2)) {\r
+    zch_shift=zch_shiftlock=zch_shift&(z-3);\r
+  } else if(z==4 && VERSION<3) {\r
+    zch_shift=zch_shiftlock=(zch_shift+1)%3;\r
+  } else if(z==5 && VERSION<3) {\r
+    zch_shift=zch_shiftlock=(zch_shift+2)%3;\r
+  } else if((z==2 && VERSION<3) || z==4) {\r
+    zch_shift=(zch_shift+1)%3;\r
+  } else if((z==3 && VERSION<3) || z==5) {\r
+    zch_shift=(zch_shift+2)%3;\r
+  } else if(z==2) {\r
+    zch_shift=6;\r
+  } else if(z==3) {\r
+    zch_shift=7;\r
+  } else if(z==6 && zch_shift==2) {\r
+    zch_shift=3;\r
+  } else if(z==7 && zch_shift==2 && VERSION!=1) {\r
+    char_print(13);\r
+    zch_shift=zch_shiftlock;\r
+  } else {\r
+    if(alphabet_table) char_print(read8(alphabet_table+z+(zch_shift*26)-6));\r
+    else if(VERSION==1) char_print(v1alpha[z+(zch_shift*26)-6]);\r
+    else char_print(v2alpha[z+(zch_shift*26)-6]);\r
+    zch_shift=zch_shiftlock;\r
+  }\r
+}\r
+\r
+uint32_t text_print(uint32_t address) {\r
+  uint16_t t;\r
+  zch_shift=zch_shiftlock=0;\r
+  for(;;) {\r
+    t=read16(address);\r
+    address+=2;\r
+    zch_print((t>>10)&31);\r
+    zch_print((t>>5)&31);\r
+    zch_print(t&31);\r
+    if(t&0x8000) return address;\r
+  }\r
+}\r
+\r
+#if (VERSION > 4)\r
+void make_rectangle(uint32_t addr,int width,int height,int skip) {\r
+  int old_column=cur_column;\r
+  int w,h;\r
+  for(h=0;h<height;h++) {\r
+    for(w=0;w<width;w++) char_print(read8(addr++));\r
+    addr+=skip;\r
+    if(h!=height-1) {\r
+      char_print(13);\r
+      for(w=0;w<old_column;w++) char_print(32);\r
+    }\r
+  }\r
+  text_flush();\r
+}\r
+#endif\r
+\r
+uint16_t fetch(uint8_t var) {\r
+  if(var&0xF0) {\r
+    return read16(global_table+((var-16)<<1));\r
+  } else if(var) {\r
+    return stack[frames[frameptr].start+var-1];\r
+  } else {\r
+    return stack[--stackptr];\r
+  }\r
+}\r
+\r
+void pushstack(uint16_t value)\r
+{\r
+    if (stackptr == STACKSIZE) {\r
+      fprintf(stderr, "stack overflow.\n");\r
+      exit(1);\r
+    }\r
+    stack[stackptr++]=value;\r
+    if (stackptr > stackmax)\r
+      stackmax = stackptr;\r
+}\r
+\r
+void store(uint8_t var,uint16_t value) {\r
+  if(var&0xF0) {\r
+    write16(global_table+((var-16)<<1),value);\r
+  } else if(var) {\r
+    stack[frames[frameptr].start+var-1]=value;\r
+  } else {\r
+    pushstack(value);\r
+  }\r
+}\r
+\r
+void storei(uint16_t value) {\r
+  store(pc(),value);\r
+}\r
+\r
+void enter_routine(uint32_t address,boolean stored,int argc) {\r
+  int c=read8(address);\r
+  int i;\r
+  if (frameptr == FRAMESIZE - 1) {\r
+    fprintf(stderr, "out of frames.\n");\r
+    exit(1);\r
+  }\r
+  frames[frameptr].pc=program_counter;\r
+  frames[++frameptr].argc=argc;\r
+  frames[frameptr].start=stackptr;\r
+  frames[frameptr].stored=stored;\r
+  program_counter=address+1;\r
+  if (frameptr > framemax)\r
+    framemax = frameptr;\r
+    \r
+  if(VERSION<5) {\r
+    for(i=0;i<c;i++) {\r
+      pushstack(read16(program_counter));\r
+      program_counter+=2;\r
+    }\r
+  } else {\r
+    for(i=0;i<c;i++) pushstack(0);\r
+  }\r
+  if(argc>c) argc=c;\r
+  for(i=0;i<argc;i++) stack[frames[frameptr].start+i]=inst_args[i+1];\r
+}\r
+\r
+void exit_routine(uint16_t result) {\r
+  stackptr=frames[frameptr].start;\r
+  program_counter=frames[--frameptr].pc;\r
+  if(frames[frameptr+1].stored) store(read8(program_counter-1),result);\r
+}\r
+\r
+void branch(uint32_t cond) {\r
+  int v=pc();\r
+  if(!(v&0x80)) cond=!cond;\r
+  if(v&0x40) v&=0x3F; else v=((v&0x3F)<<8)|pc();\r
+  if(cond) {\r
+    if(v==0 || v==1) exit_routine(v);\r
+    else program_counter+=(v&0x1FFF)-((v&0x2000)|2);\r
+  }\r
+}\r
+\r
+void obj_tree_put(obj_t obj,int f,uint16_t v) {\r
+#if (VERSION > 3)\r
+  write16(object_table+118+obj*14+f*2,v);\r
+#else\r
+  write8(object_table+57+obj*9+f, v);\r
+#endif\r
+}\r
+\r
+obj_t obj_tree_get(obj_t obj, int f)\r
+{\r
+#if (VERSION > 3)\r
+  return read16(object_table+118+obj*14+f*2);\r
+#else\r
+  return read8(object_table+57+obj*9+f);\r
+#endif\r
+}\r
+\r
+#define parent(x) obj_tree_get(x,0)\r
+#define sibling(x) obj_tree_get(x,1)\r
+#define child(x) obj_tree_get(x,2)\r
+#define set_parent(x,y) obj_tree_put(x,0,y)\r
+#define set_sibling(x,y) obj_tree_put(x,1,y)\r
+#define set_child(x,y) obj_tree_put(x,2,y)\r
+#define attribute(x) (VERSION>3?object_table+112+(x)*14:object_table+53+(x)*9)\r
+#define obj_prop_addr(o) (read16(VERSION>3?(object_table+124+(o)*14):(object_table+60+(o)*9)))\r
+\r
+/* FIXME: rewrite these directly for the two formats and using z pointers\r
+   as it'll be much shorter */\r
+void insert_object(obj_t obj,uint16_t dest) {\r
+  obj_t p=parent(obj);\r
+  obj_t s=sibling(obj);\r
+  obj_t x;\r
+  if(p) {\r
+    x=child(p);\r
+    if(x==obj) {\r
+      set_child(p,sibling(x));\r
+    } else {\r
+      while(sibling(x)) {\r
+        if(sibling(x)==obj) {\r
+          set_sibling(x,sibling(sibling(x)));\r
+          break;\r
+        }\r
+        x=sibling(x);\r
+      }\r
+    }\r
+  }\r
+  if(dest) {\r
+    // Attach object to new parent\r
+    set_sibling(obj,child(dest));\r
+    set_child(dest,obj);\r
+  } else {\r
+    set_sibling(obj,0);\r
+  }\r
+  set_parent(obj,dest);\r
+}\r
+\r
+uint32_t property_address(uint16_t obj,uint8_t p) {\r
+  uint32_t a=obj_prop_addr(obj);\r
+  uint8_t n=1;\r
+  a+=(read8(a)<<1)+1;\r
+  while(read8(a)) {            /* FIXME save and reuse this value! */\r
+    if(VERSION<4) {\r
+      n=read8(a)&31;\r
+      cur_prop_size=(read8(a)>>5)+1;\r
+    } else if(read8(a)&0x80) {\r
+      n=read8(a)&63;\r
+      cur_prop_size=read8(++a)&63;\r
+      if (cur_prop_size == 0)\r
+        cur_prop_size = 64;\r
+    } else {\r
+      n=read8(a)&63;\r
+      cur_prop_size=(read8(a)>>6)+1;\r
+    }\r
+    a++;\r
+    //if(n<p) return 0;\r
+    if(n==p) return a;\r
+    a+=cur_prop_size;\r
+  }\r
+  return 0;\r
+}\r
+\r
+uint8_t system_input(char**out) {\r
+  char*p;\r
+  uint32_t i;\r
+  time_t t;\r
+input_again:\r
+  text_flush();\r
+  cur_row=2;\r
+  cur_column=0;\r
+  time(&t);\r
+  if(!fgets(text_buffer,128, stdin)) {\r
+    fprintf(stderr,"*** Unable to continue.\n");\r
+    exit(1);\r
+  }\r
+  if (!predictable)\r
+    randv += time(NULL) - t;\r
+  p=text_buffer+strlen(text_buffer);\r
+  while(p!=text_buffer && p[-1]<32)\r
+    *--p=0; // Let's removing "CRLF", etc\r
+  *out=text_buffer;\r
+  return 13;\r
+}\r
+\r
+uint64_t dictionary_get(uint32_t addr) {\r
+  uint64_t v=0;\r
+  int c=VERSION>3?6:4;\r
+  while(c--) v=(v<<8)|read8(addr++);\r
+  return v;\r
+}\r
+\r
+uint64_t dictionary_encode(uint8_t*text,int len) {\r
+  uint64_t v=0;\r
+  int c=VERSION>3?9:6;\r
+  int i;\r
+  /* FIXME: memory direct reference still here */\r
+  const uint8_t*al=(alphabet_table?(const uint8_t*)memory+alphabet_table:(const uint8_t*)(VERSION>1?v2alpha:v1alpha));\r
+  while(c && len && *text) {\r
+    // Since line breaks cannot be in an input line of text, and VAR:252 is only available in version 5, line breaks need not be considered here.\r
+    // However, because of VAR:252, spaces still need to be considered.\r
+    if(!(c%3)) v<<=1;\r
+    if(*text==' ') {\r
+      v<<=5;\r
+    } else {\r
+      for(i=0;i<78;i++) {\r
+        if(*text==al[i] && i!=52 && i!=53) {\r
+          v<<=5;\r
+          if(i>=26) {\r
+            v|=i/26+(VERSION>2?3:1);\r
+            c--;\r
+            if(!c) return v|0x8000;\r
+            if(!(c%3)) v<<=1;\r
+            v<<=5;\r
+          }\r
+          v|=(i%26)+6;\r
+          break;\r
+        }\r
+      }\r
+      if(i==78) {\r
+        v<<=5;\r
+        v|=VERSION>2?5:3;\r
+        c--;\r
+        if(!c) return v|0x8000;\r
+        if(!(c%3)) v<<=1;\r
+        v<<=5;\r
+        v|=6;\r
+        c--;\r
+        if(!c) return v|0x8000;\r
+        if(!(c%3)) v<<=1;\r
+        v<<=5;\r
+        v|=*text>>5;\r
+        c--;\r
+        if(!c) return v|0x8000;\r
+        if(!(c%3)) v<<=1;\r
+        v<<=5;\r
+        v|=*text&31;\r
+      }\r
+    }\r
+    c--;\r
+    text++;\r
+    len--;\r
+  }\r
+  while(c) {\r
+    if(!(c%3)) v<<=1;\r
+    v<<=5;\r
+    v|=5;\r
+    c--;\r
+  }\r
+  return v|0x8000;\r
+}\r
+\r
+void add_to_parsebuf(uint32_t parsebuf,uint32_t dict,uint8_t*d,int k,int el,int ne,int p,uint16_t flag) {\r
+  uint64_t v=dictionary_encode(d,k);\r
+  uint64_t g;\r
+  int i;\r
+  for(i=0;i<ne;i++) {\r
+    g=dictionary_get(dict)|0x8000;\r
+    if(g==v) {\r
+      /* FIXME tidy re-uses */\r
+      write8(parsebuf+(read8(parsebuf+1)<<2)+5, p+1+(VERSION>4));\r
+      write8(parsebuf+(read8(parsebuf+1)<<2)+4, k);\r
+      write16(parsebuf+(read8(parsebuf+1)<<2)+2,dict);\r
+      break;\r
+    }\r
+    dict+=el;\r
+  }\r
+  if(i==ne && !flag) {\r
+    /* FIXME: tidy reuses */\r
+    write8(parsebuf+(read8(parsebuf+1)<<2)+5, p+1+(VERSION>4));\r
+    write8(parsebuf+(read8(parsebuf+1)<<2)+4, k);\r
+    write16(parsebuf+(read8(parsebuf+1)<<2)+2,0);\r
+  }\r
+  write8(parsebuf+1, read8(parsebuf+1)+1);\r
+}\r
+\r
+#define Add_to_parsebuf() if(k)add_to_parsebuf(parsebuf,dict,d,k,el,ne,p1,flag),k=0;p1=p+1;\r
+void tokenise(uint32_t text,uint32_t dict,uint32_t parsebuf,int len,uint16_t flag) {\r
+  boolean ws[256];\r
+  uint8_t d[10];\r
+  int i,el,ne,k,p,p1;\r
+  memset(ws,0,256*sizeof(boolean));\r
+  /* Direct memory references plus a big copy we should avoid */\r
+  /* FIXME change algorithms */\r
+  if(!dict) {\r
+    for(i=1;i<=memory[dictionary_table];i++) ws[memory[dictionary_table+i]]=1;\r
+    dict=dictionary_table;\r
+  }\r
+  for(i=1;i<=memory[dict];i++) ws[memory[dict+i]]=1;\r
+  memory[parsebuf+1]=0;\r
+  k=p=p1=0;\r
+  el=read8(dict+read8(dict)+1);\r
+  ne=read16(dict+read8(dict)+2);\r
+  if(ne<0) ne*=-1; // Currently, it won't care about the order; it doesn't use binary search.\r
+  dict+=memory[dict]+4;\r
+  while(p<len && read8(text+p) && read8(parsebuf+1) < read8(parsebuf)) {\r
+    i=memory[text+p];\r
+    if(i>='A' && i<='Z') i+='a'-'A';\r
+    if(i=='?' && qtospace) i=' ';\r
+    if(i==' ') {\r
+      Add_to_parsebuf();\r
+    } else if(ws[i]) {\r
+      Add_to_parsebuf();\r
+      *d=i;\r
+      k=1;\r
+      Add_to_parsebuf();\r
+    } else if(k<10) {\r
+      d[k++]=i;\r
+    } else {\r
+      k++;\r
+    }\r
+    p++;\r
+  }\r
+  Add_to_parsebuf();\r
+}\r
+#undef Add_to_parsebuf\r
+\r
+uint8_t line_input(void) {\r
+  char*ptr;\r
+  char*p;\r
+  int c,cmax;  /* FIXME: uint16_t surely ? */\r
+  uint8_t res;\r
+\r
+  res=system_input(&ptr);\r
+\r
+  /* ? is there another copy of this FIXME */\r
+  if(read8low(0x11)&1)\r
+    write8low(0x10, read8(0x10) | 4);\r
+  p=ptr;\r
+  while(*p) {\r
+    if(*p>='A' && *p<='Z') *p|=0x20;\r
+    p++;\r
+  }\r
+  p=ptr;\r
+  c=0;\r
+  cmax=read8(inst_args[0]);\r
+  if(VERSION>4) {\r
+    // "Left over" characters are not implemented.\r
+    while(*p && c<cmax) {\r
+      write8(inst_args[0]+c+2, *p++);\r
+      ++c;\r
+    }\r
+    write8(inst_args[0]+1, c);\r
+    if(inst_args[1]) tokenise(inst_args[0]+2,0,inst_args[1],c,0);\r
+  } else {\r
+    while(*p && c<cmax) {\r
+      write8(inst_args[0]+c+1, *p++);\r
+      ++c;\r
+    }\r
+    write8(c+1, 0);\r
+    tokenise(inst_args[0]+1,0,inst_args[1],c,0);\r
+  }\r
+  return res;\r
+}\r
+\r
+uint8_t char_input(void) {\r
+  char*ptr;\r
+  uint8_t res;\r
+\r
+  res=system_input(&ptr);\r
+  if(res==13 && *ptr) res=*ptr;\r
+\r
+  return res;\r
+}\r
+\r
+void game_restart(void) {\r
+  uint32_t addr=64;\r
+  stackptr=frameptr=0;\r
+  program_counter=restart_address;\r
+  clearerr(story);\r
+  fseek(story,64,SEEK_SET);\r
+  while(!feof(story)) {\r
+    /* FIXME: memory */\r
+    if(!fread(memory+addr,1024,1,story)) return;\r
+    addr+=1024;\r
+  }\r
+}\r
+\r
+void game_save_many(FILE*fp,long count) {\r
+  long i;\r
+  while(count>0) {\r
+    fputc(0,fp);\r
+    if(count>=129) {\r
+      i=count;\r
+      if(i>0x3FFF) i=0x3FFF;\r
+      fputc(((i-1)&0x7F)|0x80,fp);\r
+      fputc((i-((i-1)&0x7F)-0x80)>>7,fp);\r
+      count-=i;\r
+    } else {\r
+      fputc(count-1,fp);\r
+      count=0;\r
+    }\r
+  }\r
+}\r
+\r
+void game_save(uint8_t storage) {\r
+  char filename[1024];\r
+  FILE*fp;\r
+  int i;\r
+  uint8_t c;\r
+  long o,q;\r
+  printf("\n*** Save? ");\r
+  fflush(stdout);\r
+  gets(filename);\r
+  if(*filename=='.' && !filename[1]) sprintf(filename,"%s.sav",story_name);\r
+  cur_column=0;\r
+  if(!*filename) {\r
+    if(VERSION<4) branch(0); else store(storage,0);\r
+    return;\r
+  } else if(*filename=='*') {\r
+    if(VERSION<4) branch(1); else store(storage,strtol(filename+1,0,0));\r
+    return;\r
+  }\r
+  fp=fopen(filename,"wb");\r
+  if(!fp) {\r
+    if(VERSION<4) branch(0); else store(storage,0);\r
+    return;\r
+  }\r
+  if(VERSION<4) branch(1); else store(storage,2);\r
+  frames[frameptr].pc=program_counter;\r
+  frames[frameptr+1].start=stackptr;\r
+  fputc(frameptr+1,fp);\r
+  for(i=0;i<=frameptr;i++) {\r
+    fputc((frames[i+1].start-frames[i].start)>>1,fp);\r
+    fputc((((frames[i+1].start-frames[i].start)&1)<<7)|((!frames[i].stored)<<6)|(frames[i].pc>>16),fp);\r
+    fputc((frames[i].pc>>8)&255,fp);\r
+    fputc(frames[i].pc&255,fp);\r
+  }\r
+  for(i=0;i<stackptr;i++) {\r
+    fputc(stack[i]>>8,fp);\r
+    fputc(stack[i]&255,fp);\r
+  }\r
+  clearerr(story);\r
+  fseek(story,o=0x38,SEEK_SET);\r
+  q=0;\r
+  while(o<static_start) {\r
+    c=fgetc(story);\r
+    if(read8(o)==c) {\r
+      q++;\r
+    } else {\r
+      game_save_many(fp,q);\r
+      q=0;\r
+      fputc(read8(o)^c,fp);\r
+    }\r
+    o++;\r
+  }\r
+  fclose(fp);\r
+  if(VERSION<4) return;\r
+  fetch(storage);\r
+  store(storage,1);\r
+}\r
+\r
+void game_restore(void) {\r
+  char filename[1024];\r
+  FILE*fp;\r
+  int i,c,d;\r
+  long o;\r
+  printf("\n*** Restore? ");\r
+  fflush(stdout);\r
+  gets(filename);\r
+  if(*filename=='.' && !filename[1]) sprintf(filename,"%s.sav",story_name);\r
+  cur_column=0;\r
+  if(!*filename) return;\r
+  fp=fopen(filename,"rb");\r
+  if(!fp) return;\r
+  frameptr=fgetc(fp)-1;\r
+  stackptr=0;\r
+  for(i=0;i<=frameptr;i++) {\r
+    c=fgetc(fp);\r
+    d=fgetc(fp);\r
+    frames[i].start=stackptr;\r
+    stackptr+=(c<<1)|(d>>7);\r
+    frames[i].stored=!(d&0x40);\r
+    frames[i].pc=(d&0x3F)<<16;\r
+    frames[i].pc|=fgetc(fp)<<8;\r
+    frames[i].pc|=fgetc(fp);\r
+  }\r
+  for(i=0;i<stackptr;i++) {\r
+    stack[i]=fgetc(fp)<<8;\r
+    stack[i]|=fgetc(fp);\r
+  }\r
+  clearerr(story);\r
+  fseek(story,o=0x38,SEEK_SET);\r
+  i=0;\r
+  while(o<static_start) {\r
+    d=fgetc(fp);\r
+    if(d==EOF) break;\r
+    if(d) {\r
+      write8(o++, fgetc(story)^d);\r
+    } else {\r
+      c=fgetc(fp);\r
+      if(c&0x80) c+=fgetc(fp)<<7;\r
+      while(c-->=0) write8(o++, fgetc(story));\r
+    }\r
+  }\r
+  fclose(fp);\r
+  while(o<static_start) write8(o++, fgetc(story));\r
+  program_counter=frames[frameptr].pc;\r
+}\r
+\r
+void switch_output(int st) {\r
+  switch(st) {\r
+    case 1:\r
+      texting=1;\r
+      break;\r
+    case 2:\r
+      write8low(0x11, read8low(0x11)|1);\r
+      break;\r
+    case 3:\r
+      if(stream3ptr!=stream3addr + 15) {\r
+        *++stream3ptr=inst_args[1];\r
+        write16(inst_args[1],0);\r
+      }\r
+      break;\r
+    case 4:\r
+      break;\r
+    case -1:\r
+      texting=0;\r
+      break;\r
+    case -2:\r
+      write8low(0x11, read8low(0x11)&~1);\r
+      break;\r
+    case -3:\r
+      if(stream3ptr != stream3addr - 1)\r
+        stream3ptr--;\r
+      break;\r
+    case -4:\r
+      break;\r
+  }\r
+}\r
+\r
+\r
+void execute_instruction(void) {\r
+  uint8_t in=pc();\r
+  uint16_t at;\r
+  int16_t m,n;\r
+  uint32_t u;//=program_counter-1;\r
+  int argc;\r
+\r
+  if (!predictable)\r
+    randv -= 0x0200;\r
+\r
+  if(in&0x80) {\r
+    if(in>=0xC0 || in==0xBE) {\r
+      // variable\r
+      if(in==0xBE) in=pc();\r
+      at=pc()<<8;\r
+      if(in==0xEC || in==0xFA) at|=pc(); else at|=0x00FF;\r
+      if((at&0xC000)==0xC000) argc=0;\r
+      else if((at&0x3000)==0x3000) argc=1;\r
+      else if((at&0x0C00)==0x0C00) argc=2;\r
+      else if((at&0x0300)==0x0300) argc=3;\r
+      else if((at&0x00C0)==0x00C0) argc=4;\r
+      else if((at&0x0030)==0x0030) argc=5;\r
+      else if((at&0x000C)==0x000C) argc=6;\r
+      else if((at&0x0003)==0x0003) argc=7;\r
+      else argc=8;\r
+    } else {\r
+      // short\r
+      at=(in<<10)|0x3FFF;\r
+      argc=(in<0xB0);\r
+      if(argc) in&=0x8F;\r
+    }\r
+  } else {\r
+    // long\r
+    at=0x5FFF;\r
+    if(in&0x20) at^=0x3000;\r
+    if(in&0x40) at^=0xC000;\r
+    in&=0x1F;\r
+    in|=0xC0;\r
+    argc=2;\r
+  }\r
+  for(n=0;n<8;n++) {\r
+    switch((at>>(14-n*2))&3) {\r
+      case 0: // large\r
+        inst_args[n]=pc()<<8;\r
+        inst_args[n]|=pc();\r
+        break;\r
+      case 1: // small\r
+        inst_args[n]=pc();\r
+        break;\r
+      case 2: // variable\r
+        inst_args[n]=fetch(pc());\r
+        break;\r
+      case 3: // omit\r
+        inst_args[n]=0;\r
+        break;\r
+    }\r
+  }\r
+  switch(in) {\r
+#if (VERSION > 4)\r
+    case 0x00: // Save game or auxiliary file\r
+      if(argc) storei(0);\r
+      else game_save(pc());\r
+      break;\r
+    case 0x01: // Restore game or auxiliary file\r
+      storei(0);\r
+      if(!argc) game_restore();\r
+      break;\r
+    case 0x02: // Logical shift\r
+      if(inst_sargs[1]>0) storei(inst_args[0]<<inst_args[1]);\r
+      else storei(inst_args[0]>>-inst_args[1]);\r
+      break;\r
+    case 0x03: // Arithmetic shift\r
+      if(inst_sargs[1]>0) storei(inst_sargs[0]<<inst_sargs[1]);\r
+      else storei(inst_sargs[0]>>-inst_sargs[1]);\r
+      break;\r
+    case 0x04: // Set font\r
+      text_flush();\r
+      storei((*inst_args==1 || *inst_args==4)?4:0);\r
+      if(!tandy) putchar(*inst_args==3?14:15);\r
+      break;\r
+    case 0x08: // Set margins\r
+      if(!window) {\r
+        lmargin=inst_args[0];\r
+        rmargin=inst_args[1];\r
+        if(VERSION==5) write16(40,inst_args[0]);\r
+        if(VERSION==5) write16(41,inst_args[1]);\r
+        while(cur_column<*inst_args) {\r
+          putchar(32);\r
+          cur_column++;\r
+        }\r
+      }\r
+      break;\r
+    case 0x09: // Save undo buffer\r
+      storei(-1);\r
+      break;\r
+    case 0x0A: // Restore undo buffer\r
+      storei(-1);\r
+      break;\r
+    case 0x0B: // Call byte address\r
+      program_counter++;\r
+      enter_routine(*inst_args,1,argc-1);\r
+      break;\r
+    case 0x0C: // Get reference to stack or local variables\r
+      if(*inst_args) storei(stackptr-1);\r
+      else storei(frames[frameptr].start+*inst_args-1);\r
+      break;\r
+    case 0x0D: // Read through stack/locals reference\r
+      storei(stack[*inst_args]);\r
+      break;\r
+    case 0x0E: // Write through stack/locals reference\r
+      if(*inst_args<1024) stack[*inst_args]=inst_args[1];\r
+      break;\r
+    case 0x0F: // Read byte from long property\r
+      u=property_address(inst_args[0],inst_args[1]);\r
+      storei(read8(u));\r
+      break;\r
+    case 0x1D: // Read word from long property\r
+      u=property_address(inst_args[0],inst_args[1]);\r
+      storei(read16(u));\r
+      break;\r
+#endif      \r
+    case 0x80: // Jump if zero\r
+      branch(!*inst_args);\r
+      break;\r
+    case 0x81: // Sibling\r
+      storei(sibling(*inst_args));\r
+      branch(sibling(*inst_args));\r
+      break;\r
+    case 0x82: // Child\r
+      storei(child(*inst_args));\r
+      branch(child(*inst_args));\r
+      break;\r
+    case 0x83: // Parent\r
+      storei(parent(*inst_args));\r
+      break;\r
+    case 0x84: // Property length\r
+      in=read8(*inst_args-1);\r
+      storei(VERSION<4?(in>>5)+1:in&0x80?(in&63?(in & 63):64):(in>>6)+1);\r
+      break;\r
+    case 0x85: // Increment\r
+      store(*inst_args,fetch(*inst_args)+1);\r
+      break;\r
+    case 0x86: // Decrement\r
+      store(*inst_args,fetch(*inst_args)-1);\r
+      break;\r
+    case 0x87: // Print by byte address\r
+      text_print(*inst_args);\r
+      break;\r
+    case 0x88: // Call routine\r
+      if(*inst_args) {\r
+        program_counter++;\r
+        enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,1,argc-1);\r
+      } else {\r
+        storei(0);\r
+      }\r
+      break;\r
+    case 0x89: // Remove object\r
+      insert_object(*inst_args,0);\r
+      break;\r
+    case 0x8A: // Print short name of object\r
+      text_print(obj_prop_addr(*inst_args)+1);\r
+      break;\r
+    case 0x8B: // Return\r
+      exit_routine(*inst_args);\r
+      break;\r
+    case 0x8C: // Unconditional jump\r
+      program_counter+=*inst_sargs-2;\r
+      break;\r
+    case 0x8D: // Print by packed address\r
+      text_print((*inst_args<<PACKED_SHIFT)+text_start);\r
+      break;\r
+    case 0x8E: // Load variable\r
+      at=fetch(*inst_args);\r
+      store(*inst_args,at); // if it popped from the stack, please put it back on\r
+      storei(at);\r
+      break;\r
+    case 0x8F: // Not // Call routine and discard result\r
+      if(VERSION>4) {\r
+        if(*inst_args) enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,0,argc-1);\r
+      } else {\r
+        storei(~*inst_args);\r
+      }\r
+      break;\r
+    case 0xB0: // Return 1\r
+      exit_routine(1);\r
+      break;\r
+    case 0xB1: // Return 0\r
+      exit_routine(0);\r
+      break;\r
+    case 0xB2: // Print literal\r
+      program_counter=text_print(program_counter);\r
+      break;\r
+    case 0xB3: // Print literal and return\r
+      program_counter=text_print(program_counter);\r
+      char_print(13);\r
+      exit_routine(1);\r
+      break;\r
+    case 0xB4: // No operation\r
+      //NOP\r
+      break;\r
+    case 0xB5: // Save\r
+      if(VERSION>3) game_save(pc());\r
+      else game_save(0);\r
+      break;\r
+    case 0xB6: // Restore\r
+      if(VERSION>3) storei(0); else branch(0);\r
+      game_restore();\r
+      break;\r
+    case 0xB7: // Restart\r
+      game_restart();\r
+      break;\r
+    case 0xB8: // Return from stack\r
+      exit_routine(stack[stackptr-1]);\r
+      break;\r
+    case 0xB9: // Discard from stack // Catch\r
+      if(VERSION>4) storei(frameptr);\r
+      else stackptr--;\r
+      break;\r
+    case 0xBA: // Quit\r
+      text_flush();\r
+      fprintf(stderr, "stackmax %d framemax %d\n", stackmax, framemax);\r
+      exit(0);\r
+      break;\r
+    case 0xBB: // Line break\r
+      char_print(13);\r
+      break;\r
+    case 0xBC: // Show status\r
+      //NOP\r
+      break;\r
+    case 0xBD: // Verify checksum\r
+      branch(verify_checksum());\r
+      break;\r
+    case 0xBF: // Check if game disc is original\r
+      branch(original);\r
+      break;\r
+    case 0xC1: // Branch if equal\r
+      for(n=1;n<argc;n++) {\r
+        if(*inst_args==inst_args[n]) {\r
+          branch(1);\r
+          break;\r
+        }\r
+      }\r
+      if(n==argc) branch(0);\r
+      break;\r
+    case 0xC2: // Jump if less\r
+      branch(inst_sargs[0]<inst_sargs[1]);\r
+      break;\r
+    case 0xC3: // Jump if greater\r
+      branch(inst_sargs[0]>inst_sargs[1]);\r
+      break;\r
+    case 0xC4: // Decrement and branch if less\r
+      store(*inst_args,n=fetch(*inst_args)-1);\r
+      branch(n<inst_sargs[1]);\r
+      break;\r
+    case 0xC5: // Increment and branch if greater\r
+      store(*inst_args,n=fetch(*inst_args)+1);\r
+      branch(n>inst_sargs[1]);\r
+      break;\r
+    case 0xC6: // Check if one object is the parent of the other\r
+      branch(parent(inst_args[0])==inst_args[1]);\r
+      break;\r
+    case 0xC7: // Test bitmap\r
+      branch((inst_args[0]&inst_args[1])==inst_args[1]);\r
+      break;\r
+    case 0xC8: // Bitwise OR\r
+      storei(inst_args[0]|inst_args[1]);\r
+      break;\r
+    case 0xC9: // Bitwise AND\r
+      storei(inst_args[0]&inst_args[1]);\r
+      break;\r
+    case 0xCA: // Test attributes\r
+      branch(read8(attribute(*inst_args)+(inst_args[1]>>3))&(0x80>>(inst_args[1]&7)));\r
+      break;\r
+    case 0xCB: // Set attribute\r
+      memory[attribute(*inst_args)+(inst_args[1]>>3)]|=0x80>>(inst_args[1]&7);\r
+      break;\r
+    case 0xCC: // Clear attribute\r
+      memory[attribute(*inst_args)+(inst_args[1]>>3)]&=~(0x80>>(inst_args[1]&7));\r
+      break;\r
+    case 0xCD: // Store to variable\r
+      fetch(inst_args[0]);\r
+      store(inst_args[0],inst_args[1]);\r
+      break;\r
+    case 0xCE: // Insert object\r
+      insert_object(inst_args[0],inst_args[1]);\r
+      break;\r
+    case 0xCF: // Read 16-bit number from RAM/ROM\r
+      storei(read16(inst_args[0]+(inst_sargs[1]<<1)));\r
+      break;\r
+    case 0xD0: // Read 8-bit number from RAM/ROM\r
+      storei(read8(inst_args[0]+inst_sargs[1]));\r
+      break;\r
+    case 0xD1: // Read property\r
+      if(u=property_address(inst_args[0],inst_args[1])) storei(cur_prop_size==1?read8(u):read16(u));\r
+      else storei(read16(object_table+(inst_args[1]<<1)-2));\r
+      break;\r
+    case 0xD2: // Get address of property\r
+      storei(property_address(inst_args[0],inst_args[1]));\r
+      break;\r
+    case 0xD3: // Find next property\r
+      if(inst_args[1]) {\r
+        u=property_address(inst_args[0],inst_args[1]);\r
+        u+=cur_prop_size;\r
+        storei(read8(u)&(VERSION>3?63:31));\r
+      } else {\r
+        u=obj_prop_addr(inst_args[0]);\r
+        u+=(read8(u)<<1)+1;\r
+        storei(read8(u)&(VERSION>3?63:31));\r
+      }\r
+      break;\r
+    case 0xD4: // Addition\r
+      storei(inst_sargs[0]+inst_sargs[1]);\r
+      break;\r
+    case 0xD5: // Subtraction\r
+      storei(inst_sargs[0]-inst_sargs[1]);\r
+      break;\r
+    case 0xD6: // Multiplication\r
+      storei(inst_sargs[0]*inst_sargs[1]);\r
+      break;\r
+    case 0xD7: // Division\r
+      if(inst_args[1]) n=inst_sargs[0]/inst_sargs[1];\r
+      else fprintf(stderr,"\n*** Division by zero\n",in);\r
+      storei(n);\r
+      break;\r
+    case 0xD8: // Modulo\r
+      if(inst_args[1]) n=inst_sargs[0]%inst_sargs[1];\r
+      else fprintf(stderr,"\n*** Division by zero\n",in);\r
+      storei(n);\r
+      break;\r
+#if (VERSION > 3)\r
+    case 0xD9: // Call routine\r
+      if(*inst_args) {\r
+        program_counter++;\r
+        enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,1,argc-1);\r
+      } else {\r
+        storei(0);\r
+      }\r
+      break;\r
+#endif\r
+#if (VERSION > 4)      \r
+    case 0xDA: // Call routine and discard result\r
+      if(*inst_args) enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,0,argc-1);\r
+      break;\r
+    case 0xDB: // Set colors\r
+      //NOP\r
+      break;\r
+    case 0xDC: // Throw\r
+      frameptr=inst_args[1];\r
+      exit_routine(*inst_args);\r
+      break;\r
+    case 0xDD: // Bitwise XOR\r
+      storei(inst_args[0]^inst_args[1]);\r
+      break;\r
+#endif      \r
+    case 0xE0: // Call routine (FIXME for v1)\r
+      if(*inst_args) {\r
+        program_counter++;\r
+        enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,1,argc-1);\r
+      } else {\r
+        storei(0);\r
+      }\r
+      break;\r
+    case 0xE1: // Write 16-bit number to RAM\r
+      write16(inst_args[0]+(inst_sargs[1]<<1),inst_args[2]);\r
+      break;\r
+    case 0xE2: // Write 8-bit number to RAM\r
+      write8(inst_args[0]+inst_sargs[1],inst_args[2]);\r
+      break;\r
+    case 0xE3: // Write property\r
+      u=property_address(inst_args[0],inst_args[1]);\r
+      if(cur_prop_size==1)\r
+        write8(u, inst_args[2]);\r
+      else\r
+        write16(u,inst_args[2]);\r
+      break;\r
+    case 0xE4: // Read line of input\r
+      n=line_input();\r
+      if(VERSION>4) storei(n);\r
+      break;\r
+    case 0xE5: // Print character\r
+      char_print(*inst_args);\r
+      break;\r
+    case 0xE6: // Print number\r
+    {\r
+      static uint8_t nbuf[5];\r
+      n=*inst_sargs;\r
+      if(n==-32768) {\r
+        char_print('-');\r
+        char_print('3');\r
+        char_print('2');\r
+        char_print('7');\r
+        char_print('6');\r
+        char_print('8');\r
+      } else {\r
+        nbuf[0]=nbuf[1]=nbuf[2]=nbuf[3]=nbuf[4]=0;\r
+        if(n<0) {\r
+          char_print('-');\r
+          n*=-1;\r
+        }\r
+        nbuf[4]=(n%10)|'0';\r
+        if(n/=10) nbuf[3]=(n%10)|'0';\r
+        if(n/=10) nbuf[2]=(n%10)|'0';\r
+        if(n/=10) nbuf[1]=(n%10)|'0';\r
+        if(n/=10) nbuf[0]=(n%10)|'0';\r
+        char_print(nbuf[0]);\r
+        char_print(nbuf[1]);\r
+        char_print(nbuf[2]);\r
+        char_print(nbuf[3]);\r
+        char_print(nbuf[4]);\r
+      }\r
+      break;\r
+    }\r
+    case 0xE7: // Random number generator\r
+      if(*inst_sargs>0) storei(get_random(*inst_sargs));\r
+      else randomize(-*inst_sargs),storei(0);\r
+      break;\r
+    case 0xE8: // Push to stack\r
+      pushstack(*inst_args);\r
+      break;\r
+    case 0xE9: // Pop from stack (different in v6 FIXME)\r
+      if(*inst_args) store(*inst_args,stack[--stackptr]);\r
+      else stack[stackptr-2]=stack[stackptr-1],stackptr--;\r
+      break;\r
+    case 0xEA: // Split window\r
+      //NOP\r
+      break;\r
+    case 0xEB: // Set active window\r
+      window=*inst_args;\r
+      break;\r
+#if (VERSION > 3)\r
+    case 0xEC: // Call routine\r
+      if(*inst_args) {\r
+        program_counter++;\r
+        enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,1,argc-1);\r
+      } else {\r
+        storei(0);\r
+      }\r
+      break;\r
+    case 0xED: // Clear window\r
+      if(*inst_args!=1) {\r
+        putchar('\n');\r
+        textptr=0;\r
+        cur_row=2;\r
+        cur_column=0;\r
+        while(cur_column<lmargin) {\r
+          putchar(32);\r
+          cur_column++;\r
+        }\r
+      }\r
+      break;\r
+    case 0xEE: // Erase line\r
+      //NOP\r
+      break;\r
+    case 0xEF: // Set cursor position\r
+      //NOP\r
+      break;\r
+    case 0xF0: // Get cursor position\r
+      if(window) {\r
+        write8(*inst_args, sc_rows);\r
+        write8(*inst_args+1, cur_column+1);\r
+      } else {\r
+        write16(*inst_args, 0);\r
+      }\r
+      break;\r
+    case 0xF1: // Set text style\r
+      //NOP\r
+      break;\r
+    case 0xF2: // Buffer mode\r
+      buffering=*inst_args;\r
+      break;\r
+#endif\r
+    case 0xF3: // Select output stream\r
+      switch_output(*inst_sargs);\r
+      break;\r
+    case 0xF4: // Select input stream\r
+      break;\r
+    case 0xF5: // Sound effects\r
+      putchar(7);\r
+      break;\r
+#if (VERSION > 3)      \r
+    case 0xF6: // Read a single character\r
+      n=char_input();\r
+      storei(n);\r
+      break;\r
+    case 0xF7: // Scan a table\r
+      if(argc<4) inst_args[3]=0x82;\r
+      u=inst_args[1];\r
+      while(inst_args[2]) {\r
+        if(*inst_args==(inst_args[3]&0x80?read16(u):read8(u))) break;\r
+        u+=inst_args[3]&0x7F;\r
+        inst_args[2]--;\r
+      }\r
+      storei(inst_args[2]?u:0);\r
+      branch(inst_args[2]);\r
+      break;\r
+#endif\r
+#if (VERSION > 4)\r
+    case 0xF8: // Not\r
+      storei(~*inst_args);\r
+      break;\r
+    case 0xF9: // Call routine and discard results\r
+      if(*inst_args) {\r
+        enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,0,argc-1);\r
+      }\r
+      break;\r
+    case 0xFA: // Call routine and discard results\r
+      if(*inst_args) enter_routine((*inst_args<<PACKED_SHIFT)+routine_start,0,argc-1);\r
+      break;\r
+    case 0xFB: // Tokenise text\r
+      if(argc<4) inst_args[3]=0;\r
+      if(argc<3) inst_args[2]=0;\r
+      tokenise(inst_args[0]+2,inst_args[2],inst_args[1],\r
+                        read8(inst_args[0]+1),inst_args[3]);\r
+      break;\r
+    case 0xFC: // Encode text in dictionary format\r
+    {\r
+      uint64_t y;\r
+      /* FIXME memory ... */\r
+      y=dictionary_encode(memory+inst_args[0]+inst_args[2],inst_args[1]);\r
+      write16(inst_args[3],y>>16);\r
+      write16(inst_args[3]+2,y>>8);\r
+      write16(inst_args[3]+4,y);\r
+      break;\r
+    }\r
+    case 0xFD: // Copy a table\r
+      if(!inst_args[1]) {\r
+        // zero!\r
+        while(inst_args[2])\r
+          write8(inst_args[0]+--inst_args[2], 0);\r
+      } else if(inst_sargs[2]>0 && inst_args[1]>inst_args[0]) {\r
+        // backward!\r
+        m=inst_sargs[2];\r
+        while(m--) write8(inst_args[1]+m, read8(inst_args[0]+m));\r
+      } else {\r
+        // forward!\r
+        if(inst_sargs[2]<0) inst_sargs[2]*=-1;\r
+        m=0;\r
+        while(m<inst_sargs[2])\r
+          write8(inst_args[1]+m, read8(inst_args[0]+m),m);\r
+          m++;\r
+      }\r
+      break;\r
+    case 0xFE: // Print a rectangle of text\r
+      make_rectangle(inst_args[0],inst_args[1],argc>2?inst_args[2]:1,argc>3?inst_sargs[3]:0);\r
+      // (I assume the skip is signed, since many other things are, and +32768 isn't useful anyways.)\r
+      break;\r
+    case 0xFF: // Check argument count\r
+      branch(frames[frameptr].argc>=*inst_args);\r
+      break;\r
+#endif      \r
+    default:\r
+      fprintf(stderr,"\n*** Invalid instruction: %02X (near %06X)\n",in,program_counter);\r
+      exit(1);\r
+      break;\r
+  }\r
+}\r
+\r
+void game_begin(void) {\r
+  if(!story) story=fopen(story_name,"rb");\r
+  if(!story) {\r
+    fprintf(stderr,"\n*** Unable to load story file: %s\n",story_name);\r
+    exit(1);\r
+  }\r
+  rewind(story);\r
+  fread(memory,64,1,story);\r
+  if (read8low(0) != VERSION) {\r
+      fprintf(stderr,"\n*** Unsupported Z-machine version: %d\n",VERSION);\r
+      exit(1);\r
+  }\r
+  switch(VERSION) {\r
+    case 1:\r
+    case 2:\r
+      write8low(0x01,0x10);\r
+      break;\r
+    case 3:\r
+      write8low(0x01, (read8low(0x01)&0x8F)| 0x10 | (tandy << 3));\r
+      break;\r
+    case 4:\r
+      write8low(0x01, 0x00);\r
+      break;\r
+    case 5:\r
+      alphabet_table=read16low(0x34);\r
+      break;\r
+    case 7:\r
+      routine_start=read16low(0x28)<<3;\r
+      text_start=read16low(0x2A)<<3;\r
+      alphabet_table=read16low(0x34);\r
+      break;\r
+    case 8:\r
+      alphabet_table=read16low(0x34);\r
+      break;\r
+  }\r
+  restart_address=read16low(0x06);\r
+  dictionary_table=read16low(0x08);\r
+  object_table=read16low(0x0A);\r
+  global_table=read16low(0x0C);\r
+  static_start=read16low(0x0E);\r
+  write8low(0x11, read8low(0x11)&0x53);\r
+  if(VERSION>1) synonym_table=read16low(0x18);\r
+  if(VERSION>3) {\r
+    write8low(0x1E, tandy?11:1);\r
+    write8low(0x20, sc_rows);\r
+    write8low(0x21, sc_columns);\r
+  }\r
+  if(VERSION>4) {\r
+    write8low(0x01,0x10);\r
+    write8low(0x23,sc_columns);\r
+    write8low(0x25,sc_rows);\r
+    write8low(0x26,1);\r
+    write8low(0x27,1);\r
+    write8low(0x2C,2);\r
+    write8low(0x2D,9);\r
+  }\r
+  if(!(read8low(2)&128)) write16low(0x02,0x0802);\r
+  write8low(0x11, read8low(0x11)&0x43);\r
+  cur_row=2;\r
+  cur_column=0;\r
+  randomize(0);\r
+  putchar('\n');\r
+}\r
+\r
+int main(int argc,char**argv) {\r
+  int opt;\r
+  srand(getpid() ^ time(NULL));\r
+  while((opt = getopt(argc,argv, "tpq")) != -1) {\r
+    switch(opt) {\r
+      case 't':\r
+        tandy = 1;\r
+        break;\r
+      case 'p':\r
+        original = 0;\r
+        break;\r
+      case 'q':\r
+        qtospace = 0;\r
+        break;\r
+    }\r
+  }\r
+  if (optind >= argc) {\r
+    fprintf(stderr, "fweeplet: story name required.\n");\r
+    exit(1);\r
+  }\r
+  story_name = argv[optind];\r
+  if (argv[optind+1]) {\r
+    /* Restore file in future ?? */\r
+    fprintf(stderr, "fweeplet: only one story name.\n");\r
+    exit(1);\r
+  }\r
+  game_begin();\r
+  game_restart();\r
+  for(;;) execute_instruction();\r
+}\r