From 9bb0df3580fcb85526b27fbb646176adfad94d5c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 25 Feb 2018 00:37:02 +0000 Subject: [PATCH] fweep: work in progress Needs a lot more thinning down to be useful anywhere but 68000 --- Applications/games/fweep.c | 1491 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1491 insertions(+) create mode 100644 Applications/games/fweep.c diff --git a/Applications/games/fweep.c b/Applications/games/fweep.c new file mode 100644 index 00000000..966bb13b --- /dev/null +++ b/Applications/games/fweep.c @@ -0,0 +1,1491 @@ +/* + 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 +#include +#include +#include +#include +#include + +#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=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=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 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;ic) argc=c; + for(i=0;i 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(n3?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;i4)); + 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='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 && c0) { + 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>8,fp); + fputc(stack[i]&255,fp); + } + clearerr(story); + fseek(story,o=0x38,SEEK_SET); + q=0; + while(o>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=0) write8(o++, fgetc(story)); + } + } + fclose(fp); + while(o=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]); + break; + case 0x03: // Arithmetic shift + if(inst_sargs[1]>0) 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<4) { + if(*inst_args) enter_routine((*inst_args<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;ninst_sargs[1]); + break; + case 0xC4: // Decrement and branch if less + store(*inst_args,n=fetch(*inst_args)-1); + branch(ninst_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< 4) + case 0xDA: // Call routine and discard result + if(*inst_args) enter_routine((*inst_args<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< 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<>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(m2?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(); +} -- 2.34.1