Add Bocfel interpreter
[projects/chimara/chimara.git] / interpreters / bocfel / process.c
1 /*-
2  * Copyright 2010-2012 Chris Spiegel.
3  *
4  * This file is part of Bocfel.
5  *
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.
9  *
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.
14  *
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/>.
17  */
18
19 #include <stdlib.h>
20 #include <stdint.h>
21 #include <stddef.h>
22 #include <setjmp.h>
23
24 #ifdef ZTERP_GLK
25 #include <glk.h>
26 #endif
27
28 #include "process.h"
29 #include "branch.h"
30 #include "dict.h"
31 #include "math.h"
32 #include "memory.h"
33 #include "objects.h"
34 #include "random.h"
35 #include "screen.h"
36 #include "stack.h"
37 #include "table.h"
38 #include "util.h"
39 #include "zoom.h"
40 #include "zterp.h"
41
42 uint16_t zargs[8];
43 int znargs;
44
45 static jmp_buf *jumps;
46 static size_t njumps;
47
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.
53  */
54 static long ilevel = -1;
55
56 long interrupt_level(void)
57 {
58   return ilevel;
59 }
60
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.
66  */
67 void break_from(long level)
68 {
69   ilevel = level - 1;
70   longjmp(jumps[level], 1);
71 }
72
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
75  * used.
76  */
77 void reset_level(void)
78 {
79   ilevel = 0;
80 }
81
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.
85  */
86 static void zrestart(void)
87 {
88   ilevel = 0;
89   longjmp(jumps[0], 2);
90 }
91
92 /* Returns 1 if decoded, 0 otherwise (omitted) */
93 static int decode_base(uint8_t type, uint16_t *loc)
94 {
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. */
99
100   pc++;
101
102   return 1;
103 }
104
105 static void decode_var(uint8_t types)
106 {
107   uint16_t ret;
108
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;
117 }
118
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 };
123
124 #define op_call(opt, opnumber)  (op[opt][opnumber]())
125
126 /* This nifty trick is from Frotz. */
127 static void zextended(void)
128 {
129   uint8_t opnumber = BYTE(pc++);
130
131   decode_var(BYTE(pc++));
132
133   /* §14.2.1
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.
138    */
139   if(opnumber > 0x1d && (opnumber < 0x80 || opnumber > 0x83)) return;
140
141   op_call(EXT, opnumber);
142 }
143
144 static void illegal_opcode(void)
145 {
146 #ifndef ZTERP_NO_SAFETY_CHECKS
147   die("illegal opcode (pc = 0x%lx)", zassert_pc);
148 #else
149   die("illegal opcode");
150 #endif
151 }
152
153 void setup_opcodes(void)
154 {
155   for(int i = 0; i < 5; i++)
156   {
157     for(int j = 0; j < 256; j++)
158     {
159       op[i][j] = illegal_opcode;
160     }
161   }
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);
181
182   OP(ONE, 0x00, zjz);
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);
187   OP(ONE, 0x05, zinc);
188   OP(ONE, 0x06, zdec);
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);
193   OP(ONE, 0x0b, zret);
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);
199
200   OP(TWO, 0x01, zje);
201   OP(TWO, 0x02, zjl);
202   OP(TWO, 0x03, zjg);
203   OP(TWO, 0x04, zdec_chk);
204   OP(TWO, 0x05, zinc_chk);
205   OP(TWO, 0x06, zjin);
206   OP(TWO, 0x07, ztest);
207   OP(TWO, 0x08, zor);
208   OP(TWO, 0x09, zand);
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);
219   OP(TWO, 0x14, zadd);
220   OP(TWO, 0x15, zsub);
221   OP(TWO, 0x16, zmul);
222   OP(TWO, 0x17, zdiv);
223   OP(TWO, 0x18, zmod);
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);
228
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);
261
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);
290
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);
296 #undef OP
297 }
298
299 void process_instructions(void)
300 {
301   if(njumps <= ++ilevel)
302   {
303     jumps = realloc(jumps, ++njumps * sizeof *jumps);
304     if(jumps == NULL) die("unable to allocate memory for jump buffer");
305   }
306
307   switch(setjmp(jumps[ilevel]))
308   {
309     case 1: /* Normal break from interrupt. */
310       return;
311     case 2: /* Special break: a restart was requested. */
312       {
313         /* §6.1.3: Flags2 is preserved on a restart. */
314         uint16_t flags2 = WORD(0x10);
315
316         process_story();
317
318         STORE_WORD(0x10, flags2);
319       }
320       break;
321   }
322
323   while(1)
324   {
325     uint8_t opcode;
326
327 #if defined(ZTERP_GLK) && defined(ZTERP_GLK_TICK)
328     glk_tick();
329 #endif
330
331     ZPC(pc);
332
333     opcode = BYTE(pc++);
334
335     /* long 2OP */
336     if(opcode < 0x80)
337     {
338       znargs = 2;
339
340       if(opcode & 0x40) zargs[0] = variable(BYTE(pc++));
341       else              zargs[0] = BYTE(pc++);
342
343       if(opcode & 0x20) zargs[1] = variable(BYTE(pc++));
344       else              zargs[1] = BYTE(pc++);
345
346       op_call(TWO, opcode & 0x1f);
347     }
348
349     /* short 1OP */
350     else if(opcode < 0xb0)
351     {
352       znargs = 1;
353
354       if(opcode < 0x90) /* large constant */
355       {
356         zargs[0] = WORD(pc);
357         pc += 2;
358       }
359       else if(opcode < 0xa0) /* small constant */
360       {
361         zargs[0] = BYTE(pc++);
362       }
363       else /* variable */
364       {
365         zargs[0] = variable(BYTE(pc++));
366       }
367
368       op_call(ONE, opcode & 0x0f);
369     }
370
371     /* short 0OP (plus EXT) */
372     else if(opcode < 0xc0)
373     {
374       znargs = 0;
375
376       op_call(ZERO, opcode & 0x0f);
377     }
378
379     /* variable 2OP */
380     else if(opcode < 0xe0)
381     {
382       znargs = 0;
383
384       decode_var(BYTE(pc++));
385
386       op_call(TWO, opcode & 0x1f);
387     }
388
389     /* Double variable VAR */
390     else if(opcode == 0xec || opcode == 0xfa)
391     {
392       uint8_t types1, types2;
393
394       znargs = 0;
395
396       types1 = BYTE(pc++);
397       types2 = BYTE(pc++);
398       decode_var(types1);
399       decode_var(types2);
400
401       op_call(VAR, opcode & 0x1f);
402     }
403
404     /* variable VAR */
405     else
406     {
407       znargs = 0;
408
409       read_pc = pc - 1;
410
411       decode_var(BYTE(pc++));
412
413       op_call(VAR, opcode & 0x1f);
414     }
415   }
416 }