| /* ------------------------------------------------------------------------- */ | |
| /* "asm" : The Inform assembler */ | |
| /* */ | |
| /* Part of Inform release 5 */ | |
| /* ------------------------------------------------------------------------- */ | |
| int in_routine_flag, no_stubbed; | |
| int ignoring_routine; | |
| int return_flag; | |
| static int stub_flags[32]; | |
| static int lower_strings_flag=0; | |
| static int ifdef_stack[MAX_IFDEF_DEPTH]; | |
| static int ifdef_sp=0; | |
| static int started_ignoring=0; | |
| static long int pa_forwards[MAX_FORWARD_REFS]; | |
| static long int *pa_forwards; | |
| extern void asm_allocate_arrays(void) | |
| { | |
| pa_forwards = (long int *) my_calloc(sizeof(long int), MAX_FORWARD_REFS, | |
| "forward references"); | |
| } | |
| extern void asm_free_arrays(void) | |
| { | |
| my_free(&pa_forwards, "forward references"); | |
| } | |
| /* ------------------------------------------------------------------------- */ | |
| /* Decode arguments as constants and variables */ | |
| /* (Gratuitous Space:1999 reference by Mr Dilip Sequeira of Edinburgh */ | |
| /* University) */ | |
| /* ------------------------------------------------------------------------- */ | |
| static int constant_was_string; | |
| extern int32 constant_value(char *b) | |
| { int32 i, j, k, action_flag=0, rv, base=10, moon, alpha, victor=0, eagle; | |
| constant_was_string=0; | |
| if (b[0]=='#') b++; | |
| if (b[0]=='\'') | |
| { if ((b[2]=='\'') && (b[3]==0)) return(translate_to_ascii(b[1])); | |
| i=strlen(b); | |
| if ((i>=4)&&(b[i-1]=='\'')) | |
| { b[i-1]=0; rv=dictionary_add(b+1,0x80,0,0); return(rv); | |
| } | |
| } | |
| if (b[0]=='#') | |
| { i=find_symbol(b+1); | |
| if ((i>=0)&&(stypes[i]==18)) return(svals[i]); | |
| if (strlen(b)>60) | |
| { error_named("Action name over 60 characters long:",b); | |
| return(0); | |
| } | |
| sprintf(b+strlen(b),"Sub"); | |
| action_flag=1; b++; goto actionfound; | |
| } | |
| if (b[0]=='\"') goto stringfound; | |
| if (*b=='$') | |
| { if (*++b=='$') { b++; base=2; } | |
| else base=16; | |
| } | |
| else if (*b=='-') { b++; victor=1; } | |
| else if (b[1]=='$') goto dollarform; | |
| else for (i=0; b[i]; i++) if (!isdigit(b[i])) goto nonumber; | |
| for(moon=0;*b;b++) | |
| { alpha=isalpha(*b)?(tolower(*b)-'a'+10):*b-'0'; | |
| if(alpha>=base || alpha<0) break; else moon=moon*base+alpha; | |
| } | |
| if (victor==0) return(moon); | |
| eagle=((int32) 0x10000L)-moon; | |
| return(eagle); | |
| dollarform: | |
| switch(b[0]) | |
| { case 'a': | |
| obsolete_warning("'#a$Action' is now superceded by '##Action'"); | |
| b+=2; action_flag=1; goto actionfound; | |
| case 'w': | |
| obsolete_warning("'#w$word' is now superceded by ''word''"); | |
| k=dictionary_find(b+2,2); | |
| rv=dictionary_offset+7+((version_number==3)?7:9)*(k-1); | |
| if ((k==0)&&(pass_number==2)) | |
| error_named("Dictionary word not found for constant",b); | |
| return(rv); | |
| case 'n': | |
| if (strlen(b)>3) | |
| obsolete_warning("'#n$word' is now superceded by ''word''"); | |
| rv=dictionary_add(b+2,0x80,0,0); return(rv); | |
| case 'r': | |
| i=find_symbol(b+2); | |
| if ((i<0)||(stypes[i]!=1)) | |
| { if (pass_number==2) | |
| error_named("No such routine as",b); | |
| return(256); | |
| } | |
| rv=svals[i]; goto constantfound; | |
| } | |
| error_named("There is no such # constant form as",b); | |
| return(0); | |
| nonumber: | |
| for (j=0,i=0; b[i]; i++) if (b[i]=='_') j=1; | |
| if (j==1) | |
| { if (strcmp(b,"adjectives_table")==0) return(adjectives_offset); | |
| if (strcmp(b,"preactions_table")==0) return(preactions_offset); | |
| if (strcmp(b,"actions_table")==0) return(actions_offset); | |
| if (strcmp(b,"version_number")==0) return(actual_version); | |
| if (strcmp(b,"largest_object")==0) return(256+max_no_objects-1); | |
| if (strcmp(b,"strings_offset")==0) return(strings_offset/scale_factor); | |
| if (strcmp(b,"code_offset")==0) return(code_offset/scale_factor); | |
| if (strcmp(b,"dict_par1")==0) return((version_number==3)?4:6); | |
| if (strcmp(b,"dict_par2")==0) return((version_number==3)?5:7); | |
| if (strcmp(b,"dict_par3")==0) return((version_number==3)?6:8); | |
| } | |
| actionfound: | |
| i=find_symbol(b); | |
| if (i<0) | |
| { if (pass_number==2) | |
| { if (action_flag==1) | |
| error_named("No such action routine as",b); | |
| else | |
| error_named("No such constant as",b); | |
| } | |
| return(0); | |
| } | |
| rv=svals[i]; | |
| constantfound: | |
| switch(stypes[i]) | |
| { case 1: rv=(rv+code_offset)/scale_factor; | |
| break; | |
| case 2: | |
| case 3: error_named("Not a constant:",b); return(0); | |
| case 4: | |
| case 10: error_named("Reserved word as constant:",b); return(0); | |
| } | |
| if (action_flag==0) return(rv); | |
| j=find_action(svals[i]); | |
| return(j); | |
| stringfound: | |
| dequote_text(b); | |
| constant_was_string=1; | |
| if (lower_strings_flag==1) | |
| { j=subtract_pointers(low_strings_p,low_strings); | |
| low_strings_p=translate_text(low_strings_p,b); | |
| i= subtract_pointers(low_strings_p,low_strings); | |
| if (i>MAX_LOW_STRINGS) | |
| memoryerror("MAX_LOW_STRINGS", MAX_LOW_STRINGS); | |
| return(0x21+(j/2)); | |
| } | |
| j= subtract_pointers(strings_p,strings); | |
| { zip *c; | |
| c=translate_text(strings,b); | |
| i=subtract_pointers(c,strings); | |
| strings_p+=i; | |
| if (pass_number==2) | |
| for (c=strings; c<strings+i; c++) | |
| { fputc(*c,Temp1_fp); add_to_checksum((void *) c); | |
| } | |
| } | |
| strings_p=translate_text(strings_p,b); | |
| i=subtract_pointers(strings_p,strings); | |
| if (i>MAX_STATIC_STRINGS) | |
| memoryerror("MAX_STATIC_STRINGS",MAX_STATIC_STRINGS); | |
| while ((i%scale_factor)!=0) | |
| { i+=2; | |
| strings_p+=2; | |
| if (pass_number==2) { fputc(0,Temp1_fp); fputc(0,Temp1_fp); } | |
| *(strings_p++)=0; *(strings_p++)=0; | |
| } | |
| return((strings_offset+j)/scale_factor); | |
| } | |
| /* ------------------------------------------------------------------------- */ | |
| /* parse_argument returns: 1000+n Constant value n */ | |
| /* long_form_flag set if long form vital */ | |
| /* n Variable n */ | |
| /* ------------------------------------------------------------------------- */ | |
| static char known_unknowns[MAX_IDENTIFIER_LENGTH*16]; | |
| static int no_knowns; | |
| static long int pa_id; | |
| static int pa_ref; | |
| static int max_pa_ref; | |
| static int long_form_flag; | |
| static char one_letter_locals[16]; | |
| extern void args_begin_pass(void) | |
| { if (pass_number==2) max_pa_ref=pa_ref; | |
| pa_ref=0; pa_id=0; | |
| } | |
| static int32 parse_argument(char *b) | |
| { int i, j, flag=0; | |
| long_form_flag=0; | |
| i=b[0]; | |
| if (b[1]==0) | |
| { for (j=0;j<16;j++) | |
| if (i==one_letter_locals[j]) | |
| { used_local_variable[j]=1; | |
| return(j+1); | |
| } | |
| if (i=='0') return(1000); | |
| if (i=='1') return(1001); | |
| if (i=='2') return(1002); | |
| if (i=='3') return(1003); | |
| if (i=='4') return(1004); | |
| if (i=='5') return(1005); | |
| if (i=='6') return(1006); | |
| if (i=='7') return(1007); | |
| if (i=='8') return(1008); | |
| if (i=='9') return(1009); | |
| } | |
| if ((i=='#')||(i=='-')||(i=='$')||(i=='\"')||(i=='\'')) | |
| return(1000+constant_value(b)); | |
| for (i=0; b[i]!=0; i++) | |
| if (isdigit(b[i])==0) { flag=1; break; } | |
| if (flag==0) return(1000+constant_value(b)); | |
| if (in_routine_flag==1) | |
| { i=local_find_symbol(b); | |
| if (i>=0) return(1+svals[i]); | |
| } | |
| pa_id++; | |
| i=find_symbol(b); | |
| if (i>=0) | |
| { switch(stypes[i]) | |
| { case 1: return(1000+svals[i]); | |
| case 2: return(16+svals[i]); | |
| case 4: if (svals[i]==0) return(0); | |
| case 7: | |
| case 12: | |
| case 13: | |
| case 18: | |
| case 8: | |
| case 11: | |
| case 9: if (pass_number==2) | |
| { while ((pa_ref<max_pa_ref) | |
| &&(pa_id>pa_forwards[pa_ref])) pa_ref++; | |
| if ((pa_ref<max_pa_ref) | |
| &&(pa_id==pa_forwards[pa_ref])) | |
| { long_form_flag=1; | |
| /* printf("'%s' ref %d id %d seems forward\n",b,pa_ref, pa_id); */ | |
| } | |
| } | |
| return(1000+constant_value(b)); | |
| default: error_named("Type mismatch in argument",b); | |
| return(0); | |
| } | |
| } | |
| if (pass_number==1) | |
| { if (pa_ref==MAX_FORWARD_REFS) | |
| memoryerror("MAX_FORWARD_REFS", MAX_FORWARD_REFS); | |
| pa_forwards[pa_ref++]=pa_id; | |
| /* printf("'%s' ref %d id %d is forward\n",b,pa_ref, pa_id); */ | |
| return(1256); | |
| } | |
| for (i=0; i<no_knowns; i++) | |
| if (strcmp(known_unknowns+i*MAX_IDENTIFIER_LENGTH,b)==0) | |
| return(0); | |
| if (no_knowns<16) | |
| strcpy(known_unknowns+(no_knowns++)*MAX_IDENTIFIER_LENGTH,b); | |
| error_named("No such variable as",b); return(0); | |
| } | |
| /* ------------------------------------------------------------------------- */ | |
| /* Assembler of individual lines */ | |
| /* ------------------------------------------------------------------------- */ | |
| static void byteout(int32 i) | |
| { *zcode_p=(unsigned char) i; zcode_p++; | |
| utf_zcode_p++; | |
| if (subtract_pointers(zcode_p,zcode) >= MAX_ZCODE_SIZE) | |
| { memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE); | |
| } | |
| } | |
| static void write_operand(operand_t op) | |
| { int32 j; | |
| j=op.value; | |
| if (op.type!=0) byteout(j); | |
| else { byteout(j/256); byteout(j%256); } | |
| } | |
| static operand_t parse_operand(int32 number, opcode opco, char *b) | |
| { int32 j; int opt, type=opco.type1; operand_t oper; | |
| word(b,number+2); | |
| if (((type==CALL)||(type==NCALL))&&(number==0)) | |
| { if (pass_number==2) | |
| { | |
| j=find_symbol(b); | |
| if (j==-1) no_such_label(b); | |
| else if (stypes[j]!=1) error_named("Not a label:",b); | |
| else oper.value=(code_offset+svals[j])/scale_factor; | |
| oper.type=0; | |
| } | |
| else | |
| { oper.value=0x1000; oper.type=0; | |
| } | |
| return(oper); | |
| } | |
| if ((b[0]=='s')&&(b[1]=='p')&&(b[2]==0)) j=0; | |
| else j=parse_argument(b); | |
| if (j>=1256) { opt=0; j=j-1000; } | |
| else if (j>=1000) { opt=1; j=j-1000; } | |
| else opt=2; | |
| if ((opt==1) && (long_form_flag==1)) opt=0; | |
| if ((opco.type2==VAR)&&(opt==2)&&(number==0)) opt=1; | |
| oper.value=j; oper.type=opt; | |
| return(oper); | |
| } | |
| int a_from_one=0; | |
| int line_done_flag = 0; | |
| extern int assemble_opcode(char *b, int32 offset, int internal_number) | |
| { | |
| zip *opc, *opcname, *ac; | |
| int32 j, topbits, addr, cargs, ccode, ccode2, oldccode, oldccode2, | |
| multi, mask, flag, longf, branchword, offset_here; | |
| operand_t oper1, oper2; | |
| opcode opco; | |
| opco = opcs(internal_number); | |
| if (opco.type1==INVALID) | |
| { if (version_number==3) | |
| warning_named("Ignoring Advanced-game opcode",b); | |
| else | |
| warning_named("Ignoring Standard-game opcode",b); | |
| return(1); | |
| } | |
| return_flag=0; | |
| if ((opco.type1==RETURN) || (opco.type1==JUMP)) return_flag=1; | |
| if (opco.type1==INDIR) | |
| { byteout(0xE0); byteout(0xBF); byteout(0); byteout(0); | |
| goto Line_Done; | |
| } | |
| opcname=opco.name; | |
| switch(opco.no) | |
| { case MANY: | |
| case VARI: topbits=0xc0; break; | |
| case ZERO: topbits=0xb0; break; | |
| case ONE: topbits=0x80; break; | |
| case TWO: topbits=0x00; break; | |
| } | |
| opc=zcode_p; | |
| if (opco.no!=EXTD) | |
| { byteout(topbits+opco.code); | |
| } | |
| else | |
| { byteout(0xbe); byteout(opco.code); opco.no=VARI; | |
| } | |
| if (opco.type1==JUMP) | |
| { word(b,2); | |
| if (pass_number==1) addr=0; | |
| else | |
| { j=find_symbol(b); | |
| if (j<0) { no_such_label(b); return(1); } | |
| if ((stypes[j]!=6)&&(stypes[j]!=20)) | |
| { error_named("Not a label:",b); return(1); } | |
| addr=svals[j]-offset-1; | |
| if (addr<0) addr+=(int32) 0x10000L; | |
| } | |
| byteout(addr/256); byteout(addr%256); | |
| goto Line_Done; | |
| } | |
| if (opco.type2==TEXT) | |
| { zip *tmp; | |
| if (a_from_one==1) textword(b,1); else textword(b,2); | |
| tmp=zcode_p; zcode_p=translate_text(zcode_p,b); | |
| j=subtract_pointers(zcode_p,tmp); | |
| utf_zcode_p+=j; | |
| goto Line_Done; | |
| } | |
| switch(opco.no) | |
| { case VARI: | |
| ac=zcode_p; byteout(0); | |
| cargs= -1; ccode=0xff; | |
| while (word(b,(++cargs)+2),(b[0]!=0)) | |
| { if (b[0]=='?') { branchword=cargs+2; break; } | |
| switch(cargs) | |
| { case 0: multi=0x40; mask=0xc0; break; | |
| case 1: multi=0x10; mask=0x30; break; | |
| case 2: multi=0x04; mask=0x0c; break; | |
| case 3: multi=0x01; mask=0x03; break; | |
| case 4: multi=0; | |
| if ((opco.type1!=CALL)&&(opco.type1!=STORE)) | |
| error("Too many arguments"); | |
| break; | |
| default: error("Too many arguments"); break; | |
| } | |
| oper1=parse_operand(cargs, opco, b); | |
| write_operand(oper1); | |
| oldccode=ccode; ccode = (ccode & (~mask)) + oper1.type*multi; | |
| } | |
| if ((opco.type1==CALL)||(opco.type1==STORE)) | |
| { if (oper1.type!=2) | |
| { error("Can't store to that (no such variable)"); } | |
| *ac=oldccode; | |
| } | |
| else *ac=ccode; | |
| break; | |
| case MANY: | |
| ac=zcode_p; byteout(0); byteout(0); | |
| cargs= -1; ccode=0xff; ccode2=0xff; | |
| while (word(b,(++cargs)+2),(b[0]!=0)) | |
| { if (b[0]=='?') { branchword=cargs+2; break; } | |
| switch(cargs) | |
| { case 0: case 4: multi=0x40; mask=0xc0; break; | |
| case 1: case 5: multi=0x10; mask=0x30; break; | |
| case 2: case 6: multi=0x04; mask=0x0c; break; | |
| case 3: case 7: multi=0x01; mask=0x03; break; | |
| case 8: multi=0; | |
| if ((opco.type1!=CALL)&&(opco.type1!=STORE)) | |
| error("Too many arguments"); | |
| break; | |
| default: error("Too many arguments"); break; | |
| } | |
| oper1=parse_operand(cargs, opco, b); | |
| write_operand(oper1); | |
| oldccode=ccode; oldccode2=ccode2; | |
| if (cargs<4) | |
| ccode = (ccode & (~mask)) + oper1.type*multi; | |
| else | |
| ccode2 = (ccode2 & (~mask)) + oper1.type*multi; | |
| } | |
| if ((opco.type1==CALL)||(opco.type1==STORE)) | |
| { if (oper1.type!=2) | |
| { error("Can't store to that (no such variable)"); } | |
| *ac=oldccode; *(ac+1)=oldccode2; | |
| } | |
| else { *ac=ccode; *(ac+1)=ccode2; } | |
| break; | |
| case ONE: | |
| oper1=parse_operand(0,opco,b); | |
| *opc=(*opc) + oper1.type*0x10; | |
| write_operand(oper1); | |
| break; | |
| case TWO: | |
| oper1=parse_operand(0,opco,b); | |
| oper2=parse_operand(1,opco,b); | |
| if ((oper1.type==0)||(oper2.type==0)) | |
| { *opc=(*opc) + 0xc0; | |
| byteout(oper1.type*0x40 + oper2.type*0x10 + 0x0f); | |
| } | |
| else | |
| { if (oper1.type==2) *opc=(*opc) + 0x40; | |
| if (oper2.type==2) *opc=(*opc) + 0x20; | |
| } | |
| write_operand(oper1); | |
| write_operand(oper2); | |
| break; | |
| case ZERO: | |
| break; | |
| } | |
| if ((opco.no==ONE) || (opco.no==TWO)) | |
| { if ((opco.type1==STORE)||(opco.type1==CALL)) | |
| { if (opco.no==ONE) oper1=parse_operand(1,opco,b); | |
| if (opco.no==TWO) oper1=parse_operand(2,opco,b); | |
| if (oper1.type!=2) | |
| { error("Can't store to that (no such variable)"); } | |
| byteout(oper1.value); | |
| } | |
| } | |
| if ((opco.type1==BRANCH)||(opco.type2==OBJECT)) | |
| { int o=0; int32 pca; | |
| pca= subtract_pointers(zcode_p,opc) -1; | |
| switch(opco.no) | |
| { case ZERO: word(b,2); break; | |
| case ONE: if (opco.type2!=OBJECT) { word(b,3); break; } | |
| case TWO: word(b,4); break; | |
| case VARI: word(b,branchword); break; | |
| } | |
| if (b[0]=='?') { longf=1; o++; } | |
| else | |
| { int o2=0; | |
| if (b[0]=='~') o2=1; | |
| if (pass_number==1) | |
| { j=find_symbol(b+o2); | |
| if (j<0) longf=0; | |
| else longf=1; | |
| } | |
| else | |
| { j=find_symbol(b+o2); | |
| if (j<0) longf=0; | |
| else | |
| { if (offset-svals[j]>0) longf=1; | |
| else longf=0; | |
| if ((svals[j]-offset-pca)>30) | |
| { error("Branch too far forward: use '?'"); return(1); } | |
| } | |
| } | |
| } | |
| /* printf("Branch at %04x has longf=%d\n",offset,longf); */ | |
| if (pass_number==1) { byteout(0); if (longf==1) byteout(0); } | |
| else | |
| { if (b[o]=='~') { flag=0; o++; } else flag=1; | |
| j=find_symbol(b+o); | |
| if (j<0) { no_such_label(b+o); return(1); } | |
| switch(stypes[j]) | |
| { case 4: | |
| switch(svals[j]) | |
| { case 1: addr=1; longf=0; break; | |
| case 2: addr=0x20; longf=0; break; | |
| default: error("No such return condition for branch"); | |
| return(1); | |
| } | |
| break; | |
| case 1: error("Can't branch to a routine, only to a label"); | |
| return(1); | |
| case 6: | |
| case 20: | |
| if (longf==1) pca++; | |
| addr=svals[j]-offset-pca; | |
| if (addr<0) addr+=(int32) 0x10000L; break; | |
| default: error_named("Not a label:",b+o); return(1); | |
| } | |
| addr=addr&0x3fff; | |
| if (longf==1) | |
| { byteout(flag*0x80 + addr/256); byteout(addr%256); } | |
| else | |
| byteout(flag*0x80+ 0x40 + (addr&0x3f)); | |
| } | |
| } | |
| Line_Done: | |
| if (trace_mode==1) | |
| { printf("%04d %05lx %-14s ", current_source_line(), | |
| ((long int) offset)+((long int) code_offset),opcname); | |
| for (j=0;opc<zcode_p; j++, opc++) | |
| { printf("%02x ", *opc); | |
| if (j%16==15) printf("\n "); | |
| } | |
| printf("\n"); | |
| } | |
| if ((debugging_file==1)&&(pass_number==2) | |
| &&(line_done_flag==0)) | |
| { debug_pass=2; line_done_flag=1; | |
| write_debug_byte(10); write_debug_linenum(); | |
| offset_here = offset+code_offset; | |
| write_debug_byte((int)((offset_here/256)/256)); | |
| write_debug_byte((int)((offset_here/256)%256)); | |
| write_debug_byte((int)(offset_here%256)); | |
| debug_pass=1; | |
| } | |
| { | |
| zip *c; | |
| int32 i; | |
| i= subtract_pointers(zcode_p,zcode); | |
| if (pass_number==2) | |
| { for (c=zcode; c<zcode+i; c++) | |
| { fputc(*c,Temp2_fp); | |
| add_to_checksum((void *) c); | |
| } | |
| } | |
| zcode_p=zcode; | |
| } | |
| return(1); | |
| } | |
| /* ------------------------------------------------------------------------- */ | |
| /* Making attributes and properties (incorporating "alias" code suggested */ | |
| /* by Art Dyer) */ | |
| /* ------------------------------------------------------------------------- */ | |
| static void trace_s(char *name, int32 code, int f) | |
| { if (debugging_file==1) | |
| { write_debug_byte(5+f); write_debug_byte((int)code); | |
| write_debug_string(name); | |
| } | |
| if ((printprops_mode==0)||(pass_number==2)) return; | |
| printf("%s %02ld ",(f==0)?"Attr":"Prop",(long int) code); | |
| if (f==0) printf(" "); | |
| else printf("%s%s",(prop_longflag[code]==1)?"L":" ", | |
| (prop_additive[code]==1)?"A":" "); | |
| printf(" %s\n",name); | |
| } | |
| static void make_attribute(char *b) | |
| { int i; | |
| word(b,3); | |
| if (strcmp(b,"alias")==0) | |
| { | |
| word(b,4); | |
| if (b[0]==0) | |
| { error("Expected an attribute name after 'alias'"); | |
| return; | |
| } | |
| if (((i=find_symbol(b))==-1)||(stypes[i]!=7)) | |
| { error_named("'alias' refers to undefined attribute",b); | |
| return; | |
| } | |
| word(b,2); | |
| trace_s(b,svals[i],0); | |
| new_symbol(b,svals[i],7); | |
| return; | |
| } | |
| InV3 | |
| { if (no_attributes==32) | |
| { error("All 32 attributes already declared (compile as Advanced \ | |
| game to get an extra 16)"); return; } | |
| } | |
| InV5 | |
| { if (no_attributes==48) | |
| { error("All 48 attributes already declared"); return; } | |
| } | |
| word(b,2); | |
| trace_s(b,no_attributes,0); | |
| new_symbol(b,no_attributes++,7); | |
| return; | |
| } | |
| static void make_property(char *b) | |
| { int32 def=0; | |
| int i, fl, addflag=0; | |
| fl=2; | |
| do | |
| { word(b,fl++); | |
| if (strcmp(b,"long")==0) | |
| obsolete_warning("all properties are now automatically 'long'"); | |
| else if (strcmp(b,"additive")==0) addflag=1; | |
| else break; | |
| } while (1==1); | |
| word(b,fl); | |
| if (strcmp(b,"alias")==0) | |
| { if (addflag) | |
| { error("'alias' incompatible with 'additive'"); return; } | |
| word(b,4); | |
| if (b[0]==0) | |
| { error("Expected a property name after 'alias'"); return; } | |
| i=find_symbol(b); | |
| if (((i=find_symbol(b))==-1)||(stypes[i]!=12)) | |
| { error_named("'alias' refers to undefined property",b); | |
| return; | |
| } | |
| word(b,2); | |
| trace_s(b,svals[i],1); | |
| new_symbol(b,svals[i],12); | |
| return; | |
| } | |
| InV3 | |
| { if (no_properties==32) | |
| { error("All 30 properties already declared (compile as Advanced \ | |
| game to get an extra 32)"); return; } | |
| } | |
| InV5 | |
| { if (no_properties==64) | |
| { error("All 62 properties already declared"); return; } | |
| } | |
| if (b[0]!=0) def=constant_value(b); | |
| word(b,fl-1); | |
| prop_defaults[no_properties]=def; | |
| InV3 { prop_longflag[no_properties]=1; } | |
| InV5 { prop_longflag[no_properties]=1; } | |
| prop_additive[no_properties]=addflag; | |
| trace_s(b,no_properties,1); | |
| new_symbol(b,no_properties++,12); | |
| return; | |
| } | |
| static void make_fake_action(char *b) | |
| { int i; | |
| i=256+no_fake_actions++; | |
| word(b,2); | |
| new_symbol(b,i,18); | |
| new_action(b,i); | |
| if (debugging_file==1) | |
| { write_debug_byte(7); write_debug_byte(i); | |
| write_debug_string(b); | |
| } | |
| return; | |
| } | |
| /* ------------------------------------------------------------------------- */ | |
| /* Assembler directives: for diagnosis, and making the non-code part of */ | |
| /* the file */ | |
| /* ------------------------------------------------------------------------- */ | |
| static int lmoved_flag=0; | |
| extern void assemble_label(int32 offset, char *b) | |
| { int i; | |
| if (pass_number==1) new_symbol(b,offset,6); | |
| else | |
| { i=find_symbol(b); | |
| if ((stypes[i]==6)&&(svals[i]!=offset)) | |
| if (lmoved_flag==0) | |
| { lmoved_flag=1; | |
| if (no_errors==0) | |
| error("An error has occurred just before this point \ | |
| with what Inform thought was a constant. Perhaps it was a global \ | |
| variable used before its definition in the code."); | |
| } | |
| } | |
| if (trace_mode==1) printf(".%s\n",b); | |
| return_flag=0; | |
| } | |
| static void stack_sline(char *s1, char *b) | |
| { char rw[100]; | |
| sprintf(rw,s1,b); stack_line(rw); | |
| } | |
| static int constant_made_flag = 0; | |
| extern void assemble_directive(char *b, int32 offset, int32 code) | |
| { int i, j, condition=0, debugged_routine; int32 k; | |
| switch(code) | |
| { | |
| case IFDEF_CODE: | |
| word(b,2); | |
| if ((b[0]=='V')&&(b[1]=='N')&&(b[2]=='_')&&(strlen(b)==7)) | |
| { i=atoi(b+3); if (VNUMBER>=i) condition=1; | |
| goto generic_if; | |
| } | |
| i=find_symbol(b); | |
| if (i>=0) condition=1; | |
| goto generic_if; | |
| case IFNDEF_CODE: | |
| word(b,2); | |
| if ((b[0]=='V')&&(b[1]=='N')&&(b[2]=='_')&&(strlen(b)==7)) | |
| { i=atoi(b+3); if (VNUMBER<i) condition=1; | |
| goto generic_if; | |
| } | |
| i=find_symbol(b); | |
| if (i==-1) condition=1; | |
| goto generic_if; | |
| case IFV3_CODE: | |
| if (version_number==3) condition=1; goto generic_if; | |
| case IFV5_CODE: | |
| if (version_number==5) condition=1; goto generic_if; | |
| generic_if: | |
| if (ifdef_sp==MAX_IFDEF_DEPTH) | |
| { error("'#IF' nested too deeply: increase #define MAX_IFDEF_DEPTH"); | |
| return; | |
| } | |
| ifdef_stack[ifdef_sp++]=ignoring_mode; | |
| if (ignoring_mode==0) | |
| { ignoring_mode=1-condition; | |
| started_ignoring=ifdef_sp; | |
| } | |
| return; | |
| case ENDIF_CODE: | |
| if (ifdef_sp==0) | |
| { error("'#ENDIF' without matching '#IF'"); return; } | |
| ignoring_mode=(ifdef_stack[--ifdef_sp])%2; | |
| return; | |
| case IFNOT_CODE: | |
| if (ifdef_sp==0) | |
| { error("'#IFNOT' without matching '#IF'"); return; } | |
| if (ifdef_stack[ifdef_sp-1]>=2) | |
| { error("Two '#IFNOT's in the same '#IF'"); return; } | |
| ifdef_stack[ifdef_sp-1]+=2; | |
| if (ignoring_mode==1) | |
| { if (ifdef_sp==started_ignoring) | |
| ignoring_mode=0; | |
| } | |
| else | |
| { ignoring_mode=1; | |
| } | |
| return; | |
| case END_CODE: | |
| endofpass_flag=1; | |
| if (trace_mode==1) printf("<end>\n"); | |
| if (ifdef_sp>0) | |
| { error("End of file reached inside '#IF...'"); } | |
| break; | |
| case OPENBLOCK_CODE: | |
| if (is_systemfile()==1) | |
| { strcpy(b,"Rep__"); | |
| word(b+5,2); | |
| i=find_symbol(b); | |
| if (i!=-1) | |
| { ignoring_routine=1; /* printf("Ignoring %s\n",b+5); */ break; } | |
| } | |
| word(b,2); | |
| ignoring_routine=0; debugged_routine=0; | |
| while ((offset%scale_factor)!=0) { byteout(0); offset++; } | |
| new_symbol(b,offset,1); | |
| if (trace_mode==1) printf("<Routine %d, '%s' begins at %04x; ", | |
| no_routines,b,offset); | |
| if (debugging_file==1) | |
| { debug_pass=2; | |
| write_debug_byte(11); | |
| write_debug_byte(no_routines/256); | |
| write_debug_byte(no_routines%256); | |
| write_routine_linenum(); write_debug_string(b); | |
| } | |
| lose_local_symbols(); | |
| no_locals= -1; no_knowns=0; | |
| for (i=0;i<16;i++) one_letter_locals[i]='\''; | |
| routine_starts_line=current_source_line(); | |
| no_routines++; | |
| if (no_routines>=MAX_ROUTINES) | |
| memoryerror("MAX_ROUTINES", MAX_ROUTINES); | |
| in_routine_flag=1; return_flag=0; | |
| word(b,3); if (strcmp(b,"*")==0) debugged_routine=1; | |
| while (word(b,(++no_locals)+3+debugged_routine),(b[0]!=0)) | |
| { if ( ((b[0]==',')&&(b[1]==0)) || ((b[0]==':')&&(b[1]==0))) | |
| { error("Expected local variable but found ',' or ':' \ | |
| (probably the ';' after the '[ ...' line was forgotten)"); | |
| break; | |
| } | |
| if ((b[0]=='s')&&(b[1]=='p')&&(b[2]==0)) | |
| { error("'sp' means the stack pointer, and can't be used \ | |
| as the name of a local variable"); break; | |
| } | |
| if (b[1]==0) one_letter_locals[no_locals]=b[0]; | |
| new_symbol(b,no_locals,3); | |
| if (debugging_file==1) write_debug_string(b); | |
| } | |
| if (debugging_file==1) { write_debug_byte(0); debug_pass=1; } | |
| byteout(no_locals); | |
| if (actual_version<5) | |
| { for (i=0; i<no_locals; i++) { byteout(0); byteout(0); } | |
| } | |
| if (trace_mode==1) printf("%d locals>\n",no_locals); | |
| if (no_locals>15) error("Routine has more than 15 local variables"); | |
| if ((no_routines==1)&&(pass_number==1)) | |
| { word(b,2); make_lower_case(b); | |
| if (strcmp(b,"main")!=0) | |
| { | |
| warning_named("Since it is defined before inclusion of the library, game-play \ | |
| will begin not at 'Main' but at the routine",b); | |
| } | |
| if (no_locals!=0) | |
| error("The earliest-defined routine is not allowed to have local variables"); | |
| } | |
| if (nowarnings_mode==0) | |
| for (i=0; i<16; i++) used_local_variable[i]=0; | |
| if ((withdebug_mode==1) || (debugged_routine==1)) | |
| { word(b,2); sprintf(sub_buffer,"print \"[%s\"",b); | |
| for (i=0; (i<no_locals)&&(i<3); i++) | |
| { word(b, 3+i+debugged_routine); | |
| sprintf(sub_buffer+strlen(sub_buffer), ", \", %s=\", %s", b, b); | |
| } | |
| sprintf(sub_buffer+strlen(sub_buffer), ", \"]^\""); | |
| stack_line(sub_buffer); | |
| } | |
| break; | |
| case CLOSEBLOCK_CODE: | |
| word(b,2); | |
| if (b[0]!=0) | |
| { if ((resobj_flag==0) || (strcmp(b,",")!=0)) | |
| { | |
| if (resobj_flag>0) | |
| error_named( | |
| "Expected ',' or ';' after ']' but found", | |
| parse_buffer); | |
| else | |
| error_named( | |
| "Expected ';' after ']' but found", | |
| parse_buffer); | |
| } | |
| } | |
| if (trace_mode==1) printf("<Routine ends>\n"); | |
| if (debugging_file==1) | |
| { debug_pass=2; | |
| write_debug_byte(14); | |
| write_debug_byte((no_routines-1)/256); | |
| write_debug_byte((no_routines-1)%256); | |
| write_re_linenum(); debug_pass=1; | |
| } | |
| if (return_flag==0) | |
| { if (resobj_flag==0) stack_line("@rtrue"); | |
| else stack_line("@rfalse"); | |
| } | |
| if (resobj_flag==1) resobj_flag=3; | |
| if (brace_sp>0) | |
| { error("Brace mismatch in previous routine"); | |
| brace_sp=0; | |
| } | |
| in_routine_flag=0; | |
| if ((nowarnings_mode==0)&&(pass_number==1)) | |
| { for (i=0; i<no_locals; i++) | |
| { if (used_local_variable[i]==0) | |
| { override_error_line = routine_starts_line; | |
| warning_named("Local variable unused:", | |
| (char *) (local_varname[i])); | |
| } | |
| } | |
| } | |
| break; | |
| case ABBREVIATE_CODE: | |
| i=2; | |
| while (1>0) | |
| { word(b,i); if (b[0]==0) break; textword(b,i); | |
| if (pass_number==1) | |
| { if (no_abbrevs==MAX_ABBREVS) | |
| { error("Too many abbreviations declared"); break; } | |
| if (almade_flag==1) | |
| { error("All abbreviations must be declared together"); | |
| break; | |
| } | |
| if (strlen(b)<2) | |
| { error_named("It's not worth abbreviating",b); break; } | |
| } | |
| strcpy((char *)abbreviations_at+no_abbrevs*MAX_ABBREV_LENGTH, b); | |
| word(b,i++); | |
| abbrev_mode=0; lower_strings_flag=1; | |
| abbrev_values[no_abbrevs]=constant_value(b); | |
| abbrev_quality[no_abbrevs++]=trans_length-2; | |
| abbrev_mode=1; lower_strings_flag=0; | |
| } | |
| break; | |
| case ATTRIBUTE_CODE: | |
| make_attribute(b); break; | |
| case CONSTANT_CODE: | |
| constant_made_flag=1; | |
| word(b,3); k=constant_value(b); | |
| word(b,2); | |
| IfPass2 | |
| { i=find_symbol(b); svals[i]=k; } | |
| else | |
| { if (constant_was_string==1) new_symbol(b,k,11); | |
| else new_symbol(b,k,8); | |
| } | |
| break; | |
| case LOWSTRING_CODE: | |
| word(b,3); | |
| lower_strings_flag=1; | |
| k=constant_value(b); | |
| lower_strings_flag=0; | |
| word(b,2); | |
| IfPass2 | |
| { i=find_symbol(b); | |
| } | |
| else | |
| { if (constant_was_string==1) new_symbol(b,k,11); | |
| else new_symbol(b,i,8); | |
| } | |
| break; | |
| case DEFAULT_CODE: | |
| word(b,3); | |
| IfPass2 | |
| { | |
| } | |
| else | |
| { i=constant_value(b); | |
| word(b,2); | |
| if (find_symbol(b)==-1) | |
| { if (constant_was_string==1) | |
| { error("Defaulted constants can't be strings"); return; } | |
| new_symbol(b,i,8); | |
| } | |
| } | |
| break; | |
| case STUB_CODE: | |
| i=0; | |
| IfPass2 { if (stub_flags[no_stubbed++]==1) i=1; } | |
| else | |
| { word(b,2); if (find_symbol(b)==-1) i=1; | |
| stub_flags[no_stubbed++]=i; | |
| } | |
| if (i==1) | |
| { | |
| word(b,3); i=constant_value(b); word(b,2); | |
| switch(i) | |
| { case 0: stack_sline("[ %s",b); stack_line("rfalse"); | |
| stack_line("]"); break; | |
| case 1: stack_sline("[ %s x1",b); stack_line("@store x1 0"); | |
| stack_line("rfalse"); stack_line("]"); break; | |
| case 2: stack_sline("[ %s x1 x2",b); stack_line("@store x1 0"); | |
| stack_line("@store x2 0"); | |
| stack_line("rfalse"); stack_line("]"); break; | |
| case 3: stack_sline("[ %s x1 x2 x3",b); | |
| stack_line("@store x1 0"); stack_line("@store x2 0"); | |
| stack_line("@store x3 0"); | |
| stack_line("rfalse"); stack_line("]"); break; | |
| default: | |
| error("Must specify 0 to 3 variables in 'stub' routine"); | |
| return; | |
| } | |
| } | |
| break; | |
| case DICTIONARY_CODE: | |
| obsolete_warning("use 'word' as a constant dictionary address"); | |
| textword(b,3); i=dictionary_add(b,4,0,0); | |
| word(b,2); | |
| IfPass2 | |
| { j=find_symbol(b); svals[j]=i; } else new_symbol(b,i,8); | |
| break; | |
| case FAKE_ACTION_CODE: | |
| make_fake_action(b); break; | |
| case GLOBAL_CODE: make_global(b,0); break; | |
| case ARRAY_CODE: make_global(b,1); break; | |
| case INCLUDE_CODE: | |
| word(b,3); | |
| if (b[0]!=0) | |
| error_named("Expected ';' after 'include <file>' but found",b); | |
| word(b,2); | |
| textword(b,2); | |
| if (b[0]!='>') load_sourcefile(b,0); | |
| else load_sourcefile(b+1,1); | |
| break; | |
| case NEARBY_CODE: make_object(b,1); break; | |
| case OBJECT_CODE: make_object(b,0); break; | |
| case CLASS_CODE: make_class(b); break; | |
| case PROPERTY_CODE: make_property(b); break; | |
| case RELEASE_CODE: | |
| word(b,2); release_number=constant_value(b); break; | |
| case SWITCHES_CODE: | |
| if (ignoreswitches_mode==0) | |
| { if ((pass_number==1)&&(constant_made_flag==1)) | |
| error("A 'switches' directive must \ | |
| must come before constant definitions"); | |
| word(b,2); switches(b,0); | |
| } | |
| break; | |
| case STATUSLINE_CODE: | |
| word(b,2); | |
| On_("score") { statusline_flag=0; break; } | |
| On_("time") { statusline_flag=1; break; } | |
| error_named("Expected 'score' or 'time' after 'statusline' but found", | |
| b); break; | |
| case SERIAL_CODE: | |
| textword(b,2); | |
| if (strlen(b)!=6) | |
| { error("The serial number must be a 6-digit date in double-quotes"); | |
| break; } | |
| for (i=0; i<6; i++) | |
| if (isdigit(b[i])==0) | |
| { error("The serial number must be a 6-digit date in double-quotes"); | |
| break; } | |
| strcpy(time_given,b); time_set=1; break; | |
| case SYSTEM_CODE: | |
| declare_systemfile(); break; | |
| case REPLACE_CODE: | |
| if (replace_sf(b)==1) break; | |
| sprintf(b, "Rep__"); | |
| word(b+5, 2); | |
| new_symbol(b, 0, 19); | |
| break; | |
| case VERB_CODE: make_verb(b); break; | |
| case EXTEND_CODE: extend_verb(b); break; | |
| case VERSION_CODE: | |
| if (override_version) break; | |
| word(b,2); actual_version=constant_value(b); | |
| if (actual_version==3) version_number=3; else version_number=5; | |
| if (version_number==3) scale_factor=2; else scale_factor=4; | |
| if ((actual_version==6)||(actual_version==8)) scale_factor=8; | |
| if (actual_version==7) extend_memory_map=1; | |
| if ((actual_version>=3)&&(actual_version<=8)) break; | |
| error("The version number must be in the range 3 to 8"); break; | |
| case TRACE_CODE: IfPass2 trace_mode=1; break; | |
| case NOTRACE_CODE: IfPass2 trace_mode=tracing_mode; break; | |
| case ETRACE_CODE: | |
| IfPass2 | |
| { word(b,2); if (b[0]==0) { etrace_mode=2; break; } | |
| if (strcmp(b,"full")==0) { etrace_mode=1; break; } | |
| error_named("Expected 'full' or nothing after 'etrace' but found", b); | |
| break; | |
| } | |
| case NOETRACE_CODE: IfPass2 etrace_mode=0; break; | |
| case BTRACE_CODE: trace_mode=1; break; | |
| case NOBTRACE_CODE: trace_mode=0; break; | |
| case LTRACE_CODE: IfPass2 ltrace_mode=1; break; | |
| case NOLTRACE_CODE: IfPass2 ltrace_mode=listing_mode; break; | |
| case ATRACE_CODE: IfPass2 ltrace_mode=2; break; | |
| case NOATRACE_CODE: IfPass2 ltrace_mode=listing_mode; break; | |
| case LISTSYMBOLS_CODE: IfPass2 list_symbols(); break; | |
| case LISTOBJECTS_CODE: IfPass2 list_object_tree(); break; | |
| case LISTVERBS_CODE: IfPass2 list_verb_table(); break; | |
| case LISTDICT_CODE: IfPass2 show_dictionary(); break; | |
| default: error("Internal error - unknown directive code"); | |
| } | |
| return; | |
| } | |
| extern void init_asm_vars(void) | |
| { | |
| lower_strings_flag = 0; | |
| ifdef_sp = 0; | |
| started_ignoring = 0; | |
| a_from_one = 0; | |
| line_done_flag = 0; | |
| lmoved_flag = 0; | |
| constant_made_flag = 0; | |
| pa_forwards = NULL; | |
| } | |
Xet Storage Details
- Size:
- 38.4 kB
- Xet hash:
- 570ade70cbb9f68ed6346b14d3bf88ef8002ab860b2e945849bceb3df3baa8b4
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.