1 /*
2  * control.c
3  *
4  * Functions that alter the flow of control.
5  *
6  */
7 
8 #include "ztypes.h"
9 
10 static const char *v1_lookup_table[3] = {
11     "abcdefghijklmnopqrstuvwxyz",
12     "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
13     " 0123456789.,!?_#'\"/\\<-:()"
14 };
15 
16 static const char *v3_lookup_table[3] = {
17     "abcdefghijklmnopqrstuvwxyz",
18     "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
19     " \n0123456789.,!?_#'\"/\\-:()"
20 };
21 
22 /*
23  * check_argument
24  *
25  * Jump if argument is present.
26  *
27  */
28 
29 #ifdef __STDC__
check_argument(zword_t argc)30 void check_argument (zword_t argc)
31 #else
32 void check_argument (argc)
33 zword_t argc;
34 #endif
35 {
36 
37     conditional_jump (argc <= (zword_t) (stack[fp + 1] & ARGS_MASK));
38 
39 }/* check_argument */
40 
41 /*
42  * call
43  *
44  * Call a subroutine. Save PC and FP then load new PC and initialise stack based
45  * local arguments.
46  *
47  */
48 
49 #ifdef __STDC__
call(int argc,zword_t * argv,int type)50 int call (int argc, zword_t *argv, int type)
51 #else
52 int call (argc, argv, type)
53 int argc;
54 zword_t *argv;
55 int type;
56 #endif
57 {
58     zword_t arg;
59     int i = 1, args, status = 0;
60 
61     /* Convert calls to 0 as returning FALSE */
62 
63     if (argv[0] == 0) {
64         if (type == FUNCTION)
65             store_operand (FALSE);
66         return (0);
67     }
68 
69     /* Save current PC, FP and argument count on stack */
70 
71     stack[--sp] = (zword_t) (pc / PAGE_SIZE);
72     stack[--sp] = (zword_t) (pc % PAGE_SIZE);
73     stack[--sp] = fp;
74     stack[--sp] = (argc - 1) | type;
75 
76     /* Create FP for new subroutine and load new PC */
77 
78     fp = sp - 1;
79     pc = (unsigned long) argv[0] * story_scaler;
80     ++frame_count;
81 
82     /* Read argument count and initialise local variables */
83 
84     args = (unsigned int) read_code_byte ();
85     stack[sp] |= args << VAR_SHIFT;
86     while (--args >= 0) {
87         arg = (h_type > V4) ? 0 : read_code_word ();
88         stack[--sp] = (--argc > 0) ? argv[i++] : arg;
89     }
90 
91     /* If the call is asynchronous then call the interpreter directly.
92        We will return back here when the corresponding return frame is
93        encountered in the ret call. */
94 
95     if (type == ASYNC) {
96         status = interpret ();
97         interpreter_state = RUN;
98         interpreter_status = 1;
99     }
100 
101     return (status);
102 
103 }/* call */
104 
105 /*
106  * ret
107  *
108  * Return from subroutine. Restore FP and PC from stack.
109  *
110  */
111 
112 #ifdef __STDC__
ret(zword_t value)113 void ret (zword_t value)
114 #else
115 void ret (value)
116 zword_t value;
117 #endif
118 {
119     zword_t argc;
120 
121     /* Clean stack */
122 
123     sp = fp + 1;
124 
125     /* Restore argument count, FP and PC */
126 
127     argc = stack[sp++];
128     fp = stack[sp++];
129     pc = stack[sp++];
130     pc += (unsigned long) stack[sp++] * PAGE_SIZE;
131     --frame_count;
132 
133     /* If this was an async call then stop the interpreter and return
134        the value from the async routine. This is slightly hacky using
135        a global state variable, but ret can be called with conditional_jump
136        which in turn can be called from all over the place, sigh. A
137        better design would have all opcodes returning the status RUN, but
138        this is too much work and makes the interpreter loop look ugly */
139 
140     if ((argc & TYPE_MASK) == ASYNC) {
141 
142         interpreter_state = STOP;
143         interpreter_status = (int) value;
144 
145     } else {
146 
147         /* Return subroutine value for function call only */
148 
149         if ((argc & TYPE_MASK) == FUNCTION)
150             store_operand (value);
151 
152     }
153 
154 }/* ret */
155 
156 /*
157  * jump
158  *
159  * Unconditional jump. Jump is PC relative.
160  *
161  */
162 
163 #ifdef __STDC__
jump(zword_t offset)164 void jump (zword_t offset)
165 #else
166 void jump (offset)
167 zword_t offset;
168 #endif
169 {
170 
171     pc = (unsigned long) (pc + (short) offset - 2);
172 
173 }/* jump */
174 
175 /*
176  * restart
177  *
178  * Restart game by initialising environment and reloading start PC.
179  *
180  */
181 
182 #ifdef __STDC__
restart(void)183 void restart (void)
184 #else
185 void restart ()
186 #endif
187 {
188     unsigned int i, j, restart_size, scripting_flag;
189 
190     /* Reset output buffer */
191 
192     flush_buffer (TRUE);
193 
194     /* Reset text control flags */
195 
196     formatting = ON;
197     outputting = ON;
198     redirect_depth = 0;
199     scripting_disable = OFF;
200 
201     /* Randomise */
202 
203     SRANDOM_FUNC ((unsigned int) time (NULL));
204 
205     /* Remember scripting state */
206 
207     scripting_flag = get_word (H_FLAGS) & SCRIPTING_FLAG;
208 
209     /* Load restart size and reload writeable data area */
210 
211     restart_size = (h_restart_size / PAGE_SIZE) + 1;
212     for (i = 0; i < restart_size; i++)
213         read_page (i, &datap[i * PAGE_SIZE]);
214 
215     /* Restart the screen */
216 
217     set_status_size (0);
218     set_attribute (NORMAL);
219     erase_window (SCREEN);
220 
221     restart_screen ();
222 
223     /* Reset the interpreter state */
224 
225     restart_interp (scripting_flag);
226 
227     /* Initialise the character translation lookup tables */
228 
229     for (i = 0; i < 3; i++) {
230         for (j = 0; j < 26; j++) {
231             if (h_alternate_alphabet_offset) {
232                 lookup_table[i][j] = get_byte (h_alternate_alphabet_offset + (i * 26) + j);
233             } else {
234                 if (h_type == V1)
235                     lookup_table[i][j] = v1_lookup_table[i][j];
236                 else
237                     lookup_table[i][j] = v3_lookup_table[i][j];
238             }
239         }
240     }
241 
242     /* Load start PC, SP and FP */
243 
244     pc = h_start_pc;
245     sp = STACK_SIZE;
246     fp = STACK_SIZE - 1;
247     frame_count = 0;
248 
249 }/* restart */
250 
251 /*
252  * restart_interp
253  *
254  * Do all the things which need to be done after startup, restart, and restore
255  * commands.
256  *
257  */
258 
259 #ifdef __STDC__
restart_interp(int scripton_flag)260 void restart_interp (int scripton_flag)
261 #else
262 void restart_interp (scripton_flag)
263 int scripton_flag;
264 #endif
265 {
266     if (scripton_flag)
267         set_word (H_FLAGS, (get_word (H_FLAGS) | SCRIPTING_FLAG));
268 
269     set_byte (H_INTERPRETER, h_interpreter);
270     set_byte (H_INTERPRETER_VERSION, h_interpreter_version);
271     if (strictz_declare_spec) {
272       set_byte (0x32, 0x01);
273       set_byte (0x33, 0x00);
274     }
275     set_byte (H_SCREEN_ROWS, screen_rows); /* Screen dimension in characters */
276     set_byte (H_SCREEN_COLUMNS, screen_cols);
277 
278     set_byte (H_SCREEN_LEFT, 0); /* Screen dimension in smallest addressable units, ie. pixels */
279     set_byte (H_SCREEN_RIGHT, screen_cols);
280     set_byte (H_SCREEN_TOP, 0);
281     set_byte (H_SCREEN_BOTTOM, screen_rows);
282 
283     set_byte (H_MAX_CHAR_WIDTH, 1); /* Size of a character in screen units */
284     set_byte (H_MAX_CHAR_HEIGHT, 1);
285 
286     /* Initialise status region */
287 
288     if (h_type < V4) {
289         set_status_size (0);
290         blank_status_line ();
291     }
292 
293 }/* restart_interp */
294 
295 /*
296  * get_fp
297  *
298  * Return the value of the frame pointer (FP) for later use with unwind.
299  * Before V5 games this was a simple pop.
300  *
301  */
302 
303 #ifdef __STDC__
get_fp(void)304 void get_fp (void)
305 #else
306 void get_fp ()
307 #endif
308 {
309 
310     if (h_type > V4)
311         store_operand (frame_count);
312     else
313         sp++;
314 
315 }/* get_fp */
316 
317 /*
318  * unwind
319  *
320  * Remove one or more stack frames and return. Works like longjmp, see get_fp.
321  *
322  */
323 
324 #ifdef __STDC__
unwind(zword_t value,zword_t new_fp)325 void unwind (zword_t value, zword_t new_fp)
326 #else
327 void unwind (value, new_fp)
328 zword_t value;
329 zword_t new_fp;
330 #endif
331 {
332 
333     if (new_fp > frame_count)
334         fatal ("Bad frame for unwind");
335 
336     for (; new_fp < frame_count; --frame_count) {
337         sp = fp + 1;
338 	fp = stack[sp+1];
339     }
340 
341     ret (value);
342 
343 }/* unwind */
344