1 /* process.c - Interpreter loop and program control
2 * Copyright (c) 1995-1997 Stefan Jokisch
4 * This file is part of Frotz.
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
31 static int finished = 0;
33 static void __extended__ (void);
34 static void __illegal__ (void);
36 void (*op0_opcodes[0x10]) (void) = {
55 void (*op1_opcodes[0x10]) (void) = {
74 void (*var_opcodes[0x40]) (void) = {
141 void (*ext_opcodes[0x1e]) (void) = {
147 __illegal__, // glkify - z_draw_picture,
148 __illegal__, // glkify - z_picture_data,
149 __illegal__, // glkify - z_erase_picture,
150 __illegal__, // glkify - z_set_margins,
155 z_set_true_colour, /* spec 1.1 */
158 __illegal__, // glkify - z_move_window,
159 __illegal__, // glkify - z_window_size,
160 __illegal__, // glkify - z_window_style,
161 __illegal__, // glkify - z_get_wind_prop,
162 __illegal__, // glkify - z_scroll_window,
164 __illegal__, // glkify - z_read_mouse,
165 __illegal__, // glkify - z_mouse_window,
167 __illegal__, // glkify - z_put_wind_prop,
170 __illegal__, // glkify - z_picture_table
171 z_buffer_screen, /* spec 1.1 */
178 * Initialize process variables.
182 void init_proc (void)
191 * Load an operand, either a variable or a constant.
195 static void load_operand (zbyte type)
199 if (type & 2) { /* variable */
207 else if (variable < 16)
208 value = *(fp - variable);
210 zword addr = h_globals + 2 * (variable - 16);
211 LOW_WORD (addr, value)
214 } else if (type & 1) { /* small constant */
221 } else CODE_WORD (value) /* large constant */
223 zargs[zargc++] = value;
230 * Given the operand specifier byte, load all (up to four) operands
231 * for a VAR or EXT opcode.
235 static void load_all_operands (zbyte specifier)
239 for (i = 6; i >= 0; i -= 2) {
241 zbyte type = (specifier >> i) & 0x03;
250 }/* load_all_operands */
255 * Z-code interpreter main loop
259 void interpret (void)
269 if (opcode < 0x80) { /* 2OP opcodes */
271 load_operand ((zbyte) (opcode & 0x40) ? 2 : 1);
272 load_operand ((zbyte) (opcode & 0x20) ? 2 : 1);
274 var_opcodes[opcode & 0x1f] ();
276 } else if (opcode < 0xb0) { /* 1OP opcodes */
278 load_operand ((zbyte) (opcode >> 4));
280 op1_opcodes[opcode & 0x0f] ();
282 } else if (opcode < 0xc0) { /* 0OP opcodes */
284 op0_opcodes[opcode - 0xb0] ();
286 } else { /* VAR opcodes */
291 if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */
292 CODE_BYTE (specifier1) /* and 0xfa are */
293 CODE_BYTE (specifier2) /* call opcodes */
294 load_all_operands (specifier1); /* with up to 8 */
295 load_all_operands (specifier2); /* arguments */
297 CODE_BYTE (specifier1)
298 load_all_operands (specifier1);
301 var_opcodes[opcode - 0xc0] ();
305 #if defined(DJGPP) && defined(SOUND_SUPPORT)
306 if (end_of_sound_flag)
310 } while (finished == 0);
319 * Call a subroutine. Save PC and FP then load new PC and initialise
320 * new stack frame. Note that the caller may legally provide less or
321 * more arguments than the function actually has. The call type "ct"
322 * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
326 void call (zword routine, int argc, zword *args, int ct)
334 runtime_error (ERR_STK_OVF);
338 *--sp = (zword) (pc >> 9);
339 *--sp = (zword) (pc & 0x1ff);
340 *--sp = (zword) (fp - stack - 1);
341 *--sp = (zword) (argc | (ct << (option_save_quetzal ? 12 : 8)));
346 /* Calculate byte address of routine */
349 pc = (long) routine << 1;
350 else if (h_version <= V5)
351 pc = (long) routine << 2;
352 else if (h_version <= V7)
353 pc = ((long) routine << 2) + ((long) h_functions_offset << 3);
354 else if (h_version <= V8)
355 pc = (long) routine << 3;
356 else /* h_version == V9 */ {
357 long indirect = (long) routine << 2;
358 HIGH_LONG(indirect, pc);
361 if (pc >= story_size)
362 runtime_error (ERR_ILL_CALL_ADDR);
366 /* Initialise local variables */
371 runtime_error (ERR_CALL_NON_RTN);
372 if (sp - stack < count)
373 runtime_error (ERR_STK_OVF);
375 if (option_save_quetzal)
376 fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */
380 for (i = 0; i < count; i++) {
382 if (h_version <= V4) /* V1 to V4 games provide default */
383 CODE_WORD (value) /* values for all local variables */
385 *--sp = (zword) ((argc-- > 0) ? args[i] : value);
389 /* Start main loop for direct calls */
399 * Return from the current subroutine and restore the previous stack
400 * frame. The result may be stored (0), thrown away (1) or pushed on
401 * the stack (2). In the latter case a direct call has been finished
402 * and we must exit the interpreter loop.
406 void ret (zword value)
412 runtime_error (ERR_STK_UNDF);
416 ct = *sp++ >> (option_save_quetzal ? 12 : 8);
418 fp = stack + 1 + *sp++;
420 pc = ((long) *sp++ << 9) | pc;
424 /* Handle resulting value */
431 /* Stop main loop for direct calls */
441 * Take a jump after an instruction based on the flag, either true or
442 * false. The branch can be short or long; it is encoded in one or two
443 * bytes respectively. When bit 7 of the first byte is set, the jump
444 * takes place if the flag is true; otherwise it is taken if the flag
445 * is false. When bit 6 of the first byte is set, the branch is short;
446 * otherwise it is long. The offset occupies the bottom 6 bits of the
447 * first byte plus all the bits in the second byte for long branches.
448 * Uniquely, an offset of 0 means return false, and an offset of 1 is
453 void branch (bool flag)
461 CODE_BYTE (specifier)
463 off1 = specifier & 0x3f;
468 if (!(specifier & 0x40)) { /* it's a long branch */
470 if (off1 & 0x20) /* propagate sign bit */
475 offset = (off1 << 8) | off2;
477 } else offset = off1; /* it's a short branch */
479 if (specifier & 0x80) {
481 if (offset > 1) { /* normal branch */
484 pc += (short) offset - 2;
487 } else ret (offset); /* special case, return 0 or 1 */
495 * Store an operand, either as a variable or pushed on the stack.
499 void store (zword value)
507 else if (variable < 16)
508 *(fp - variable) = value;
510 zword addr = h_globals + 2 * (variable - 16);
511 SET_WORD (addr, value)
519 * Call the interpreter loop directly. This is necessary when
521 * - a sound effect has been finished
522 * - a read instruction has timed out
523 * - a newline countdown has hit zero
525 * The interpreter returns the result value on the stack.
529 int direct_call (zword addr)
531 zword saved_zargs[8];
535 /* Calls to address 0 return false */
540 /* Save operands and operand count */
542 for (i = 0; i < 8; i++)
543 saved_zargs[i] = zargs[i];
547 /* Call routine directly */
549 call (addr, 0, 0, 2);
551 /* Restore operands and operand count */
553 for (i = 0; i < 8; i++)
554 zargs[i] = saved_zargs[i];
558 /* Resulting value lies on top of the stack */
560 return (short) *sp++;
567 * Load and execute an extended opcode.
571 static void __extended__ (void)
577 CODE_BYTE (specifier)
579 load_all_operands (specifier);
581 if (opcode < 0x1e) /* extended opcodes from 0x1e on */
582 ext_opcodes[opcode] (); /* are reserved for future spec' */
589 * Exit game because an unknown opcode has been hit.
593 static void __illegal__ (void)
596 runtime_error (ERR_ILL_OPCODE);
601 * z_catch, store the current stack frame for later use with z_throw.
610 store (option_save_quetzal ? frame_count : (zword) (fp - stack));
615 * z_throw, go back to the given stack frame and return the given value.
617 * zargs[0] = value to return
618 * zargs[1] = stack frame
625 if (option_save_quetzal) {
626 if (zargs[1] > frame_count)
627 runtime_error (ERR_BAD_FRAME);
629 /* Unwind the stack a frame at a time. */
630 for (; frame_count > zargs[1]; --frame_count)
631 fp = stack + 1 + fp[1];
633 if (zargs[1] > STACK_SIZE)
634 runtime_error (ERR_BAD_FRAME);
636 fp = stack + zargs[1];
644 * z_call_n, call a subroutine and discard its result.
646 * zargs[0] = packed address of subroutine
647 * zargs[1] = first argument (optional)
649 * zargs[7] = seventh argument (optional)
657 call (zargs[0], zargc - 1, zargs + 1, 1);
662 * z_call_s, call a subroutine and store its result.
664 * zargs[0] = packed address of subroutine
665 * zargs[1] = first argument (optional)
667 * zargs[7] = seventh argument (optional)
675 call (zargs[0], zargc - 1, zargs + 1, 0);
682 * z_check_arg_count, branch if subroutine was called with >= n arg's.
684 * zargs[0] = number of arguments
688 void z_check_arg_count (void)
691 if (fp == stack + STACK_SIZE)
692 branch (zargs[0] == 0);
694 branch (zargs[0] <= (*fp & 0xff));
696 }/* z_check_arg_count */
699 * z_jump, jump unconditionally to the given address.
701 * zargs[0] = PC relative address
711 pc += (short) zargs[0] - 2;
713 if (pc >= story_size)
714 runtime_error (ERR_ILL_JUMP_ADDR);
721 * z_nop, no operation.
735 * z_quit, stop game and exit interpreter.
749 * z_ret, return from a subroutine with the given value.
751 * zargs[0] = value to return
763 * z_ret_popped, return from a subroutine with a value popped off the stack.
769 void z_ret_popped (void)
777 * z_rfalse, return from a subroutine with false (0).
791 * z_rtrue, return from a subroutine with true (1).