diff options
author | Torne Wuff <torne@wolfpuppy.org.uk> | 2010-01-17 22:15:13 +0000 |
---|---|---|
committer | Torne Wuff <torne@wolfpuppy.org.uk> | 2010-01-17 22:15:13 +0000 |
commit | 7f28c94eda576e3f972fc05468188986f2e45885 (patch) | |
tree | e03b94613028d16855a5d3df0f4853e077931214 /apps/plugins/frotz/process.c | |
parent | 563f2602f471208cb8544a36539a79dcceaad643 (diff) | |
download | rockbox-7f28c94eda576e3f972fc05468188986f2e45885.tar.gz rockbox-7f28c94eda576e3f972fc05468188986f2e45885.zip |
New plugin: frotz, a Z-machine interpreter, for playing interactive fiction.
The interpreter more or less passes all the tests in the z-machine test suite.
It should build for every target except Archos (for which it is disabled).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24267 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/frotz/process.c')
-rw-r--r-- | apps/plugins/frotz/process.c | 798 |
1 files changed, 798 insertions, 0 deletions
diff --git a/apps/plugins/frotz/process.c b/apps/plugins/frotz/process.c new file mode 100644 index 0000000000..99ad82ca85 --- /dev/null +++ b/apps/plugins/frotz/process.c | |||
@@ -0,0 +1,798 @@ | |||
1 | /* process.c - Interpreter loop and program control | ||
2 | * Copyright (c) 1995-1997 Stefan Jokisch | ||
3 | * | ||
4 | * This file is part of Frotz. | ||
5 | * | ||
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. | ||
10 | * | ||
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. | ||
15 | * | ||
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 | ||
19 | */ | ||
20 | |||
21 | #include "frotz.h" | ||
22 | |||
23 | #ifdef DJGPP | ||
24 | #include "djfrotz.h" | ||
25 | #endif | ||
26 | |||
27 | |||
28 | zword zargs[8]; | ||
29 | int zargc; | ||
30 | |||
31 | static int finished = 0; | ||
32 | |||
33 | static void __extended__ (void); | ||
34 | static void __illegal__ (void); | ||
35 | |||
36 | void (*op0_opcodes[0x10]) (void) = { | ||
37 | z_rtrue, | ||
38 | z_rfalse, | ||
39 | z_print, | ||
40 | z_print_ret, | ||
41 | z_nop, | ||
42 | z_save, | ||
43 | z_restore, | ||
44 | z_restart, | ||
45 | z_ret_popped, | ||
46 | z_catch, | ||
47 | z_quit, | ||
48 | z_new_line, | ||
49 | z_show_status, | ||
50 | z_verify, | ||
51 | __extended__, | ||
52 | z_piracy | ||
53 | }; | ||
54 | |||
55 | void (*op1_opcodes[0x10]) (void) = { | ||
56 | z_jz, | ||
57 | z_get_sibling, | ||
58 | z_get_child, | ||
59 | z_get_parent, | ||
60 | z_get_prop_len, | ||
61 | z_inc, | ||
62 | z_dec, | ||
63 | z_print_addr, | ||
64 | z_call_s, | ||
65 | z_remove_obj, | ||
66 | z_print_obj, | ||
67 | z_ret, | ||
68 | z_jump, | ||
69 | z_print_paddr, | ||
70 | z_load, | ||
71 | z_call_n | ||
72 | }; | ||
73 | |||
74 | void (*var_opcodes[0x40]) (void) = { | ||
75 | __illegal__, | ||
76 | z_je, | ||
77 | z_jl, | ||
78 | z_jg, | ||
79 | z_dec_chk, | ||
80 | z_inc_chk, | ||
81 | z_jin, | ||
82 | z_test, | ||
83 | z_or, | ||
84 | z_and, | ||
85 | z_test_attr, | ||
86 | z_set_attr, | ||
87 | z_clear_attr, | ||
88 | z_store, | ||
89 | z_insert_obj, | ||
90 | z_loadw, | ||
91 | z_loadb, | ||
92 | z_get_prop, | ||
93 | z_get_prop_addr, | ||
94 | z_get_next_prop, | ||
95 | z_add, | ||
96 | z_sub, | ||
97 | z_mul, | ||
98 | z_div, | ||
99 | z_mod, | ||
100 | z_call_s, | ||
101 | z_call_n, | ||
102 | z_set_colour, | ||
103 | z_throw, | ||
104 | __illegal__, | ||
105 | __illegal__, | ||
106 | __illegal__, | ||
107 | z_call_s, | ||
108 | z_storew, | ||
109 | z_storeb, | ||
110 | z_put_prop, | ||
111 | z_read, | ||
112 | z_print_char, | ||
113 | z_print_num, | ||
114 | z_random, | ||
115 | z_push, | ||
116 | z_pull, | ||
117 | z_split_window, | ||
118 | z_set_window, | ||
119 | z_call_s, | ||
120 | z_erase_window, | ||
121 | z_erase_line, | ||
122 | z_set_cursor, | ||
123 | z_get_cursor, | ||
124 | z_set_text_style, | ||
125 | z_buffer_mode, | ||
126 | z_output_stream, | ||
127 | z_input_stream, | ||
128 | z_sound_effect, | ||
129 | z_read_char, | ||
130 | z_scan_table, | ||
131 | z_not, | ||
132 | z_call_n, | ||
133 | z_call_n, | ||
134 | z_tokenise, | ||
135 | z_encode_text, | ||
136 | z_copy_table, | ||
137 | z_print_table, | ||
138 | z_check_arg_count | ||
139 | }; | ||
140 | |||
141 | void (*ext_opcodes[0x1d]) (void) = { | ||
142 | z_save, | ||
143 | z_restore, | ||
144 | z_log_shift, | ||
145 | z_art_shift, | ||
146 | z_set_font, | ||
147 | z_draw_picture, | ||
148 | z_picture_data, | ||
149 | z_erase_picture, | ||
150 | z_set_margins, | ||
151 | z_save_undo, | ||
152 | z_restore_undo, | ||
153 | z_print_unicode, | ||
154 | z_check_unicode, | ||
155 | __illegal__, | ||
156 | __illegal__, | ||
157 | __illegal__, | ||
158 | z_move_window, | ||
159 | z_window_size, | ||
160 | z_window_style, | ||
161 | z_get_wind_prop, | ||
162 | z_scroll_window, | ||
163 | z_pop_stack, | ||
164 | z_read_mouse, | ||
165 | z_mouse_window, | ||
166 | z_push_stack, | ||
167 | z_put_wind_prop, | ||
168 | z_print_form, | ||
169 | z_make_menu, | ||
170 | z_picture_table | ||
171 | }; | ||
172 | |||
173 | |||
174 | /* | ||
175 | * init_process | ||
176 | * | ||
177 | * Initialize process variables. | ||
178 | * | ||
179 | */ | ||
180 | |||
181 | void init_process (void) | ||
182 | { | ||
183 | finished = 0; | ||
184 | } /* init_process */ | ||
185 | |||
186 | |||
187 | /* | ||
188 | * load_operand | ||
189 | * | ||
190 | * Load an operand, either a variable or a constant. | ||
191 | * | ||
192 | */ | ||
193 | |||
194 | static void load_operand (zbyte type) | ||
195 | { | ||
196 | zword value; | ||
197 | |||
198 | if (type & 2) { /* variable */ | ||
199 | |||
200 | zbyte variable; | ||
201 | |||
202 | CODE_BYTE (variable) | ||
203 | |||
204 | if (variable == 0) | ||
205 | value = *sp++; | ||
206 | else if (variable < 16) | ||
207 | value = *(fp - variable); | ||
208 | else { | ||
209 | zword addr = h_globals + 2 * (variable - 16); | ||
210 | LOW_WORD (addr, value) | ||
211 | } | ||
212 | |||
213 | } else if (type & 1) { /* small constant */ | ||
214 | |||
215 | zbyte bvalue; | ||
216 | |||
217 | CODE_BYTE (bvalue) | ||
218 | value = bvalue; | ||
219 | |||
220 | } else CODE_WORD (value) /* large constant */ | ||
221 | |||
222 | zargs[zargc++] = value; | ||
223 | |||
224 | }/* load_operand */ | ||
225 | |||
226 | /* | ||
227 | * load_all_operands | ||
228 | * | ||
229 | * Given the operand specifier byte, load all (up to four) operands | ||
230 | * for a VAR or EXT opcode. | ||
231 | * | ||
232 | */ | ||
233 | |||
234 | static void load_all_operands (zbyte specifier) | ||
235 | { | ||
236 | int i; | ||
237 | |||
238 | for (i = 6; i >= 0; i -= 2) { | ||
239 | |||
240 | zbyte type = (specifier >> i) & 0x03; | ||
241 | |||
242 | if (type == 3) | ||
243 | break; | ||
244 | |||
245 | load_operand (type); | ||
246 | |||
247 | } | ||
248 | |||
249 | }/* load_all_operands */ | ||
250 | |||
251 | /* | ||
252 | * interpret | ||
253 | * | ||
254 | * Z-code interpreter main loop | ||
255 | * | ||
256 | */ | ||
257 | |||
258 | void interpret (void) | ||
259 | { | ||
260 | |||
261 | do { | ||
262 | |||
263 | zbyte opcode; | ||
264 | |||
265 | CODE_BYTE (opcode) | ||
266 | |||
267 | zargc = 0; | ||
268 | |||
269 | if (opcode < 0x80) { /* 2OP opcodes */ | ||
270 | |||
271 | load_operand ((zbyte) (opcode & 0x40) ? 2 : 1); | ||
272 | load_operand ((zbyte) (opcode & 0x20) ? 2 : 1); | ||
273 | |||
274 | var_opcodes[opcode & 0x1f] (); | ||
275 | |||
276 | } else if (opcode < 0xb0) { /* 1OP opcodes */ | ||
277 | |||
278 | load_operand ((zbyte) (opcode >> 4)); | ||
279 | |||
280 | op1_opcodes[opcode & 0x0f] (); | ||
281 | |||
282 | } else if (opcode < 0xc0) { /* 0OP opcodes */ | ||
283 | |||
284 | op0_opcodes[opcode - 0xb0] (); | ||
285 | |||
286 | } else { /* VAR opcodes */ | ||
287 | |||
288 | zbyte specifier1; | ||
289 | zbyte specifier2; | ||
290 | |||
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 */ | ||
296 | } else { | ||
297 | CODE_BYTE (specifier1) | ||
298 | load_all_operands (specifier1); | ||
299 | } | ||
300 | |||
301 | var_opcodes[opcode - 0xc0] (); | ||
302 | |||
303 | } | ||
304 | |||
305 | #if defined(DJGPP) && defined(SOUND_SUPPORT) | ||
306 | if (end_of_sound_flag) | ||
307 | end_of_sound (); | ||
308 | #endif | ||
309 | |||
310 | } while (finished == 0); | ||
311 | |||
312 | finished--; | ||
313 | |||
314 | }/* interpret */ | ||
315 | |||
316 | /* | ||
317 | * call | ||
318 | * | ||
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). | ||
323 | * | ||
324 | */ | ||
325 | |||
326 | void call (zword routine, int argc, zword *args, int ct) | ||
327 | { | ||
328 | long pc; | ||
329 | zword value; | ||
330 | zbyte count; | ||
331 | int i; | ||
332 | |||
333 | if (sp - stack < 4) | ||
334 | runtime_error (ERR_STK_OVF); | ||
335 | |||
336 | GET_PC (pc) | ||
337 | |||
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))); | ||
342 | |||
343 | fp = sp; | ||
344 | frame_count++; | ||
345 | |||
346 | /* Calculate byte address of routine */ | ||
347 | |||
348 | if (h_version <= V3) | ||
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; | ||
356 | |||
357 | if (pc >= story_size) | ||
358 | runtime_error (ERR_ILL_CALL_ADDR); | ||
359 | |||
360 | SET_PC (pc) | ||
361 | |||
362 | /* Initialise local variables */ | ||
363 | |||
364 | CODE_BYTE (count) | ||
365 | |||
366 | if (count > 15) | ||
367 | runtime_error (ERR_CALL_NON_RTN); | ||
368 | if (sp - stack < count) | ||
369 | runtime_error (ERR_STK_OVF); | ||
370 | |||
371 | if (f_setup.save_quetzal) | ||
372 | fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */ | ||
373 | |||
374 | value = 0; | ||
375 | |||
376 | for (i = 0; i < count; i++) { | ||
377 | |||
378 | if (h_version <= V4) /* V1 to V4 games provide default */ | ||
379 | CODE_WORD (value) /* values for all local variables */ | ||
380 | |||
381 | *--sp = (zword) ((argc-- > 0) ? args[i] : value); | ||
382 | |||
383 | } | ||
384 | |||
385 | /* Start main loop for direct calls */ | ||
386 | |||
387 | if (ct == 2) | ||
388 | interpret (); | ||
389 | |||
390 | }/* call */ | ||
391 | |||
392 | /* | ||
393 | * ret | ||
394 | * | ||
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. | ||
399 | * | ||
400 | */ | ||
401 | |||
402 | void ret (zword value) | ||
403 | { | ||
404 | long pc; | ||
405 | int ct; | ||
406 | |||
407 | if (sp > fp) | ||
408 | runtime_error (ERR_STK_UNDF); | ||
409 | |||
410 | sp = fp; | ||
411 | |||
412 | ct = *sp++ >> (f_setup.save_quetzal ? 12 : 8); | ||
413 | frame_count--; | ||
414 | fp = stack + 1 + *sp++; | ||
415 | pc = *sp++; | ||
416 | pc = ((long) *sp++ << 9) | pc; | ||
417 | |||
418 | SET_PC (pc) | ||
419 | |||
420 | /* Handle resulting value */ | ||
421 | |||
422 | if (ct == 0) | ||
423 | store (value); | ||
424 | if (ct == 2) | ||
425 | *--sp = value; | ||
426 | |||
427 | /* Stop main loop for direct calls */ | ||
428 | |||
429 | if (ct == 2) | ||
430 | finished++; | ||
431 | |||
432 | }/* ret */ | ||
433 | |||
434 | /* | ||
435 | * branch | ||
436 | * | ||
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 | ||
445 | * return true. | ||
446 | * | ||
447 | */ | ||
448 | |||
449 | void branch (bool flag) | ||
450 | { | ||
451 | long pc; | ||
452 | zword offset; | ||
453 | zbyte specifier; | ||
454 | zbyte off1; | ||
455 | zbyte off2; | ||
456 | |||
457 | CODE_BYTE (specifier) | ||
458 | |||
459 | off1 = specifier & 0x3f; | ||
460 | |||
461 | if (!flag) | ||
462 | specifier ^= 0x80; | ||
463 | |||
464 | if (!(specifier & 0x40)) { /* it's a long branch */ | ||
465 | |||
466 | if (off1 & 0x20) /* propagate sign bit */ | ||
467 | off1 |= 0xc0; | ||
468 | |||
469 | CODE_BYTE (off2) | ||
470 | |||
471 | offset = (off1 << 8) | off2; | ||
472 | |||
473 | } else offset = off1; /* it's a short branch */ | ||
474 | |||
475 | if (specifier & 0x80) { | ||
476 | |||
477 | if (offset > 1) { /* normal branch */ | ||
478 | |||
479 | GET_PC (pc) | ||
480 | pc += (short) offset - 2; | ||
481 | SET_PC (pc) | ||
482 | |||
483 | } else ret (offset); /* special case, return 0 or 1 */ | ||
484 | } | ||
485 | |||
486 | }/* branch */ | ||
487 | |||
488 | /* | ||
489 | * store | ||
490 | * | ||
491 | * Store an operand, either as a variable or pushed on the stack. | ||
492 | * | ||
493 | */ | ||
494 | |||
495 | void store (zword value) | ||
496 | { | ||
497 | zbyte variable; | ||
498 | |||
499 | CODE_BYTE (variable) | ||
500 | |||
501 | if (variable == 0) | ||
502 | *--sp = value; | ||
503 | else if (variable < 16) | ||
504 | *(fp - variable) = value; | ||
505 | else { | ||
506 | zword addr = h_globals + 2 * (variable - 16); | ||
507 | SET_WORD (addr, value) | ||
508 | } | ||
509 | |||
510 | }/* store */ | ||
511 | |||
512 | /* | ||
513 | * direct_call | ||
514 | * | ||
515 | * Call the interpreter loop directly. This is necessary when | ||
516 | * | ||
517 | * - a sound effect has been finished | ||
518 | * - a read instruction has timed out | ||
519 | * - a newline countdown has hit zero | ||
520 | * | ||
521 | * The interpreter returns the result value on the stack. | ||
522 | * | ||
523 | */ | ||
524 | |||
525 | int direct_call (zword addr) | ||
526 | { | ||
527 | zword saved_zargs[8]; | ||
528 | int saved_zargc; | ||
529 | int i; | ||
530 | |||
531 | /* Calls to address 0 return false */ | ||
532 | |||
533 | if (addr == 0) | ||
534 | return 0; | ||
535 | |||
536 | /* Save operands and operand count */ | ||
537 | |||
538 | for (i = 0; i < 8; i++) | ||
539 | saved_zargs[i] = zargs[i]; | ||
540 | |||
541 | saved_zargc = zargc; | ||
542 | |||
543 | /* Call routine directly */ | ||
544 | |||
545 | call (addr, 0, 0, 2); | ||
546 | |||
547 | /* Restore operands and operand count */ | ||
548 | |||
549 | for (i = 0; i < 8; i++) | ||
550 | zargs[i] = saved_zargs[i]; | ||
551 | |||
552 | zargc = saved_zargc; | ||
553 | |||
554 | /* Resulting value lies on top of the stack */ | ||
555 | |||
556 | return (short) *sp++; | ||
557 | |||
558 | }/* direct_call */ | ||
559 | |||
560 | /* | ||
561 | * __extended__ | ||
562 | * | ||
563 | * Load and execute an extended opcode. | ||
564 | * | ||
565 | */ | ||
566 | |||
567 | static void __extended__ (void) | ||
568 | { | ||
569 | zbyte opcode; | ||
570 | zbyte specifier; | ||
571 | |||
572 | CODE_BYTE (opcode) | ||
573 | CODE_BYTE (specifier) | ||
574 | |||
575 | load_all_operands (specifier); | ||
576 | |||
577 | if (opcode < 0x1d) /* extended opcodes from 0x1d on */ | ||
578 | ext_opcodes[opcode] (); /* are reserved for future spec' */ | ||
579 | |||
580 | }/* __extended__ */ | ||
581 | |||
582 | /* | ||
583 | * __illegal__ | ||
584 | * | ||
585 | * Exit game because an unknown opcode has been hit. | ||
586 | * | ||
587 | */ | ||
588 | |||
589 | static void __illegal__ (void) | ||
590 | { | ||
591 | |||
592 | runtime_error (ERR_ILL_OPCODE); | ||
593 | |||
594 | }/* __illegal__ */ | ||
595 | |||
596 | /* | ||
597 | * z_catch, store the current stack frame for later use with z_throw. | ||
598 | * | ||
599 | * no zargs used | ||
600 | * | ||
601 | */ | ||
602 | |||
603 | void z_catch (void) | ||
604 | { | ||
605 | |||
606 | store (f_setup.save_quetzal ? frame_count : (zword) (fp - stack)); | ||
607 | |||
608 | }/* z_catch */ | ||
609 | |||
610 | /* | ||
611 | * z_throw, go back to the given stack frame and return the given value. | ||
612 | * | ||
613 | * zargs[0] = value to return | ||
614 | * zargs[1] = stack frame | ||
615 | * | ||
616 | */ | ||
617 | |||
618 | void z_throw (void) | ||
619 | { | ||
620 | |||
621 | if (f_setup.save_quetzal) { | ||
622 | if (zargs[1] > frame_count) | ||
623 | runtime_error (ERR_BAD_FRAME); | ||
624 | |||
625 | /* Unwind the stack a frame at a time. */ | ||
626 | for (; frame_count > zargs[1]; --frame_count) | ||
627 | fp = stack + 1 + fp[1]; | ||
628 | } else { | ||
629 | if (zargs[1] > STACK_SIZE) | ||
630 | runtime_error (ERR_BAD_FRAME); | ||
631 | |||
632 | fp = stack + zargs[1]; | ||
633 | } | ||
634 | |||
635 | ret (zargs[0]); | ||
636 | |||
637 | }/* z_throw */ | ||
638 | |||
639 | /* | ||
640 | * z_call_n, call a subroutine and discard its result. | ||
641 | * | ||
642 | * zargs[0] = packed address of subroutine | ||
643 | * zargs[1] = first argument (optional) | ||
644 | * ... | ||
645 | * zargs[7] = seventh argument (optional) | ||
646 | * | ||
647 | */ | ||
648 | |||
649 | void z_call_n (void) | ||
650 | { | ||
651 | |||
652 | if (zargs[0] != 0) | ||
653 | call (zargs[0], zargc - 1, zargs + 1, 1); | ||
654 | |||
655 | }/* z_call_n */ | ||
656 | |||
657 | /* | ||
658 | * z_call_s, call a subroutine and store its result. | ||
659 | * | ||
660 | * zargs[0] = packed address of subroutine | ||
661 | * zargs[1] = first argument (optional) | ||
662 | * ... | ||
663 | * zargs[7] = seventh argument (optional) | ||
664 | * | ||
665 | */ | ||
666 | |||
667 | void z_call_s (void) | ||
668 | { | ||
669 | |||
670 | if (zargs[0] != 0) | ||
671 | call (zargs[0], zargc - 1, zargs + 1, 0); | ||
672 | else | ||
673 | store (0); | ||
674 | |||
675 | }/* z_call_s */ | ||
676 | |||
677 | /* | ||
678 | * z_check_arg_count, branch if subroutine was called with >= n arg's. | ||
679 | * | ||
680 | * zargs[0] = number of arguments | ||
681 | * | ||
682 | */ | ||
683 | |||
684 | void z_check_arg_count (void) | ||
685 | { | ||
686 | |||
687 | if (fp == stack + STACK_SIZE) | ||
688 | branch (zargs[0] == 0); | ||
689 | else | ||
690 | branch (zargs[0] <= (*fp & 0xff)); | ||
691 | |||
692 | }/* z_check_arg_count */ | ||
693 | |||
694 | /* | ||
695 | * z_jump, jump unconditionally to the given address. | ||
696 | * | ||
697 | * zargs[0] = PC relative address | ||
698 | * | ||
699 | */ | ||
700 | |||
701 | void z_jump (void) | ||
702 | { | ||
703 | long pc; | ||
704 | |||
705 | GET_PC (pc) | ||
706 | |||
707 | pc += (short) zargs[0] - 2; | ||
708 | |||
709 | if (pc >= story_size) | ||
710 | runtime_error (ERR_ILL_JUMP_ADDR); | ||
711 | |||
712 | SET_PC (pc) | ||
713 | |||
714 | }/* z_jump */ | ||
715 | |||
716 | /* | ||
717 | * z_nop, no operation. | ||
718 | * | ||
719 | * no zargs used | ||
720 | * | ||
721 | */ | ||
722 | |||
723 | void z_nop (void) | ||
724 | { | ||
725 | |||
726 | /* Do nothing */ | ||
727 | |||
728 | }/* z_nop */ | ||
729 | |||
730 | /* | ||
731 | * z_quit, stop game and exit interpreter. | ||
732 | * | ||
733 | * no zargs used | ||
734 | * | ||
735 | */ | ||
736 | |||
737 | void z_quit (void) | ||
738 | { | ||
739 | |||
740 | finished = 9999; | ||
741 | |||
742 | }/* z_quit */ | ||
743 | |||
744 | /* | ||
745 | * z_ret, return from a subroutine with the given value. | ||
746 | * | ||
747 | * zargs[0] = value to return | ||
748 | * | ||
749 | */ | ||
750 | |||
751 | void z_ret (void) | ||
752 | { | ||
753 | |||
754 | ret (zargs[0]); | ||
755 | |||
756 | }/* z_ret */ | ||
757 | |||
758 | /* | ||
759 | * z_ret_popped, return from a subroutine with a value popped off the stack. | ||
760 | * | ||
761 | * no zargs used | ||
762 | * | ||
763 | */ | ||
764 | |||
765 | void z_ret_popped (void) | ||
766 | { | ||
767 | |||
768 | ret (*sp++); | ||
769 | |||
770 | }/* z_ret_popped */ | ||
771 | |||
772 | /* | ||
773 | * z_rfalse, return from a subroutine with false (0). | ||
774 | * | ||
775 | * no zargs used | ||
776 | * | ||
777 | */ | ||
778 | |||
779 | void z_rfalse (void) | ||
780 | { | ||
781 | |||
782 | ret (0); | ||
783 | |||
784 | }/* z_rfalse */ | ||
785 | |||
786 | /* | ||
787 | * z_rtrue, return from a subroutine with true (1). | ||
788 | * | ||
789 | * no zargs used | ||
790 | * | ||
791 | */ | ||
792 | |||
793 | void z_rtrue (void) | ||
794 | { | ||
795 | |||
796 | ret (1); | ||
797 | |||
798 | }/* z_rtrue */ | ||