/* process.c - Interpreter loop and program control * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" #ifdef DJGPP#include "djfrotz.h"#endif zword zargs[8];int zargc; static int finished = 0; static void __extended__ (void);static void __illegal__ (void); void (*op0_opcodes[0x10]) (void) = { z_rtrue, z_rfalse, z_print, z_print_ret, z_nop, z_save, z_restore, z_restart, z_ret_popped, z_catch, z_quit, z_new_line, z_show_status, z_verify, __extended__, z_piracy}; void (*op1_opcodes[0x10]) (void) = { z_jz, z_get_sibling, z_get_child, z_get_parent, z_get_prop_len, z_inc, z_dec, z_print_addr, z_call_s, z_remove_obj, z_print_obj, z_ret, z_jump, z_print_paddr, z_load, z_call_n}; void (*var_opcodes[0x40]) (void) = { __illegal__, z_je, z_jl, z_jg, z_dec_chk, z_inc_chk, z_jin, z_test, z_or, z_and, z_test_attr, z_set_attr, z_clear_attr, z_store, z_insert_obj, z_loadw, z_loadb, z_get_prop, z_get_prop_addr, z_get_next_prop, z_add, z_sub, z_mul, z_div, z_mod, z_call_s, z_call_n, z_set_colour, z_throw, __illegal__, __illegal__, __illegal__, z_call_s, z_storew, z_storeb, z_put_prop, z_read, z_print_char, z_print_num, z_random, z_push, z_pull, z_split_window, z_set_window, z_call_s, z_erase_window, z_erase_line, z_set_cursor, z_get_cursor, z_set_text_style, z_buffer_mode, z_output_stream, z_input_stream, z_sound_effect, z_read_char, z_scan_table, z_not, z_call_n, z_call_n, z_tokenise, z_encode_text, z_copy_table, z_print_table, z_check_arg_count}; void (*ext_opcodes[0x1d]) (void) = { z_save, z_restore, z_log_shift, z_art_shift, z_set_font, z_draw_picture, z_picture_data, z_erase_picture, z_set_margins, z_save_undo, z_restore_undo, z_print_unicode, z_check_unicode, __illegal__, __illegal__, __illegal__, z_move_window, z_window_size, z_window_style, z_get_wind_prop, z_scroll_window, z_pop_stack, z_read_mouse, z_mouse_window, z_push_stack, z_put_wind_prop, z_print_form, z_make_menu, z_picture_table}; /* * init_process * * Initialize process variables. * */void init_process (void){ finished = 0;} /* init_process */ /* * load_operand * * Load an operand, either a variable or a constant. * */static void load_operand (zbyte type){ zword value; if (type & 2) { /* variable */ zbyte variable; CODE_BYTE (variable) if (variable == 0) value = *sp++; else if (variable < 16) value = *(fp - variable); else { zword addr = h_globals + 2 * (variable - 16); LOW_WORD (addr, value) } } else if (type & 1) { /* small constant */ zbyte bvalue; CODE_BYTE (bvalue) value = bvalue; } else CODE_WORD (value) /* large constant */ zargs[zargc++] = value; }/* load_operand */ /* * load_all_operands * * Given the operand specifier byte, load all (up to four) operands * for a VAR or EXT opcode. * */static void load_all_operands (zbyte specifier){ int i; for (i = 6; i >= 0; i -= 2) { zbyte type = (specifier >> i) & 0x03; if (type == 3) break; load_operand (type); } }/* load_all_operands */ /* * interpret * * Z-code interpreter main loop * */void interpret (void){ do { zbyte opcode; CODE_BYTE (opcode) zargc = 0; if (opcode < 0x80) { /* 2OP opcodes */ load_operand ((zbyte) (opcode & 0x40) ? 2 : 1); load_operand ((zbyte) (opcode & 0x20) ? 2 : 1); var_opcodes[opcode & 0x1f] (); } else if (opcode < 0xb0) { /* 1OP opcodes */ load_operand ((zbyte) (opcode >> 4)); op1_opcodes[opcode & 0x0f] (); } else if (opcode < 0xc0) { /* 0OP opcodes */ op0_opcodes[opcode - 0xb0] (); } else { /* VAR opcodes */ zbyte specifier1; zbyte specifier2; if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */ CODE_BYTE (specifier1) /* and 0xfa are */ CODE_BYTE (specifier2) /* call opcodes */ load_all_operands (specifier1); /* with up to 8 */ load_all_operands (specifier2); /* arguments */ } else { CODE_BYTE (specifier1) load_all_operands (specifier1); } var_opcodes[opcode - 0xc0] (); } #if defined(DJGPP) && defined(SOUND_SUPPORT) if (end_of_sound_flag) end_of_sound ();#endif } while (finished == 0); finished--; }/* interpret */ /* * call * * Call a subroutine. Save PC and FP then load new PC and initialise * new stack frame. Note that the caller may legally provide less or * more arguments than the function actually has. The call type "ct" * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call). * */void call (zword routine, int argc, zword *args, int ct){ long pc; zword value; zbyte count; int i; if (sp - stack < 4) runtime_error (ERR_STK_OVF); GET_PC (pc) *--sp = (zword) (pc >> 9); *--sp = (zword) (pc & 0x1ff); *--sp = (zword) (fp - stack - 1); *--sp = (zword) (argc | (ct << 12)); fp = sp; frame_count++; /* Calculate byte address of routine */ if (h_version <= V3) pc = (long) routine << 1; else if (h_version <= V5) pc = (long) routine << 2; else if (h_version <= V7) pc = ((long) routine << 2) + ((long) h_functions_offset << 3); else /* h_version == V8 */ pc = (long) routine << 3; if (pc >= story_size) runtime_error (ERR_ILL_CALL_ADDR); SET_PC (pc) /* Initialise local variables */ CODE_BYTE (count) if (count > 15) runtime_error (ERR_CALL_NON_RTN); if (sp - stack < count) runtime_error (ERR_STK_OVF); fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */ value = 0; for (i = 0; i < count; i++) { if (h_version <= V4) /* V1 to V4 games provide default */ CODE_WORD (value) /* values for all local variables */ *--sp = (zword) ((argc-- > 0) ? args[i] : value); } /* Start main loop for direct calls */ if (ct == 2) interpret (); }/* call */ /* * ret * * Return from the current subroutine and restore the previous stack * frame. The result may be stored (0), thrown away (1) or pushed on * the stack (2). In the latter case a direct call has been finished * and we must exit the interpreter loop. * */void ret (zword value){ long pc; int ct; if (sp > fp) runtime_error (ERR_STK_UNDF); sp = fp; ct = *sp++ >> 12; frame_count--; fp = stack + 1 + *sp++; pc = *sp++; pc = ((long) *sp++ << 9) | pc; SET_PC (pc) /* Handle resulting value */ if (ct == 0) store (value); if (ct == 2) *--sp = value; /* Stop main loop for direct calls */ if (ct == 2) finished++; }/* ret */ /* * branch * * Take a jump after an instruction based on the flag, either true or * false. The branch can be short or long; it is encoded in one or two * bytes respectively. When bit 7 of the first byte is set, the jump * takes place if the flag is true; otherwise it is taken if the flag * is false. When bit 6 of the first byte is set, the branch is short; * otherwise it is long. The offset occupies the bottom 6 bits of the * first byte plus all the bits in the second byte for long branches. * Uniquely, an offset of 0 means return false, and an offset of 1 is * return true. * */void branch (bool flag){ long pc; zword offset; zbyte specifier; zbyte off1; zbyte off2; CODE_BYTE (specifier) off1 = specifier & 0x3f; if (!flag) specifier ^= 0x80; if (!(specifier & 0x40)) { /* it's a long branch */ if (off1 & 0x20) /* propagate sign bit */ off1 |= 0xc0; CODE_BYTE (off2) offset = (off1 << 8) | off2; } else offset = off1; /* it's a short branch */ if (specifier & 0x80) { if (offset > 1) { /* normal branch */ GET_PC (pc) pc += (short) offset - 2; SET_PC (pc) } else ret (offset); /* special case, return 0 or 1 */ } }/* branch */ /* * store * * Store an operand, either as a variable or pushed on the stack. * */void store (zword value){ zbyte variable; CODE_BYTE (variable) if (variable == 0) *--sp = value; else if (variable < 16)Lines 1–500 of 776. Load more
/* process.c - Interpreter loop and program control * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" #ifdef DJGPP #include "djfrotz.h" #endif zword zargs[8]; int zargc; static int finished = 0; static void __extended__ (void); static void __illegal__ (void); void (*op0_opcodes[0x10]) (void) = { z_rtrue, z_rfalse, z_print, z_print_ret, z_nop, z_save, z_restore, z_restart, z_ret_popped, z_catch, z_quit, z_new_line, z_show_status, z_verify, __extended__, z_piracy }; void (*op1_opcodes[0x10]) (void) = { z_jz, z_get_sibling, z_get_child, z_get_parent, z_get_prop_len, z_inc, z_dec, z_print_addr, z_call_s, z_remove_obj, z_print_obj, z_ret, z_jump, z_print_paddr, z_load, z_call_n }; void (*var_opcodes[0x40]) (void) = { __illegal__, z_je, z_jl, z_jg, z_dec_chk, z_inc_chk, z_jin, z_test, z_or, z_and, z_test_attr, z_set_attr, z_clear_attr, z_store, z_insert_obj, z_loadw, z_loadb, z_get_prop, z_get_prop_addr, z_get_next_prop, z_add, z_sub, z_mul, z_div, z_mod, z_call_s, z_call_n, z_set_colour, z_throw, __illegal__, __illegal__, __illegal__, z_call_s, z_storew, z_storeb, z_put_prop, z_read, z_print_char, z_print_num, z_random, z_push, z_pull, z_split_window, z_set_window, z_call_s, z_erase_window, z_erase_line, z_set_cursor, z_get_cursor, z_set_text_style, z_buffer_mode, z_output_stream, z_input_stream, z_sound_effect, z_read_char, z_scan_table, z_not, z_call_n, z_call_n, z_tokenise, z_encode_text, z_copy_table, z_print_table, z_check_arg_count }; void (*ext_opcodes[0x1d]) (void) = { z_save, z_restore, z_log_shift, z_art_shift, z_set_font, z_draw_picture, z_picture_data, z_erase_picture, z_set_margins, z_save_undo, z_restore_undo, z_print_unicode, z_check_unicode, __illegal__, __illegal__, __illegal__, z_move_window, z_window_size, z_window_style, z_get_wind_prop, z_scroll_window, z_pop_stack, z_read_mouse, z_mouse_window, z_push_stack, z_put_wind_prop, z_print_form, z_make_menu, z_picture_table }; /* * init_process * * Initialize process variables. * */ void init_process (void) { finished = 0; } /* init_process */ /* * load_operand * * Load an operand, either a variable or a constant. * */ static void load_operand (zbyte type) { zword value; if (type & 2) { /* variable */ zbyte variable; CODE_BYTE (variable) if (variable == 0) value = *sp++; else if (variable < 16) value = *(fp - variable); else { zword addr = h_globals + 2 * (variable - 16); LOW_WORD (addr, value) } } else if (type & 1) { /* small constant */ zbyte bvalue; CODE_BYTE (bvalue) value = bvalue; } else CODE_WORD (value) /* large constant */ zargs[zargc++] = value; }/* load_operand */ /* * load_all_operands * * Given the operand specifier byte, load all (up to four) operands * for a VAR or EXT opcode. * */ static void load_all_operands (zbyte specifier) { int i; for (i = 6; i >= 0; i -= 2) { zbyte type = (specifier >> i) & 0x03; if (type == 3) break; load_operand (type); } }/* load_all_operands */ /* * interpret * * Z-code interpreter main loop * */ void interpret (void) { do { zbyte opcode; CODE_BYTE (opcode) zargc = 0; if (opcode < 0x80) { /* 2OP opcodes */ load_operand ((zbyte) (opcode & 0x40) ? 2 : 1); load_operand ((zbyte) (opcode & 0x20) ? 2 : 1); var_opcodes[opcode & 0x1f] (); } else if (opcode < 0xb0) { /* 1OP opcodes */ load_operand ((zbyte) (opcode >> 4)); op1_opcodes[opcode & 0x0f] (); } else if (opcode < 0xc0) { /* 0OP opcodes */ op0_opcodes[opcode - 0xb0] (); } else { /* VAR opcodes */ zbyte specifier1; zbyte specifier2; if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */ CODE_BYTE (specifier1) /* and 0xfa are */ CODE_BYTE (specifier2) /* call opcodes */ load_all_operands (specifier1); /* with up to 8 */ load_all_operands (specifier2); /* arguments */ } else { CODE_BYTE (specifier1) load_all_operands (specifier1); } var_opcodes[opcode - 0xc0] (); } #if defined(DJGPP) && defined(SOUND_SUPPORT) if (end_of_sound_flag) end_of_sound (); #endif } while (finished == 0); finished--; }/* interpret */ /* * call * * Call a subroutine. Save PC and FP then load new PC and initialise * new stack frame. Note that the caller may legally provide less or * more arguments than the function actually has. The call type "ct" * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call). * */ void call (zword routine, int argc, zword *args, int ct) { long pc; zword value; zbyte count; int i; if (sp - stack < 4) runtime_error (ERR_STK_OVF); GET_PC (pc) *--sp = (zword) (pc >> 9); *--sp = (zword) (pc & 0x1ff); *--sp = (zword) (fp - stack - 1); *--sp = (zword) (argc | (ct << 12)); fp = sp; frame_count++; /* Calculate byte address of routine */ if (h_version <= V3) pc = (long) routine << 1; else if (h_version <= V5) pc = (long) routine << 2; else if (h_version <= V7) pc = ((long) routine << 2) + ((long) h_functions_offset << 3); else /* h_version == V8 */ pc = (long) routine << 3; if (pc >= story_size) runtime_error (ERR_ILL_CALL_ADDR); SET_PC (pc) /* Initialise local variables */ CODE_BYTE (count) if (count > 15) runtime_error (ERR_CALL_NON_RTN); if (sp - stack < count) runtime_error (ERR_STK_OVF); fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */ value = 0; for (i = 0; i < count; i++) { if (h_version <= V4) /* V1 to V4 games provide default */ CODE_WORD (value) /* values for all local variables */ *--sp = (zword) ((argc-- > 0) ? args[i] : value); } /* Start main loop for direct calls */ if (ct == 2) interpret (); }/* call */ /* * ret * * Return from the current subroutine and restore the previous stack * frame. The result may be stored (0), thrown away (1) or pushed on * the stack (2). In the latter case a direct call has been finished * and we must exit the interpreter loop. * */ void ret (zword value) { long pc; int ct; if (sp > fp) runtime_error (ERR_STK_UNDF); sp = fp; ct = *sp++ >> 12; frame_count--; fp = stack + 1 + *sp++; pc = *sp++; pc = ((long) *sp++ << 9) | pc; SET_PC (pc) /* Handle resulting value */ if (ct == 0) store (value); if (ct == 2) *--sp = value; /* Stop main loop for direct calls */ if (ct == 2) finished++; }/* ret */ /* * branch * * Take a jump after an instruction based on the flag, either true or * false. The branch can be short or long; it is encoded in one or two * bytes respectively. When bit 7 of the first byte is set, the jump * takes place if the flag is true; otherwise it is taken if the flag * is false. When bit 6 of the first byte is set, the branch is short; * otherwise it is long. The offset occupies the bottom 6 bits of the * first byte plus all the bits in the second byte for long branches. * Uniquely, an offset of 0 means return false, and an offset of 1 is * return true. * */ void branch (bool flag) { long pc; zword offset; zbyte specifier; zbyte off1; zbyte off2; CODE_BYTE (specifier) off1 = specifier & 0x3f; if (!flag) specifier ^= 0x80; if (!(specifier & 0x40)) { /* it's a long branch */ if (off1 & 0x20) /* propagate sign bit */ off1 |= 0xc0; CODE_BYTE (off2) offset = (off1 << 8) | off2; } else offset = off1; /* it's a short branch */ if (specifier & 0x80) { if (offset > 1) { /* normal branch */ GET_PC (pc) pc += (short) offset - 2; SET_PC (pc) } else ret (offset); /* special case, return 0 or 1 */ } }/* branch */ /* * store * * Store an operand, either as a variable or pushed on the stack. * */ void store (zword value) { zbyte variable; CODE_BYTE (variable) if (variable == 0) *--sp = value; else if (variable < 16)