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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "frotz.h"
22 
23 #ifdef DJGPP
24 #include "djfrotz.h"
25 #endif
26 
27 zword zargs[8];
28 int zargc;
29 
30 static int finished = 0;
31 
32 static void __extended__(void);
33 static void __illegal__(void);
34 
35 void (*op0_opcodes[0x10])(void) = {
36 	z_rtrue,
37 	z_rfalse,
38 	z_print,
39 	z_print_ret,
40 	z_nop,
41 	z_save,
42 	z_restore,
43 	z_restart,
44 	z_ret_popped,
45 	z_catch,
46 	z_quit,
47 	z_new_line,
48 	z_show_status,
49 	z_verify,
50 	__extended__,
51 	z_piracy
52 };
53 
54 void (*op1_opcodes[0x10])(void) = {
55 	z_jz,
56 	z_get_sibling,
57 	z_get_child,
58 	z_get_parent,
59 	z_get_prop_len,
60 	z_inc,
61 	z_dec,
62 	z_print_addr,
63 	z_call_s,
64 	z_remove_obj,
65 	z_print_obj,
66 	z_ret,
67 	z_jump,
68 	z_print_paddr,
69 	z_load,
70 	z_call_n
71 };
72 
73 void (*var_opcodes[0x40])(void) = {
74 	__illegal__,
75 	z_je,
76 	z_jl,
77 	z_jg,
78 	z_dec_chk,
79 	z_inc_chk,
80 	z_jin,
81 	z_test,
82 	z_or,
83 	z_and,
84 	z_test_attr,
85 	z_set_attr,
86 	z_clear_attr,
87 	z_store,
88 	z_insert_obj,
89 	z_loadw,
90 	z_loadb,
91 	z_get_prop,
92 	z_get_prop_addr,
93 	z_get_next_prop,
94 	z_add,
95 	z_sub,
96 	z_mul,
97 	z_div,
98 	z_mod,
99 	z_call_s,
100 	z_call_n,
101 	z_set_colour,
102 	z_throw,
103 	__illegal__,
104 	__illegal__,
105 	__illegal__,
106 	z_call_s,
107 	z_storew,
108 	z_storeb,
109 	z_put_prop,
110 	z_read,
111 	z_print_char,
112 	z_print_num,
113 	z_random,
114 	z_push,
115 	z_pull,
116 	z_split_window,
117 	z_set_window,
118 	z_call_s,
119 	z_erase_window,
120 	z_erase_line,
121 	z_set_cursor,
122 	z_get_cursor,
123 	z_set_text_style,
124 	z_buffer_mode,
125 	z_output_stream,
126 	z_input_stream,
127 	z_sound_effect,
128 	z_read_char,
129 	z_scan_table,
130 	z_not,
131 	z_call_n,
132 	z_call_n,
133 	z_tokenise,
134 	z_encode_text,
135 	z_copy_table,
136 	z_print_table,
137 	z_check_arg_count
138 };
139 
140 void (*ext_opcodes[0x1d])(void) = {
141 	z_save,
142 	z_restore,
143 	z_log_shift,
144 	z_art_shift,
145 	z_set_font,
146 	z_draw_picture,
147 	z_picture_data,
148 	z_erase_picture,
149 	z_set_margins,
150 	z_save_undo,
151 	z_restore_undo,
152 	z_print_unicode,
153 	z_check_unicode,
154 	__illegal__,
155 	__illegal__,
156 	__illegal__,
157 	z_move_window,
158 	z_window_size,
159 	z_window_style,
160 	z_get_wind_prop,
161 	z_scroll_window,
162 	z_pop_stack,
163 	z_read_mouse,
164 	z_mouse_window,
165 	z_push_stack,
166 	z_put_wind_prop,
167 	z_print_form,
168 	z_make_menu,
169 	z_picture_table
170 };
171 
172 
173 /*
174  * init_process
175  *
176  * Initialize process variables.
177  *
178  */
init_process(void)179 void init_process(void)
180 {
181 	finished = 0;
182 }
183 
184 
185 /*
186  * load_operand
187  *
188  * Load an operand, either a variable or a constant.
189  *
190  */
load_operand(zbyte type)191 static void load_operand(zbyte type)
192 {
193 	zword value;
194 
195 	if (type & 2) {		/* variable */
196 		zbyte variable;
197 
198 		CODE_BYTE(variable)
199 		if (variable == 0)
200 			value = *sp++;
201 		else if (variable < 16)
202 			value = *(fp - variable);
203 		else {
204 			zword addr = z_header.globals + 2 * (variable - 16);
205 			LOW_WORD(addr, value)
206 		}
207 	} else if (type & 1) {	/* small constant */
208 		zbyte bvalue;
209 
210 		CODE_BYTE(bvalue)
211 		value = bvalue;
212 	} else
213 		CODE_WORD(value)	/* large constant */
214 	zargs[zargc++] = value;
215 } /* load_operand */
216 
217 
218 /*
219  * load_all_operands
220  *
221  * Given the operand specifier byte, load all (up to four) operands
222  * for a VAR or EXT opcode.
223  *
224  */
load_all_operands(zbyte specifier)225 static void load_all_operands(zbyte specifier)
226 {
227 	int i;
228 
229 	for (i = 6; i >= 0; i -= 2) {
230 		zbyte type = (specifier >> i) & 0x03;
231 
232 		if (type == 3)
233 			break;
234 		load_operand(type);
235 	}
236 } /* load_all_operands */
237 
238 
239 /*
240  * interpret
241  *
242  * Z-code interpreter main loop
243  *
244  */
interpret(void)245 void interpret(void)
246 {
247 	/* If we got a save file on the command line, use it now. */
248 	if (f_setup.restore_mode == 1) {
249 		z_restore();
250 		f_setup.restore_mode = 0;
251 	}
252 
253 	do {
254 		zbyte opcode;
255 
256 		CODE_BYTE(opcode)
257 		zargc = 0;
258 
259 		if (opcode < 0x80) {	/* 2OP opcodes */
260 			load_operand((zbyte) (opcode & 0x40) ? 2 : 1);
261 			load_operand((zbyte) (opcode & 0x20) ? 2 : 1);
262 			var_opcodes[opcode & 0x1f] ();
263 		} else if (opcode < 0xb0) {	/* 1OP opcodes */
264 			load_operand((zbyte) (opcode >> 4));
265 			op1_opcodes[opcode & 0x0f] ();
266 		} else if (opcode < 0xc0) {	/* 0OP opcodes */
267 			op0_opcodes[opcode - 0xb0] ();
268 		} else {	/* VAR opcodes */
269 			zbyte specifier1;
270 			zbyte specifier2;
271 			if (opcode == 0xec || opcode == 0xfa) {		/* opcodes 0xec */
272 				CODE_BYTE(specifier1)			/* and 0xfa are */
273 				    CODE_BYTE(specifier2)		/* call opcodes */
274 				    load_all_operands(specifier1);	/* with up to 8 */
275 				load_all_operands(specifier2);		/* arguments    */
276 			} else {
277 				CODE_BYTE(specifier1)
278 				    load_all_operands(specifier1);
279 			}
280 			var_opcodes[opcode - 0xc0] ();
281 		}
282 
283 #if defined(DJGPP) && defined(SOUND_SUPPORT)
284 		if (end_of_sound_flag)
285 			end_of_sound();
286 #endif
287 
288 		os_tick();
289 	} while (finished == 0);
290 
291 	finished--;
292 } /* interpret */
293 
294 
295 /*
296  * call
297  *
298  * Call a subroutine. Save PC and FP then load new PC and initialise
299  * new stack frame. Note that the caller may legally provide less or
300  * more arguments than the function actually has. The call type "ct"
301  * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
302  *
303  */
call(zword routine,int argc,zword * args,int ct)304 void call(zword routine, int argc, zword * args, int ct)
305 {
306 	long pc;
307 	zword value;
308 	zbyte count;
309 	int i;
310 
311 	if (sp - stack < 4)
312 		runtime_error(ERR_STK_OVF);
313 
314 	GET_PC(pc)
315 	* --sp = (zword) (pc >> 9);
316 	*--sp = (zword) (pc & 0x1ff);
317 	*--sp = (zword) (fp - stack - 1);
318 	*--sp = (zword) (argc | (ct << 12));
319 
320 	fp = sp;
321 	frame_count++;
322 
323 	/* Calculate byte address of routine */
324 
325 	if (z_header.version <= V3)
326 		pc = (long)routine << 1;
327 	else if (z_header.version <= V5)
328 		pc = (long)routine << 2;
329 	else if (z_header.version <= V7)
330 		pc = ((long)routine << 2) + ((long)z_header.functions_offset << 3);
331 	else			/* z_header.version == V8 */
332 		pc = (long)routine << 3;
333 
334 	if (pc >= story_size)
335 		runtime_error(ERR_ILL_CALL_ADDR);
336 
337 	SET_PC(pc)
338 	/* Initialise local variables */
339 	CODE_BYTE(count)
340 
341 	if (count > 15)
342 		runtime_error(ERR_CALL_NON_RTN);
343 	if (sp - stack < count)
344 		runtime_error(ERR_STK_OVF);
345 
346 	fp[0] |= (zword) count << 8;	 /* Save local var count for Quetzal. */
347 	value = 0;
348 	for (i = 0; i < count; i++) {
349 		if (z_header.version <= V4)	  /* V1 to V4 games provide default */
350 			CODE_WORD(value)  /* values for all local variables */
351 			*--sp = (zword) ((argc-- > 0) ? args[i] : value);
352 	}
353 
354 	/* Start main loop for direct calls */
355 	if (ct == 2)
356 		interpret();
357 } /* call */
358 
359 
360 /*
361  * ret
362  *
363  * Return from the current subroutine and restore the previous stack
364  * frame. The result may be stored (0), thrown away (1) or pushed on
365  * the stack (2). In the latter case a direct call has been finished
366  * and we must exit the interpreter loop.
367  *
368  */
ret(zword value)369 void ret(zword value)
370 {
371 	long pc;
372 	int ct;
373 
374 	if (sp > fp)
375 		runtime_error(ERR_STK_UNDF);
376 
377 	sp = fp;
378 
379 	ct = *sp++ >> 12;
380 	frame_count--;
381 	fp = stack + 1 + *sp++;
382 	pc = *sp++;
383 	pc = ((long)*sp++ << 9) | pc;
384 
385 	SET_PC(pc)
386 	/* Handle resulting value */
387 	if (ct == 0)
388 		store(value);
389 	if (ct == 2)
390 		*--sp = value;
391 
392 	/* Stop main loop for direct calls */
393 	if (ct == 2)
394 		finished++;
395 } /* ret */
396 
397 /*
398  * branch
399  *
400  * Take a jump after an instruction based on the flag, either true or
401  * false. The branch can be short or long; it is encoded in one or two
402  * bytes respectively. When bit 7 of the first byte is set, the jump
403  * takes place if the flag is true; otherwise it is taken if the flag
404  * is false. When bit 6 of the first byte is set, the branch is short;
405  * otherwise it is long. The offset occupies the bottom 6 bits of the
406  * first byte plus all the bits in the second byte for long branches.
407  * Uniquely, an offset of 0 means return false, and an offset of 1 is
408  * return true.
409  *
410  */
branch(bool flag)411 void branch(bool flag)
412 {
413 	long pc;
414 	zword offset;
415 	zbyte specifier;
416 	zbyte off1;
417 	zbyte off2;
418 
419 	CODE_BYTE(specifier)
420 	off1 = specifier & 0x3f;
421 
422 	if (!flag)
423 		specifier ^= 0x80;
424 
425 	if (!(specifier & 0x40)) {	/* it's a long branch */
426 		if (off1 & 0x20)	/* propagate sign bit */
427 			off1 |= 0xc0;
428 
429 		CODE_BYTE(off2)
430 		offset = (off1 << 8) | off2;
431 	} else
432 		offset = off1;	/* it's a short branch */
433 
434 	if (specifier & 0x80) {
435 		if (offset > 1) {	/* normal branch */
436 			GET_PC(pc)
437 			pc += (short)offset - 2;
438 			SET_PC(pc)
439 		} else
440 			ret(offset);	/* special case, return 0 or 1 */
441 	}
442 } /* branch */
443 
444 
445 /*
446  * store
447  *
448  * Store an operand, either as a variable or pushed on the stack.
449  *
450  */
store(zword value)451 void store(zword value)
452 {
453 	zbyte variable;
454 
455 	CODE_BYTE(variable)
456 	if (variable == 0)
457 		*--sp = value;
458 	else if (variable < 16)
459 		*(fp - variable) = value;
460 	else {
461 		zword addr = z_header.globals + 2 * (variable - 16);
462 		SET_WORD(addr, value)
463 	}
464 } /* store */
465 
466 
467 /*
468  * direct_call
469  *
470  * Call the interpreter loop directly. This is necessary when
471  *
472  * - a sound effect has been finished
473  * - a read instruction has timed out
474  * - a newline countdown has hit zero
475  *
476  * The interpreter returns the result value on the stack.
477  *
478  */
direct_call(zword addr)479 int direct_call(zword addr)
480 {
481 	zword saved_zargs[8];
482 	int saved_zargc;
483 	int i;
484 
485 	/* Calls to address 0 return false */
486 	if (addr == 0)
487 		return 0;
488 
489 	/* Save operands and operand count */
490 	for (i = 0; i < 8; i++)
491 		saved_zargs[i] = zargs[i];
492 	saved_zargc = zargc;
493 
494 	/* Call routine directly */
495 	call(addr, 0, 0, 2);
496 
497 	/* Restore operands and operand count */
498 	for (i = 0; i < 8; i++)
499 		zargs[i] = saved_zargs[i];
500 	zargc = saved_zargc;
501 
502 	/* Resulting value lies on top of the stack */
503 	return (short)*sp++;
504 } /* direct_call */
505 
506 
507 /*
508  * __extended__
509  *
510  * Load and execute an extended opcode.
511  *
512  */
__extended__(void)513 static void __extended__(void)
514 {
515 	zbyte opcode;
516 	zbyte specifier;
517 
518 	CODE_BYTE(opcode)
519 	CODE_BYTE(specifier)
520 	load_all_operands(specifier);
521 
522 	/* extended opcodes from 0x1d on */
523 	if (opcode < 0x1d)
524 		ext_opcodes[opcode] ();	/* are reserved for future spec' */
525 } /* __extended__ */
526 
527 
528 /*
529  * __illegal__
530  *
531  * Exit game because an unknown opcode has been hit.
532  *
533  */
__illegal__(void)534 static void __illegal__(void)
535 {
536 	runtime_error(ERR_ILL_OPCODE);
537 } /* __illegal__ */
538 
539 
540 /*
541  * z_catch, store the current stack frame for later use with z_throw.
542  *
543  *	no zargs used
544  *
545  */
z_catch(void)546 void z_catch(void)
547 {
548 	store(frame_count);
549 }  /* z_catch */
550 
551 
552 /*
553  * z_throw, go back to the given stack frame and return the given value.
554  *
555  *	zargs[0] = value to return
556  *	zargs[1] = stack frame
557  *
558  */
z_throw(void)559 void z_throw(void)
560 {
561 	if (zargs[1] > frame_count)
562 		runtime_error(ERR_BAD_FRAME);
563 
564 	/* Unwind the stack a frame at a time. */
565 	for (; frame_count > zargs[1]; --frame_count)
566 		fp = stack + 1 + fp[1];
567 
568 	ret(zargs[0]);
569 } /* z_throw */
570 
571 
572 /*
573  * z_call_n, call a subroutine and discard its result.
574  *
575  * 	zargs[0] = packed address of subroutine
576  *	zargs[1] = first argument (optional)
577  *	...
578  *	zargs[7] = seventh argument (optional)
579  *
580  */
z_call_n(void)581 void z_call_n(void)
582 {
583 	if (zargs[0] != 0)
584 		call(zargs[0], zargc - 1, zargs + 1, 1);
585 } /* z_call_n */
586 
587 
588 /*
589  * z_call_s, call a subroutine and store its result.
590  *
591  * 	zargs[0] = packed address of subroutine
592  *	zargs[1] = first argument (optional)
593  *	...
594  *	zargs[7] = seventh argument (optional)
595  *
596  */
z_call_s(void)597 void z_call_s(void)
598 {
599 	if (zargs[0] != 0)
600 		call(zargs[0], zargc - 1, zargs + 1, 0);
601 	else
602 		store(0);
603 } /* z_call_s */
604 
605 
606 /*
607  * z_check_arg_count, branch if subroutine was called with >= n arg's.
608  *
609  * 	zargs[0] = number of arguments
610  *
611  */
z_check_arg_count(void)612 void z_check_arg_count(void)
613 {
614 	if (fp == stack + STACK_SIZE)
615 		branch(zargs[0] == 0);
616 	else
617 		branch(zargs[0] <= (*fp & 0xff));
618 
619 } /* z_check_arg_count */
620 
621 
622 /*
623  * z_jump, jump unconditionally to the given address.
624  *
625  *	zargs[0] = PC relative address
626  *
627  */
z_jump(void)628 void z_jump(void)
629 {
630 	long pc;
631 
632 	GET_PC(pc)
633 	pc += (short)zargs[0] - 2;
634 
635 	if (pc >= story_size)
636 		runtime_error(ERR_ILL_JUMP_ADDR);
637 
638 	SET_PC(pc)
639 } /* z_jump */
640 
641 
642 /*
643  * z_nop, no operation.
644  *
645  *	no zargs used
646  *
647  */
z_nop(void)648 void z_nop(void)
649 {
650 	/* Do nothing */
651 } /* z_nop */
652 
653 
654 /*
655  * z_quit, stop game and exit interpreter.
656  *
657  *	no zargs used
658  *
659  */
z_quit(void)660 void z_quit(void)
661 {
662 	finished = 9999;
663 } /* z_quit */
664 
665 
666 /*
667  * z_ret, return from a subroutine with the given value.
668  *
669  *	zargs[0] = value to return
670  *
671  */
z_ret(void)672 void z_ret(void)
673 {
674 	ret(zargs[0]);
675 } /* z_ret */
676 
677 
678 /*
679  * z_ret_popped, return from a subroutine with a value popped off the stack.
680  *
681  *	no zargs used
682  *
683  */
z_ret_popped(void)684 void z_ret_popped(void)
685 {
686 	ret(*sp++);
687 } /* z_ret_popped */
688 
689 
690 /*
691  * z_rfalse, return from a subroutine with false (0).
692  *
693  * 	no zargs used
694  *
695  */
z_rfalse(void)696 void z_rfalse(void)
697 {
698 	ret(0);
699 } /* z_rfalse */
700 
701 
702 /*
703  * z_rtrue, return from a subroutine with true (1).
704  *
705  * 	no zargs used
706  *
707  */
z_rtrue(void)708 void z_rtrue(void)
709 {
710 	ret(1);
711 } /* z_rtrue */
712