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