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