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) = {
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)
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
<< (f_setup
.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 /* h_version == V8 */
355 pc
= (long) routine
<< 3;
357 if (pc
>= story_size
)
358 runtime_error (ERR_ILL_CALL_ADDR
);
362 /* Initialise local variables */
367 runtime_error (ERR_CALL_NON_RTN
);
368 if (sp
- stack
< count
)
369 runtime_error (ERR_STK_OVF
);
371 if (f_setup
.save_quetzal
)
372 fp
[0] |= (zword
) count
<< 8; /* Save local var count for Quetzal. */
376 for (i
= 0; i
< count
; i
++) {
378 if (h_version
<= V4
) /* V1 to V4 games provide default */
379 CODE_WORD (value
) /* values for all local variables */
381 *--sp
= (zword
) ((argc
-- > 0) ? args
[i
] : value
);
385 /* Start main loop for direct calls */
395 * Return from the current subroutine and restore the previous stack
396 * frame. The result may be stored (0), thrown away (1) or pushed on
397 * the stack (2). In the latter case a direct call has been finished
398 * and we must exit the interpreter loop.
402 void ret (zword value
)
408 runtime_error (ERR_STK_UNDF
);
412 ct
= *sp
++ >> (f_setup
.save_quetzal
? 12 : 8);
414 fp
= stack
+ 1 + *sp
++;
416 pc
= ((long) *sp
++ << 9) | pc
;
420 /* Handle resulting value */
427 /* Stop main loop for direct calls */
437 * Take a jump after an instruction based on the flag, either true or
438 * false. The branch can be short or long; it is encoded in one or two
439 * bytes respectively. When bit 7 of the first byte is set, the jump
440 * takes place if the flag is true; otherwise it is taken if the flag
441 * is false. When bit 6 of the first byte is set, the branch is short;
442 * otherwise it is long. The offset occupies the bottom 6 bits of the
443 * first byte plus all the bits in the second byte for long branches.
444 * Uniquely, an offset of 0 means return false, and an offset of 1 is
449 void branch (bool flag
)
457 CODE_BYTE (specifier
)
459 off1
= specifier
& 0x3f;
464 if (!(specifier
& 0x40)) { /* it's a long branch */
466 if (off1
& 0x20) /* propagate sign bit */
471 offset
= (off1
<< 8) | off2
;
473 } else offset
= off1
; /* it's a short branch */
475 if (specifier
& 0x80) {
477 if (offset
> 1) { /* normal branch */
480 pc
+= (short) offset
- 2;
483 } else ret (offset
); /* special case, return 0 or 1 */
491 * Store an operand, either as a variable or pushed on the stack.
495 void store (zword value
)
503 else if (variable
< 16)
504 *(fp
- variable
) = value
;
506 zword addr
= h_globals
+ 2 * (variable
- 16);
507 SET_WORD (addr
, value
)
515 * Call the interpreter loop directly. This is necessary when
517 * - a sound effect has been finished
518 * - a read instruction has timed out
519 * - a newline countdown has hit zero
521 * The interpreter returns the result value on the stack.
525 int direct_call (zword addr
)
527 zword saved_zargs
[8];
531 /* Calls to address 0 return false */
536 /* Save operands and operand count */
538 for (i
= 0; i
< 8; i
++)
539 saved_zargs
[i
] = zargs
[i
];
543 /* Call routine directly */
545 call (addr
, 0, 0, 2);
547 /* Restore operands and operand count */
549 for (i
= 0; i
< 8; i
++)
550 zargs
[i
] = saved_zargs
[i
];
554 /* Resulting value lies on top of the stack */
556 return (short) *sp
++;
563 * Load and execute an extended opcode.
567 static void __extended__ (void)
573 CODE_BYTE (specifier
)
575 load_all_operands (specifier
);
577 if (opcode
< 0x1d) /* extended opcodes from 0x1d on */
578 ext_opcodes
[opcode
] (); /* are reserved for future spec' */
585 * Exit game because an unknown opcode has been hit.
589 static void __illegal__ (void)
592 runtime_error (ERR_ILL_OPCODE
);
597 * z_catch, store the current stack frame for later use with z_throw.
606 store (f_setup
.save_quetzal
? frame_count
: (zword
) (fp
- stack
));
611 * z_throw, go back to the given stack frame and return the given value.
613 * zargs[0] = value to return
614 * zargs[1] = stack frame
621 if (f_setup
.save_quetzal
) {
622 if (zargs
[1] > frame_count
)
623 runtime_error (ERR_BAD_FRAME
);
625 /* Unwind the stack a frame at a time. */
626 for (; frame_count
> zargs
[1]; --frame_count
)
627 fp
= stack
+ 1 + fp
[1];
629 if (zargs
[1] > STACK_SIZE
)
630 runtime_error (ERR_BAD_FRAME
);
632 fp
= stack
+ zargs
[1];
640 * z_call_n, call a subroutine and discard its result.
642 * zargs[0] = packed address of subroutine
643 * zargs[1] = first argument (optional)
645 * zargs[7] = seventh argument (optional)
653 call (zargs
[0], zargc
- 1, zargs
+ 1, 1);
658 * z_call_s, call a subroutine and store its result.
660 * zargs[0] = packed address of subroutine
661 * zargs[1] = first argument (optional)
663 * zargs[7] = seventh argument (optional)
671 call (zargs
[0], zargc
- 1, zargs
+ 1, 0);
678 * z_check_arg_count, branch if subroutine was called with >= n arg's.
680 * zargs[0] = number of arguments
684 void z_check_arg_count (void)
687 if (fp
== stack
+ STACK_SIZE
)
688 branch (zargs
[0] == 0);
690 branch (zargs
[0] <= (*fp
& 0xff));
692 }/* z_check_arg_count */
695 * z_jump, jump unconditionally to the given address.
697 * zargs[0] = PC relative address
707 pc
+= (short) zargs
[0] - 2;
709 if (pc
>= story_size
)
710 runtime_error (ERR_ILL_JUMP_ADDR
);
717 * z_nop, no operation.
731 * z_quit, stop game and exit interpreter.
745 * z_ret, return from a subroutine with the given value.
747 * zargs[0] = value to return
759 * z_ret_popped, return from a subroutine with a value popped off the stack.
765 void z_ret_popped (void)
773 * z_rfalse, return from a subroutine with false (0).
787 * z_rtrue, return from a subroutine with true (1).