X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=interpreters%2Ffrotz%2Fprocess.c;fp=interpreters%2Ffrotz%2Fprocess.c;h=e79edf518eea73f7a9e752c4a2eae4fe698454b1;hb=b1f1dc50b22b30c4d7176e1ff7c0805e80fe0724;hp=0000000000000000000000000000000000000000;hpb=50176172d18ae72d019181725c5629d45d21c548;p=projects%2Fchimara%2Fchimara.git diff --git a/interpreters/frotz/process.c b/interpreters/frotz/process.c new file mode 100644 index 0000000..e79edf5 --- /dev/null +++ b/interpreters/frotz/process.c @@ -0,0 +1,797 @@ +/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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, + __illegal__, // glkify - z_draw_picture, + __illegal__, // glkify - z_picture_data, + __illegal__, // glkify - z_erase_picture, + __illegal__, // glkify - z_set_margins, + z_save_undo, + z_restore_undo, + z_print_unicode, + z_check_unicode, + __illegal__, + __illegal__, + __illegal__, + __illegal__, // glkify - z_move_window, + __illegal__, // glkify - z_window_size, + __illegal__, // glkify - z_window_style, + __illegal__, // glkify - z_get_wind_prop, + __illegal__, // glkify - z_scroll_window, + z_pop_stack, + __illegal__, // glkify - z_read_mouse, + __illegal__, // glkify - z_mouse_window, + z_push_stack, + __illegal__, // glkify - z_put_wind_prop, + z_print_form, + z_make_menu, + __illegal__, // glkify - 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 << (f_setup.save_quetzal ? 12 : 8))); + + 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); + + if (f_setup.save_quetzal) + 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++ >> (f_setup.save_quetzal ? 12 : 8); + 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) + *(fp - variable) = value; + else { + zword addr = h_globals + 2 * (variable - 16); + SET_WORD (addr, value) + } + +}/* store */ + +/* + * direct_call + * + * Call the interpreter loop directly. This is necessary when + * + * - a sound effect has been finished + * - a read instruction has timed out + * - a newline countdown has hit zero + * + * The interpreter returns the result value on the stack. + * + */ + +int direct_call (zword addr) +{ + zword saved_zargs[8]; + int saved_zargc; + int i; + + /* Calls to address 0 return false */ + + if (addr == 0) + return 0; + + /* Save operands and operand count */ + + for (i = 0; i < 8; i++) + saved_zargs[i] = zargs[i]; + + saved_zargc = zargc; + + /* Call routine directly */ + + call (addr, 0, 0, 2); + + /* Restore operands and operand count */ + + for (i = 0; i < 8; i++) + zargs[i] = saved_zargs[i]; + + zargc = saved_zargc; + + /* Resulting value lies on top of the stack */ + + return (short) *sp++; + +}/* direct_call */ + +/* + * __extended__ + * + * Load and execute an extended opcode. + * + */ + +static void __extended__ (void) +{ + zbyte opcode; + zbyte specifier; + + CODE_BYTE (opcode) + CODE_BYTE (specifier) + + load_all_operands (specifier); + + if (opcode < 0x1d) /* extended opcodes from 0x1d on */ + ext_opcodes[opcode] (); /* are reserved for future spec' */ + +}/* __extended__ */ + +/* + * __illegal__ + * + * Exit game because an unknown opcode has been hit. + * + */ + +static void __illegal__ (void) +{ + + runtime_error (ERR_ILL_OPCODE); + +}/* __illegal__ */ + +/* + * z_catch, store the current stack frame for later use with z_throw. + * + * no zargs used + * + */ + +void z_catch (void) +{ + + store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack)); + +}/* z_catch */ + +/* + * z_throw, go back to the given stack frame and return the given value. + * + * zargs[0] = value to return + * zargs[1] = stack frame + * + */ + +void z_throw (void) +{ + + if (f_setup.save_quetzal) { + if (zargs[1] > frame_count) + runtime_error (ERR_BAD_FRAME); + + /* Unwind the stack a frame at a time. */ + for (; frame_count > zargs[1]; --frame_count) + fp = stack + 1 + fp[1]; + } else { + if (zargs[1] > STACK_SIZE) + runtime_error (ERR_BAD_FRAME); + + fp = stack + zargs[1]; + } + + ret (zargs[0]); + +}/* z_throw */ + +/* + * z_call_n, call a subroutine and discard its result. + * + * zargs[0] = packed address of subroutine + * zargs[1] = first argument (optional) + * ... + * zargs[7] = seventh argument (optional) + * + */ + +void z_call_n (void) +{ + + if (zargs[0] != 0) + call (zargs[0], zargc - 1, zargs + 1, 1); + +}/* z_call_n */ + +/* + * z_call_s, call a subroutine and store its result. + * + * zargs[0] = packed address of subroutine + * zargs[1] = first argument (optional) + * ... + * zargs[7] = seventh argument (optional) + * + */ + +void z_call_s (void) +{ + + if (zargs[0] != 0) + call (zargs[0], zargc - 1, zargs + 1, 0); + else + store (0); + +}/* z_call_s */ + +/* + * z_check_arg_count, branch if subroutine was called with >= n arg's. + * + * zargs[0] = number of arguments + * + */ + +void z_check_arg_count (void) +{ + + if (fp == stack + STACK_SIZE) + branch (zargs[0] == 0); + else + branch (zargs[0] <= (*fp & 0xff)); + +}/* z_check_arg_count */ + +/* + * z_jump, jump unconditionally to the given address. + * + * zargs[0] = PC relative address + * + */ + +void z_jump (void) +{ + long pc; + + GET_PC (pc) + + pc += (short) zargs[0] - 2; + + if (pc >= story_size) + runtime_error (ERR_ILL_JUMP_ADDR); + + SET_PC (pc) + +}/* z_jump */ + +/* + * z_nop, no operation. + * + * no zargs used + * + */ + +void z_nop (void) +{ + + /* Do nothing */ + +}/* z_nop */ + +/* + * z_quit, stop game and exit interpreter. + * + * no zargs used + * + */ + +void z_quit (void) +{ + + finished = 9999; + +}/* z_quit */ + +/* + * z_ret, return from a subroutine with the given value. + * + * zargs[0] = value to return + * + */ + +void z_ret (void) +{ + + ret (zargs[0]); + +}/* z_ret */ + +/* + * z_ret_popped, return from a subroutine with a value popped off the stack. + * + * no zargs used + * + */ + +void z_ret_popped (void) +{ + + ret (*sp++); + +}/* z_ret_popped */ + +/* + * z_rfalse, return from a subroutine with false (0). + * + * no zargs used + * + */ + +void z_rfalse (void) +{ + + ret (0); + +}/* z_rfalse */ + +/* + * z_rtrue, return from a subroutine with true (1). + * + * no zargs used + * + */ + +void z_rtrue (void) +{ + + ret (1); + +}/* z_rtrue */