--- /dev/null
+/*\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