summaryrefslogtreecommitdiff
path: root/apps/plugins/frotz/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/frotz/process.c')
-rw-r--r--apps/plugins/frotz/process.c798
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
28zword zargs[8];
29int zargc;
30
31static int finished = 0;
32
33static void __extended__ (void);
34static void __illegal__ (void);
35
36void (*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
55void (*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
74void (*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
141void (*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
181void 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
194static 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
234static 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
258void 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
326void 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
402void 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
449void 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
495void 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
525int 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
567static 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
589static 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
603void 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
618void 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
649void 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
667void 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
684void 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
701void 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
723void 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
737void 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
751void 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
765void 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
779void 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
793void z_rtrue (void)
794{
795
796 ret (1);
797
798}/* z_rtrue */