inform/ 755 0 0 0 10322327146 120145ustar00ccollinsccollinsinform/arrays.c 644 0 0 55341 10274416413 13513ustar00ccollinsccollins/* ------------------------------------------------------------------------- */ /* "arrays" : Parses array declarations and constructs arrays from them; */ /* likewise global variables, which are in some ways a */ /* simpler form of the same thing. */ /* */ /* Part of Inform 6.30 */ /* copyright (c) Graham Nelson 1993 - 2004 */ /* */ /* ------------------------------------------------------------------------- */ #include "header.h" /* ------------------------------------------------------------------------- */ /* Arrays defined below: */ /* */ /* int dynamic_array_area[] Initial values for the bytes of */ /* the dynamic array area */ /* int32 global_initial_value[n] The initialised value of the nth */ /* global variable (counting 0 - 239) */ /* */ /* The "dynamic array area" is the Z-machine area holding the current */ /* values of the global variables (in 240x2 = 480 bytes) followed by any */ /* (dynamic) arrays which may be defined. Owing to a poor choice of name */ /* some years ago, this is also called the "static data area", which is */ /* why the memory setting for its maximum extent is "MAX_STATIC_DATA". */ /* */ /* In Glulx, that 240 is changed to MAX_GLOBAL_VAR_NUMBER, and we take */ /* correspondingly more space for the globals. This *really* ought to be */ /* split into two segments. */ /* ------------------------------------------------------------------------- */ int *dynamic_array_area; /* See above */ int32 *global_initial_value; int no_globals; /* Number of global variables used by the programmer (Inform itself uses the top seven -- but these do not count) */ /* In Glulx, Inform uses the bottom ten. */ int dynamic_array_area_size; /* Size in bytes */ int no_arrays; int32 *array_symbols; int *array_sizes, *array_types; static int array_entry_size, /* 1 for byte array, 2 for word array */ array_base; /* Offset in dynamic array area of the array being constructed. During the same time, dynamic_array_area_size is the offset of the initial entry in the array: so for "table" and "string" arrays, these numbers are different (by 2 and 1 bytes resp) */ /* In Glulx, of course, that will be 4 instead of 2. */ extern void finish_array(int32 i) { /* Write the array size into the 0th byte/word of the array, if it's a "table" or "string" array */ if (!glulx_mode) { if (array_base!=dynamic_array_area_size) { if (dynamic_array_area_size-array_base==2) { dynamic_array_area[array_base] = i/256; dynamic_array_area[array_base+1] = i%256; } else { if (i>=256) error("A 'string' array can have at most 256 entries"); dynamic_array_area[array_base] = i; } } } else { if (array_base!=dynamic_array_area_size) { if (dynamic_array_area_size-array_base==4) { dynamic_array_area[array_base] = (i >> 24) & 0xFF; dynamic_array_area[array_base+1] = (i >> 16) & 0xFF; dynamic_array_area[array_base+2] = (i >> 8) & 0xFF; dynamic_array_area[array_base+3] = (i) & 0xFF; } else { if (i>=256) error("A 'string' array can have at most 256 entries"); dynamic_array_area[array_base] = i; } } } /* Move on the dynamic array size so that it now points to the next available free space */ dynamic_array_area_size += i*array_entry_size; } extern void array_entry(int32 i, assembly_operand VAL) { if (!glulx_mode) { /* Array entry i (initial entry has i=0) is set to Z-machine value j */ if (dynamic_array_area_size+i*array_entry_size >= MAX_STATIC_DATA) memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); if (array_entry_size==1) { dynamic_array_area[dynamic_array_area_size+i] = (VAL.value)%256; if (VAL.marker != 0) error("Entries in byte arrays and strings must be known constants"); /* If the entry is too large for a byte array, issue a warning and truncate the value */ else if (VAL.value >= 256) warning("Entry in '->', 'string' or 'buffer' array not in range 0 to 255"); } else { dynamic_array_area[dynamic_array_area_size + 2*i] = (VAL.value)/256; dynamic_array_area[dynamic_array_area_size + 2*i+1] = (VAL.value)%256; if (VAL.marker != 0) backpatch_zmachine(VAL.marker, DYNAMIC_ARRAY_ZA, dynamic_array_area_size + 2*i); } } else { /* Array entry i (initial entry has i=0) is set to value j */ if (dynamic_array_area_size+i*array_entry_size >= MAX_STATIC_DATA) memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); if (array_entry_size==1) { dynamic_array_area[dynamic_array_area_size+i] = (VAL.value) & 0xFF; if (VAL.marker != 0) error("Entries in byte arrays and strings must be known constants"); /* If the entry is too large for a byte array, issue a warning and truncate the value */ else if (VAL.value >= 256) warning("Entry in '->', 'string' or 'buffer' array not in range 0 to 255"); } else if (array_entry_size==4) { dynamic_array_area[dynamic_array_area_size + 4*i] = (VAL.value >> 24) & 0xFF; dynamic_array_area[dynamic_array_area_size + 4*i+1] = (VAL.value >> 16) & 0xFF; dynamic_array_area[dynamic_array_area_size + 4*i+2] = (VAL.value >> 8) & 0xFF; dynamic_array_area[dynamic_array_area_size + 4*i+3] = (VAL.value) & 0xFF; if (VAL.marker != 0) backpatch_zmachine(VAL.marker, ARRAY_ZA, dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES + 4*i); } else { error("Somehow created an array of shorts"); } } } /* ------------------------------------------------------------------------- */ /* Global and Array directives. */ /* */ /* Global | */ /* | = */ /* | */ /* */ /* Array */ /* */ /* where an array specification is: */ /* */ /* | -> | */ /* | --> | ... */ /* | string | [ [,] [;] ... ]; */ /* | table */ /* */ /* ------------------------------------------------------------------------- */ extern void set_variable_value(int i, int32 v) { global_initial_value[i]=v; } /* There are four ways to initialise arrays: */ #define UNSPECIFIED_AI -1 #define NULLS_AI 0 #define DATA_AI 1 #define ASCII_AI 2 #define BRACKET_AI 3 extern void make_global(int array_flag, int name_only) { /* array_flag is TRUE for an Array directive, FALSE for a Global; name_only is only TRUE for parsing an imported variable name, so array_flag is always FALSE in that case. */ int32 i; /* char *varname; */ int array_type, data_type; assembly_operand AO; directive_keywords.enabled = FALSE; get_next_token(); i = token_value; /* varname = token_text; */ if (!glulx_mode) { if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T) && (svals[i] >= LOWEST_SYSTEM_VAR_NUMBER)) goto RedefinitionOfSystemVar; } else { if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T)) goto RedefinitionOfSystemVar; } if ((token_type != SYMBOL_TT) || (!(sflags[i] & UNKNOWN_SFLAG))) { if (array_flag) ebf_error("new array name", token_text); else ebf_error("new global variable name", token_text); panic_mode_error_recovery(); return; } if ((!array_flag) && (sflags[i] & USED_SFLAG)) error_named("Variable must be defined before use:", token_text); if (array_flag) { if (!glulx_mode) assign_symbol(i, dynamic_array_area_size, ARRAY_T); else assign_symbol(i, dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES, ARRAY_T); if (no_arrays == MAX_ARRAYS) memoryerror("MAX_ARRAYS", MAX_ARRAYS); array_symbols[no_arrays] = i; } else { if (!glulx_mode && no_globals==233) { error("All 233 global variables already declared"); panic_mode_error_recovery(); return; } if (glulx_mode && no_globals==MAX_GLOBAL_VARIABLES) { error_numbered("All global variables already declared; max is", MAX_GLOBAL_VARIABLES); panic_mode_error_recovery(); return; } variable_tokens[MAX_LOCAL_VARIABLES+no_globals] = i; assign_symbol(i, MAX_LOCAL_VARIABLES+no_globals, GLOBAL_VARIABLE_T); variable_tokens[svals[i]] = i; if (debugfile_switch) { write_debug_byte(GLOBAL_DBR); write_debug_byte(no_globals); write_debug_string(token_text); } if (name_only) import_symbol(i); else global_initial_value[no_globals++]=0; } directive_keywords.enabled = TRUE; RedefinitionOfSystemVar: if (name_only) return; get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) { if (array_flag) ebf_error("array definition", token_text); put_token_back(); return; } if (!array_flag) { if ((token_type == SEP_TT) && (token_value == SETEQUALS_SEP)) { AO = parse_expression(CONSTANT_CONTEXT); if (!glulx_mode) { if (AO.marker != 0) backpatch_zmachine(AO.marker, DYNAMIC_ARRAY_ZA, 2*(no_globals-1)); } else { if (AO.marker != 0) backpatch_zmachine(AO.marker, GLOBALVAR_ZA, 4*(no_globals-1)); } global_initial_value[no_globals-1] = AO.value; return; } obsolete_warning("more modern to use 'Array', not 'Global'"); if (!glulx_mode) { backpatch_zmachine(ARRAY_MV, DYNAMIC_ARRAY_ZA, 2*(no_globals-1)); global_initial_value[no_globals-1] = dynamic_array_area_size+variables_offset; } else { backpatch_zmachine(ARRAY_MV, GLOBALVAR_ZA, 4*(no_globals-1)); global_initial_value[no_globals-1] = dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES; } } array_type = BYTE_ARRAY; data_type = UNSPECIFIED_AI; if ((!array_flag) && ((token_type==DIR_KEYWORD_TT)&&(token_value==DATA_DK))) data_type=NULLS_AI; else if ((!array_flag) && ((token_type==DIR_KEYWORD_TT)&&(token_value==INITIAL_DK))) data_type=DATA_AI; else if ((!array_flag) && ((token_type==DIR_KEYWORD_TT)&&(token_value==INITSTR_DK))) data_type=ASCII_AI; else if ((token_type==SEP_TT)&&(token_value==ARROW_SEP)) array_type = BYTE_ARRAY; else if ((token_type==SEP_TT)&&(token_value==DARROW_SEP)) array_type = WORD_ARRAY; else if ((token_type==DIR_KEYWORD_TT)&&(token_value==STRING_DK)) array_type = STRING_ARRAY; else if ((token_type==DIR_KEYWORD_TT)&&(token_value==TABLE_DK)) array_type = TABLE_ARRAY; else if ((token_type==DIR_KEYWORD_TT)&&(token_value==BUFFER_DK)) array_type = BUFFER_ARRAY; else { if (array_flag) ebf_error("'->', '-->', 'string', 'table' or 'buffer'", token_text); else ebf_error("'=', '->', '-->', 'string', 'table' or 'buffer'", token_text); panic_mode_error_recovery(); return; } array_entry_size=1; if ((array_type==WORD_ARRAY) || (array_type==TABLE_ARRAY)) array_entry_size=WORDSIZE; get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) { error("No array size or initial values given"); put_token_back(); return; } switch(data_type) { case UNSPECIFIED_AI: if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP)) data_type = BRACKET_AI; else { data_type = NULLS_AI; if (token_type == DQ_TT) data_type = ASCII_AI; get_next_token(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) data_type = DATA_AI; put_token_back(); put_token_back(); } break; case NULLS_AI: obsolete_warning("use '->' instead of 'data'"); break; case DATA_AI: obsolete_warning("use '->' instead of 'initial'"); break; case ASCII_AI: obsolete_warning("use '->' instead of 'initstr'"); break; } array_base = dynamic_array_area_size; /* Leave room to write the array size in later, if string/table array */ if ((array_type==STRING_ARRAY) || (array_type==TABLE_ARRAY)) dynamic_array_area_size += array_entry_size; if (array_type==BUFFER_ARRAY) dynamic_array_area_size += WORDSIZE; array_types[no_arrays] = array_type; switch(data_type) { case NULLS_AI: AO = parse_expression(CONSTANT_CONTEXT); CalculatedArraySize: if (module_switch && (AO.marker != 0)) { error("Array sizes must be known now, not externally defined"); break; } if (!glulx_mode) { if ((AO.value <= 0) || (AO.value >= 32768)) { error("An array must have between 1 and 32767 entries"); AO.value = 1; } } else { if (AO.value <= 0 || (AO.value & 0x80000000)) { error("An array may not have 0 or fewer entries"); AO.value = 1; } } { for (i=0; i= 256) { error("Unicode characters beyond Latin-1 are not yet supported in Glulx"); } else { chars.value = unicode; } } else /* Z-code */ { zscii = unicode_to_zscii(unicode); if ((zscii != 5) && (zscii < 0x100)) chars.value = zscii; else { unicode_char_error("Character can only be used if declared in \ advance as part of 'Zcharacter table':", unicode); chars.value = '?'; } } chars.marker = 0; set_constant_ot(&chars); array_entry(i, chars); } } break; case BRACKET_AI: /* In this case the array is initialised to the sequence of constant values given over a whole range of compiler-lines, between square brackets [ and ] */ i = 0; while (TRUE) { get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) continue; if ((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)) break; if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP)) { /* Minimal error recovery: we assume that a ] has been missed, and the programmer is now starting a new routine */ ebf_error("']'", token_text); put_token_back(); break; } put_token_back(); array_entry(i, parse_expression(ARRAY_CONTEXT)); i++; } } finish_array(i); if ((array_type==BYTE_ARRAY) || (array_type==WORD_ARRAY)) i--; if (array_type==BUFFER_ARRAY) i+=WORDSIZE-1; array_sizes[no_arrays++] = i; } extern int32 begin_table_array(void) { /* The "box" statement needs to be able to construct (static) table arrays of strings like this */ array_base = dynamic_array_area_size; array_entry_size = WORDSIZE; /* Leave room to write the array size in later */ dynamic_array_area_size += array_entry_size; if (!glulx_mode) return array_base; else return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES; } extern int32 begin_word_array(void) { /* The "random(a, b, ...)" function needs to be able to construct (static) word arrays like this */ array_base = dynamic_array_area_size; array_entry_size = WORDSIZE; if (!glulx_mode) return array_base; else return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES; } /* ========================================================================= */ /* Data structure management routines */ /* ------------------------------------------------------------------------- */ extern void init_arrays_vars(void) { dynamic_array_area = NULL; global_initial_value = NULL; array_sizes = NULL; array_symbols = NULL; array_types = NULL; } extern void arrays_begin_pass(void) { no_arrays = 0; if (!glulx_mode) no_globals=0; else no_globals=11; dynamic_array_area_size = WORDSIZE * MAX_GLOBAL_VARIABLES; } extern void arrays_allocate_arrays(void) { dynamic_array_area = my_calloc(sizeof(int), MAX_STATIC_DATA, "static data"); array_sizes = my_calloc(sizeof(int), MAX_ARRAYS, "array sizes"); array_types = my_calloc(sizeof(int), MAX_ARRAYS, "array types"); array_symbols = my_calloc(sizeof(int32), MAX_ARRAYS, "array symbols"); global_initial_value = my_calloc(sizeof(int32), MAX_GLOBAL_VARIABLES, "global values"); } extern void arrays_free_arrays(void) { my_free(&dynamic_array_area, "static data"); my_free(&global_initial_value, "global values"); my_free(&array_sizes, "array sizes"); my_free(&array_types, "array sizes"); my_free(&array_symbols, "array sizes"); } /* ========================================================================= */ } array_type = BYTE_ARRAY; data_type = UNSPECIFIED_AI; if ((!array_flag) && ((token_type==DIR_KEYWORD_TT)&&(token_value==DATA_DK))) data_type=NULLS_AI; else if ((!array_flag) && ((token_type==DIR_KEYWORD_TT)&&(token_value==inform/asm.c 644 0 0 314021 10274416413 13003ustar00ccollinsccollins/* ------------------------------------------------------------------------- */ /* "asm" : The Inform assembler */ /* */ /* Part of Inform 6.30 */ /* copyright (c) Graham Nelson 1993 - 2004 */ /* */ /* ------------------------------------------------------------------------- */ #include "header.h" uchar *zcode_holding_area; /* Area holding code yet to be transferred to either zcode_area or temp file no 1 */ uchar *zcode_markers; /* Bytes holding marker values for this code */ static int zcode_ha_size; /* Number of bytes in holding area */ memory_block zcode_area; /* Block to hold assembled code (if temporary files are not being used) */ int32 zmachine_pc; /* PC position of assembly (byte offset from start of Z-code area) */ int32 no_instructions; /* Number of instructions assembled */ int execution_never_reaches_here, /* TRUE if the current PC value in the code area cannot be reached: e.g. if the previous instruction was a "quit" opcode and no label is set to here */ next_label, /* Used to count the labels created all over Inform in current routine, from 0 */ next_sequence_point; /* Likewise, for sequence points */ int no_sequence_points; /* Kept for statistics purposes only */ static int label_moved_error_already_given; /* When one label has moved, all subsequent ones probably have too, and this flag suppresses the runaway chain of error messages which would otherwise result */ int sequence_point_follows; /* Will the next instruction assembled */ /* be at a sequence point in the routine? */ dbgl debug_line_ref; /* Source code ref of current statement */ int32 *variable_tokens; /* The allocated size is (MAX_LOCAL_VARIABLES + MAX_GLOBAL_VARIABLES). The entries MAX_LOCAL_VARIABLES and up give the symbol table index for the names of the global variables */ int *variable_usage; /* TRUE if referred to, FALSE otherwise */ assembly_instruction AI; /* A structure used to hold the full specification of a single Z-code instruction: effectively this is the input to the routine assemble_instruction() */ static char opcode_syntax_string[128]; /* Text buffer holding the correct syntax for an opcode: used to produce helpful assembler error messages */ static int routine_locals; /* The number of local variables used by the routine currently being compiled */ static int32 routine_start_pc; int32 *named_routine_symbols; static void transfer_routine_z(void); static void transfer_routine_g(void); /* ------------------------------------------------------------------------- */ /* Label data */ /* ------------------------------------------------------------------------- */ static int first_label, last_label; static int32 *label_offsets; /* Double-linked list of label offsets */ static int *label_next, /* (i.e. zmachine_pc values) in PC order */ *label_prev; static int32 *label_symbols; /* Symbol numbers if defined in source */ static int *sequence_point_labels; /* Label numbers for each */ static dbgl *sequence_point_refs; /* Source code references for each */ /* (used for making debugging file) */ static void set_label_offset(int label, int32 offset) { if (label >= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS); label_offsets[label] = offset; if (last_label == -1) { label_prev[label] = -1; first_label = label; } else { label_prev[label] = last_label; label_next[last_label] = label; } last_label = label; label_next[label] = -1; label_symbols[label] = -1; } /* ------------------------------------------------------------------------- */ /* Useful tool for building operands */ /* ------------------------------------------------------------------------- */ extern void set_constant_ot(assembly_operand *AO) { if (!glulx_mode) { if (AO->value >= 0 && AO->value <= 255) AO->type = SHORT_CONSTANT_OT; else AO->type = LONG_CONSTANT_OT; } else { if (AO->value == 0) AO->type = ZEROCONSTANT_OT; else if (AO->value >= -0x80 && AO->value < 0x80) AO->type = BYTECONSTANT_OT; else if (AO->value >= -0x8000 && AO->value < 0x8000) AO->type = HALFCONSTANT_OT; else AO->type = CONSTANT_OT; } } extern int is_constant_ot(int otval) { if (!glulx_mode) { return ((otval == LONG_CONSTANT_OT) || (otval == SHORT_CONSTANT_OT)); } else { return ((otval == CONSTANT_OT) || (otval == HALFCONSTANT_OT) || (otval == BYTECONSTANT_OT) || (otval == ZEROCONSTANT_OT)); } } extern int is_variable_ot(int otval) { if (!glulx_mode) { return (otval == VARIABLE_OT); } else { return ((otval == LOCALVAR_OT) || (otval == GLOBALVAR_OT)); } } /* ------------------------------------------------------------------------- */ /* Used in printing assembly traces */ /* ------------------------------------------------------------------------- */ extern char *variable_name(int32 i) { if (i==0) return("sp"); if (i"); return; } printf("%d", o.value); } static void print_operand_g(assembly_operand o) { switch (o.type) { case EXPRESSION_OT: printf("expr_"); break; case CONSTANT_OT: printf("long_"); break; case HALFCONSTANT_OT: printf("short_"); break; case BYTECONSTANT_OT: printf("byte_"); break; case ZEROCONSTANT_OT: printf("zero_"); return; case DEREFERENCE_OT: printf("*"); break; case GLOBALVAR_OT: printf("%s (global_%d)", variable_name(o.value), o.value); return; case LOCALVAR_OT: if (o.value == 0) printf("stackptr"); else printf("%s (local_%d)", variable_name(o.value), o.value-1); return; case SYSFUN_OT: printf("sysfun_"); break; case OMITTED_OT: printf(""); return; /* case STACK_OT: printf(""); return; */ default: printf("???_"); break; } printf("%d", o.value); } extern void print_operand(assembly_operand o) { if (!glulx_mode) print_operand_z(o); else print_operand_g(o); } /* ------------------------------------------------------------------------- */ /* Writing bytes to the code area */ /* ------------------------------------------------------------------------- */ static void byteout(int32 i, int mv) { if (zcode_ha_size >= MAX_ZCODE_SIZE) memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE); zcode_markers[zcode_ha_size] = (uchar) mv; zcode_holding_area[zcode_ha_size++] = (uchar) i; zmachine_pc++; } /* ------------------------------------------------------------------------- */ /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */ /* And of the however-many-there-are Glulx opcode */ /* ------------------------------------------------------------------------- */ typedef struct opcodez { uchar *name; /* Lower case standard name */ int version1; /* Valid from this version number... */ int version2; /* ...until this one (or forever if this is 0) */ int extension; /* In later versions, see this line in extension table: if -1, the opcode is illegal in later versions */ int code; /* Opcode number within its operand-number block */ int flags; /* Flags (see below) */ int op_rules; /* Any unusual operand rule applying (see below) */ int flags2_set; /* If not zero, set this bit in Flags 2 in the header of any game using the opcode */ int no; /* Number of operands (see below) */ } opcodez; typedef struct opcodeg { uchar *name; /* Lower case standard name */ int32 code; /* Opcode number */ int flags; /* Flags (see below) */ int op_rules; /* Any unusual operand rule applying (see below) */ int no; /* Number of operands */ } opcodeg; /* Flags which can be set */ #define St 1 /* Store */ #define Br 2 /* Branch */ #define Rf 4 /* "Return flag": execution never continues after this opcode (e.g., is a return or unconditional jump) */ #define St2 8 /* Store2 (second-to-last operand is store (Glulx)) */ /* Codes for any unusual operand assembly rules */ #define VARIAB 1 /* First operand expected to be a variable name and assembled to a short constant: the variable number */ #define TEXT 2 /* One text operand, to be Z-encoded into the program */ #define LABEL 3 /* One operand, a label, given as long constant offset */ #define CALL 4 /* First operand is name of a routine, to be assembled as long constant (the routine's packed address): as if the name were prefixed by #r$ */ /* Codes for the number of operands */ #define TWO 1 /* 2 (with certain types of operand, compiled as VAR) */ #define VAR 2 /* 0 to 4 */ #define VAR_LONG 3 /* 0 to 8 */ #define ONE 4 /* 1 */ #define ZERO 5 /* 0 */ #define EXT 6 /* Extended opcode set VAR: 0 to 4 */ #define EXT_LONG 7 /* Extended: 0 to 8 (not used by the canonical opcodes) */ static opcodez opcodes_table_z[] = { /* Opcodes introduced in Version 3 */ /* 0 */ { (uchar *) "je", 3, 0, -1, 0x01, Br, 0, 0, TWO }, /* 1 */ { (uchar *) "jl", 3, 0, -1, 0x02, Br, 0, 0, TWO }, /* 2 */ { (uchar *) "jg", 3, 0, -1, 0x03, Br, 0, 0, TWO }, /* 3 */ { (uchar *) "dec_chk", 3, 0, -1, 0x04, Br, VARIAB, 0, TWO }, /* 4 */ { (uchar *) "inc_chk", 3, 0, -1, 0x05, Br, VARIAB, 0, TWO }, /* 5 */ { (uchar *) "jin", 3, 0, -1, 0x06, Br, 0, 0, TWO }, /* 6 */ { (uchar *) "test", 3, 0, -1, 0x07, Br, 0, 0, TWO }, /* 7 */ { (uchar *) "or", 3, 0, -1, 0x08, St, 0, 0, TWO }, /* 8 */ { (uchar *) "and", 3, 0, -1, 0x09, St, 0, 0, TWO }, /* 9 */ { (uchar *) "test_attr", 3, 0, -1, 0x0A, Br, 0, 0, TWO }, /* 10 */ {(uchar *) "set_attr", 3, 0, -1, 0x0B, 0, 0, 0, TWO }, /* 11 */ {(uchar *) "clear_attr", 3, 0, -1, 0x0C, 0, 0, 0, TWO }, /* 12 */ {(uchar *) "store", 3, 0, -1, 0x0D, 0, VARIAB, 0, TWO }, /* 13 */ {(uchar *) "insert_obj", 3, 0, -1, 0x0E, 0, 0, 0, TWO }, /* 14 */ {(uchar *) "loadw", 3, 0, -1, 0x0F, St, 0, 0, TWO }, /* 15 */ {(uchar *) "loadb", 3, 0, -1, 0x10, St, 0, 0, TWO }, /* 16 */ {(uchar *) "get_prop", 3, 0, -1, 0x11, St, 0, 0, TWO }, /* 17 */ {(uchar *) "get_prop_addr", 3, 0, -1, 0x12, St, 0, 0, TWO }, /* 18 */ {(uchar *) "get_next_prop", 3, 0, -1, 0x13, St, 0, 0, TWO }, /* 19 */ {(uchar *) "add", 3, 0, -1, 0x14, St, 0, 0, TWO }, /* 20 */ {(uchar *) "sub", 3, 0, -1, 0x15, St, 0, 0, TWO }, /* 21 */ {(uchar *) "mul", 3, 0, -1, 0x16, St, 0, 0, TWO }, /* 22 */ {(uchar *) "div", 3, 0, -1, 0x17, St, 0, 0, TWO }, /* 23 */ {(uchar *) "mod", 3, 0, -1, 0x18, St, 0, 0, TWO }, /* 24 */ {(uchar *) "call", 3, 0, -1, 0x20, St, CALL, 0, VAR }, /* 25 */ {(uchar *) "storew", 3, 0, -1, 0x21, 0, 0, 0, VAR }, /* 26 */ {(uchar *) "storeb", 3, 0, -1, 0x22, 0, 0, 0, VAR }, /* 27 */ {(uchar *) "put_prop", 3, 0, -1, 0x23, 0, 0, 0, VAR }, /* This is the version of "read" called "sread" internally: */ /* 28 */ {(uchar *) "read", 3, 0, -1, 0x24, 0, 0, 0, VAR }, /* 29 */ {(uchar *) "print_char", 3, 0, -1, 0x25, 0, 0, 0, VAR }, /* 30 */ {(uchar *) "print_num", 3, 0, -1, 0x26, 0, 0, 0, VAR }, /* 31 */ {(uchar *) "random", 3, 0, -1, 0x27, St, 0, 0, VAR }, /* 32 */ {(uchar *) "push", 3, 0, -1, 0x28, 0, 0, 0, VAR }, /* 33 */ {(uchar *) "pull", 3, 5, 6, 0x29, 0, VARIAB, 0, VAR }, /* 34 */ {(uchar *) "split_window", 3, 0, -1, 0x2A, 0, 0, 0, VAR }, /* 35 */ {(uchar *) "set_window", 3, 0, -1, 0x2B, 0, 0, 0, VAR }, /* 36 */ {(uchar *) "output_stream", 3, 0, -1, 0x33, 0, 0, 0, VAR }, /* 37 */ {(uchar *) "input_stream", 3, 0, -1, 0x34, 0, 0, 0, VAR }, /* 38 */ {(uchar *) "sound_effect", 3, 0, -1, 0x35, 0, 0, 7, VAR }, /* 39 */ {(uchar *) "jz", 3, 0, -1, 0x00, Br, 0, 0, ONE }, /* 40 */ {(uchar *) "get_sibling", 3, 0, -1, 0x01, St+Br, 0, 0, ONE }, /* 41 */ {(uchar *) "get_child", 3, 0, -1, 0x02, St+Br, 0, 0, ONE }, /* 42 */ {(uchar *) "get_parent", 3, 0, -1, 0x03, St, 0, 0, ONE }, /* 43 */ {(uchar *) "get_prop_len", 3, 0, -1, 0x04, St, 0, 0, ONE }, /* 44 */ {(uchar *) "inc", 3, 0, -1, 0x05, 0, VARIAB, 0, ONE }, /* 45 */ {(uchar *) "dec", 3, 0, -1, 0x06, 0, VARIAB, 0, ONE }, /* 46 */ {(uchar *) "print_addr", 3, 0, -1, 0x07, 0, 0, 0, ONE }, /* 47 */ {(uchar *) "remove_obj", 3, 0, -1, 0x09, 0, 0, 0, ONE }, /* 48 */ {(uchar *) "print_obj", 3, 0, -1, 0x0A, 0, 0, 0, ONE }, /* 49 */ {(uchar *) "ret", 3, 0, -1, 0x0B, Rf, 0, 0, ONE }, /* 50 */ {(uchar *) "jump", 3, 0, -1, 0x0C, Rf, LABEL, 0, ONE }, /* 51 */ {(uchar *) "print_paddr", 3, 0, -1, 0x0D, 0, 0, 0, ONE }, /* 52 */ {(uchar *) "load", 3, 0, -1, 0x0E, St, VARIAB, 0, ONE }, /* 53 */ {(uchar *) "not", 3, 3, 0, 0x0F, St, 0, 0, ONE }, /* 54 */ {(uchar *) "rtrue", 3, 0, -1, 0x00, Rf, 0, 0,ZERO }, /* 55 */ {(uchar *) "rfalse", 3, 0, -1, 0x01, Rf, 0, 0,ZERO }, /* 56 */ {(uchar *) "print", 3, 0, -1, 0x02, 0, TEXT, 0,ZERO }, /* 57 */ {(uchar *) "print_ret", 3, 0, -1, 0x03, Rf, TEXT, 0,ZERO }, /* 58 */ {(uchar *) "nop", 3, 0, -1, 0x04, 0, 0, 0,ZERO }, /* 59 */ {(uchar *) "save", 3, 3, 1, 0x05, Br, 0, 0,ZERO }, /* 60 */ {(uchar *) "restore", 3, 3, 2, 0x06, Br, 0, 0,ZERO }, /* 61 */ {(uchar *) "restart", 3, 0, -1, 0x07, 0, 0, 0,ZERO }, /* 62 */ {(uchar *) "ret_popped", 3, 0, -1, 0x08, Rf, 0, 0,ZERO }, /* 63 */ {(uchar *) "pop", 3, 4, -1, 0x09, 0, 0, 0,ZERO }, /* 64 */ {(uchar *) "quit", 3, 0, -1, 0x0A, Rf, 0, 0,ZERO }, /* 65 */ {(uchar *) "new_line", 3, 0, -1, 0x0B, 0, 0, 0,ZERO }, /* 66 */ {(uchar *) "show_status", 3, 3, -1, 0x0C, 0, 0, 0,ZERO }, /* 67 */ {(uchar *) "verify", 3, 0, -1, 0x0D, Br, 0, 0,ZERO }, /* Opcodes introduced in Version 4 */ /* 68 */ {(uchar *) "call_2s", 4, 0, -1, 0x19, St, CALL, 0, TWO }, /* 69 */ {(uchar *) "call_vs", 4, 0, -1, 0x20, St, CALL, 0, VAR }, /* This is the version of "read" called "aread" internally: */ /* 70 */ {(uchar *) "read", 4, 0, -1, 0x24, St, 0, 0, VAR }, /* 71 */ {(uchar *) "call_vs2", 4, 0, -1, 0x2C, St, CALL, 0, VAR_LONG }, /* 72 */ {(uchar *) "erase_window", 4, 0, -1, 0x2D, 0, 0, 0, VAR }, /* 73 */ {(uchar *) "erase_line", 4, 0, -1, 0x2E, 0, 0, 0, VAR }, /* 74 */ {(uchar *) "set_cursor", 4, 0, -1, 0x2F, 0, 0, 0, VAR }, /* 75 */ {(uchar *) "get_cursor", 4, 0, -1, 0x30, 0, 0, 0, VAR }, /* 76 */ {(uchar *) "set_text_style", 4, 0, -1, 0x31, 0, 0, 0, VAR }, /* 77 */ {(uchar *) "buffer_mode", 4, 0, -1, 0x32, 0, 0, 0, VAR }, /* 78 */ {(uchar *) "read_char", 4, 0, -1, 0x36, St, 0, 0, VAR }, /* 79 */ {(uchar *) "scan_table", 4, 0, -1, 0x37, St+Br, 0, 0, VAR }, /* 80 */ {(uchar *) "call_1s", 4, 0, -1, 0x08, St, CALL, 0, ONE }, /* Opcodes introduced in Version 5 */ /* 81 */ {(uchar *) "call_2n", 5, 0, -1, 0x1a, 0, CALL, 0, TWO }, /* 82 */ {(uchar *) "set_colour", 5, 0, -1, 0x1b, 0, 0, 6, TWO }, /* 83 */ {(uchar *) "throw", 5, 0, -1, 0x1c, 0, 0, 0, TWO }, /* 84 */ {(uchar *) "call_vn", 5, 0, -1, 0x39, 0, CALL, 0, VAR }, /* 85 */ {(uchar *) "call_vn2", 5, 0, -1, 0x3a, 0, CALL, 0, VAR_LONG }, /* 86 */ {(uchar *) "tokenise", 5, 0, -1, 0x3b, 0, 0, 0, VAR }, /* 87 */ {(uchar *) "encode_text", 5, 0, -1, 0x3c, 0, 0, 0, VAR }, /* 88 */ {(uchar *) "copy_table", 5, 0, -1, 0x3d, 0, 0, 0, VAR }, /* 89 */ {(uchar *) "print_table", 5, 0, -1, 0x3e, 0, 0, 0, VAR }, /* 90 */ {(uchar *) "check_arg_count", 5, 0, -1, 0x3f, Br, 0, 0, VAR }, /* 91 */ {(uchar *) "call_1n", 5, 0, -1, 0x0F, 0, CALL, 0, ONE }, /* 92 */ {(uchar *) "catch", 5, 0, -1, 0x09, St, 0, 0, ZERO }, /* 93 */ {(uchar *) "piracy", 5, 0, -1, 0x0F, Br, 0, 0, ZERO }, /* 94 */ {(uchar *) "log_shift", 5, 0, -1, 0x02, St, 0, 0, EXT }, /* 95 */ {(uchar *) "art_shift", 5, 0, -1, 0x03, St, 0, 0, EXT }, /* 96 */ {(uchar *) "set_font", 5, 0, -1, 0x04, St, 0, 0, EXT }, /* 97 */ {(uchar *) "save_undo", 5, 0, -1, 0x09, St, 0, 4, EXT }, /* 98 */ {(uchar *) "restore_undo", 5, 0, -1, 0x0A, St, 0, 4, EXT }, /* Opcodes introduced in Version 6 */ /* 99 */ { (uchar *) "draw_picture", 6, 6, -1, 0x05, 0, 0, 3, EXT }, /* 100 */ { (uchar *) "picture_data", 6, 6, -1, 0x06, Br, 0, 3, EXT }, /* 101 */ { (uchar *) "erase_picture", 6, 6, -1, 0x07, 0, 0, 3, EXT }, /* 102 */ { (uchar *) "set_margins", 6, 6, -1, 0x08, 0, 0, 0, EXT }, /* 103 */ { (uchar *) "move_window", 6, 6, -1, 0x10, 0, 0, 0, EXT }, /* 104 */ { (uchar *) "window_size", 6, 6, -1, 0x11, 0, 0, 0, EXT }, /* 105 */ { (uchar *) "window_style", 6, 6, -1, 0x12, 0, 0, 0, EXT }, /* 106 */ { (uchar *) "get_wind_prop", 6, 6, -1, 0x13, St, 0, 0, EXT }, /* 107 */ { (uchar *) "scroll_window", 6, 6, -1, 0x14, 0, 0, 0, EXT }, /* 108 */ { (uchar *) "pop_stack", 6, 6, -1, 0x15, 0, 0, 0, EXT }, /* 109 */ { (uchar *) "read_mouse", 6, 6, -1, 0x16, 0, 0, 5, EXT }, /* 110 */ { (uchar *) "mouse_window", 6, 6, -1, 0x17, 0, 0, 5, EXT }, /* 111 */ { (uchar *) "push_stack", 6, 6, -1, 0x18, Br, 0, 0, EXT }, /* 112 */ { (uchar *) "put_wind_prop", 6, 6, -1, 0x19, 0, 0, 0, EXT }, /* 113 */ { (uchar *) "print_form", 6, 6, -1, 0x1a, 0, 0, 0, EXT }, /* 114 */ { (uchar *) "make_menu", 6, 6, -1, 0x1b, Br, 0, 8, EXT }, /* 115 */ { (uchar *) "picture_table", 6, 6, -1, 0x1c, 0, 0, 3, EXT }, /* Opcodes introduced in Z-Machine Specification Standard 1.0 */ /* 116 */ { (uchar *) "print_unicode", 5, 0, -1, 0x0b, 0, 0, 0, EXT }, /* 117 */ { (uchar *) "check_unicode", 5, 0, -1, 0x0c, St, 0, 0, EXT } }; /* Subsequent forms for opcodes whose meaning changes with version */ static opcodez extension_table_z[] = { /* 0 */ { (uchar *) "not", 4, 4, 3, 0x0F, St, 0, 0, ONE }, /* 1 */ { (uchar *) "save", 4, 4, 4, 0x05, St, 0, 0,ZERO }, /* 2 */ { (uchar *) "restore", 4, 4, 5, 0x06, St, 0, 0,ZERO }, /* 3 */ { (uchar *) "not", 5, 0, -1, 0x38, St, 0, 0, VAR }, /* 4 */ { (uchar *) "save", 5, 0, -1, 0x00, St, 0, 0, EXT }, /* 5 */ { (uchar *) "restore", 5, 0, -1, 0x01, St, 0, 0, EXT }, /* 6 */ { (uchar *) "pull", 6, 6, -1, 0x29, St, 0, 0, VAR } }; static opcodez invalid_opcode_z = { (uchar *) "invalid", 0, 0, -1, 0xff, 0, 0, 0, ZERO}; static opcodez custom_opcode_z; /* Note that this table assumes that all opcodes have at most two branch-label or store operands, and that if they exist, they are the last operands. Glulx does not actually guarantee this. But it is true for all opcodes in the current Glulx spec, so we will assume it for now. Also note that Inform can only compile branches to constant offsets, even though the Glulx machine can handle stack or memory-loaded operands in a branch instruction. */ static opcodeg opcodes_table_g[] = { { (uchar *) "nop", 0x00, 0, 0, 0 }, { (uchar *) "add", 0x10, St, 0, 3 }, { (uchar *) "sub", 0x11, St, 0, 3 }, { (uchar *) "mul", 0x12, St, 0, 3 }, { (uchar *) "div", 0x13, St, 0, 3 }, { (uchar *) "mod", 0x14, St, 0, 3 }, { (uchar *) "neg", 0x15, St, 0, 2 }, { (uchar *) "bitand", 0x18, St, 0, 3 }, { (uchar *) "bitor", 0x19, St, 0, 3 }, { (uchar *) "bitxor", 0x1A, St, 0, 3 }, { (uchar *) "bitnot", 0x1B, St, 0, 2 }, { (uchar *) "shiftl", 0x1C, St, 0, 3 }, { (uchar *) "sshiftr", 0x1D, St, 0, 3 }, { (uchar *) "ushiftr", 0x1E, St, 0, 3 }, { (uchar *) "jump", 0x20, Br|Rf, 0, 1 }, { (uchar *) "jz", 0x22, Br, 0, 2 }, { (uchar *) "jnz", 0x23, Br, 0, 2 }, { (uchar *) "jeq", 0x24, Br, 0, 3 }, { (uchar *) "jne", 0x25, Br, 0, 3 }, { (uchar *) "jlt", 0x26, Br, 0, 3 }, { (uchar *) "jge", 0x27, Br, 0, 3 }, { (uchar *) "jgt", 0x28, Br, 0, 3 }, { (uchar *) "jle", 0x29, Br, 0, 3 }, { (uchar *) "jltu", 0x2A, Br, 0, 3 }, { (uchar *) "jgeu", 0x2B, Br, 0, 3 }, { (uchar *) "jgtu", 0x2C, Br, 0, 3 }, { (uchar *) "jleu", 0x2D, Br, 0, 3 }, { (uchar *) "call", 0x30, St, 0, 3 }, { (uchar *) "return", 0x31, Rf, 0, 1 }, { (uchar *) "catch", 0x32, Br|St, 0, 2 }, { (uchar *) "throw", 0x33, Rf, 0, 2 }, { (uchar *) "tailcall", 0x34, Rf, 0, 2 }, { (uchar *) "copy", 0x40, St, 0, 2 }, { (uchar *) "copys", 0x41, St, 0, 2 }, { (uchar *) "copyb", 0x42, St, 0, 2 }, { (uchar *) "sexs", 0x44, St, 0, 2 }, { (uchar *) "sexb", 0x45, St, 0, 2 }, { (uchar *) "aload", 0x48, St, 0, 3 }, { (uchar *) "aloads", 0x49, St, 0, 3 }, { (uchar *) "aloadb", 0x4A, St, 0, 3 }, { (uchar *) "aloadbit", 0x4B, St, 0, 3 }, { (uchar *) "astore", 0x4C, 0, 0, 3 }, { (uchar *) "astores", 0x4D, 0, 0, 3 }, { (uchar *) "astoreb", 0x4E, 0, 0, 3 }, { (uchar *) "astorebit", 0x4F, 0, 0, 3 }, { (uchar *) "stkcount", 0x50, St, 0, 1 }, { (uchar *) "stkpeek", 0x51, St, 0, 2 }, { (uchar *) "stkswap", 0x52, 0, 0, 0 }, { (uchar *) "stkroll", 0x53, 0, 0, 2 }, { (uchar *) "stkcopy", 0x54, 0, 0, 1 }, { (uchar *) "streamchar", 0x70, 0, 0, 1 }, { (uchar *) "streamnum", 0x71, 0, 0, 1 }, { (uchar *) "streamstr", 0x72, 0, 0, 1 }, { (uchar *) "gestalt", 0x0100, St, 0, 3 }, { (uchar *) "debugtrap", 0x0101, 0, 0, 1 }, { (uchar *) "getmemsize", 0x0102, St, 0, 1 }, { (uchar *) "setmemsize", 0x0103, St, 0, 2 }, { (uchar *) "jumpabs", 0x0104, Rf, 0, 1 }, { (uchar *) "random", 0x0110, St, 0, 2 }, { (uchar *) "setrandom", 0x0111, 0, 0, 1 }, { (uchar *) "quit", 0x0120, Rf, 0, 0 }, { (uchar *) "verify", 0x0121, St, 0, 1 }, { (uchar *) "restart", 0x0122, 0, 0, 0 }, { (uchar *) "save", 0x0123, St, 0, 2 }, { (uchar *) "restore", 0x0124, St, 0, 2 }, { (uchar *) "saveundo", 0x0125, St, 0, 1 }, { (uchar *) "restoreundo", 0x0126, St, 0, 1 }, { (uchar *) "protect", 0x0127, 0, 0, 2 }, { (uchar *) "glk", 0x0130, St, 0, 3 }, { (uchar *) "getstringtbl", 0x0140, St, 0, 1 }, { (uchar *) "setstringtbl", 0x0141, 0, 0, 1 }, { (uchar *) "getiosys", 0x0148, St|St2, 0, 2 }, { (uchar *) "setiosys", 0x0149, 0, 0, 2 }, { (uchar *) "linearsearch", 0x0150, St, 0, 8 }, { (uchar *) "binarysearch", 0x0151, St, 0, 8 }, { (uchar *) "linkedsearch", 0x0152, St, 0, 7 }, { (uchar *) "callf", 0x0160, St, 0, 2 }, { (uchar *) "callfi", 0x0161, St, 0, 3 }, { (uchar *) "callfii", 0x0162, St, 0, 4 }, { (uchar *) "callfiii", 0x0163, St, 0, 5 }, }; static opcodeg custom_opcode_g; static opcodez internal_number_to_opcode_z(int32 i) { opcodez x; ASSERT_ZCODE(); if (i == -1) return custom_opcode_z; x = opcodes_table_z[i]; if (instruction_set_number < x.version1) return invalid_opcode_z; if (x.version2 == 0) return x; if (instruction_set_number <= x.version2) return x; i = x.extension; x = extension_table_z[i]; if (instruction_set_number < x.version1) return invalid_opcode_z; if (x.version2 == 0) return x; if (instruction_set_number <= x.version2) return x; return extension_table_z[x.extension]; } static void make_opcode_syntax_z(opcodez opco) { char *p = "", *q = opcode_syntax_string; sprintf(q, "%s", opco.name); switch(opco.no) { case ONE: p=" "; break; case TWO: p=" "; break; case EXT: case VAR: p=" <0 to 4 operands>"; break; case VAR_LONG: p=" <0 to 8 operands>"; break; } switch(opco.op_rules) { case TEXT: sprintf(q+strlen(q), " "); return; case LABEL: sprintf(q+strlen(q), "