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