1 /* Nitfol - z-machine interpreter using Glk for output.
2 Copyright (C) 1999 Evin Robertson
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
18 The author can be reached at nitfol@deja.com
22 /* Contains everything which references the stack directly. */
24 /* Uses two stacks because I don't want to worry about alignment issues */
26 typedef struct Stack_frame Stack_frame;
29 offset stack_stack_start; /* points into stack_stack to local variables
30 for this frame, which are followed by
31 stack for pushing/popping */
34 zword arguments; /* Number of arguments used for check_count */
35 int result_variable; /* Where to place the result upon returning */
38 static zword *stack_stack = NULL; /* Holds local variables and pushed values */
39 static offset stack_pointer; /* Current offset into stack_stack */
40 static offset stack_min; /* Minimum for stack_pointer (how much we can pop) */
41 static offset stack_max; /* Maximum for stack_pointer (size of stack) */
42 static zword *local_vars; /* Pointer to local variables for current frame */
44 static Stack_frame *stack_frames = NULL;
45 static zword frame_count; /* number of frames on the stack */
46 static zword frame_max;
49 void init_stack(offset initialstack_stack_size, zword initialframe_size)
52 stack_stack = (zword *) n_malloc(sizeof(*stack_stack) *
53 initialstack_stack_size);
56 stack_max = initialstack_stack_size;
59 stack_frames = (Stack_frame *) n_malloc(sizeof(*stack_frames) *
62 if(stacklimit && initialframe_size > stacklimit)
63 frame_max = stacklimit;
65 frame_max = initialframe_size;
67 stack_frames[frame_count].stack_stack_start = 0;
68 stack_frames[frame_count].return_PC = 0;
69 stack_frames[frame_count].num_locals = 0;
70 stack_frames[frame_count].arguments = 0;
71 stack_frames[frame_count].result_variable = -2;
72 local_vars = stack_stack + stack_frames[frame_count].stack_stack_start;
83 /* Perform various sanity checks on the stack to make sure all is well in
85 BOOL verify_stack(void)
88 if(frame_count > frame_max) {
89 n_show_error(E_STACK, "more frames than max", frame_count);
93 n_show_error(E_STACK, "no frames", 0);
96 for(f = 0; f < frame_count; f++) {
97 if(stack_frames[f].stack_stack_start > stack_pointer) {
98 n_show_error(E_STACK, "stack start after end", f);
101 if(stack_frames[f].return_PC > total_size) {
102 n_show_error(E_STACK, "PC after end of game", f);
105 if(stack_frames[f].num_locals > 15) {
106 n_show_error(E_STACK, "too many locals", f);
109 if(stack_frames[f].arguments > 7) {
110 n_show_error(E_STACK, "too many arguments", f);
113 if(stack_frames[f].result_variable > 255) {
114 n_show_error(E_STACK, "result variable too big", f);
117 if(stack_frames[f].result_variable < -2) {
118 n_show_error(E_STACK, "unknown magic result variable", f);
126 /* Insure we have at least addsize more zwords available on the stack, and
127 * if not, allocate more space
129 static void check_stack_stack(offset addsize)
131 if(stack_pointer + addsize >= stack_max) {
133 stack_stack = (zword *) n_realloc(stack_stack,
134 sizeof(*stack_stack) * stack_max);
136 n_show_port(E_STACK, "stack larger than available on some interps", stack_max);
138 local_vars = stack_stack + stack_frames[frame_count].stack_stack_start;
143 void add_stack_frame(offset return_PC, unsigned num_locals, zword *locals,
144 unsigned num_args, int result_var)
147 /* Don't increment the frame yet because we have error messages yet to
148 show which need to receive a valid frame to output local variables */
149 if(frame_count+1 >= frame_max) {
151 if(stacklimit && frame_max > stacklimit) {
152 frame_max = stacklimit;
153 if(frame_count+1 >= frame_max) {
154 n_show_fatal(E_STACK, "recursed deeper than allowed", frame_count+1);
157 stack_frames = (Stack_frame *)
158 n_realloc(stack_frames, sizeof(*stack_frames) * frame_max);
159 n_show_port(E_STACK, "deep recursion not available on some 'terps", frame_max);
162 stack_frames[frame_count].stack_stack_start = stack_pointer;
163 stack_frames[frame_count].return_PC = return_PC;
164 stack_frames[frame_count].num_locals = num_locals;
165 stack_frames[frame_count].arguments = num_args;
166 stack_frames[frame_count].result_variable = result_var;
169 check_stack_stack(num_locals);
170 for(n = 0; n < num_locals; n++)
171 stack_stack[stack_pointer++] = locals[n];
173 stack_min = stack_pointer;
175 local_vars = stack_stack + stack_frames[frame_count].stack_stack_start;
179 void remove_stack_frame(void)
182 if(frame_count == 0) {
183 n_show_error(E_STACK, "attempt to remove initial stack frame", 0);
187 stack_pointer = stack_frames[frame_count].stack_stack_start;
189 stack_min = stack_frames[frame_count].stack_stack_start +
190 stack_frames[frame_count].num_locals;
191 local_vars = stack_stack + stack_frames[frame_count].stack_stack_start;
195 void check_set_var(int var, zword val)
198 default: set_var(var, val); break;
199 case -2: exit_decoder = TRUE; time_ret = val; /* timer junk */
205 void mop_func_return(zword ret_val)
208 PC = stack_frames[frame_count].return_PC;
209 var = stack_frames[frame_count].result_variable;
210 remove_stack_frame();
211 check_set_var(var, ret_val);
212 /* printf("retn %x\n", PC); */
218 mop_store_result(frame_count);
222 unsigned stack_get_numlocals(int frame)
225 return stack_frames[frame].num_locals;
233 if(operand[1] > frame_count) {
234 n_show_error(E_STACK, "attempting to throw away non-existent frames", operand[1]);
238 if(operand[1] != 0) {
239 frame_count = operand[1];
240 mop_func_return(operand[0]);
242 n_show_error(E_STACK, "attempting to throw away initial frame", operand[0]);
246 void op_check_arg_count(void)
248 if(stack_frames[frame_count].arguments >= operand[0])
255 static zword stack_pop(void)
258 if(stack_pointer <= stack_min) {
259 n_show_error(E_STACK, "underflow - excessive popping", stack_pointer);
263 return stack_stack[--stack_pointer];
267 static void stack_push(zword n)
269 check_stack_stack(1);
270 stack_stack[stack_pointer++] = n;
276 stack_push(operand[0]);
288 if(zversion == 6) { /* v6 uses user stacks */
289 if(numoperands == 0 || operand[0] == 0)
290 mop_store_result(stack_pop());
292 zword space = LOWORD(operand[0]) + 1; /* One more slot is free */
293 LOWORDwrite(operand[0], space);
294 mop_store_result(LOWORD(operand[0] + space * ZWORD_SIZE));
297 zword val = stack_pop();
298 set_var(operand[0], val);
303 void op_pop_stack(void)
306 if(numoperands < 2 || operand[1] == 0) {
307 for(i = 0; i < operand[0]; i++)
310 zword space = LOWORD(operand[1]) + operand[0];
311 LOWORDwrite(operand[1], space);
316 void op_push_stack(void)
318 zword space = LOWORD(operand[1]);
320 LOWORDwrite(operand[1] + space * ZWORD_SIZE, operand[0]);
321 LOWORDwrite(operand[1], space - 1);
329 void mop_store_result(zword val)
331 set_var(HIBYTE(PC), val);
336 void op_ret_popped(void)
338 mop_func_return(stack_pop());
341 unsigned stack_get_depth(void)
346 BOOL frame_is_valid(unsigned frame)
348 return frame <= frame_count;
351 offset frame_get_PC(unsigned frame)
353 if(frame == frame_count) {
356 return stack_frames[frame+1].return_PC;
359 zword frame_get_var(unsigned frame, int var)
361 if(var == 0 || var > 0x10) {
362 n_show_error(E_STACK, "variable not readable from arbitrary frame", var);
366 if(var > stack_frames[frame].num_locals)
367 n_show_error(E_STACK, "reading nonexistant local", var);
369 return stack_stack[stack_frames[frame].stack_stack_start + (var - 1)];
373 void frame_set_var(unsigned frame, int var, zword val)
375 if(var == 0 || var > 0x10) {
376 n_show_error(E_STACK, "variable not writable from arbitrary frame", var);
380 if(var > stack_frames[frame].num_locals)
381 n_show_error(E_STACK, "writing nonexistant local", var);
383 stack_stack[stack_frames[frame].stack_stack_start + (var - 1)] = val;;
386 N_INLINE zword get_var(int var)
391 if(var > stack_frames[frame_count].num_locals)
392 n_show_error(E_INSTR, "reading nonexistant local", var);
394 return local_vars[var - 1];
398 return LOWORD(z_globaltable + (var - 0x10) * ZWORD_SIZE);
401 N_INLINE void set_var(int var, zword val)
406 if(var > stack_frames[frame_count].num_locals)
407 n_show_error(E_INSTR, "setting nonexistant local", var);
409 local_vars[var - 1] = val;
414 LOWORDwrite(z_globaltable + (var - 0x10) * ZWORD_SIZE, val);
419 const unsigned qstackframe[] = { 3, 1, 1, 1, 2, 0 };
420 enum qstacknames { qreturnPC, qflags, qvar, qargs, qeval };
422 BOOL quetzal_stack_restore(strid_t stream, glui32 qsize)
428 init_stack(1024, 128);
438 i += fillstruct(stream, qstackframe, qframe, NULL);
440 if(qframe[qreturnPC] > total_size) {
441 n_show_error(E_SAVE, "function return PC past end of memory",
446 if((qframe[qflags] & b11100000) != 0) {
447 n_show_error(E_SAVE, "expected top bits of flag to be zero", qframe[qflags]);
452 if(qframe[qflags] & b00010000) /* from a call_n */
455 num_locals = qframe[qflags] & b00001111;
457 if(num_locals > 15) {
458 n_show_error(E_SAVE, "too many locals", num_locals);
463 switch(qframe[qargs]) {
465 n_show_error(E_SAVE, "invalid argument count", qframe[qargs]);
467 case b01111111: num_args++;
468 case b00111111: num_args++;
469 case b00011111: num_args++;
470 case b00001111: num_args++;
471 case b00000111: num_args++;
472 case b00000011: num_args++;
473 case b00000001: num_args++;
477 for(n = 0; n < num_locals; n++) {
478 unsigned char v[ZWORD_SIZE];
479 glk_get_buffer_stream(stream, (char *) v, ZWORD_SIZE);
480 locals[n] = MSBdecodeZ(v);
484 if(zversion != 6 && num_frames == 0)
485 ; /* dummy stack frame; don't really use it */
487 add_stack_frame(qframe[qreturnPC],
491 for(n = 0; n < qframe[qeval]; n++) {
492 unsigned char v[ZWORD_SIZE];
493 glk_get_buffer_stream(stream, (char *) v, ZWORD_SIZE);
494 stack_push(MSBdecodeZ(v));
500 if(!verify_stack()) {
501 n_show_error(E_SAVE, "restored stack fails verification", 0);
508 glui32 get_quetzal_stack_size(void)
512 framespace = frame_count * 8;
513 stackspace = stack_pointer * ZWORD_SIZE;
515 framespace += 8; /* for the dummy frame */
516 return framespace + stackspace;
520 BOOL quetzal_stack_save(strid_t stream)
522 unsigned frame_num = 0;
527 if(!verify_stack()) {
528 n_show_error(E_SAVE, "stack did not pass verification before saving", 0);
532 /* We have to look one ahead to see how much stack space a frame uses; when
533 we get to the last frame, there will be no next frame, so this won't work
534 if there wasn't a frame there earlier with the correct info. Add and
535 remove a frame to make things happy */
536 add_stack_frame(0, 0, NULL, 0, 0);
537 remove_stack_frame();
539 for(; frame_num <= frame_count; frame_num++) {
546 const unsigned char argarray[8] = {
547 b00000000, b00000001, b00000011, b00000111,
548 b00001111, b00011111, b00111111, b01111111
551 qframe[qreturnPC] = stack_frames[frame_num].return_PC;
553 qframe[qvar] = stack_frames[frame_num].result_variable;
555 num_locals = stack_frames[frame_num].num_locals;
557 if(num_locals > 15) {
558 n_show_error(E_SAVE, "num_locals too big", num_locals);
562 qframe[qflags] = num_locals;
564 if(stack_frames[frame_num].result_variable == -1) {
565 qframe[qflags] |= b00010000;
569 if(stack_frames[frame_num].arguments > 7) {
570 n_show_error(E_SAVE, "too many function arguments", stack_frames[frame_num].arguments);
574 qframe[qargs] = argarray[stack_frames[frame_num].arguments];
576 stack_size = (stack_frames[frame_num+1].stack_stack_start -
577 stack_frames[frame_num].stack_stack_start -
580 qframe[qeval] = stack_size;
583 qframe[qreturnPC] = 0;
589 emptystruct(stream, qstackframe, qframe);
591 for(n = 0; n < num_locals + stack_size; n++) {
592 unsigned char v[ZWORD_SIZE];
593 zword z = stack_stack[stack_frames[frame_num].stack_stack_start + n];
595 w_glk_put_buffer_stream(stream, (char *) v, ZWORD_SIZE);