1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/glulx/glulx.h"
24
25 namespace Glk {
26 namespace Glulx {
27
28 /**
29 * The actual immutable structures which lookup_operandlist() returns.
30 */
31 static const operandlist_t list_none = { 0, 4, nullptr };
32
33 static const int array_S[1] = { modeform_Store };
34 static const operandlist_t list_S = { 1, 4, &array_S[0] };
35 static const int array_LS[2] = { modeform_Load, modeform_Store };
36 static const operandlist_t list_LS = { 2, 4, &array_LS[0] };
37 static const int array_LLS[3] = { modeform_Load, modeform_Load, modeform_Store };
38 static const operandlist_t list_LLS = { 3, 4, &array_LLS[0] };
39 static const int array_LLLS[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Store };
40 static const operandlist_t list_LLLS = { 4, 4, &array_LLLS[0] };
41 static const int array_LLLLS[5] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
42 static const operandlist_t list_LLLLS = { 5, 4, &array_LLLLS[0] };
43 /* static const int array_LLLLLS[6] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
44 static const operandlist_t list_LLLLLS = { 6, 4, &array_LLLLLS }; */ /* not currently used */
45 static const int array_LLLLLLS[7] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
46 static const operandlist_t list_LLLLLLS = { 7, 4, &array_LLLLLLS[0] };
47 static const int array_LLLLLLLS[8] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Load, modeform_Store };
48 static const operandlist_t list_LLLLLLLS = { 8, 4, &array_LLLLLLLS[0] };
49
50 static const int array_L[1] = { modeform_Load };
51 static const operandlist_t list_L = { 1, 4, &array_L[0] };
52 static const int array_LL[2] = { modeform_Load, modeform_Load };
53 static const operandlist_t list_LL = { 2, 4, &array_LL[0] };
54 static const int array_LLL[3] = { modeform_Load, modeform_Load, modeform_Load };
55 static const operandlist_t list_LLL = { 3, 4, &array_LLL[0] };
56 static const operandlist_t list_2LS = { 2, 2, &array_LS[0] };
57 static const operandlist_t list_1LS = { 2, 1, &array_LS[0] };
58 static const int array_LLLL[4] = { modeform_Load, modeform_Load, modeform_Load, modeform_Load };
59 static const operandlist_t list_LLLL = { 4, 4, &array_LLLL[0] };
60 static const int array_SL[2] = { modeform_Store, modeform_Load };
61 static const operandlist_t list_SL = { 2, 4, &array_SL[0] };
62 static const int array_SS[2] = { modeform_Store, modeform_Store };
63 static const operandlist_t list_SS = { 2, 4, &array_SS[0] };
64 static const int array_LLSS[4] = { modeform_Load, modeform_Load, modeform_Store, modeform_Store };
65 static const operandlist_t list_LLSS = { 4, 4, &array_LLSS[0] };
66
init_operands()67 void Glulx::init_operands() {
68 for (int ix = 0; ix < 0x80; ix++)
69 fast_operandlist[ix] = lookup_operandlist(ix);
70 }
71
lookup_operandlist(uint opcode)72 const operandlist_t *Glulx::lookup_operandlist(uint opcode) {
73 switch (opcode) {
74 case op_nop:
75 return &list_none;
76
77 case op_add:
78 case op_sub:
79 case op_mul:
80 case op_div:
81 case op_mod:
82 case op_bitand:
83 case op_bitor:
84 case op_bitxor:
85 case op_shiftl:
86 case op_sshiftr:
87 case op_ushiftr:
88 return &list_LLS;
89
90 case op_neg:
91 case op_bitnot:
92 return &list_LS;
93
94 case op_jump:
95 case op_jumpabs:
96 return &list_L;
97 case op_jz:
98 case op_jnz:
99 return &list_LL;
100 case op_jeq:
101 case op_jne:
102 case op_jlt:
103 case op_jge:
104 case op_jgt:
105 case op_jle:
106 case op_jltu:
107 case op_jgeu:
108 case op_jgtu:
109 case op_jleu:
110 return &list_LLL;
111
112 case op_call:
113 return &list_LLS;
114 case op_return:
115 return &list_L;
116 case op_catch:
117 return &list_SL;
118 case op_throw:
119 return &list_LL;
120 case op_tailcall:
121 return &list_LL;
122
123 case op_sexb:
124 case op_sexs:
125 return &list_LS;
126
127 case op_copy:
128 return &list_LS;
129 case op_copys:
130 return &list_2LS;
131 case op_copyb:
132 return &list_1LS;
133 case op_aload:
134 case op_aloads:
135 case op_aloadb:
136 case op_aloadbit:
137 return &list_LLS;
138 case op_astore:
139 case op_astores:
140 case op_astoreb:
141 case op_astorebit:
142 return &list_LLL;
143
144 case op_stkcount:
145 return &list_S;
146 case op_stkpeek:
147 return &list_LS;
148 case op_stkswap:
149 return &list_none;
150 case op_stkroll:
151 return &list_LL;
152 case op_stkcopy:
153 return &list_L;
154
155 case op_streamchar:
156 case op_streamunichar:
157 case op_streamnum:
158 case op_streamstr:
159 return &list_L;
160 case op_getstringtbl:
161 return &list_S;
162 case op_setstringtbl:
163 return &list_L;
164 case op_getiosys:
165 return &list_SS;
166 case op_setiosys:
167 return &list_LL;
168
169 case op_random:
170 return &list_LS;
171 case op_setrandom:
172 return &list_L;
173
174 case op_verify:
175 return &list_S;
176 case op_restart:
177 return &list_none;
178 case op_save:
179 case op_restore:
180 return &list_LS;
181 case op_saveundo:
182 case op_restoreundo:
183 return &list_S;
184 case op_protect:
185 return &list_LL;
186
187 case op_quit:
188 return &list_none;
189
190 case op_gestalt:
191 return &list_LLS;
192
193 case op_debugtrap:
194 return &list_L;
195
196 case op_getmemsize:
197 return &list_S;
198 case op_setmemsize:
199 return &list_LS;
200
201 case op_linearsearch:
202 return &list_LLLLLLLS;
203 case op_binarysearch:
204 return &list_LLLLLLLS;
205 case op_linkedsearch:
206 return &list_LLLLLLS;
207
208 case op_glk:
209 return &list_LLS;
210
211 case op_callf:
212 return &list_LS;
213 case op_callfi:
214 return &list_LLS;
215 case op_callfii:
216 return &list_LLLS;
217 case op_callfiii:
218 return &list_LLLLS;
219
220 case op_mzero:
221 return &list_LL;
222 case op_mcopy:
223 return &list_LLL;
224 case op_malloc:
225 return &list_LS;
226 case op_mfree:
227 return &list_L;
228
229 case op_accelfunc:
230 case op_accelparam:
231 return &list_LL;
232
233 #ifdef FLOAT_SUPPORT
234
235 case op_numtof:
236 case op_ftonumz:
237 case op_ftonumn:
238 case op_ceil:
239 case op_floor:
240 case op_sqrt:
241 case op_exp:
242 case op_log:
243 return &list_LS;
244 case op_fadd:
245 case op_fsub:
246 case op_fmul:
247 case op_fdiv:
248 case op_pow:
249 case op_atan2:
250 return &list_LLS;
251 case op_fmod:
252 return &list_LLSS;
253 case op_sin:
254 case op_cos:
255 case op_tan:
256 case op_asin:
257 case op_acos:
258 case op_atan:
259 return &list_LS;
260 case op_jfeq:
261 case op_jfne:
262 return &list_LLLL;
263 case op_jflt:
264 case op_jfle:
265 case op_jfgt:
266 case op_jfge:
267 return &list_LLL;
268 case op_jisnan:
269 case op_jisinf:
270 return &list_LL;
271
272 #endif /* FLOAT_SUPPORT */
273
274 #ifdef GLULX_EXTEND_OPERANDS
275 GLULX_EXTEND_OPERANDS
276 #endif /* GLULX_EXTEND_OPERANDS */
277
278 default:
279 return nullptr;
280 }
281 }
282
parse_operands(oparg_t * args,const operandlist_t * oplist)283 void Glulx::parse_operands(oparg_t *args, const operandlist_t *oplist) {
284 int ix;
285 oparg_t *curarg;
286 int numops = oplist->num_ops;
287 int argsize = oplist->arg_size;
288 uint modeaddr = pc;
289 int modeval = 0;
290
291 pc += (numops + 1) / 2;
292
293 for (ix = 0, curarg = args; ix < numops; ix++, curarg++) {
294 int mode;
295 uint value;
296 uint addr;
297
298 curarg->desttype = 0;
299
300 if ((ix & 1) == 0) {
301 modeval = Mem1(modeaddr);
302 mode = (modeval & 0x0F);
303 } else {
304 mode = ((modeval >> 4) & 0x0F);
305 modeaddr++;
306 }
307
308 if (oplist->formlist[ix] == modeform_Load) {
309
310 switch (mode) {
311
312 case 8: /* pop off stack */
313 if (stackptr < valstackbase + 4) {
314 fatal_error("Stack underflow in operand.");
315 }
316 stackptr -= 4;
317 value = Stk4(stackptr);
318 break;
319
320 case 0: /* constant zero */
321 value = 0;
322 break;
323
324 case 1: /* one-byte constant */
325 /* Sign-extend from 8 bits to 32 */
326 value = (int)(signed char)(Mem1(pc));
327 pc++;
328 break;
329
330 case 2: /* two-byte constant */
331 /* Sign-extend the first byte from 8 bits to 32; the subsequent
332 byte must not be sign-extended. */
333 value = (int)(signed char)(Mem1(pc));
334 pc++;
335 value = (value << 8) | (uint)(Mem1(pc));
336 pc++;
337 break;
338
339 case 3: /* four-byte constant */
340 /* Bytes must not be sign-extended. */
341 value = Mem4(pc);
342 pc += 4;
343 break;
344
345 case 15: /* main memory RAM, four-byte address */
346 addr = Mem4(pc);
347 addr += ramstart;
348 pc += 4;
349 goto MainMemAddr;
350
351 case 14: /* main memory RAM, two-byte address */
352 addr = (uint)Mem2(pc);
353 addr += ramstart;
354 pc += 2;
355 goto MainMemAddr;
356
357 case 13: /* main memory RAM, one-byte address */
358 addr = (uint)(Mem1(pc));
359 addr += ramstart;
360 pc++;
361 goto MainMemAddr;
362
363 case 7: /* main memory, four-byte address */
364 addr = Mem4(pc);
365 pc += 4;
366 goto MainMemAddr;
367
368 case 6: /* main memory, two-byte address */
369 addr = (uint)Mem2(pc);
370 pc += 2;
371 goto MainMemAddr;
372
373 case 5: /* main memory, one-byte address */
374 addr = (uint)(Mem1(pc));
375 pc++;
376 /* fall through */
377
378 MainMemAddr:
379 /* cases 5, 6, 7, 13, 14, 15 all wind up here. */
380 if (argsize == 4) {
381 value = Mem4(addr);
382 } else if (argsize == 2) {
383 value = Mem2(addr);
384 } else {
385 value = Mem1(addr);
386 }
387 break;
388
389 case 11: /* locals, four-byte address */
390 addr = Mem4(pc);
391 pc += 4;
392 goto LocalsAddr;
393
394 case 10: /* locals, two-byte address */
395 addr = (uint)Mem2(pc);
396 pc += 2;
397 goto LocalsAddr;
398
399 case 9: /* locals, one-byte address */
400 addr = (uint)(Mem1(pc));
401 pc++;
402 /* fall through */
403
404 LocalsAddr:
405 /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
406 be four-byte aligned, but we don't check this explicitly.
407 A "strict mode" interpreter probably should. It's also illegal
408 for addr to be less than zero or greater than the size of
409 the locals segment. */
410 addr += localsbase;
411 if (argsize == 4) {
412 value = Stk4(addr);
413 } else if (argsize == 2) {
414 value = Stk2(addr);
415 } else {
416 value = Stk1(addr);
417 }
418 break;
419
420 default:
421 value = 0;
422 fatal_error("Unknown addressing mode in load operand.");
423 }
424
425 curarg->value = value;
426
427 } else { /* modeform_Store */
428 switch (mode) {
429
430 case 0: /* discard value */
431 curarg->desttype = 0;
432 curarg->value = 0;
433 break;
434
435 case 8: /* push on stack */
436 curarg->desttype = 3;
437 curarg->value = 0;
438 break;
439
440 case 15: /* main memory RAM, four-byte address */
441 addr = Mem4(pc);
442 addr += ramstart;
443 pc += 4;
444 goto WrMainMemAddr;
445
446 case 14: /* main memory RAM, two-byte address */
447 addr = (uint)Mem2(pc);
448 addr += ramstart;
449 pc += 2;
450 goto WrMainMemAddr;
451
452 case 13: /* main memory RAM, one-byte address */
453 addr = (uint)(Mem1(pc));
454 addr += ramstart;
455 pc++;
456 goto WrMainMemAddr;
457
458 case 7: /* main memory, four-byte address */
459 addr = Mem4(pc);
460 pc += 4;
461 goto WrMainMemAddr;
462
463 case 6: /* main memory, two-byte address */
464 addr = (uint)Mem2(pc);
465 pc += 2;
466 goto WrMainMemAddr;
467
468 case 5: /* main memory, one-byte address */
469 addr = (uint)(Mem1(pc));
470 pc++;
471 /* fall through */
472
473 WrMainMemAddr:
474 /* cases 5, 6, 7 all wind up here. */
475 curarg->desttype = 1;
476 curarg->value = addr;
477 break;
478
479 case 11: /* locals, four-byte address */
480 addr = Mem4(pc);
481 pc += 4;
482 goto WrLocalsAddr;
483
484 case 10: /* locals, two-byte address */
485 addr = (uint)Mem2(pc);
486 pc += 2;
487 goto WrLocalsAddr;
488
489 case 9: /* locals, one-byte address */
490 addr = (uint)(Mem1(pc));
491 pc++;
492 /* fall through */
493
494 WrLocalsAddr:
495 /* cases 9, 10, 11 all wind up here. It's illegal for addr to not
496 be four-byte aligned, but we don't check this explicitly.
497 A "strict mode" interpreter probably should. It's also illegal
498 for addr to be less than zero or greater than the size of
499 the locals segment. */
500 curarg->desttype = 2;
501 /* We don't add localsbase here; the store address for desttype 2
502 is relative to the current locals segment, not an absolute
503 stack position. */
504 curarg->value = addr;
505 break;
506
507 case 1:
508 case 2:
509 case 3:
510 fatal_error("Constant addressing mode in store operand.");
511 break;
512
513 default:
514 fatal_error("Unknown addressing mode in store operand.");
515 }
516 }
517 }
518 }
519
store_operand(uint desttype,uint destaddr,uint storeval)520 void Glulx::store_operand(uint desttype, uint destaddr, uint storeval) {
521 switch (desttype) {
522
523 case 0: /* do nothing; discard the value. */
524 return;
525
526 case 1: /* main memory. */
527 MemW4(destaddr, storeval);
528 return;
529
530 case 2: /* locals. */
531 destaddr += localsbase;
532 StkW4(destaddr, storeval);
533 return;
534
535 case 3: /* push on stack. */
536 if (stackptr + 4 > stacksize) {
537 fatal_error("Stack overflow in store operand.");
538 }
539 StkW4(stackptr, storeval);
540 stackptr += 4;
541 return;
542
543 default:
544 fatal_error("Unknown destination type in store operand.");
545
546 }
547 }
548
store_operand_s(uint desttype,uint destaddr,uint storeval)549 void Glulx::store_operand_s(uint desttype, uint destaddr, uint storeval) {
550 storeval &= 0xFFFF;
551
552 switch (desttype) {
553
554 case 0: /* do nothing; discard the value. */
555 return;
556
557 case 1: /* main memory. */
558 MemW2(destaddr, storeval);
559 return;
560
561 case 2: /* locals. */
562 destaddr += localsbase;
563 StkW2(destaddr, storeval);
564 return;
565
566 case 3: /* push on stack. A four-byte value is actually pushed. */
567 if (stackptr + 4 > stacksize) {
568 fatal_error("Stack overflow in store operand.");
569 }
570 StkW4(stackptr, storeval);
571 stackptr += 4;
572 return;
573
574 default:
575 fatal_error("Unknown destination type in store operand.");
576
577 }
578 }
579
store_operand_b(uint desttype,uint destaddr,uint storeval)580 void Glulx::store_operand_b(uint desttype, uint destaddr, uint storeval) {
581 storeval &= 0xFF;
582
583 switch (desttype) {
584
585 case 0: /* do nothing; discard the value. */
586 return;
587
588 case 1: /* main memory. */
589 MemW1(destaddr, storeval);
590 return;
591
592 case 2: /* locals. */
593 destaddr += localsbase;
594 StkW1(destaddr, storeval);
595 return;
596
597 case 3: /* push on stack. A four-byte value is actually pushed. */
598 if (stackptr + 4 > stacksize) {
599 fatal_error("Stack overflow in store operand.");
600 }
601 StkW4(stackptr, storeval);
602 stackptr += 4;
603 return;
604
605 default:
606 fatal_error("Unknown destination type in store operand.");
607
608 }
609 }
610
611 } // End of namespace Glulx
612 } // End of namespace Glk
613