2 * Copyright 2010-2012 Chris Spiegel.
4 * This file is part of Bocfel.
6 * Bocfel is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version
8 * 2 or 3, as published by the Free Software Foundation.
10 * Bocfel is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Bocfel. If not, see <http://www.gnu.org/licenses/>.
45 static jmp_buf *jumps;
48 /* Each time an interrupt happens, process_instructions() is called
49 * (effectively starting a whole new round of interpreting). This
50 * variable holds the current level of interpreting: 0 for no
51 * interrupts, 1 if one interrupt has been called, 2 if an interrupt was
52 * called inside of an interrupt, and so on.
54 static long ilevel = -1;
56 long interrupt_level(void)
61 /* When this is called, the interrupt at level “level” will stop
62 * running: if a single interrupt is running, then break_from(1) will
63 * stop the interrupt, going back to the main program. Breaking from
64 * interrupt level 0 (which is not actually an interrupt) will end the
65 * program. This is how @quit is implemented.
67 void break_from(long level)
70 longjmp(jumps[level], 1);
73 /* If a restore happens inside of an interrupt, the level needs to be
74 * set back to 0, but without a longjmp(), so break_from() cannot be
77 void reset_level(void)
82 /* To signal a restart, longjmp() is called with 2; this advises
83 * process_instructions() to restart the story file and then continue
84 * execution, whereas a value of 1 tells it to return immediately.
86 static void zrestart(void)
92 /* Returns 1 if decoded, 0 otherwise (omitted) */
93 static int decode_base(uint8_t type, uint16_t *loc)
95 if (type == 0) *loc = WORD(pc++); /* Large constant. */
96 else if(type == 1) *loc = BYTE(pc); /* Small constant. */
97 else if(type == 2) *loc = variable(BYTE(pc)); /* Variable. */
98 else return 0; /* Omitted. */
105 static void decode_var(uint8_t types)
109 if(!decode_base((types >> 6) & 0x03, &ret)) return;
110 zargs[znargs++] = ret;
111 if(!decode_base((types >> 4) & 0x03, &ret)) return;
112 zargs[znargs++] = ret;
113 if(!decode_base((types >> 2) & 0x03, &ret)) return;
114 zargs[znargs++] = ret;
115 if(!decode_base((types >> 0) & 0x03, &ret)) return;
116 zargs[znargs++] = ret;
119 /* op[0] is 0OP, op[1] is 1OP, etc */
120 static void (*op[5][256])(void);
121 static const char *opnames[5][256];
122 enum { ZERO, ONE, TWO, VAR, EXT };
124 #define op_call(opt, opnumber) (op[opt][opnumber]())
126 /* This nifty trick is from Frotz. */
127 static void zextended(void)
129 uint8_t opnumber = BYTE(pc++);
131 decode_var(BYTE(pc++));
134 * The exception for 0x80–0x83 is for the Zoom extensions.
135 * Standard 1.1 implicitly updates §14.2.1 to recommend ignoring
136 * opcodes in the range EXT:30 to EXT:255, due to the fact that
137 * @buffer_screen is EXT:29.
139 if(opnumber > 0x1d && (opnumber < 0x80 || opnumber > 0x83)) return;
141 op_call(EXT, opnumber);
144 static void illegal_opcode(void)
146 #ifndef ZTERP_NO_SAFETY_CHECKS
147 die("illegal opcode (pc = 0x%lx)", zassert_pc);
149 die("illegal opcode");
153 void setup_opcodes(void)
155 for(int i = 0; i < 5; i++)
157 for(int j = 0; j < 256; j++)
159 op[i][j] = illegal_opcode;
162 #define OP(args, opcode, fn) do { op[args][opcode] = fn; opnames[args][opcode] = #fn; } while(0)
163 OP(ZERO, 0x00, zrtrue);
164 OP(ZERO, 0x01, zrfalse);
165 OP(ZERO, 0x02, zprint);
166 OP(ZERO, 0x03, zprint_ret);
167 OP(ZERO, 0x04, znop);
168 if(zversion <= 4) OP(ZERO, 0x05, zsave);
169 if(zversion <= 4) OP(ZERO, 0x06, zrestore);
170 OP(ZERO, 0x07, zrestart);
171 OP(ZERO, 0x08, zret_popped);
172 if(zversion <= 4) OP(ZERO, 0x09, zpop);
173 else OP(ZERO, 0x09, zcatch);
174 OP(ZERO, 0x0a, zquit);
175 OP(ZERO, 0x0b, znew_line);
176 if (zversion == 3) OP(ZERO, 0x0c, zshow_status);
177 else if(zversion >= 4) OP(ZERO, 0x0c, znop); /* §15: Technically illegal in V4+, but a V5 Wishbringer accidentally uses this opcode. */
178 if(zversion >= 3) OP(ZERO, 0x0d, zverify);
179 if(zversion >= 5) OP(ZERO, 0x0e, zextended);
180 if(zversion >= 5) OP(ZERO, 0x0f, zpiracy);
183 OP(ONE, 0x01, zget_sibling);
184 OP(ONE, 0x02, zget_child);
185 OP(ONE, 0x03, zget_parent);
186 OP(ONE, 0x04, zget_prop_len);
189 OP(ONE, 0x07, zprint_addr);
190 if(zversion >= 4) OP(ONE, 0x08, zcall_1s);
191 OP(ONE, 0x09, zremove_obj);
192 OP(ONE, 0x0a, zprint_obj);
194 OP(ONE, 0x0c, zjump);
195 OP(ONE, 0x0d, zprint_paddr);
196 OP(ONE, 0x0e, zload);
197 if(zversion <= 4) OP(ONE, 0x0f, znot);
198 else OP(ONE, 0x0f, zcall_1n);
203 OP(TWO, 0x04, zdec_chk);
204 OP(TWO, 0x05, zinc_chk);
206 OP(TWO, 0x07, ztest);
209 OP(TWO, 0x0a, ztest_attr);
210 OP(TWO, 0x0b, zset_attr);
211 OP(TWO, 0x0c, zclear_attr);
212 OP(TWO, 0x0d, zstore);
213 OP(TWO, 0x0e, zinsert_obj);
214 OP(TWO, 0x0f, zloadw);
215 OP(TWO, 0x10, zloadb);
216 OP(TWO, 0x11, zget_prop);
217 OP(TWO, 0x12, zget_prop_addr);
218 OP(TWO, 0x13, zget_next_prop);
224 if(zversion >= 4) OP(TWO, 0x19, zcall_2s);
225 if(zversion >= 5) OP(TWO, 0x1a, zcall_2n);
226 if(zversion >= 5) OP(TWO, 0x1b, zset_colour);
227 if(zversion >= 5) OP(TWO, 0x1c, zthrow);
229 OP(VAR, 0x00, zcall);
230 OP(VAR, 0x01, zstorew);
231 OP(VAR, 0x02, zstoreb);
232 OP(VAR, 0x03, zput_prop);
233 OP(VAR, 0x04, zread);
234 OP(VAR, 0x05, zprint_char);
235 OP(VAR, 0x06, zprint_num);
236 OP(VAR, 0x07, zrandom);
237 OP(VAR, 0x08, zpush);
238 OP(VAR, 0x09, zpull);
239 if(zversion >= 3) OP(VAR, 0x0a, zsplit_window);
240 if(zversion >= 3) OP(VAR, 0x0b, zset_window);
241 if(zversion >= 4) OP(VAR, 0x0c, zcall_vs2);
242 if(zversion >= 4) OP(VAR, 0x0d, zerase_window);
243 if(zversion >= 4) OP(VAR, 0x0e, zerase_line);
244 if(zversion >= 4) OP(VAR, 0x0f, zset_cursor);
245 if(zversion >= 4) OP(VAR, 0x10, zget_cursor);
246 if(zversion >= 4) OP(VAR, 0x11, zset_text_style);
247 if(zversion >= 4) OP(VAR, 0x12, znop); /* XXX buffer_mode */
248 if(zversion >= 3) OP(VAR, 0x13, zoutput_stream);
249 if(zversion >= 3) OP(VAR, 0x14, zinput_stream);
250 if(zversion >= 3) OP(VAR, 0x15, zsound_effect);
251 if(zversion >= 4) OP(VAR, 0x16, zread_char);
252 if(zversion >= 4) OP(VAR, 0x17, zscan_table);
253 if(zversion >= 5) OP(VAR, 0x18, znot);
254 if(zversion >= 5) OP(VAR, 0x19, zcall_vn);
255 if(zversion >= 5) OP(VAR, 0x1a, zcall_vn2);
256 if(zversion >= 5) OP(VAR, 0x1b, ztokenise);
257 if(zversion >= 5) OP(VAR, 0x1c, zencode_text);
258 if(zversion >= 5) OP(VAR, 0x1d, zcopy_table);
259 if(zversion >= 5) OP(VAR, 0x1e, zprint_table);
260 if(zversion >= 5) OP(VAR, 0x1f, zcheck_arg_count);
262 if(zversion >= 5) OP(EXT, 0x00, zsave5);
263 if(zversion >= 5) OP(EXT, 0x01, zrestore5);
264 if(zversion >= 5) OP(EXT, 0x02, zlog_shift);
265 if(zversion >= 5) OP(EXT, 0x03, zart_shift);
266 if(zversion >= 5) OP(EXT, 0x04, zset_font);
267 if(zversion >= 6) OP(EXT, 0x05, znop); /* XXX draw_picture */
268 if(zversion >= 6) OP(EXT, 0x06, zpicture_data);
269 if(zversion >= 6) OP(EXT, 0x07, znop); /* XXX erase_picture */
270 if(zversion >= 6) OP(EXT, 0x08, znop); /* XXX set_margins */
271 if(zversion >= 5) OP(EXT, 0x09, zsave_undo);
272 if(zversion >= 5) OP(EXT, 0x0a, zrestore_undo);
273 if(zversion >= 5) OP(EXT, 0x0b, zprint_unicode);
274 if(zversion >= 5) OP(EXT, 0x0c, zcheck_unicode);
275 if(zversion >= 5) OP(EXT, 0x0d, zset_true_colour);
276 if(zversion >= 6) OP(EXT, 0x10, znop); /* XXX move_window */
277 if(zversion >= 6) OP(EXT, 0x11, znop); /* XXX window_size */
278 if(zversion >= 6) OP(EXT, 0x12, znop); /* XXX window_style */
279 if(zversion >= 6) OP(EXT, 0x13, zget_wind_prop);
280 if(zversion >= 6) OP(EXT, 0x14, znop); /* XXX scroll_window */
281 if(zversion >= 6) OP(EXT, 0x15, zpop_stack);
282 if(zversion >= 6) OP(EXT, 0x16, znop); /* XXX read_mouse */
283 if(zversion >= 6) OP(EXT, 0x17, znop); /* XXX mouse_window */
284 if(zversion >= 6) OP(EXT, 0x18, zpush_stack);
285 if(zversion >= 6) OP(EXT, 0x19, znop); /* XXX put_wind_prop */
286 if(zversion >= 6) OP(EXT, 0x1a, zprint_form);
287 if(zversion >= 6) OP(EXT, 0x1b, zmake_menu);
288 if(zversion >= 6) OP(EXT, 0x1c, znop); /* XXX picture_table */
289 if(zversion >= 6) OP(EXT, 0x1d, zbuffer_screen);
291 /* Zoom extensions. */
292 OP(EXT, 0x80, zstart_timer);
293 OP(EXT, 0x81, zstop_timer);
294 OP(EXT, 0x82, zread_timer);
295 OP(EXT, 0x83, zprint_timer);
299 void process_instructions(void)
301 if(njumps <= ++ilevel)
303 jumps = realloc(jumps, ++njumps * sizeof *jumps);
304 if(jumps == NULL) die("unable to allocate memory for jump buffer");
307 switch(setjmp(jumps[ilevel]))
309 case 1: /* Normal break from interrupt. */
311 case 2: /* Special break: a restart was requested. */
313 /* §6.1.3: Flags2 is preserved on a restart. */
314 uint16_t flags2 = WORD(0x10);
318 STORE_WORD(0x10, flags2);
327 #if defined(ZTERP_GLK) && defined(ZTERP_GLK_TICK)
340 if(opcode & 0x40) zargs[0] = variable(BYTE(pc++));
341 else zargs[0] = BYTE(pc++);
343 if(opcode & 0x20) zargs[1] = variable(BYTE(pc++));
344 else zargs[1] = BYTE(pc++);
346 op_call(TWO, opcode & 0x1f);
350 else if(opcode < 0xb0)
354 if(opcode < 0x90) /* large constant */
359 else if(opcode < 0xa0) /* small constant */
361 zargs[0] = BYTE(pc++);
365 zargs[0] = variable(BYTE(pc++));
368 op_call(ONE, opcode & 0x0f);
371 /* short 0OP (plus EXT) */
372 else if(opcode < 0xc0)
376 op_call(ZERO, opcode & 0x0f);
380 else if(opcode < 0xe0)
384 decode_var(BYTE(pc++));
386 op_call(TWO, opcode & 0x1f);
389 /* Double variable VAR */
390 else if(opcode == 0xec || opcode == 0xfa)
392 uint8_t types1, types2;
401 op_call(VAR, opcode & 0x1f);
411 decode_var(BYTE(pc++));
413 op_call(VAR, opcode & 0x1f);