1 /*
2   Interp
3 */
4 
5 #include "interp.h"
6 
7 #include "arith.h"
8 #include "compare.h"
9 #include "console.h"
10 #include "dummy.h"
11 #include "file.h"
12 #include "head.h"
13 #include "input.h"
14 #include "jump.h"
15 #include "mem.h"
16 #include "object.h"
17 #include "page.h"
18 #include "pc.h"
19 #include "print.h"
20 #include "prop.h"
21 #include "random.h"
22 #include "shared.h"
23 #include "stack.h"
24 #include "status.h"
25 #include "stop.h"
26 #include "support.h"
27 #include "var.h"
28 #include "window.h"
29 #include "wio.h"
30 
illegal_opcode(void)31 static void illegal_opcode(void)
32 {
33   display((byte *) "Illegal opcode");
34   quit();
35 }
36 
init_interpreter(bool init_pc)37 void init_interpreter(bool init_pc)
38 {
39   if(init_pc)
40   {
41     word start = hd_start();
42     stk_init();
43     set_pc(pg_page(start), pg_offset(start));
44   }
45   init_print();
46   if(!hd_plus())
47     hd_set_screen();
48   else
49     hd_init();
50   split_screen(0);
51 }
52 
push_params(word modes,int count)53 static void push_params(word modes, int count)
54 {
55   extern word param_stack[];
56   int i = 0;
57   while(i < count)
58   {
59     int shift = 2 * (count - (i + 1));
60     int mode = (modes >> shift) & 3;
61     if(mode != 3)
62       param_stack[++i] = load(mode);
63     else
64       break;
65   }
66   param_stack[0] = i;
67 }
68 
69 /*
70   For now, if an opcode is disallowed say so.
71   Use a goto to share the common code between branches (reduces code size).
72   Can remove the tests simply by redefining the macro; this is a refinment.
73 */
74 
75 #if 1
76 #define require(p)  if(!(p)) goto illegal
77 #else
78 #define require(p)
79 #endif
80 
dispatch4(word op)81 static void dispatch4(word op)
82 {
83   extern word param_stack[];
84   require(hd_five());
85   switch(op)
86   {
87     case 0x00:
88       save_game();
89       break;
90     case 0x01:
91       restore_game();
92       break;
93     case 0x02:
94       logical_shift(param_stack[1], param_stack[2]);
95       break;
96     case 0x03:
97       arithmetic_shift(param_stack[1], param_stack[2]);
98       break;
99     case 0x05:
100       clear_flag();
101       break;
102     case 0x06:
103       test_byte_array(param_stack[1], param_stack[2]);
104       break;
105     case 0x07:
106       set_flag();
107       break;
108     case 0x08:
109       /* null(); */
110       break;
111     case 0x04:
112       store(0); /* set_font */
113       break;
114     case 0x09:
115       pg_save_undo();
116       break;
117     case 0x0A:
118       pg_restore_undo();
119       break;
120     default:
121     illegal:
122       illegal_opcode();
123       break;
124   }
125 }
126 
dispatch0(word op)127 static void dispatch0(word op)
128 {
129   switch(op)
130   {
131     case 0x0:
132       ret_true();
133       break;
134     case 0x1:
135       ret_false();
136       break;
137     case 0x2:
138       wrt();
139       break;
140     case 0x3:
141       writeln();
142       break;
143     case 0x4:
144       /* null(); */
145       break;
146     case 0x5:
147       require(!hd_five());
148       save_game();
149       break;
150     case 0x6:
151       require(!hd_five());
152       restore_game();
153       break;
154     case 0x7:
155       restart();
156       break;
157     case 0x8:
158       rts();
159       break;
160     case 0x9:
161       if(hd_five())
162         adv_pop_stack();
163       else
164         (void) stk_pop();
165       break;
166     case 0xA:
167       quit();
168       break;
169     case 0xB:
170       new_line();
171       break;
172     case 0xC:
173       require(hd_version() == VERSION_3);
174       status();
175       break;
176     case 0xD:
177       require(!EARLY || hd_version() >= VERSION_3);
178       verify();
179       break;
180     case 0xE:
181       {
182         int actual_op = next_byte() & 0x3F;
183         push_params(next_byte(), 4);
184         dispatch4(actual_op);
185       }
186       break;
187     case 0xF:
188       require(hd_five());
189       ret_value(1);
190       break;
191     illegal:
192       illegal_opcode();
193   }
194 }
195 
dispatch1(word op,word param)196 static void dispatch1(word op, word param)
197 {
198   switch(op)
199   {
200     case 0x0:
201       cp_zero(param);
202       break;
203     case 0x1:
204       obj_link(param);
205       break;
206     case 0x2:
207       obj_holds(param);
208       break;
209     case 0x3:
210       obj_loc(param);
211       break;
212     case 0x4:
213       prop_get_p_len(param);
214       break;
215     case 0x5:
216       inc_var(param);
217       break;
218     case 0x6:
219       dec_var(param);
220       break;
221     case 0x7:
222       print1(param);
223       break;
224     case 0x8:
225       require(hd_plus());
226       gosub2(param);
227       break;
228     case 0x9:
229       obj_remove(param);
230       break;
231     case 0xA:
232       obj_print(param);
233       break;
234     case 0xB:
235       active_return(param);
236       break;
237     case 0xC:
238       jump(param);
239       break;
240     case 0xD:
241       print2(param);
242       break;
243     case 0xE:
244       get_var(param);
245       break;
246     case 0xF:
247       if(hd_five())
248         gosub5(param);
249       else
250         not(param);
251       break;
252     illegal:
253       illegal_opcode();
254   }
255 }
256 
stack2(word param1,word param2)257 static void stack2(word param1, word param2)
258 {
259   extern word param_stack[];
260   param_stack[0] = 2;
261   param_stack[1] = param1;
262   param_stack[2] = param2;
263 }
264 
dispatch2f(word op,word param1,word param2)265 static void dispatch2f(word op, word param1, word param2)
266 {
267   switch(op)
268   {
269     case 0x01:
270       stack2(param1, param2);
271       compare();
272       break;
273     case 0x02:
274       less_than(param1, param2);
275       break;
276     case 0x03:
277       greater_than(param1, param2);
278       break;
279     case 0x04:
280       dec_chk(param1, param2);
281       break;
282     case 0x05:
283       inc_chk(param1, param2);
284       break;
285     case 0x06:
286       obj_check(param1, param2);
287       break;
288     case 0x07:
289       bit(param1, param2);
290       break;
291     case 0x08:
292       or(param1, param2);
293       break;
294     case 0x09:
295       and(param1, param2);
296       break;
297     case 0x0A:
298       obj_test(param1, param2);
299       break;
300     case 0x0B:
301       obj_set(param1, param2);
302       break;
303     case 0x0C:
304       obj_clr(param1, param2);
305       break;
306     case 0x0D:
307       put_var(param1, param2);
308       break;
309     case 0x0E:
310       obj_transfer(param1, param2);
311       break;
312     case 0x0F:
313       load_word_array(param1, param2);
314       break;
315     case 0x10:
316       load_byte_array(param1, param2);
317       break;
318     case 0x11:
319       prop_getprop(param1, param2);
320       break;
321     case 0x12:
322       prop_get_prop_addr(param1, param2);
323       break;
324     case 0x13:
325       prop_get_next_prop(param1, param2);
326       break;
327     case 0x14:
328       plus(param1, param2);
329       break;
330     case 0x15:
331       minus(param1, param2);
332       break;
333     case 0x16:
334       multiply(param1, param2);
335       break;
336     case 0x17:
337       divide(param1, param2);
338       break;
339     case 0x18:
340       mod(param1, param2);
341       break;
342     case 0x19:
343       stack2(param1, param2);
344       active_gosub();
345       break;
346     case 0x1A:
347       require(hd_five());
348       stack2(param1, param2);
349       gosub4();
350       break;
351     case 0x1B:
352       require(hd_five());
353       set_text_colour(param1, param2);
354       break;
355     case 0x1C:
356       require(hd_five());
357       throw_away_stack_frame(param1, param2);
358       break;
359     default:
360     illegal:
361       illegal_opcode();
362       break;
363   }
364 }
365 
dispatch2v(word op)366 static void dispatch2v(word op)
367 {
368   extern word param_stack[];
369   /* Just special case those which can have more than 2 arguments */
370   switch(op)
371   {
372     case 0x01: compare(); break;
373     case 0x19: active_gosub(); break;
374     case 0x1A: gosub4(); break;
375     default:   dispatch2f(op, param_stack[1], param_stack[2]); break;
376   }
377 }
378 
dispatch3(word op)379 static void dispatch3(word op)
380 {
381   extern word param_stack[];
382   word param1 = param_stack[1];
383   word param2 = param_stack[2];
384   if(hd_five())
385     switch(op)
386     {
387       case 0x17:
388         adv_compare2();
389         return;
390       case 0x18:
391         not(param1);
392         return;
393       case 0x19:
394         gosub4();
395         return;
396       case 0x1A:
397         gosub4();
398         return;
399       case 0x1B:
400         parse();
401         return;
402       case 0x1C:
403         ncrypt(param1, param2, param_stack[3], param_stack[4]);
404         return;
405       case 0x1D:
406         block_copy(param1, param2, param_stack[3]);
407         return;
408       case 0x1E:
409         print_text();
410         return;
411       case 0x1F:
412         num_local_params(param1);
413         return;
414     }
415   if(hd_plus())
416     switch(op)
417     {
418       case 0x07:
419         plus_random(param1);
420         return;
421       case 0x0A:
422         split_screen(param1);
423         return;
424       case 0x0B:
425         set_current_window(param1);
426         return;
427       case 0x0D:
428         do_clear_screen(param1);
429         return;
430       case 0x0E:
431         erase_line(param1);
432         return;
433       case 0x0F:
434         set_cursor_posn(param1, param2);
435         return;
436       case 0x10:
437         /* null(); */
438         return;
439       case 0x11:
440         set_text_mode(param1);
441         return;
442       case 0x12:
443         io_buffer_mode(param1);
444         return;
445       case 0x13:
446         io_mode(param1, param2);
447         return;
448       case 0x14:
449         /* null(); */
450         return;
451       case 0x15:
452         do_beep(param1);
453         return;
454       case 0x16:
455         get_key();
456         return;
457       case 0x17:
458         plus_compare2();
459         return;
460     }
461   switch(op)
462   {
463     case 0x00:
464     case 0x0C:
465       active_gosub();
466       break;
467 
468     case 0x01:
469       save_word_array(param1, param2, param_stack[3]);
470       break;
471     case 0x02:
472       save_byte_array(param1, param2, param_stack[3]);
473       break;
474     case 0x03:
475       prop_put_prop(param1, param2, param_stack[3]);
476       break;
477     case 0x04:
478       input();
479       break;
480     case 0x05:
481       print_char(param1);
482       break;
483     case 0x06:
484       print_num(param1);
485       break;
486     case 0x07:
487       std_random(param1);
488       break;
489     case 0x08:
490       push(param1);
491       break;
492     case 0x09:
493       pop(param1);
494       break;
495     case 0x0A:
496       split_screen(param1);
497       break;
498     case 0x0B:
499       set_current_window(param1);
500       break;
501     default:
502       illegal_opcode();
503       break;
504   }
505 }
506 
execute_opcode(void)507 void execute_opcode(void)
508 {
509   while(!stopped())
510   {
511     word op = next_byte();
512     switch(op >> 4)
513     {
514       case 0x0: case 0x1: case 0x2: case 0x3:
515       case 0x4: case 0x5: case 0x6: case 0x7:
516         /*
517           Splitting these branches is an obvious optimisation:
518             It allows the first (and maybe second) conditional to be removed
519             That reduces the load to calls to next_byte in the first case and
520             to next_byte followed by an indirection in the second (inlining the
521             logic from load() in var.c
522         */
523         {
524           word param1 = load((op & 0x40) ? 2 : 1);
525           word param2 = load((op & 0x20) ? 2 : 1);
526           dispatch2f(op & 0x1F, param1, param2);
527         }
528         break;
529       case 0x8: case 0x9: case 0xA:
530         {
531           word param = load((op >> 4) & 3);
532           dispatch1(op & 0x0F, param);
533         }
534         break;
535       case 0xB:
536         dispatch0(op & 0x0F);
537         break;
538       case 0xC: case 0xD:
539         push_params(next_byte(), 4);
540         dispatch2v(op & 0x1F);
541         break;
542       case 0xE: case 0xF:
543         if(op == 0xEC || op == 0xFA)
544           push_params(next_word(), 8);
545         else
546           push_params(next_byte(), 4);
547         dispatch3(op & 0x1F);
548         break;
549     }
550   }
551 }
552