/* txd.c V7/3 * * Z code disassembler for Infocom game files * * Requires txio.c, getopt.c, showverb.c and tx.h. * * Works for all V1, V2, V3, V4, V5, V6, V7 and V8 games. * * Usage: txd story-file-name * * Mark Howell 25 August 1992 howell_ma@movies.enet.dec.com * * History: * Merge separate disassemblers for each type into one program * Fix logic error in low routine scan * Force PC past start PC in middle routine scan * Update opcodes * Add pre-action and action verb names to routines * Add print mode descriptions * Wrap long lines of text * Cleanup for 16 bit machines * Change JGE and JLE to JG and JL * Add support for V1 and V2 games * Fix bug in printing of last string * Add symbolic names for routines and labels * Add verb syntax * Improve verb formatting * Add command line options * Fix check for low address * Add a switch to turn off grammar * Add support for V6 games * Make start of code for low scan the end of dictionary data * Generate Inform style syntax as an option * Add dump style and width option * Fix lint warnings * Fix inter-routine backward jumps * Update operand names * Add support for V7 and V8 games * Limit cache size to MAX_CACHE pages * Improve translation of constants to symbols * Distinguish between pre-actions and Inform parsing routines * Introduce indirect operands, eg. load [sp] sp * Fix object 0 problem * Add support for European characters (codes up to 223) * Add support for Inform 6 (helped by Matthew T. Russotto) * Add support for GV2 (MTR) * Add support for Infocom V6 games * Fix dependencies on sizeof(int) == 2 (mostly cosmetic) NOT DONE * Add -S dump-strings at address option * Remove GV2A support * Add unicode disassembly support * Add Inform and user symbol table support */ #include "tx.h" #define MAX_PCS 100 #define ROUND_CODE(address) ((address + (code_scaler - 1)) & ~(code_scaler - 1)) #define ROUND_DATA(address) ((address + (story_scaler - 1)) & ~(story_scaler - 1)) #ifndef HAS_GETOPT #ifdef __STDC__ extern int getopt (int, char *[], const char *); #else extern int getopt (); #endif #endif #ifdef __STDC__ static void process_story (const char *); static void decode_program (void); static int decode_routine (void); static int decode_code (void); static int decode_opcode (void); static int decode_operands (const char *, int, int, int, int, int, int); static int decode_parameters (int *); static int decode_parameter (int, int); static int decode_extra (void); static void decode_strings (unsigned long); static void scan_strings (unsigned long); static int lookup_string (unsigned long); static void lookup_verb (unsigned long); static void setup_dictionary (void); static int in_dictionary (unsigned long); static void add_label (unsigned long); static void add_routine (unsigned long); static int lookup_label (unsigned long, int); static int lookup_routine (unsigned long, int); static void renumber_cref (cref_item_t *); static void free_cref (cref_item_t *); static int print_object_desc (unsigned int); static void print_text (unsigned long *); static void print_integer (unsigned int, int); static void dump_data (unsigned long, unsigned long); static void dump_opcode (unsigned long, int, int, int *, int); static void dump_operand (unsigned long *, int, int, int *, int *); static void print_variable (int); #else static void process_story (); static void decode_program (); static int decode_routine (); static int decode_code (); static int decode_opcode (); static int decode_operands (); static int decode_parameters (); static int decode_parameter (); static int decode_extra (); static void decode_strings (); static void scan_strings (); static int lookup_string (); static void lookup_verb (); static void setup_dictionary (); static int in_dictionary (); static void add_label (); static void add_routine (); static int lookup_label (); static int lookup_routine (); static void renumber_cref (); static void free_cref (); static int print_object_desc (); static void print_text (); static void print_integer (); static void dump_data (); static void dump_opcode (); static void dump_operand (); static void print_variable (); #endif static unsigned long pctable[MAX_PCS]; static int pcindex = 0; static unsigned long start_data_pc, end_data_pc; static decode_t decode; static opcode_t opcode; static cref_item_t *strings_base = NULL; static cref_item_t *routines_base = NULL; static cref_item_t *current_routine = NULL; static int locals_count = 0; static unsigned long start_of_routine = 0; static unsigned int verb_count = 0; static unsigned int action_count = 0; static unsigned int parse_count = 0; static unsigned int parser_type = 0; static unsigned int prep_type = 0; static unsigned long verb_table_base = 0; static unsigned long verb_data_base = 0; static unsigned long action_table_base = 0; static unsigned long preact_table_base = 0; static unsigned long prep_table_base = 0; static unsigned long prep_table_end = 0; static const int verb_sizes[4] = { 2, 4, 7, 7 }; static unsigned long dict_start = 0; static unsigned long dict_end = 0; static unsigned long word_size = 0; static unsigned long word_count = 0; static unsigned long code_base = 0; static unsigned int obj_count = 0; static unsigned long obj_table_base = 0, obj_table_end = 0, obj_data_base = 0, obj_data_end = 0; static unsigned short inform_version = 0; static unsigned long class_numbers_base = 0, class_numbers_end = 0; static unsigned long property_names_base = 0, property_names_end = 0; static unsigned long attr_names_base = 0, attr_names_end = 0; #ifndef HAS_GETOPT /* getopt linkages */ extern int optind; extern const char *optarg; #endif static int option_labels = 1; static int option_grammar = 1; static int option_dump = 0; static int option_width = 79; static int option_symbols = 0; static unsigned long string_location = 0; #ifdef __STDC__ int main (int argc, char *argv []) #else int main (argc, argv) int argc; char *argv []; #endif { int c, errflg = 0; /* Parse the options */ while ((c = getopt (argc, argv, "abdghnsw:S:u:")) != EOF) { switch (c) { case 'a': option_inform = 6; break; case 'd': option_dump = 1; break; case 'g': option_grammar = 0; break; case 'n': option_labels = 0; break; case 'w': option_width = atoi (optarg); break; case 'u': init_symbols(optarg); /*FALLTHRU*/ case 's': option_symbols = 1; break; case 'S': #ifdef HAS_STRTOUL string_location = strtoul(optarg, (char **)NULL, 0); #else string_location = atoi(optarg); #endif break; case 'h': case '?': default: errflg++; } } /* Display usage if unknown flag or no story file */ if (errflg || optind >= argc) { (void) fprintf (stderr, "usage: %s [options...] story-file [story-file...]\n\n", argv[0]); (void) fprintf (stderr, "TXD version 7/3 - disassemble Infocom story files. By Mark Howell\n"); (void) fprintf (stderr, "Works with V1 to V8 Infocom games.\n\n"); (void) fprintf (stderr, "\t-a generate alternate syntax used by Inform\n"); (void) fprintf (stderr, "\t-d dump hex of opcodes and data\n"); (void) fprintf (stderr, "\t-g turn off grammar for action routines\n"); (void) fprintf (stderr, "\t-n use addresses instead of labels\n"); (void) fprintf (stderr, "\t-w n display width (0 = no wrap)\n"); (void) fprintf (stderr, "\t-s Symbolic mode (Inform 6+ only)\n"); (void) fprintf (stderr, "\t-u Read user symbol table, implies -s for Inform games\n"); (void) fprintf (stderr, "\t-S n Dump high strings only, starting at address n\n"); exit (EXIT_FAILURE); } /* Process any story files on the command line */ for (; optind < argc; optind++) process_story (argv[optind]); exit (EXIT_SUCCESS); return (0); }/* main */ #ifdef __STDC__ static void process_story (const char *file_name) #else static void process_story (file_name) const char *file_name; #endif { tx_set_width (option_width); open_story (file_name); configure (V1, V8); load_cache (); setup_dictionary (); if (option_grammar) configure_parse_tables (&verb_count, &action_count, &parse_count, &parser_type, &prep_type, &verb_table_base, &verb_data_base, &action_table_base, &preact_table_base, &prep_table_base, &prep_table_end); if (option_symbols && (parser_type >= inform_gv1)) { configure_object_tables (&obj_count, &obj_table_base, &obj_table_end, &obj_data_base, &obj_data_end); configure_inform_tables(obj_data_end, &inform_version, &class_numbers_base, &class_numbers_end, &property_names_base, &property_names_end, &attr_names_base, &attr_names_end); } if (header.version != V6 && header.version != V7) { decode.pc = code_base = dict_end; decode.initial_pc = (unsigned long) header.start_pc - 1; } else { decode.pc = code_base = (unsigned long) header.routines_offset * story_scaler; decode.initial_pc = decode.pc + (unsigned long) header.start_pc * code_scaler; } tx_printf ("Resident data ends at %lx, program starts at %lx, file ends at %lx\n", (unsigned long) header.resident_size, (unsigned long) decode.initial_pc, (unsigned long) file_size); tx_printf ("\nStarting analysis pass at address %lx\n", (unsigned long) decode.pc); #if defined(TXD_DEBUG) decode.first_pass = 0; decode.low_address = decode.initial_pc; decode.high_address = file_size; #if 0 decode.low_address = atoi (getenv ("LOW_ADDRESS")); decode.high_address = atoi (getenv ("HIGH_ADDRESS")); #endif #else decode.first_pass = 1; #endif if (string_location) { decode_strings (string_location); exit(0); } decode_program (); scan_strings (decode.pc); #if !defined(TXD_DEBUG) tx_printf ("\nEnd of analysis pass, low address = %lx, high address = %lx\n", (unsigned long) decode.low_address, (unsigned long) decode.high_address); if (start_data_pc) tx_printf ("\n%ld bytes of data in code from %lx to %lx\n", (unsigned long) (end_data_pc - start_data_pc), (unsigned long) start_data_pc, (unsigned long) end_data_pc); if ((decode.low_address - code_base) >= story_scaler) { tx_printf ("\n%ld bytes of data before code from %lx to %lx\n", (unsigned long) (decode.low_address - code_base), (unsigned long) code_base, (unsigned long) decode.low_address); if (option_dump) { tx_printf ("\n[Start of data"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) code_base); tx_printf ("]\n\n"); dump_data (code_base, decode.low_address - 1); tx_printf ("\n[End of data"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) (decode.low_address - 1)); tx_printf ("]\n"); } } if (option_labels) renumber_cref (routines_base); decode.first_pass = 0; decode_program (); decode_strings (decode.pc); #endif close_story (); }/* process_story */ /* decode_program - Decode Z code in two passes */ #ifdef __STDC__ static void decode_program (void) #else static void decode_program () #endif { unsigned long pc, low_pc, high_pc, prev_low_pc, prev_high_pc; int i, flag, vars; if (decode.first_pass) { if (decode.pc < decode.initial_pc) { /* Scan for low routines */ decode.pc = ROUND_CODE (decode.pc); for (pc = decode.pc, flag = 0; pc < decode.initial_pc && flag == 0; pc += code_scaler) { for (vars = (char) read_data_byte (&pc); vars < 0 || vars > 15; vars = (char) read_data_byte (&pc)) pc = ROUND_CODE (pc); decode.pc = pc - 1; for (i = 0, flag = 1; i < 3 && flag; i++) { pcindex = 0; decode.pc = ROUND_CODE (decode.pc); if (decode_routine () != END_OF_ROUTINE || pcindex) flag = 0; } decode.pc = pc - 1; } if (flag && (unsigned int) header.version < V5) { pc = decode.pc; vars = (char) read_data_byte (&pc); low_pc = decode.pc; for (pc = pc + (vars * 2) - 1, flag = 0; pc >= low_pc && flag == 0; pc -= story_scaler) { decode.pc = pc; for (i = 0, flag = 1; i < 3 && flag; i++) { pcindex = 0; decode.pc = ROUND_CODE (decode.pc); if (decode_routine () != END_OF_ROUTINE || pcindex) flag = 0; } decode.pc = pc; } } if (flag == 0 || decode.pc > decode.initial_pc) decode.pc = decode.initial_pc; } /* Fill in middle routines */ decode.low_address = decode.pc; decode.high_address = decode.pc; start_data_pc = 0; end_data_pc = 0; do { if (option_labels) { free_cref (routines_base); routines_base = NULL; } prev_low_pc = decode.low_address; prev_high_pc = decode.high_address; flag = 0; pcindex = 0; low_pc = decode.low_address; high_pc = decode.high_address; pc = decode.pc = decode.low_address; while (decode.pc <= high_pc || decode.pc <= decode.initial_pc) { if (start_data_pc == decode.pc) decode.pc = end_data_pc; if (decode_routine () != END_OF_ROUTINE) { if (start_data_pc == 0) start_data_pc = decode.pc; flag = 1; end_data_pc = 0; pcindex = 0; pc = ROUND_CODE (pc); do { pc += code_scaler; vars = (char) read_data_byte (&pc); pc--; } while (vars < 0 || vars > 15); decode.pc = pc; } else { if (pc < decode.initial_pc && decode.pc > decode.initial_pc) { pc = decode.pc = decode.initial_pc; decode.low_address = low_pc; decode.high_address = high_pc; } else { if (start_data_pc && end_data_pc == 0) end_data_pc = pc; pc = ROUND_CODE (decode.pc); if (flag == 0) { low_pc = decode.low_address; high_pc = decode.high_address; } } } } decode.low_address = low_pc; decode.high_address = high_pc; } while (low_pc < prev_low_pc || high_pc > prev_high_pc); /* Scan for high routines */ pc = decode.pc; while (decode_routine () == END_OF_ROUTINE) { decode.high_address = pc; pc = decode.pc; } } else { tx_printf ("\n[Start of code"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) decode.low_address); tx_printf ("]\n"); for (decode.pc = decode.low_address; decode.pc <= decode.high_address; ) (void) decode_routine (); tx_printf ("\n[End of code"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) decode.pc); tx_printf ("]\n"); } }/* decode_program */ /* decode_routine - Decode a routine from start address to last instruction */ #ifdef __STDC__ static int decode_routine (void) #else static int decode_routine () #endif { unsigned long old_pc, old_start; cref_item_t *cref_item; int vars, status, i, locals; if (decode.first_pass) { cref_item = NULL; if (option_labels) cref_item = current_routine; old_start = start_of_routine; locals = locals_count; old_pc = decode.pc; decode.pc = ROUND_CODE (decode.pc); vars = read_data_byte (&decode.pc); if (vars >= 0 && vars <= 15) { if (option_labels) add_routine (decode.pc - 1); locals_count = vars; if ((unsigned int) header.version < V5) for (; vars; vars--) (void) read_data_word (&decode.pc); start_of_routine = decode.pc; if (decode_code () == END_OF_ROUTINE) return (END_OF_ROUTINE); if (option_labels) current_routine = cref_item; start_of_routine = old_start; locals_count = locals; } decode.pc = old_pc; if ((status = decode_code ()) != END_OF_ROUTINE) { decode.pc = old_pc; } else { pctable[pcindex++] = old_pc; if (pcindex == MAX_PCS) { (void) fprintf (stderr, "\nFatal: too many orphan code fragments\n"); exit (EXIT_FAILURE); } } } else { if (decode.pc == start_data_pc) { if (option_dump) { tx_printf ("\n[Start of data"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) start_data_pc); tx_printf ("]\n\n"); dump_data (start_data_pc, end_data_pc - 1); tx_printf ("\n[End of data"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) (end_data_pc - 1)); tx_printf ("]\n"); } decode.pc = end_data_pc; } for (i = 0; i < pcindex && decode.pc != pctable[i]; i++) ; if (i == pcindex) { decode.pc = ROUND_CODE (decode.pc); start_of_routine = decode.pc; vars = read_data_byte (&decode.pc); if (option_labels) { tx_printf ("%soutine %c%04d, %d local", (decode.pc - 1 == decode.initial_pc) ? "\nMain r" : "\nR", (option_inform) ? 'r' : 'R', (int) lookup_routine (decode.pc - 1, 1), (int) vars); } else { tx_printf ("%soutine %lx, %d local", (decode.pc - 1 == decode.initial_pc) ? "\nMain r" : "\nR", (unsigned long) (decode.pc - 1), (int) vars); } if (vars != 1) tx_printf ("s"); if ((unsigned int) header.version < V5) { tx_printf (" ("); tx_fix_margin (1); for (; vars; vars--) { tx_printf ("%04x", (unsigned int) read_data_word (&decode.pc)); if (vars > 1) tx_printf (", "); } tx_fix_margin (0); tx_printf (")"); } tx_printf ("\n"); lookup_verb (start_of_routine); tx_printf ("\n"); } else tx_printf ("\norphan code fragment:\n\n"); status = decode_code (); } return (status); }/* decode_routine */ /* decode_code - grab opcode and determine the class */ #ifdef __STDC__ static int decode_code (void) #else static int decode_code () #endif { int status; int label; decode.high_pc = decode.pc; do { if (decode.first_pass == 0) { if (option_labels) { label = lookup_label (decode.pc, 0); if (label != 0) tx_printf ("%c%04d: ", (option_inform) ? 'l' : 'L', (int) label); else tx_printf (" "); } else tx_printf ("%5lx: ", (unsigned long) decode.pc); } opcode.opcode = read_data_byte (&decode.pc); if ((unsigned int) header.version > V4 && opcode.opcode == 0xbe) { opcode.opcode = read_data_byte (&decode.pc); opcode.class = EXTENDED_OPERAND; } else if (opcode.opcode < 0x80) opcode.class = TWO_OPERAND; else if (opcode.opcode < 0xb0) opcode.class = ONE_OPERAND; else if (opcode.opcode < 0xc0) opcode.class = ZERO_OPERAND; else opcode.class = VARIABLE_OPERAND; status = decode_opcode (); } while (status == END_OF_INSTRUCTION); return (status); }/* decode_code */ /* decode_opcode - Check and decode the opcode itself */ #define caseline(opc, text, par1, par2, par3, par4, extra, type) \ case opc: return (decode_operands (text, par1, par2, par3, par4, extra, type)) #ifdef __STDC__ static int decode_opcode (void) #else static int decode_opcode () #endif { int code; code = opcode.opcode; switch (opcode.class) { case EXTENDED_OPERAND: code &= 0x3f; switch (code) { caseline (0x00, "SAVE", LOW_ADDR, NUMBER, LOW_ADDR, NIL, STORE, PLAIN); caseline (0x01, "RESTORE", LOW_ADDR, NUMBER, LOW_ADDR, NIL, STORE, PLAIN); caseline (0x02, "LOG_SHIFT", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x03, "ART_SHIFT", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x04, "SET_FONT", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x05, "DRAW_PICTURE", NUMBER, NUMBER, NUMBER, NIL, NONE, PLAIN); caseline (0x06, "PICTURE_DATA", NUMBER, LOW_ADDR, NIL, NIL, BRANCH, PLAIN); caseline (0x07, "ERASE_PICTURE", NUMBER, NUMBER, NUMBER, NIL, NONE, PLAIN); caseline (0x08, "SET_MARGINS", NUMBER, NUMBER, NUMBER, NIL, NONE, PLAIN); caseline (0x09, "SAVE_UNDO", NIL, NIL, NIL, NIL, STORE, PLAIN); caseline (0x0A, "RESTORE_UNDO", NIL, NIL, NIL, NIL, STORE, PLAIN); caseline (0x10, "MOVE_WINDOW", NUMBER, NUMBER, NUMBER, NIL, NONE, PLAIN); caseline (0x11, "WINDOW_SIZE", NUMBER, NUMBER, NUMBER, NIL, NONE, PLAIN); caseline (0x12, "WINDOW_STYLE", NUMBER, NUMBER, NUMBER, NIL, NONE, PLAIN); caseline (0x13, "GET_WIND_PROP", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x14, "SCROLL_WINDOW", NUMBER, NUMBER, NIL, NIL, NONE, PLAIN); caseline (0x15, "POP_STACK", NUMBER, LOW_ADDR, NIL, NIL, NONE, PLAIN); caseline (0x16, "READ_MOUSE", LOW_ADDR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x17, "MOUSE_WINDOW", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x18, "PUSH_STACK", NUMBER, LOW_ADDR, NIL, NIL, BRANCH, PLAIN); caseline (0x19, "PUT_WIND_PROP", NUMBER, NUMBER, ANYTHING, NIL, NONE, PLAIN); caseline (0x1A, "PRINT_FORM", LOW_ADDR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x1B, "MAKE_MENU", NUMBER, LOW_ADDR, NIL, NIL, BRANCH, PLAIN); caseline (0x1C, "PICTURE_TABLE", LOW_ADDR, NIL, NIL, NIL, NONE, PLAIN); default: return (decode_operands ("ILLEGAL", NIL, NIL, NIL, NIL, NONE, ILLEGAL)); } case TWO_OPERAND: code &= 0x1f; case VARIABLE_OPERAND: code &= 0x3f; switch (code) { caseline (0x01, "JE", ANYTHING, ANYTHING, ANYTHING, ANYTHING, BRANCH, PLAIN); caseline (0x02, "JL", NUMBER, NUMBER, NIL, NIL, BRANCH, PLAIN); caseline (0x03, "JG", NUMBER, NUMBER, NIL, NIL, BRANCH, PLAIN); caseline (0x04, "DEC_CHK", VAR, NUMBER, NIL, NIL, BRANCH, PLAIN); caseline (0x05, "INC_CHK", VAR, NUMBER, NIL, NIL, BRANCH, PLAIN); caseline (0x06, "JIN", OBJECT, OBJECT, NIL, NIL, BRANCH, PLAIN); caseline (0x07, "TEST", NUMBER, NUMBER, NIL, NIL, BRANCH, PLAIN); caseline (0x08, "OR", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x09, "AND", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x0A, "TEST_ATTR", OBJECT, ATTRNUM, NIL, NIL, BRANCH, PLAIN); caseline (0x0B, "SET_ATTR", OBJECT, ATTRNUM, NIL, NIL, NONE, PLAIN); caseline (0x0C, "CLEAR_ATTR", OBJECT, ATTRNUM, NIL, NIL, NONE, PLAIN); caseline (0x0D, "STORE", VAR, ANYTHING, NIL, NIL, NONE, PLAIN); caseline (0x0E, "INSERT_OBJ", OBJECT, OBJECT, NIL, NIL, NONE, PLAIN); caseline (0x0F, "LOADW", LOW_ADDR, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x10, "LOADB", LOW_ADDR, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x11, "GET_PROP", OBJECT, PROPNUM, NIL, NIL, STORE, PLAIN); caseline (0x12, "GET_PROP_ADDR", OBJECT, PROPNUM, NIL, NIL, STORE, PLAIN); caseline (0x13, "GET_NEXT_PROP", OBJECT, PROPNUM, NIL, NIL, STORE, PLAIN); caseline (0x14, "ADD", LOW_ADDR, LOW_ADDR, NIL, NIL, STORE, PLAIN); caseline (0x15, "SUB", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x16, "MUL", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x17, "DIV", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x18, "MOD", NUMBER, NUMBER, NIL, NIL, STORE, PLAIN); caseline (0x21, "STOREW", LOW_ADDR, NUMBER, ANYTHING, NIL, NONE, PLAIN); caseline (0x22, "STOREB", LOW_ADDR, NUMBER, ANYTHING, NIL, NONE, PLAIN); caseline (0x23, "PUT_PROP", OBJECT, NUMBER, ANYTHING, NIL, NONE, PLAIN); caseline (0x25, "PRINT_CHAR", PCHAR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x26, "PRINT_NUM", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x27, "RANDOM", NUMBER, NIL, NIL, NIL, STORE, PLAIN); caseline (0x28, "PUSH", ANYTHING, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2A, "SPLIT_WINDOW", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2B, "SET_WINDOW", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x33, "OUTPUT_STREAM", PATTR, LOW_ADDR, NUMBER, NIL, NONE, PLAIN); caseline (0x34, "INPUT_STREAM", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x35, "SOUND_EFFECT", NUMBER, NUMBER, NUMBER, ROUTINE, NONE, PLAIN); default: switch ((unsigned int) header.version) { case V1: case V2: case V3: switch (code) { caseline (0x20, "CALL", ROUTINE, ANYTHING, ANYTHING, ANYTHING, STORE, CALL); caseline (0x24, "READ", LOW_ADDR, LOW_ADDR, NIL, NIL, NONE, PLAIN); caseline (0x29, "PULL", VAR, NIL, NIL, NIL, NONE, PLAIN); } case V4: switch (code) { caseline (0x19, "CALL_2S", ROUTINE, ANYTHING, NIL, NIL, STORE, CALL); caseline (0x20, "CALL_VS", ROUTINE, ANYTHING, ANYTHING, ANYTHING, STORE, CALL); caseline (0x24, "READ", LOW_ADDR, LOW_ADDR, NUMBER, ROUTINE, NONE, PLAIN); caseline (0x29, "PULL", VAR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2C, "CALL_VS2", ROUTINE, ANYTHING, ANYTHING, ANYTHING, STORE, CALL); caseline (0x2D, "ERASE_WINDOW", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2E, "ERASE_LINE", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2F, "SET_CURSOR", NUMBER, NUMBER, NIL, NIL, NONE, PLAIN); caseline (0x31, "SET_TEXT_STYLE", VATTR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x32, "BUFFER_MODE", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x36, "READ_CHAR", NUMBER, NUMBER, ROUTINE, NIL, STORE, PLAIN); caseline (0x37, "SCAN_TABLE", ANYTHING, LOW_ADDR, NUMBER, NUMBER, BOTH, PLAIN); } case V5: case V7: case V8: switch (code) { caseline (0x19, "CALL_2S", ROUTINE, ANYTHING, NIL, NIL, STORE, CALL); caseline (0x1A, "CALL_2N", ROUTINE, ANYTHING, NIL, NIL, NONE, CALL); caseline (0x1B, "SET_COLOUR", NUMBER, NUMBER, NIL, NIL, NONE, PLAIN); caseline (0x20, "CALL_VS", ROUTINE, ANYTHING, ANYTHING, ANYTHING, STORE, CALL); caseline (0x24, "READ", LOW_ADDR, LOW_ADDR, NUMBER, ROUTINE, STORE, PLAIN); caseline (0x29, "PULL", VAR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2C, "CALL_VS2", ROUTINE, ANYTHING, ANYTHING, ANYTHING, STORE, CALL); caseline (0x2D, "ERASE_WINDOW", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2E, "ERASE_LINE", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2F, "SET_CURSOR", NUMBER, NUMBER, NIL, NIL, NONE, PLAIN); caseline (0x31, "SET_TEXT_STYLE", VATTR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x32, "BUFFER_MODE", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x36, "READ_CHAR", NUMBER, NUMBER, ROUTINE, NIL, STORE, PLAIN); caseline (0x37, "SCAN_TABLE", ANYTHING, LOW_ADDR, NUMBER, NUMBER, BOTH, PLAIN); caseline (0x39, "CALL_VN", ROUTINE, ANYTHING, ANYTHING, ANYTHING, NONE, CALL); caseline (0x3A, "CALL_VN2", ROUTINE, ANYTHING, ANYTHING, ANYTHING, NONE, CALL); caseline (0x3B, "TOKENISE", LOW_ADDR, LOW_ADDR, LOW_ADDR, NUMBER, NONE, PLAIN); caseline (0x3C, "ENCODE_TEXT", LOW_ADDR, NUMBER, NUMBER, LOW_ADDR, NONE, PLAIN); caseline (0x3D, "COPY_TABLE", LOW_ADDR, LOW_ADDR, NUMBER, NIL, NONE, PLAIN); caseline (0x3E, "PRINT_TABLE", LOW_ADDR, NUMBER, NUMBER, NUMBER, NONE, PLAIN); caseline (0x3F, "CHECK_ARG_COUNT", NUMBER, NIL, NIL, NIL, BRANCH, PLAIN); } case V6: switch (code) { caseline (0x19, "CALL_2S", ROUTINE, ANYTHING, NIL, NIL, STORE, CALL); caseline (0x1A, "CALL_2N", ROUTINE, ANYTHING, NIL, NIL, NONE, CALL); caseline (0x1B, "SET_COLOUR", NUMBER, NUMBER, NIL, NIL, NONE, PLAIN); caseline (0x1C, "THROW", ANYTHING, NUMBER, NIL, NIL, NONE, PLAIN); caseline (0x20, "CALL_VS", ROUTINE, ANYTHING, ANYTHING, ANYTHING, STORE, CALL); caseline (0x24, "READ", LOW_ADDR, LOW_ADDR, NUMBER, ROUTINE, STORE, PLAIN); caseline (0x29, "PULL", LOW_ADDR, NIL, NIL, NIL, STORE, PLAIN); caseline (0x2C, "CALL_VS2", ROUTINE, ANYTHING, ANYTHING, ANYTHING, STORE, CALL); caseline (0x2D, "ERASE_WINDOW", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2E, "ERASE_LINE", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x2F, "SET_CURSOR", NUMBER, NUMBER, NUMBER, NIL, NONE, PLAIN); caseline (0x30, "GET_CURSOR", LOW_ADDR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x31, "SET_TEXT_STYLE", VATTR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x32, "BUFFER_MODE", NUMBER, NIL, NIL, NIL, NONE, PLAIN); caseline (0x36, "READ_CHAR", NUMBER, NUMBER, ROUTINE, NIL, STORE, PLAIN); caseline (0x37, "SCAN_TABLE", ANYTHING, LOW_ADDR, NUMBER, NUMBER, BOTH, PLAIN); caseline (0x38, "NOT", NUMBER, NIL, NIL, NIL, STORE, PLAIN); caseline (0x39, "CALL_VN", ROUTINE, ANYTHING, ANYTHING, ANYTHING, NONE, CALL); caseline (0x3A, "CALL_VN2", ROUTINE, ANYTHING, ANYTHING, ANYTHING, NONE, CALL); caseline (0x3B, "TOKENISE", LOW_ADDR, LOW_ADDR, LOW_ADDR, NUMBER, NONE, PLAIN); caseline (0x3C, "ENCODE_TEXT", LOW_ADDR, NUMBER, NUMBER, LOW_ADDR, NONE, PLAIN); caseline (0x3D, "COPY_TABLE", LOW_ADDR, NUMBER, LOW_ADDR, NIL, NONE, PLAIN); caseline (0x3E, "PRINT_TABLE", LOW_ADDR, NUMBER, NUMBER, NUMBER, NONE, PLAIN); caseline (0x3F, "CHECK_ARG_COUNT", NUMBER, NIL, NIL, NIL, BRANCH, PLAIN); } } return (decode_operands ("ILLEGAL", NIL, NIL, NIL, NIL, NONE, ILLEGAL)); } case ONE_OPERAND: code &= 0x0f; switch (code) { caseline (0x00, "JZ", NUMBER, NIL, NIL, NIL, BRANCH, PLAIN); caseline (0x01, "GET_SIBLING", OBJECT, NIL, NIL, NIL, BOTH, PLAIN); caseline (0x02, "GET_CHILD", OBJECT, NIL, NIL, NIL, BOTH, PLAIN); caseline (0x03, "GET_PARENT", OBJECT, NIL, NIL, NIL, STORE, PLAIN); caseline (0x04, "GET_PROP_LEN", LOW_ADDR, NIL, NIL, NIL, STORE, PLAIN); caseline (0x05, "INC", VAR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x06, "DEC", VAR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x07, "PRINT_ADDR", LOW_ADDR, NIL, NIL, NIL, NONE, PLAIN); caseline (0x09, "REMOVE_OBJ", OBJECT, NIL, NIL, NIL, NONE, PLAIN); caseline (0x0A, "PRINT_OBJ", OBJECT, NIL, NIL, NIL, NONE, PLAIN); caseline (0x0B, "RET", ANYTHING, NIL, NIL, NIL, NONE, RETURN); caseline (0x0C, "JUMP", LABEL, NIL, NIL, NIL, NONE, RETURN); caseline (0x0D, "PRINT_PADDR", STATIC, NIL, NIL, NIL, NONE, PLAIN); caseline (0x0E, "LOAD", VAR, NIL, NIL, NIL, STORE, PLAIN); default: switch ((unsigned int) header.version) { case V1: case V2: case V3: switch (code) { caseline (0x0F, "NOT", NUMBER, NIL, NIL, NIL, STORE, PLAIN); } case V4: switch (code) { caseline (0x08, "CALL_1S", ROUTINE, NIL, NIL, NIL, STORE, CALL); caseline (0x0F, "NOT", NUMBER, NIL, NIL, NIL, STORE, PLAIN); } case V5: case V6: case V7: case V8: switch (code) { caseline (0x08, "CALL_1S", ROUTINE, NIL, NIL, NIL, STORE, CALL); caseline (0x0F, "CALL_1N", ROUTINE, NIL, NIL, NIL, NONE, CALL); } } return (decode_operands ("ILLEGAL", NIL, NIL, NIL, NIL, NONE, ILLEGAL)); } case ZERO_OPERAND: code &= 0x0f; switch (code) { caseline (0x00, "RTRUE", NIL, NIL, NIL, NIL, NONE, RETURN); caseline (0x01, "RFALSE", NIL, NIL, NIL, NIL, NONE, RETURN); caseline (0x02, "PRINT", NIL, NIL, NIL, NIL, TEXT, PLAIN); caseline (0x03, "PRINT_RET", NIL, NIL, NIL, NIL, TEXT, RETURN); caseline (0x04, "NOP", NIL, NIL, NIL, NIL, NONE, PLAIN); caseline (0x07, "RESTART", NIL, NIL, NIL, NIL, NONE, PLAIN); caseline (0x08, "RET_POPPED", NIL, NIL, NIL, NIL, NONE, RETURN); caseline (0x0A, "QUIT", NIL, NIL, NIL, NIL, NONE, RETURN); caseline (0x0B, "NEW_LINE", NIL, NIL, NIL, NIL, NONE, PLAIN); caseline (0x0D, "VERIFY", NIL, NIL, NIL, NIL, BRANCH, PLAIN); default: switch ((unsigned int) header.version) { case V1: case V2: case V3: switch (code) { caseline (0x05, "SAVE", NIL, NIL, NIL, NIL, BRANCH, PLAIN); caseline (0x06, "RESTORE", NIL, NIL, NIL, NIL, BRANCH, PLAIN); caseline (0x09, "POP", NIL, NIL, NIL, NIL, NONE, PLAIN); caseline (0x0C, "SHOW_STATUS", NIL, NIL, NIL, NIL, NONE, PLAIN); } case V4: switch (code) { caseline (0x09, "POP", NIL, NIL, NIL, NIL, NONE, PLAIN); caseline (0x05, "SAVE", NIL, NIL, NIL, NIL, STORE, PLAIN); caseline (0x06, "RESTORE", NIL, NIL, NIL, NIL, STORE, PLAIN); } case V5: case V7: case V8: switch (code) { caseline (0x09, "CATCH", NIL, NIL, NIL, NIL, STORE, PLAIN); /* From a bug in Wishbringer V23 */ caseline (0x0C, "SHOW_STATUS", NIL, NIL, NIL, NIL, NONE, PLAIN); } case V6: switch (code) { caseline (0x09, "CATCH", NIL, NIL, NIL, NIL, STORE, PLAIN); caseline (0x0F, "PIRACY", NIL, NIL, NIL, NIL, BRANCH, PLAIN); } } return (decode_operands ("ILLEGAL", NIL, NIL, NIL, NIL, NONE, ILLEGAL)); } default: (void) fprintf (stderr, "\nFatal: bad class (%d)\n", (int) opcode.class); exit (EXIT_FAILURE); } return (BAD_OPCODE); }/* decode_opcode */ #undef caseline /* decode_operands - Decode operands of opcode */ #ifdef __STDC__ static int decode_operands (const char *opcode_name, int par1, int par2, int par3, int par4, int extra, int type) #else static int decode_operands (opcode_name, par1, par2, par3, par4, extra, type) const char *opcode_name; int par1; int par2; int par3; int par4; int extra; int type; #endif { size_t len; int i, opers, status; opcode.par[0] = par1; opcode.par[1] = par2; opcode.par[2] = par3; opcode.par[3] = par4; opcode.extra = extra; opcode.type = type; if (opcode.type == ILLEGAL) return (BAD_OPCODE); if (decode.first_pass) { status = decode_parameters (&opers); if (status) return (BAD_OPCODE); status = decode_extra (); } else { if (option_dump) dump_opcode (decode.pc, opcode.opcode, opcode.class, opcode.par, opcode.extra); if (option_inform) { len = strlen (opcode_name); for (i = 0; i < len; i++) tx_printf ("%c", tolower (opcode_name[i])); } else { tx_printf (opcode_name); len = strlen (opcode_name); } for (; len < 16; len++) tx_printf (" "); (void) decode_parameters (&opers); if (opers > 0 && opcode.extra != NONE) tx_printf (" "); status = decode_extra (); tx_printf ("\n"); } if (decode.pc > decode.high_pc) decode.high_pc = decode.pc; return (status); }/* decode_operands */ /* decode_parameters - Decode input parameters */ #ifdef __STDC__ static int decode_parameters (int *opers) #else static int decode_parameters (opers) int *opers; #endif { int status, modes, addr_mode, maxopers; *opers = 0; switch (opcode.class) { case ONE_OPERAND: status = decode_parameter ((opcode.opcode >> 4) & 0x03, 0); if (status) return (status); *opers = 1; break; case TWO_OPERAND: status = decode_parameter ((opcode.opcode & 0x40) ? VARIABLE : BYTE_IMMED, 0); if (status) return (status); if (decode.first_pass == 0) { if (!option_inform && opcode.type == CALL) tx_printf (" ("); else tx_printf ("%c", (option_inform) ? ' ' : ','); } status = decode_parameter ((opcode.opcode & 0x20) ? VARIABLE : BYTE_IMMED, 1); if (status) return (status); *opers = 2; if (!option_inform && decode.first_pass == 0 && opcode.type == CALL && *opers > 1) tx_printf (")"); break; case VARIABLE_OPERAND: case EXTENDED_OPERAND: if ((opcode.opcode & 0x3f) == 0x2c || (opcode.opcode & 0x3f) == 0x3a) { modes = read_data_word (&decode.pc); maxopers = 8; } else { modes = read_data_byte (&decode.pc); maxopers = 4; } for (addr_mode = 0, *opers = 0; (addr_mode != NO_OPERAND) && maxopers; maxopers--) { addr_mode = (modes >> ((maxopers - 1) * 2)) & 0x03; if (addr_mode != NO_OPERAND) { if (decode.first_pass == 0 && *opers) { if (!option_inform && opcode.type == CALL && *opers == 1) tx_printf (" ("); else tx_printf ("%c", (option_inform) ? ' ' : ','); } status = decode_parameter (addr_mode, *opers); if (status) return (status); (*opers)++; } } if (!option_inform && decode.first_pass == 0 && opcode.type == CALL && *opers > 1) tx_printf (")"); break; case ZERO_OPERAND: break; default: (void) fprintf (stderr, "\nFatal: bad class (%d)\n", (int) opcode.class); exit (EXIT_FAILURE); } return (0); }/* decode_parameters */ /* decode_parameter - Decode one input parameter */ #ifdef __STDC__ static int decode_parameter (int addr_mode, int opers) #else static int decode_parameter (addr_mode, opers) int addr_mode; int opers; #endif { unsigned long addr; unsigned int value; int routine, vars, par, dictionary, string; par = (opers < 4) ? opcode.par[opers] : ANYTHING; switch (addr_mode) { case WORD_IMMED: value = (unsigned int) read_data_word (&decode.pc); break; case BYTE_IMMED: value = (unsigned int) read_data_byte (&decode.pc); break; case VARIABLE: value = (unsigned int) read_data_byte (&decode.pc); par = VAR; break; case NO_OPERAND: return (0); default: (void) fprintf (stderr, "\nFatal: bad addressing mode (%d)\n", (int) addr_mode); exit (EXIT_FAILURE); } /* * To make the code more readable, VAR type operands are not translated * as constants, eg. INC 5 is actually printed as INC L05. However, if * the VAR type operand _is_ a variable, the translation should look like * INC [L05], ie. increment the variable which is given by the contents * of local variable #5. Earlier versions of "txd" translated both cases * as INC L05. This bug was finally detected by Graham Nelson. */ if (opers < 4 && opcode.par[opers] == VAR) par = (addr_mode == VARIABLE) ? INDIRECT : VAR; switch (par) { case NIL: if (decode.first_pass == 0) { fprintf(stderr, "\nWarning: Unexpected Parameter #%d near %05lx\n", opers, decode.pc); print_integer (value, addr_mode == BYTE_IMMED); } break; case ANYTHING: if (decode.first_pass == 0) { addr = (unsigned long) code_scaler * value + (unsigned long) story_scaler * header.strings_offset; string = lookup_string (addr); if (string) tx_printf ("%c%03d", (option_inform) ? 's' : 'S', string); addr = (unsigned long) value; dictionary = in_dictionary (addr); if (dictionary) { if (string) tx_printf (" %s ", (option_inform) ? "or" : "OR"); tx_printf ("\""); (void) decode_text (&addr); tx_printf ("\""); } if (!dictionary && !string) print_integer (value, addr_mode == BYTE_IMMED); } break; case VAR: if (decode.first_pass == 0) { if (value == 0) tx_printf ("%s", (option_inform) ? "sp" : "(SP)+"); else print_variable(value); #ifdef DELETEME if (value < 16) { if (option_inform) tx_printf ("local%d", value - 1); else tx_printf ("L%02x", value - 1); } else tx_printf ("%c%02x", (option_inform) ? 'g' : 'G', value - 16); #endif } else { if ((int) value > 0 && (int) value < 16 && (int) value > locals_count) return (1); } break; case NUMBER: if (decode.first_pass == 0) print_integer (value, addr_mode == BYTE_IMMED); break; case PROPNUM: if (decode.first_pass == 0) if (!print_property_name(property_names_base, value)) print_integer (value, addr_mode == BYTE_IMMED); break; case ATTRNUM: if (decode.first_pass == 0) if (!print_attribute_name(attr_names_base, value)) print_integer (value, addr_mode == BYTE_IMMED); break; case LOW_ADDR: if (decode.first_pass == 0) { addr = (unsigned long) value; dictionary = in_dictionary (addr); if (dictionary) { tx_printf ("\""); (void) decode_text (&addr); tx_printf ("\""); } else print_integer (value, addr_mode == BYTE_IMMED); } break; case ROUTINE: addr = (unsigned long) code_scaler * value + (unsigned long) header.routines_offset * story_scaler; if (decode.first_pass == 0) { if (option_labels) if (value != 0) { routine = lookup_routine (addr, 0); if (routine != 0) tx_printf ("%c%04d", (option_inform) ? 'r' : 'R', routine); else { (void) fprintf (stderr, "\nWarning: found call to nonexistent routine!\n"); tx_printf ("%lx", addr); } } else print_integer (value, addr_mode == BYTE_IMMED); else tx_printf ("%lx", addr); } else { if (addr < decode.low_address && addr >= code_base) { vars = read_data_byte (&addr); if (vars >= 0 && vars <= 15) decode.low_address = addr - 1; } if (addr > decode.high_address && addr < file_size) { vars = read_data_byte (&addr); if (vars >= 0 && vars <= 15) decode.high_address = addr - 1; } } break; case OBJECT: if (decode.first_pass == 0) { if (value == 0 || print_object_desc (value) == 0) print_integer (value, addr_mode == BYTE_IMMED); } break; case STATIC: if (decode.first_pass == 0) { addr = (unsigned long) code_scaler * value + (unsigned long) story_scaler * header.strings_offset; string = lookup_string (addr); if (string != 0) tx_printf ("%c%03d", (option_inform) ? 's' : 'S', (int) string); else { #ifndef TXD_DEBUG (void) fprintf (stderr, "\nWarning: printing of nonexistent string\n"); #endif tx_printf ("%lx", addr); } } break; case LABEL: addr = decode.pc + (short) value - 2; if (decode.first_pass && addr < decode.low_address) return (1); if (option_labels) { if (decode.first_pass) add_label (addr); else tx_printf ("%c%04d", (option_inform) ? 'l' : 'L', lookup_label (addr, 1)); } else { if (decode.first_pass == 0) tx_printf ("%lx", addr); } if (addr > decode.high_pc) decode.high_pc = addr; break; case PCHAR: if (decode.first_pass == 0) if (isprint ((char) value)) tx_printf ("\'%c\'", (char) value); else print_integer (value, addr_mode == BYTE_IMMED); break; case VATTR: if (decode.first_pass == 0) { if (value == ROMAN) tx_printf ("%s", (option_inform) ? "roman" : "ROMAN"); else if (value == REVERSE) tx_printf ("%s", (option_inform) ? "reverse" : "REVERSE"); else if (value == BOLDFACE) tx_printf ("%s", (option_inform) ? "boldface" : "BOLDFACE"); else if (value == EMPHASIS) tx_printf ("%s", (option_inform) ? "emphasis" : "EMPHASIS"); else if (value == FIXED_FONT) tx_printf ("%s", (option_inform) ? "fixed_font" : "FIXED_FONT"); else print_integer (value, addr_mode == BYTE_IMMED); } break; case PATTR: if (decode.first_pass == 0) { if ((int) value == 1) tx_printf ("%s", (option_inform) ? "output_enable" : "OUTPUT_ENABLE"); else if ((int) value == 2) tx_printf ("%s", (option_inform) ? "scripting_enable" : "SCRIPTING_ENABLE"); else if ((int) value == 3) tx_printf ("%s", (option_inform) ? "redirect_enable" : "REDIRECT_ENABLE"); else if ((int) value == 4) tx_printf ("%s", (option_inform) ? "record_enable" : "RECORD_ENABLE"); else if ((int) value == -1) tx_printf ("%s", (option_inform) ? "output_disable" : "OUTPUT_DISABLE"); else if ((int) value == -2) tx_printf ("%s", (option_inform) ? "scripting_disable" : "SCRIPTING_DISABLE"); else if ((int) value == -3) tx_printf ("%s", (option_inform) ? "redirect_disable" : "REDIRECT_DISABLE"); else if ((int) value == -4) tx_printf ("%s", (option_inform) ? "record_disable" : "RECORD_DISABLE"); else print_integer (value, addr_mode == BYTE_IMMED); } break; case INDIRECT: if (decode.first_pass == 0) { if (value == 0) tx_printf ("[%s]", (option_inform) ? "sp" : "(SP)+"); else { tx_printf("["); print_variable(value); tx_printf("]"); #ifdef DELETEME if (value < 16) { if (option_inform) tx_printf ("[local%d]", value - 1); else tx_printf ("[L%02x]", value - 1); } else tx_printf ("[%c%02x]", (option_inform) ? 'g' : 'G', value - 16); #endif } } break; default: (void) fprintf (stderr, "\nFatal: bad operand type (%d)\n", (int) par); exit (EXIT_FAILURE); } return (0); }/* decode_parameter */ /* decode_extra - Decode branches, stores and text */ #ifdef __STDC__ static int decode_extra (void) #else static int decode_extra () #endif { unsigned long addr; zbyte_t firstbyte; if (opcode.extra == STORE || opcode.extra == BOTH) { addr = (zbyte_t) read_data_byte (&decode.pc); if (decode.first_pass == 0) { if (!option_inform || (option_inform >= 6)) tx_printf ("-> "); if (addr == 0) tx_printf ("%s", (option_inform) ? "sp" : "-(SP)"); else print_variable(addr); #ifdef DELETEME if (addr < 16) { if (option_inform) tx_printf ("local%ld", (unsigned long) (addr - 1)); else tx_printf ("L%02lx", (unsigned long) (addr - 1)); } else tx_printf ("%c%02lx", (option_inform) ? 'g' : 'G', (unsigned long) (addr - 16)); #endif if (opcode.extra == BOTH) tx_printf (" "); } else { if (addr > 0 && addr < 16 && addr > (unsigned long) locals_count) return (BAD_OPCODE); } } if (opcode.extra == BRANCH || opcode.extra == BOTH) { addr = firstbyte = (zbyte_t) read_data_byte (&decode.pc); addr &= 0x7f; if (addr & 0x40) addr &= 0x3f; else { addr = (addr << 8) | (unsigned long) read_data_byte (&decode.pc); if (addr & 0x2000) { addr &= 0x1fff; addr |= ~0x1fff; } } if (decode.first_pass == 0) { if ((addr > 1) && !(firstbyte & 0x40) && (option_inform >= 6) && (option_labels)) { tx_printf("?"); /* Inform 6 long branch */ } if (firstbyte & 0x80) tx_printf ("%s", (option_inform) ? "" : "[TRUE]"); else tx_printf ("%s", (option_inform) ? "~" : "[FALSE]"); } if (addr == 0) { if (decode.first_pass == 0) tx_printf ("%s", (option_inform) ? "rfalse" : " RFALSE"); } else if (addr == 1) { if (decode.first_pass == 0) tx_printf ("%s", (option_inform) ? "rtrue" : " RTRUE"); } else { addr = decode.pc + addr - 2; if (decode.first_pass && addr < start_of_routine) return (BAD_OPCODE); if (option_labels) { if (decode.first_pass) add_label (addr); else tx_printf ("%s%04d", (option_inform) ? "l" : " L", (int) lookup_label (addr, 1)); } else { if (decode.first_pass == 0) tx_printf ("%s%lx", (option_inform) ? "" : " ", (unsigned long) addr); } if (addr > decode.high_pc) decode.high_pc = addr; } } if (opcode.extra == TEXT) { if (decode.first_pass) { while ((short) read_data_word (&decode.pc) >= 0) ; } else print_text (&decode.pc); } if (opcode.type == RETURN) if (decode.pc > decode.high_pc) return (END_OF_ROUTINE); return (END_OF_INSTRUCTION); }/* decode_outputs */ /* decode_strings - Dump text after end of code */ #ifdef __STDC__ static void decode_strings (unsigned long pc) #else static void decode_strings (pc) unsigned long pc; #endif { int count = 1; pc = ROUND_DATA (pc); tx_printf ("\n[Start of text"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) pc); tx_printf ("]\n\n"); while (pc < file_size) { if (option_labels) tx_printf ("%c%03d: ", (option_inform) ? 's' : 'S', (int) count++); else tx_printf ("%5lx: S%03d ", (unsigned long) pc, (int) count++); print_text (&pc); tx_printf ("\n"); pc = ROUND_CODE (pc); } tx_printf ("\n[End of text"); if (option_labels == 0) tx_printf (" at %lx", (unsigned long) pc); tx_printf ("]\n\n[End of file]\n"); }/* decode_strings */ /* scan_strings - build string address table */ #ifdef __STDC__ static void scan_strings (unsigned long pc) #else static void scan_strings (pc) unsigned long pc; #endif { unsigned long old_pc; int count = 1; cref_item_t *cref_item; zword_t data; pc = ROUND_DATA (pc); old_pc = pc; while (pc < file_size) { cref_item = (cref_item_t *) malloc (sizeof (cref_item_t)); if (cref_item == NULL) { (void) fprintf (stderr, "\nFatal: insufficient memory\n"); exit (EXIT_FAILURE); } cref_item->address = pc; cref_item->number = count++; cref_item->next = strings_base; strings_base = cref_item; old_pc = pc; do data = (zword_t) read_data_word (&pc); while (pc < file_size && ((unsigned int) data & 0x8000) == 0); pc = ROUND_CODE (pc); if ((unsigned int) data & 0x8000) old_pc = pc; } file_size = old_pc; }/* scan_strings */ /* lookup_string - lookup a string address */ #ifdef __STDC__ static int lookup_string (unsigned long addr) #else static int lookup_string (addr) unsigned long addr; #endif { cref_item_t *cref_item; if (addr <= decode.high_address || addr >= file_size) return (0); for (cref_item = strings_base; cref_item != NULL; cref_item = cref_item->next) if (cref_item->address == addr) return (cref_item->number); return (0); }/* lookup_string */ #ifdef __STDC__ static void lookup_verb (unsigned long addr) #else static void lookup_verb (addr) unsigned long addr; #endif { unsigned long address, routine; unsigned int i, first; first = 1; address = action_table_base; for (i = 0; i < action_count; i++) { routine = (unsigned long) read_data_word (&address) * code_scaler + (unsigned long) story_scaler * header.routines_offset; if (routine == addr) { if (first) { tx_printf (" Action routine for:\n"); tx_printf (" "); tx_fix_margin(1); first = 0; } show_syntax_of_action(i, verb_table_base, verb_count, parser_type, prep_type, prep_table_base, attr_names_base); } } tx_fix_margin(0); first = 1; address = preact_table_base; if (parser_type >= inform_gv2) { if (is_gv2_parsing_routine(addr, verb_table_base, verb_count)) { tx_printf (" Parsing routine for:\n"); tx_printf (" "); tx_fix_margin(1); first = 0; show_syntax_of_parsing_routine( addr, verb_table_base, verb_count, parser_type, prep_type, prep_table_base, attr_names_base); } } else if (parser_type >= inform5_grammar) { for (i = 0; i < parse_count; i++) { routine = (unsigned long) read_data_word (&address) * code_scaler + (unsigned long) story_scaler * header.routines_offset; if (routine == addr) { if (first) { tx_printf (" Parsing routine for:\n"); tx_printf (" "); tx_fix_margin(1); first = 0; } show_syntax_of_parsing_routine(i, verb_table_base, verb_count, parser_type, prep_type, prep_table_base, attr_names_base); } } } else { for (i = 0; i < action_count; i++) { routine = (unsigned long) read_data_word (&address) * code_scaler + (unsigned long) story_scaler * header.routines_offset; if (routine == addr) { if (first) { tx_printf (" Pre-action routine for:\n"); tx_printf (" "); tx_fix_margin(1); first = 0; } show_syntax_of_action(i, verb_table_base, verb_count, parser_type, prep_type, prep_table_base, attr_names_base); } } } tx_fix_margin(0); }/* lookup_verb */ #ifdef __STC__ static void setup_dictionary (void) #else static void setup_dictionary () #endif { dict_start = (unsigned long) header.dictionary; dict_start += (unsigned long) read_data_byte (&dict_start); word_size = (unsigned long) read_data_byte (&dict_start); word_count = (unsigned long) read_data_word (&dict_start); dict_end = dict_start + (word_count * word_size); }/* setup_dictionary */ #ifdef __STDC__ static int in_dictionary (unsigned long word_address) #else static int in_dictionary (word_address) unsigned long word_address; #endif { if (word_address < dict_start || word_address > dict_end) return (0); if ((word_address - dict_start) % word_size == 0) return (1); return (0); }/* in_dictionary */ #ifdef __STDC__ static void add_label (unsigned long addr) #else static void add_label (addr) unsigned long addr; #endif { cref_item_t *cref_item, **prev_item, *next_item; if (current_routine == NULL) return; prev_item = ¤t_routine->child; next_item = current_routine->child; while (next_item != NULL && next_item->address < addr) { prev_item = &(next_item->next); next_item = next_item->next; } if (next_item == NULL || next_item->address != addr) { cref_item = (cref_item_t *) malloc (sizeof (cref_item_t)); if (cref_item == NULL) { (void) fprintf (stderr, "\nFatal: insufficient memory\n"); exit (EXIT_FAILURE); } cref_item->next = next_item; *prev_item = cref_item; cref_item->child = NULL; cref_item->address = addr; cref_item->number = 0; } }/* add_label */ #ifdef __STDC__ static void add_routine (unsigned long addr) #else static void add_routine (addr) unsigned long addr; #endif { cref_item_t *cref_item, **prev_item, *next_item; prev_item = &routines_base; next_item = routines_base; while (next_item != NULL && next_item->address < addr) { prev_item = &(next_item->next); next_item = next_item->next; } if (next_item == NULL || next_item->address != addr) { cref_item = (cref_item_t *) malloc (sizeof (cref_item_t)); if (cref_item == NULL) { (void) fprintf (stderr, "\nFatal: insufficient memory\n"); exit (EXIT_FAILURE); } cref_item->next = next_item; *prev_item = cref_item; cref_item->child = NULL; cref_item->address = addr; cref_item->number = 0; } else cref_item = next_item; current_routine = cref_item; }/* add_routine */ #ifdef __STDC__ static int lookup_label (unsigned long addr, int flag) #else static int lookup_label (addr, flag) unsigned long addr; int flag; #endif { cref_item_t *cref_item = current_routine->child; int label; while (cref_item != NULL && cref_item->address != addr) cref_item = cref_item->next; if (cref_item == NULL) { label = 0; if (flag) { (void) fprintf (stderr, "\nFatal: cannot find label!\n"); exit (EXIT_FAILURE); } } else label = cref_item->number; return (label); }/* lookup_label */ #ifdef __STDC__ static int lookup_routine (unsigned long addr, int flag) #else static int lookup_routine (addr, flag) unsigned long addr; int flag; #endif { cref_item_t *cref_item = routines_base; while (cref_item != NULL && cref_item->address != addr) cref_item = cref_item->next; if (cref_item == NULL) { if (flag) { (void) fprintf (stderr, "\nFatal: cannot find routine!\n"); exit (EXIT_FAILURE); } else return (0); } if (flag) current_routine = cref_item; return (cref_item->number); }/* lookup_routine */ #ifdef __STDC__ static void renumber_cref (cref_item_t *cref_item) #else static void renumber_cref (cref_item) cref_item_t *cref_item; #endif { int number = 1; while (cref_item != NULL) { if (start_data_pc == 0 || cref_item->address < start_data_pc || cref_item->address >= end_data_pc) cref_item->number = number++; renumber_cref (cref_item->child); cref_item = cref_item->next; } }/* renumber_cref */ #ifdef __STDC__ static void free_cref (cref_item_t *cref_item) #else static void free_cref (cref_item) cref_item_t *cref_item; #endif { cref_item_t *t; while (cref_item != NULL) { free_cref (cref_item->child); t = cref_item->next; free (cref_item); cref_item = t; } }/* free_cref */ #ifdef __STDC__ static int print_object_desc (unsigned int obj) #else static int print_object_desc (obj) unsigned int obj; #endif { unsigned long address; address = (unsigned long) header.objects; if ((unsigned int) header.version < V4) address += ((P3_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * O3_SIZE) + O3_PROPERTY_OFFSET; else address += ((P4_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * O4_SIZE) + O4_PROPERTY_OFFSET; address = (unsigned long) read_data_word (&address); if ((unsigned int) read_data_byte (&address)) { tx_printf ("\""); (void) decode_text (&address); tx_printf ("\""); } else obj = 0; return (obj); }/* print_object_desc */ #ifdef __STDC__ static void print_text (unsigned long *addr) #else static void print_text (addr) unsigned long *addr; #endif { tx_printf ("\""); (void) decode_text (addr); tx_printf ("\""); }/* print_text */ #ifdef __STDC___ static void print_integer (unsigned int value, int flag) #else static void print_integer (value, flag) unsigned int value; int flag; #endif { if (flag) tx_printf ("#%02x", value); else tx_printf ("#%04x", value); } #ifdef __STDC__ static void dump_data (unsigned long start_addr, unsigned long end_addr) #else static void dump_data (start_addr, end_addr) unsigned long start_addr; unsigned long end_addr; #endif { int i, c; unsigned long addr, save_addr, low_addr, high_addr; low_addr = start_addr & ~15; high_addr = (end_addr + 15) & ~15; for (addr = low_addr; addr < high_addr; ) { tx_printf ("%5lx: ", (unsigned long) addr); save_addr = addr; for (i = 0; i < 16; i++) { if (addr < start_addr || addr > end_addr) { tx_printf (" "); addr++; } else tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); } addr = save_addr; for (i = 0; i < 16; i++) { if (addr < start_addr || addr > end_addr) { tx_printf (" "); addr++; } else { c = read_data_byte (&addr); tx_printf ("%c", (char) ((isprint (c)) ? c : '.')); } } tx_printf ("\n"); } }/* dump_data */ #ifdef __STDC__ static void dump_opcode (unsigned long addr, int op, int class, int *par, int extra) #else static void dump_opcode (addr, op, class, par, extra) unsigned long addr; int op; int class; int *par; int extra; #endif { int opers, modes, addr_mode, maxopers, count; unsigned char t; unsigned long save_addr; count = 0; addr--; if (class == EXTENDED_OPERAND) { addr--; tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); count++; } tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); count++; if (class == ONE_OPERAND) dump_operand (&addr, (op >> 4) & 0x03, 0, par, &count); if (class == TWO_OPERAND) { dump_operand (&addr, (op & 0x40) ? VARIABLE : BYTE_IMMED, 0, par, &count); dump_operand (&addr, (op & 0x20) ? VARIABLE : BYTE_IMMED, 1, par, &count); } if (class == VARIABLE_OPERAND || class == EXTENDED_OPERAND) { if ((op & 0x3f) == 0x2c || (op & 0x3f) == 0x3a) { save_addr = addr; modes = read_data_word (&addr); addr = save_addr; tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); count += 2; maxopers = 8; } else { save_addr = addr; modes = read_data_byte (&addr); addr = save_addr; tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); count++; maxopers = 4; } for (addr_mode = 0, opers = 0; (addr_mode != NO_OPERAND) && maxopers; maxopers--) { addr_mode = (modes >> ((maxopers - 1) * 2)) & 0x03; if (addr_mode != NO_OPERAND) { dump_operand (&addr, addr_mode, opers, par, &count); opers++; } } } if (extra == TEXT) { tx_printf ("..."); count++; } if (extra == STORE || extra == BOTH) { tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); count++; } if (extra == BRANCH || extra == BOTH) { t = (unsigned char) read_data_byte (&addr); tx_printf ("%02x ", (unsigned int) t); count++; if (((unsigned int) t & 0x40) == 0) { tx_printf ("%02x ", (unsigned int) read_data_byte (&addr)); count++; } } if (count > 8) tx_printf ("\n "); else for (; count < 8; count++) tx_printf (" "); }/* dump_opcode */ #ifdef __STDC__ static void dump_operand (unsigned long *addr, int addr_mode, int opers, int *par, int *count) #else static void dump_operand (addr, addr_mode, opers, par, count) unsigned long *addr; int addr_mode; int opers; int *par; int *count; #endif { if (opers < 4 && par[opers] == VAR) addr_mode = VARIABLE; if (addr_mode == WORD_IMMED) { tx_printf ("%02x ", (unsigned int) read_data_byte (addr)); tx_printf ("%02x ", (unsigned int) read_data_byte (addr)); *count += 2; } if (addr_mode == BYTE_IMMED || addr_mode == VARIABLE) { tx_printf ("%02x ", (unsigned int) read_data_byte (addr)); (*count)++; } }/* dump_operand */ #ifdef __STDC__ static void print_variable (int varnum) #else static void print_variable (varnum) int varnum; #endif { if (varnum < 16) { if (option_symbols && print_local_name(start_of_routine, varnum - 1)) /* null */; else if (option_inform) tx_printf ("local%d", varnum - 1); else tx_printf ("L%02x", varnum - 1); } else if (option_symbols && print_global_name(start_of_routine, varnum - 16)) /* null */; else tx_printf ("%c%02x", (option_inform) ? 'g' : 'G', varnum - 16); }