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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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[0x1d]) (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,
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
177 * Initialize process variables.
181 void init_process (void)
190 * Load an operand, either a variable or a constant.
194 static void load_operand (zbyte type)
198 if (type & 2) { /* variable */
206 else if (variable < 16)
207 value = *(fp - variable);
209 zword addr = h_globals + 2 * (variable - 16);
210 LOW_WORD (addr, value)
213 } else if (type & 1) { /* small constant */
220 } else CODE_WORD (value) /* large constant */
222 zargs[zargc++] = value;
229 * Given the operand specifier byte, load all (up to four) operands
230 * for a VAR or EXT opcode.
234 static void load_all_operands (zbyte specifier)
238 for (i = 6; i >= 0; i -= 2) {
240 zbyte type = (specifier >> i) & 0x03;
249 }/* load_all_operands */
254 * Z-code interpreter main loop
258 void interpret (void)
268 if (opcode < 0x80) { /* 2OP opcodes */
270 load_operand ((zbyte) (opcode & 0x40) ? 2 : 1);
271 load_operand ((zbyte) (opcode & 0x20) ? 2 : 1);
273 var_opcodes[opcode & 0x1f] ();
275 } else if (opcode < 0xb0) { /* 1OP opcodes */
277 load_operand ((zbyte) (opcode >> 4));
279 op1_opcodes[opcode & 0x0f] ();
281 } else if (opcode < 0xc0) { /* 0OP opcodes */
283 op0_opcodes[opcode - 0xb0] ();
285 } else { /* VAR opcodes */
290 if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */
291 CODE_BYTE (specifier1) /* and 0xfa are */
292 CODE_BYTE (specifier2) /* call opcodes */
293 load_all_operands (specifier1); /* with up to 8 */
294 load_all_operands (specifier2); /* arguments */
296 CODE_BYTE (specifier1)
297 load_all_operands (specifier1);
300 var_opcodes[opcode - 0xc0] ();
304 #if defined(DJGPP) && defined(SOUND_SUPPORT)
305 if (end_of_sound_flag)
309 } while (finished == 0);
318 * Call a subroutine. Save PC and FP then load new PC and initialise
319 * new stack frame. Note that the caller may legally provide less or
320 * more arguments than the function actually has. The call type "ct"
321 * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
325 void call (zword routine, int argc, zword *args, int ct)
333 runtime_error (ERR_STK_OVF);
337 *--sp = (zword) (pc >> 9);
338 *--sp = (zword) (pc & 0x1ff);
339 *--sp = (zword) (fp - stack - 1);
340 *--sp = (zword) (argc | (ct << (f_setup.save_quetzal ? 12 : 8)));
345 /* Calculate byte address of routine */
348 pc = (long) routine << 1;
349 else if (h_version <= V5)
350 pc = (long) routine << 2;
351 else if (h_version <= V7)
352 pc = ((long) routine << 2) + ((long) h_functions_offset << 3);
353 else /* h_version == V8 */
354 pc = (long) routine << 3;
356 if (pc >= story_size)
357 runtime_error (ERR_ILL_CALL_ADDR);
361 /* Initialise local variables */
366 runtime_error (ERR_CALL_NON_RTN);
367 if (sp - stack < count)
368 runtime_error (ERR_STK_OVF);
370 if (f_setup.save_quetzal)
371 fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */
375 for (i = 0; i < count; i++) {
377 if (h_version <= V4) /* V1 to V4 games provide default */
378 CODE_WORD (value) /* values for all local variables */
380 *--sp = (zword) ((argc-- > 0) ? args[i] : value);
384 /* Start main loop for direct calls */
394 * Return from the current subroutine and restore the previous stack
395 * frame. The result may be stored (0), thrown away (1) or pushed on
396 * the stack (2). In the latter case a direct call has been finished
397 * and we must exit the interpreter loop.
401 void ret (zword value)
407 runtime_error (ERR_STK_UNDF);
411 ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8);
413 fp = stack + 1 + *sp++;
415 pc = ((long) *sp++ << 9) | pc;
419 /* Handle resulting value */
426 /* Stop main loop for direct calls */
436 * Take a jump after an instruction based on the flag, either true or
437 * false. The branch can be short or long; it is encoded in one or two
438 * bytes respectively. When bit 7 of the first byte is set, the jump
439 * takes place if the flag is true; otherwise it is taken if the flag
440 * is false. When bit 6 of the first byte is set, the branch is short;
441 * otherwise it is long. The offset occupies the bottom 6 bits of the
442 * first byte plus all the bits in the second byte for long branches.
443 * Uniquely, an offset of 0 means return false, and an offset of 1 is
448 void branch (bool flag)
456 CODE_BYTE (specifier)
458 off1 = specifier & 0x3f;
463 if (!(specifier & 0x40)) { /* it's a long branch */
465 if (off1 & 0x20) /* propagate sign bit */
470 offset = (off1 << 8) | off2;
472 } else offset = off1; /* it's a short branch */
474 if (specifier & 0x80) {
476 if (offset > 1) { /* normal branch */
479 pc += (short) offset - 2;
482 } else ret (offset); /* special case, return 0 or 1 */
490 * Store an operand, either as a variable or pushed on the stack.
494 void store (zword value)
502 else if (variable < 16)
503 *(fp - variable) = value;
505 zword addr = h_globals + 2 * (variable - 16);
506 SET_WORD (addr, value)
514 * Call the interpreter loop directly. This is necessary when
516 * - a sound effect has been finished
517 * - a read instruction has timed out
518 * - a newline countdown has hit zero
520 * The interpreter returns the result value on the stack.
524 int direct_call (zword addr)
526 zword saved_zargs[8];
530 /* Calls to address 0 return false */
535 /* Save operands and operand count */
537 for (i = 0; i < 8; i++)
538 saved_zargs[i] = zargs[i];
542 /* Call routine directly */
544 call (addr, 0, 0, 2);
546 /* Restore operands and operand count */
548 for (i = 0; i < 8; i++)
549 zargs[i] = saved_zargs[i];
553 /* Resulting value lies on top of the stack */
555 return (short) *sp++;
562 * Load and execute an extended opcode.
566 static void __extended__ (void)
572 CODE_BYTE (specifier)
574 load_all_operands (specifier);
576 if (opcode < 0x1d) /* extended opcodes from 0x1d on */
577 ext_opcodes[opcode] (); /* are reserved for future spec' */
584 * Exit game because an unknown opcode has been hit.
588 static void __illegal__ (void)
591 runtime_error (ERR_ILL_OPCODE);
596 * z_catch, store the current stack frame for later use with z_throw.
605 store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack));
610 * z_throw, go back to the given stack frame and return the given value.
612 * zargs[0] = value to return
613 * zargs[1] = stack frame
620 if (f_setup.save_quetzal) {
621 if (zargs[1] > frame_count)
622 runtime_error (ERR_BAD_FRAME);
624 /* Unwind the stack a frame at a time. */
625 for (; frame_count > zargs[1]; --frame_count)
626 fp = stack + 1 + fp[1];
628 if (zargs[1] > STACK_SIZE)
629 runtime_error (ERR_BAD_FRAME);
631 fp = stack + zargs[1];
639 * z_call_n, call a subroutine and discard its result.
641 * zargs[0] = packed address of subroutine
642 * zargs[1] = first argument (optional)
644 * zargs[7] = seventh argument (optional)
652 call (zargs[0], zargc - 1, zargs + 1, 1);
657 * z_call_s, call a subroutine and store its result.
659 * zargs[0] = packed address of subroutine
660 * zargs[1] = first argument (optional)
662 * zargs[7] = seventh argument (optional)
670 call (zargs[0], zargc - 1, zargs + 1, 0);
677 * z_check_arg_count, branch if subroutine was called with >= n arg's.
679 * zargs[0] = number of arguments
683 void z_check_arg_count (void)
686 if (fp == stack + STACK_SIZE)
687 branch (zargs[0] == 0);
689 branch (zargs[0] <= (*fp & 0xff));
691 }/* z_check_arg_count */
694 * z_jump, jump unconditionally to the given address.
696 * zargs[0] = PC relative address
706 pc += (short) zargs[0] - 2;
708 if (pc >= story_size)
709 runtime_error (ERR_ILL_JUMP_ADDR);
716 * z_nop, no operation.
730 * z_quit, stop game and exit interpreter.
744 * z_ret, return from a subroutine with the given value.
746 * zargs[0] = value to return
758 * z_ret_popped, return from a subroutine with a value popped off the stack.
764 void z_ret_popped (void)
772 * z_rfalse, return from a subroutine with false (0).
786 * z_rtrue, return from a subroutine with true (1).