aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorPaul Gilbert2019-04-14 21:07:25 -0700
committerPaul Gilbert2019-04-17 20:46:06 -0700
commit6307fd08ce7cbb0b939f520f9741aef884f66f88 (patch)
tree22d665b969305df24735777df38ac638a9b7a29f /engines
parentc3ebb5e68a0b36d992a48b508ab15c13a2edfd9c (diff)
downloadscummvm-rg350-6307fd08ce7cbb0b939f520f9741aef884f66f88.tar.gz
scummvm-rg350-6307fd08ce7cbb0b939f520f9741aef884f66f88.tar.bz2
scummvm-rg350-6307fd08ce7cbb0b939f520f9741aef884f66f88.zip
GLK: GLULXE: Added exec methods
Diffstat (limited to 'engines')
-rw-r--r--engines/glk/glulxe/exec.cpp1069
-rw-r--r--engines/glk/glulxe/glulxe.cpp1
-rw-r--r--engines/glk/glulxe/glulxe.h9
-rw-r--r--engines/glk/glulxe/glulxe_types.h218
-rw-r--r--engines/glk/module.mk1
5 files changed, 1255 insertions, 43 deletions
diff --git a/engines/glk/glulxe/exec.cpp b/engines/glk/glulxe/exec.cpp
new file mode 100644
index 0000000000..f3b511f43c
--- /dev/null
+++ b/engines/glk/glulxe/exec.cpp
@@ -0,0 +1,1069 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/glk/glulxe/glulxe.h"
+
+namespace Glk {
+namespace Glulxe {
+
+void Glulxe::execute_loop() {
+ bool done_executing = false;
+ int ix;
+ uint opcode;
+ operandlist_t *oplist;
+ oparg_t inst[MAX_OPERANDS];
+ uint value, addr, val0, val1;
+ int vals0, vals1;
+ uint *arglist;
+ uint arglistfix[3];
+#ifdef FLOAT_SUPPORT
+ gfloat32 valf, valf1, valf2;
+#endif /* FLOAT_SUPPORT */
+
+ while (!done_executing) {
+
+ profile_tick();
+ debugger_tick();
+ /* Do OS-specific processing, if appropriate. */
+ glk_tick();
+
+ /* Stash the current opcode's address, in case the interpreter needs to serialize the VM state out-of-band. */
+ prevpc = pc;
+
+ /* Fetch the opcode number. */
+ opcode = Mem1(pc);
+ pc++;
+ if (opcode & 0x80) {
+ /* More than one-byte opcode. */
+ if (opcode & 0x40) {
+ /* Four-byte opcode */
+ opcode &= 0x3F;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ }
+ else {
+ /* Two-byte opcode */
+ opcode &= 0x7F;
+ opcode = (opcode << 8) | Mem1(pc);
+ pc++;
+ }
+ }
+
+ /* Now we have an opcode number. */
+
+ /* Fetch the structure that describes how the operands for this
+ opcode are arranged. This is a pointer to an immutable,
+ static object. */
+ if (opcode < 0x80)
+ oplist = fast_operandlist[opcode];
+ else
+ oplist = lookup_operandlist(opcode);
+
+ if (!oplist)
+ fatal_error_i("Encountered unknown opcode.", opcode);
+
+ /* Based on the oplist structure, load the actual operand values
+ into inst. This moves the PC up to the end of the instruction. */
+ parse_operands(inst, oplist);
+
+ /* Perform the opcode. This switch statement is split in two, based
+ on some paranoid suspicions about the ability of compilers to
+ optimize large-range switches. Ignore that. */
+
+ if (opcode < 0x80) {
+
+ switch (opcode) {
+
+ case op_nop:
+ break;
+
+ case op_add:
+ value = inst[0].value + inst[1].value;
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_sub:
+ value = inst[0].value - inst[1].value;
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_mul:
+ value = inst[0].value * inst[1].value;
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_div:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals1 == 0)
+ fatal_error("Division by zero.");
+ /* Since C doesn't guarantee the results of division of negative
+ numbers, we carefully convert everything to positive values
+ first. They have to be unsigned values, too, otherwise the
+ 0x80000000 case goes wonky. */
+ if (vals0 < 0) {
+ val0 = (-vals0);
+ if (vals1 < 0) {
+ val1 = (-vals1);
+ value = val0 / val1;
+ }
+ else {
+ val1 = vals1;
+ value = -(int)(val0 / val1);
+ }
+ }
+ else {
+ val0 = vals0;
+ if (vals1 < 0) {
+ val1 = (-vals1);
+ value = -(int)(val0 / val1);
+ }
+ else {
+ val1 = vals1;
+ value = val0 / val1;
+ }
+ }
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_mod:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals1 == 0)
+ fatal_error("Division by zero doing remainder.");
+ if (vals1 < 0) {
+ val1 = -vals1;
+ }
+ else {
+ val1 = vals1;
+ }
+ if (vals0 < 0) {
+ val0 = (-vals0);
+ value = -(int)(val0 % val1);
+ }
+ else {
+ val0 = vals0;
+ value = val0 % val1;
+ }
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_neg:
+ vals0 = inst[0].value;
+ value = (-vals0);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_bitand:
+ value = (inst[0].value & inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_bitor:
+ value = (inst[0].value | inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_bitxor:
+ value = (inst[0].value ^ inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_bitnot:
+ value = ~(inst[0].value);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_shiftl:
+ vals0 = inst[1].value;
+ if (vals0 < 0 || vals0 >= 32)
+ value = 0;
+ else
+ value = ((uint)(inst[0].value) << (uint)vals0);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_ushiftr:
+ vals0 = inst[1].value;
+ if (vals0 < 0 || vals0 >= 32)
+ value = 0;
+ else
+ value = ((uint)(inst[0].value) >> (uint)vals0);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_sshiftr:
+ vals0 = inst[1].value;
+ if (vals0 < 0 || vals0 >= 32) {
+ if (inst[0].value & 0x80000000)
+ value = 0xFFFFFFFF;
+ else
+ value = 0;
+ }
+ else {
+ /* This is somewhat foolhardy -- C doesn't guarantee that
+ right-shifting a signed value replicates the sign bit.
+ We'll assume it for now. */
+ value = ((int)(inst[0].value) >> (int)vals0);
+ }
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_jump:
+ value = inst[0].value;
+ /* fall through to PerformJump label. */
+
+ PerformJump: /* goto label for successful jumping... ironic, no? */
+ if (value == 0 || value == 1) {
+ /* Return from function. This is exactly what happens in
+ return_op, but it's only a few lines of code, so I won't
+ bother with a "goto". */
+ leave_function();
+ if (stackptr == 0) {
+ done_executing = true;
+ break;
+ }
+ pop_callstub(value); /* zero or one */
+ }
+ else {
+ /* Branch to a new PC value. */
+ pc = (pc + value - 2);
+ }
+ break;
+
+ case op_jz:
+ if (inst[0].value == 0) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jnz:
+ if (inst[0].value != 0) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jeq:
+ if (inst[0].value == inst[1].value) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jne:
+ if (inst[0].value != inst[1].value) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jlt:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 < vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jgt:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 > vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jle:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 <= vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jge:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 >= vals1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jltu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 < val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jgtu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 > val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jleu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 <= val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jgeu:
+ val0 = inst[0].value;
+ val1 = inst[1].value;
+ if (val0 >= val1) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+
+ case op_call:
+ value = inst[1].value;
+ arglist = pop_arguments(value, 0);
+ push_callstub(inst[2].desttype, inst[2].value);
+ enter_function(inst[0].value, value, arglist);
+ break;
+ case op_return:
+ leave_function();
+ if (stackptr == 0) {
+ done_executing = true;
+ break;
+ }
+ pop_callstub(inst[0].value);
+ break;
+ case op_tailcall:
+ value = inst[1].value;
+ arglist = pop_arguments(value, 0);
+ leave_function();
+ enter_function(inst[0].value, value, arglist);
+ break;
+
+ case op_catch:
+ push_callstub(inst[0].desttype, inst[0].value);
+ value = inst[1].value;
+ val0 = stackptr;
+ store_operand(inst[0].desttype, inst[0].value, val0);
+ goto PerformJump;
+ break;
+ case op_throw:
+ profile_fail("throw");
+ value = inst[0].value;
+ stackptr = inst[1].value;
+ pop_callstub(value);
+ break;
+
+ case op_copy:
+ value = inst[0].value;
+#ifdef TOLERATE_SUPERGLUS_BUG
+ if (inst[1].desttype == 1 && inst[1].value == 0)
+ inst[1].desttype = 0;
+#endif /* TOLERATE_SUPERGLUS_BUG */
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_copys:
+ value = inst[0].value;
+ store_operand_s(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_copyb:
+ value = inst[0].value;
+ store_operand_b(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_sexs:
+ val0 = inst[0].value;
+ if (val0 & 0x8000)
+ val0 |= 0xFFFF0000;
+ else
+ val0 &= 0x0000FFFF;
+ store_operand(inst[1].desttype, inst[1].value, val0);
+ break;
+ case op_sexb:
+ val0 = inst[0].value;
+ if (val0 & 0x80)
+ val0 |= 0xFFFFFF00;
+ else
+ val0 &= 0x000000FF;
+ store_operand(inst[1].desttype, inst[1].value, val0);
+ break;
+
+ case op_aload:
+ value = inst[0].value;
+ value += 4 * inst[1].value;
+ val0 = Mem4(value);
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+ case op_aloads:
+ value = inst[0].value;
+ value += 2 * inst[1].value;
+ val0 = Mem2(value);
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+ case op_aloadb:
+ value = inst[0].value;
+ value += inst[1].value;
+ val0 = Mem1(value);
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+ case op_aloadbit:
+ value = inst[0].value;
+ vals0 = inst[1].value;
+ val1 = (vals0 & 7);
+ if (vals0 >= 0)
+ value += (vals0 >> 3);
+ else
+ value -= (1 + ((-1 - vals0) >> 3));
+ if (Mem1(value) & (1 << val1))
+ val0 = 1;
+ else
+ val0 = 0;
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ break;
+
+ case op_astore:
+ value = inst[0].value;
+ value += 4 * inst[1].value;
+ val0 = inst[2].value;
+ MemW4(value, val0);
+ break;
+ case op_astores:
+ value = inst[0].value;
+ value += 2 * inst[1].value;
+ val0 = inst[2].value;
+ MemW2(value, val0);
+ break;
+ case op_astoreb:
+ value = inst[0].value;
+ value += inst[1].value;
+ val0 = inst[2].value;
+ MemW1(value, val0);
+ break;
+ case op_astorebit:
+ value = inst[0].value;
+ vals0 = inst[1].value;
+ val1 = (vals0 & 7);
+ if (vals0 >= 0)
+ value += (vals0 >> 3);
+ else
+ value -= (1 + ((-1 - vals0) >> 3));
+ val0 = Mem1(value);
+ if (inst[2].value)
+ val0 |= (1 << val1);
+ else
+ val0 &= ~((uint)(1 << val1));
+ MemW1(value, val0);
+ break;
+
+ case op_stkcount:
+ value = (stackptr - valstackbase) / 4;
+ store_operand(inst[0].desttype, inst[0].value, value);
+ break;
+ case op_stkpeek:
+ vals0 = inst[0].value * 4;
+ if (vals0 < 0 || vals0 >= (int)(stackptr - valstackbase))
+ fatal_error("Stkpeek outside current stack range.");
+ value = Stk4(stackptr - (vals0+4));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_stkswap:
+ if (stackptr < valstackbase+8) {
+ fatal_error("Stack underflow in stkswap.");
+ }
+ val0 = Stk4(stackptr-4);
+ val1 = Stk4(stackptr-8);
+ StkW4(stackptr-4, val1);
+ StkW4(stackptr-8, val0);
+ break;
+ case op_stkcopy:
+ vals0 = inst[0].value;
+ if (vals0 < 0)
+ fatal_error("Negative operand in stkcopy.");
+ if (vals0 == 0)
+ break;
+ if (stackptr < valstackbase+vals0*4)
+ fatal_error("Stack underflow in stkcopy.");
+ if (stackptr + vals0*4 > stacksize)
+ fatal_error("Stack overflow in stkcopy.");
+ addr = stackptr - vals0*4;
+ for (ix=0; ix<vals0; ix++) {
+ value = Stk4(addr + ix*4);
+ StkW4(stackptr + ix*4, value);
+ }
+ stackptr += vals0*4;
+ break;
+ case op_stkroll:
+ vals0 = inst[0].value;
+ vals1 = inst[1].value;
+ if (vals0 < 0)
+ fatal_error("Negative operand in stkroll.");
+ if (stackptr < valstackbase+vals0*4)
+ fatal_error("Stack underflow in stkroll.");
+ if (vals0 == 0)
+ break;
+ /* The following is a bit ugly. We want to do vals1 = vals0-vals1,
+ because rolling down is sort of easier than rolling up. But
+ we also want to take the result mod vals0. The % operator is
+ annoying for negative numbers, so we need to do this in two
+ cases. */
+ if (vals1 > 0) {
+ vals1 = vals1 % vals0;
+ vals1 = (vals0) - vals1;
+ }
+ else {
+ vals1 = (-vals1) % vals0;
+ }
+ if (vals1 == 0)
+ break;
+ addr = stackptr - vals0*4;
+ for (ix=0; ix<vals1; ix++) {
+ value = Stk4(addr + ix*4);
+ StkW4(stackptr + ix*4, value);
+ }
+ for (ix=0; ix<vals0; ix++) {
+ value = Stk4(addr + (vals1+ix)*4);
+ StkW4(addr + ix*4, value);
+ }
+ break;
+
+ case op_streamchar:
+ profile_in(0xE0000001, stackptr, false);
+ value = inst[0].value & 0xFF;
+ (*stream_char_handler)(value);
+ profile_out(stackptr);
+ break;
+ case op_streamunichar:
+ profile_in(0xE0000002, stackptr, false);
+ value = inst[0].value;
+ (*stream_unichar_handler)(value);
+ profile_out(stackptr);
+ break;
+ case op_streamnum:
+ profile_in(0xE0000003, stackptr, false);
+ vals0 = inst[0].value;
+ stream_num(vals0, false, 0);
+ profile_out(stackptr);
+ break;
+ case op_streamstr:
+ profile_in(0xE0000004, stackptr, false);
+ stream_string(inst[0].value, 0, 0);
+ profile_out(stackptr);
+ break;
+
+ default:
+ fatal_error_i("Executed unknown opcode.", opcode);
+ }
+ }
+ else {
+
+ switch (opcode) {
+
+ case op_gestalt:
+ value = do_gestalt(inst[0].value, inst[1].value);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_debugtrap:
+#if VM_DEBUGGER
+ /* We block and handle debug commands, but only if the
+ library has invoked debug features. (Meaning, has
+ the cycle handler ever been called.) */
+ if (debugger_ever_invoked()) {
+ debugger_block_and_debug("user debugtrap, pausing...");
+ break;
+ }
+#endif /* VM_DEBUGGER */
+ fatal_error_i("user debugtrap encountered.", inst[0].value);
+
+ case op_jumpabs:
+ pc = inst[0].value;
+ break;
+
+ case op_callf:
+ push_callstub(inst[1].desttype, inst[1].value);
+ enter_function(inst[0].value, 0, arglistfix);
+ break;
+ case op_callfi:
+ arglistfix[0] = inst[1].value;
+ push_callstub(inst[2].desttype, inst[2].value);
+ enter_function(inst[0].value, 1, arglistfix);
+ break;
+ case op_callfii:
+ arglistfix[0] = inst[1].value;
+ arglistfix[1] = inst[2].value;
+ push_callstub(inst[3].desttype, inst[3].value);
+ enter_function(inst[0].value, 2, arglistfix);
+ break;
+ case op_callfiii:
+ arglistfix[0] = inst[1].value;
+ arglistfix[1] = inst[2].value;
+ arglistfix[2] = inst[3].value;
+ push_callstub(inst[4].desttype, inst[4].value);
+ enter_function(inst[0].value, 3, arglistfix);
+ break;
+
+ case op_getmemsize:
+ store_operand(inst[0].desttype, inst[0].value, endmem);
+ break;
+ case op_setmemsize:
+ value = change_memsize(inst[0].value, false);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_getstringtbl:
+ value = stream_get_table();
+ store_operand(inst[0].desttype, inst[0].value, value);
+ break;
+ case op_setstringtbl:
+ stream_set_table(inst[0].value);
+ break;
+
+ case op_getiosys:
+ stream_get_iosys(&val0, &val1);
+ store_operand(inst[0].desttype, inst[0].value, val0);
+ store_operand(inst[1].desttype, inst[1].value, val1);
+ break;
+ case op_setiosys:
+ stream_set_iosys(inst[0].value, inst[1].value);
+ break;
+
+ case op_glk:
+ profile_in(0xF0000000+inst[0].value, stackptr, false);
+ value = inst[1].value;
+ arglist = pop_arguments(value, 0);
+ val0 = perform_glk(inst[0].value, value, arglist);
+#ifdef TOLERATE_SUPERGLUS_BUG
+ if (inst[2].desttype == 1 && inst[2].value == 0)
+ inst[2].desttype = 0;
+#endif /* TOLERATE_SUPERGLUS_BUG */
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ profile_out(stackptr);
+ break;
+
+ case op_random:
+ vals0 = inst[0].value;
+ if (vals0 == 0)
+ value = glulx_random();
+ else if (vals0 >= 1)
+ value = glulx_random() % (uint)(vals0);
+ else
+ value = -(int)(glulx_random() % (uint)(-vals0));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_setrandom:
+ glulx_setrandom(inst[0].value);
+ break;
+
+ case op_verify:
+ value = perform_verify();
+ store_operand(inst[0].desttype, inst[0].value, value);
+ break;
+
+ case op_restart:
+ profile_fail("restart");
+ vm_restart();
+ break;
+
+ case op_protect:
+ val0 = inst[0].value;
+ val1 = val0 + inst[1].value;
+ if (val0 == val1) {
+ val0 = 0;
+ val1 = 0;
+ }
+ protectstart = val0;
+ protectend = val1;
+ break;
+
+ case op_save:
+ push_callstub(inst[1].desttype, inst[1].value);
+ value = perform_save(find_stream_by_id(inst[0].value));
+ pop_callstub(value);
+ break;
+
+ case op_restore:
+ value = perform_restore(find_stream_by_id(inst[0].value), false);
+ if (value == 0) {
+ /* We've succeeded, and the stack now contains the callstub
+ saved during saveundo. Ignore this opcode's operand. */
+ value = (uint)-1;
+ pop_callstub(value);
+ }
+ else {
+ /* We've failed, so we must store the failure in this opcode's
+ operand. */
+ store_operand(inst[1].desttype, inst[1].value, value);
+ }
+ break;
+
+ case op_saveundo:
+ push_callstub(inst[0].desttype, inst[0].value);
+ value = perform_saveundo();
+ pop_callstub(value);
+ break;
+
+ case op_restoreundo:
+ value = perform_restoreundo();
+ if (value == 0) {
+ /* We've succeeded, and the stack now contains the callstub
+ saved during saveundo. Ignore this opcode's operand. */
+ value = (uint)-1;
+ pop_callstub(value);
+ }
+ else {
+ /* We've failed, so we must store the failure in this opcode's
+ operand. */
+ store_operand(inst[0].desttype, inst[0].value, value);
+ }
+ break;
+
+ case op_quit:
+ done_executing = true;
+ break;
+
+ case op_linearsearch:
+ value = linear_search(inst[0].value, inst[1].value, inst[2].value,
+ inst[3].value, inst[4].value, inst[5].value, inst[6].value);
+ store_operand(inst[7].desttype, inst[7].value, value);
+ break;
+ case op_binarysearch:
+ value = binary_search(inst[0].value, inst[1].value, inst[2].value,
+ inst[3].value, inst[4].value, inst[5].value, inst[6].value);
+ store_operand(inst[7].desttype, inst[7].value, value);
+ break;
+ case op_linkedsearch:
+ value = linked_search(inst[0].value, inst[1].value, inst[2].value,
+ inst[3].value, inst[4].value, inst[5].value);
+ store_operand(inst[6].desttype, inst[6].value, value);
+ break;
+
+ case op_mzero: {
+ uint lx;
+ uint count = inst[0].value;
+ addr = inst[1].value;
+ for (lx=0; lx<count; lx++, addr++) {
+ MemW1(addr, 0);
+ }
+ }
+ break;
+ case op_mcopy: {
+ uint lx;
+ uint count = inst[0].value;
+ uint addrsrc = inst[1].value;
+ uint addrdest = inst[2].value;
+ if (addrdest < addrsrc) {
+ for (lx=0; lx<count; lx++, addrsrc++, addrdest++) {
+ value = Mem1(addrsrc);
+ MemW1(addrdest, value);
+ }
+ }
+ else {
+ addrsrc += (count-1);
+ addrdest += (count-1);
+ for (lx=0; lx<count; lx++, addrsrc--, addrdest--) {
+ value = Mem1(addrsrc);
+ MemW1(addrdest, value);
+ }
+ }
+ }
+ break;
+ case op_malloc:
+ value = heap_alloc(inst[0].value);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_mfree:
+ heap_free(inst[0].value);
+ break;
+
+ case op_accelfunc:
+ accel_set_func(inst[0].value, inst[1].value);
+ break;
+ case op_accelparam:
+ accel_set_param(inst[0].value, inst[1].value);
+ break;
+
+#ifdef FLOAT_SUPPORT
+
+ case op_numtof:
+ vals0 = inst[0].value;
+ value = encode_float((gfloat32)vals0);
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_ftonumz:
+ valf = decode_float(inst[0].value);
+ if (!signbit(valf)) {
+ if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
+ vals0 = 0x7FFFFFFF;
+ else
+ vals0 = (int)(truncf(valf));
+ }
+ else {
+ if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+ vals0 = 0x80000000;
+ else
+ vals0 = (int)(truncf(valf));
+ }
+ store_operand(inst[1].desttype, inst[1].value, vals0);
+ break;
+ case op_ftonumn:
+ valf = decode_float(inst[0].value);
+ if (!signbit(valf)) {
+ if (isnan(valf) || isinf(valf) || (valf > 2147483647.0))
+ vals0 = 0x7FFFFFFF;
+ else
+ vals0 = (int)(roundf(valf));
+ }
+ else {
+ if (isnan(valf) || isinf(valf) || (valf < -2147483647.0))
+ vals0 = 0x80000000;
+ else
+ vals0 = (int)(roundf(valf));
+ }
+ store_operand(inst[1].desttype, inst[1].value, vals0);
+ break;
+
+ case op_fadd:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 + valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_fsub:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 - valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_fmul:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 * valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+ case op_fdiv:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(valf1 / valf2);
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_fmod:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ valf = fmodf(valf1, valf2);
+ val0 = encode_float(valf);
+ val1 = encode_float((valf1-valf) / valf2);
+ if (val1 == 0x0 || val1 == 0x80000000) {
+ /* When the quotient is zero, the sign has been lost in the
+ shuffle. We'll set that by hand, based on the original
+ arguments. */
+ val1 = (inst[0].value ^ inst[1].value) & 0x80000000;
+ }
+ store_operand(inst[2].desttype, inst[2].value, val0);
+ store_operand(inst[3].desttype, inst[3].value, val1);
+ break;
+
+ case op_floor:
+ valf = decode_float(inst[0].value);
+ value = encode_float(floorf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_ceil:
+ valf = decode_float(inst[0].value);
+ value = encode_float(ceilf(valf));
+ if (value == 0x0 || value == 0x80000000) {
+ /* When the result is zero, the sign may have been lost in the
+ shuffle. (This is a bug in some C libraries.) We'll set the
+ sign by hand, based on the original argument. */
+ value = inst[0].value & 0x80000000;
+ }
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+
+ case op_sqrt:
+ valf = decode_float(inst[0].value);
+ value = encode_float(sqrtf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_log:
+ valf = decode_float(inst[0].value);
+ value = encode_float(logf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_exp:
+ valf = decode_float(inst[0].value);
+ value = encode_float(expf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_pow:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(glulx_powf(valf1, valf2));
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_sin:
+ valf = decode_float(inst[0].value);
+ value = encode_float(sinf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_cos:
+ valf = decode_float(inst[0].value);
+ value = encode_float(cosf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_tan:
+ valf = decode_float(inst[0].value);
+ value = encode_float(tanf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_asin:
+ valf = decode_float(inst[0].value);
+ value = encode_float(asinf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_acos:
+ valf = decode_float(inst[0].value);
+ value = encode_float(acosf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_atan:
+ valf = decode_float(inst[0].value);
+ value = encode_float(atanf(valf));
+ store_operand(inst[1].desttype, inst[1].value, value);
+ break;
+ case op_atan2:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ value = encode_float(atan2f(valf1, valf2));
+ store_operand(inst[2].desttype, inst[2].value, value);
+ break;
+
+ case op_jisinf:
+ /* Infinity is well-defined, so we don't bother to convert to
+ float. */
+ val0 = inst[0].value;
+ if (val0 == 0x7F800000 || val0 == 0xFF800000) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jisnan:
+ /* NaN is well-defined, so we don't bother to convert to
+ float. */
+ val0 = inst[0].value;
+ if ((val0 & 0x7F800000) == 0x7F800000 && (val0 & 0x007FFFFF) != 0) {
+ value = inst[1].value;
+ goto PerformJump;
+ }
+ break;
+
+ case op_jfeq:
+ if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) {
+ /* The delta is NaN, which can never match. */
+ val0 = 0;
+ }
+ else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000)
+ && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) {
+ /* Both are infinite. Opposite infinities are never equal,
+ even if the difference is infinite, so this is easy. */
+ val0 = (inst[0].value == inst[1].value);
+ }
+ else {
+ valf1 = decode_float(inst[1].value) - decode_float(inst[0].value);
+ valf2 = fabs(decode_float(inst[2].value));
+ val0 = (valf1 <= valf2 && valf1 >= -valf2);
+ }
+ if (val0) {
+ value = inst[3].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfne:
+ if ((inst[2].value & 0x7F800000) == 0x7F800000 && (inst[2].value & 0x007FFFFF) != 0) {
+ /* The delta is NaN, which can never match. */
+ val0 = 0;
+ }
+ else if ((inst[0].value == 0x7F800000 || inst[0].value == 0xFF800000)
+ && (inst[1].value == 0x7F800000 || inst[1].value == 0xFF800000)) {
+ /* Both are infinite. Opposite infinities are never equal,
+ even if the difference is infinite, so this is easy. */
+ val0 = (inst[0].value == inst[1].value);
+ }
+ else {
+ valf1 = decode_float(inst[1].value) - decode_float(inst[0].value);
+ valf2 = fabs(decode_float(inst[2].value));
+ val0 = (valf1 <= valf2 && valf1 >= -valf2);
+ }
+ if (!val0) {
+ value = inst[3].value;
+ goto PerformJump;
+ }
+ break;
+
+ case op_jflt:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 < valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfgt:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 > valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfle:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 <= valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+ case op_jfge:
+ valf1 = decode_float(inst[0].value);
+ valf2 = decode_float(inst[1].value);
+ if (valf1 >= valf2) {
+ value = inst[2].value;
+ goto PerformJump;
+ }
+ break;
+
+#endif /* FLOAT_SUPPORT */
+
+#ifdef GLULX_EXTEND_OPCODES
+ GLULX_EXTEND_OPCODES
+#endif /* GLULX_EXTEND_OPCODES */
+
+ default:
+ fatal_error_i("Executed unknown opcode.", opcode);
+ }
+ }
+ }
+ /* done executing */
+#if VM_DEBUGGER
+ debugger_handle_quit();
+#endif /* VM_DEBUGGER */
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 6fdfb05ba5..1f05b9432a 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -34,6 +34,7 @@ Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst,
memmap(nullptr), stack(nullptr), ramstart(0), endgamefile(0), origendmem(0), stacksize(0),
startfuncaddr(0), checksum(0), stackptr(0), frameptr(0), pc(0), prevpc(0), origstringtable(0),
stringtable(0), valstackbase(0), localsbase(0), endmem(0), protectstart(0), protectend(0),
+ stream_char_handler(nullptr), stream_unichar_handler(nullptr),
// Accel
classes_table(0), indiv_prop_start(0), class_metaclass(0), object_metaclass(0),
routine_metaclass(0), string_metaclass(0), self(0), num_attr_bytes(0), cpv__start(0),
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index 839e9eb524..fb7f63b3d6 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -60,6 +60,9 @@ public:
uint protectstart, protectend;
uint prevpc;
+ void (*Glulxe::stream_char_handler)(unsigned char ch);
+ void (*Glulxe::stream_unichar_handler)(uint ch);
+
/**
* \defgroup accel fields
* @{
@@ -288,7 +291,11 @@ public:
* \defgroup Exec access methods
* @{
*/
- void execute_loop(void);
+
+ /**
+ * The main interpreter loop. This repeats until the program is done
+ */
+ void execute_loop();
/**@}*/
diff --git a/engines/glk/glulxe/glulxe_types.h b/engines/glk/glulxe/glulxe_types.h
index b06e43536a..b4c9a14569 100644
--- a/engines/glk/glulxe/glulxe_types.h
+++ b/engines/glk/glulxe/glulxe_types.h
@@ -26,53 +26,53 @@
#include "common/scummsys.h"
namespace Glk {
-namespace Glulxe {
+ namespace Glulxe {
-class Glulxe;
+ class Glulxe;
-/**
- * Comment this definition to turn off memory-address checking. With verification on,
- * all reads and writes to main memory will be checked to ensure they're in range.
- * This is slower, but prevents malformed game files from crashing the interpreter.
- */
+ /**
+ * Comment this definition to turn off memory-address checking. With verification on,
+ * all reads and writes to main memory will be checked to ensure they're in range.
+ * This is slower, but prevents malformed game files from crashing the interpreter.
+ */
#define VERIFY_MEMORY_ACCESS (1)
-/**
- * Uncomment this definition to permit an exception for memory-address checking for @glk and @copy
- * opcodes that try to write to memory address 0. This was a bug in old Superglus-built game files.
- */
-/* #define TOLERATE_SUPERGLUS_BUG (1) */
+ /**
+ * Uncomment this definition to permit an exception for memory-address checking for @glk and @copy
+ * opcodes that try to write to memory address 0. This was a bug in old Superglus-built game files.
+ */
+ /* #define TOLERATE_SUPERGLUS_BUG (1) */
-/**
- * Uncomment this definition to turn on Glulx VM profiling. In this mode, all function calls are timed,
- * and the timing information is written to a data file called "profile-raw".
- * (Build note: on Linux, glibc may require you to also define _BSD_SOURCE or _DEFAULT_SOURCE or both
- * for the timeradd() macro.)
- */
-/* #define VM_PROFILING (1) */
+ /**
+ * Uncomment this definition to turn on Glulx VM profiling. In this mode, all function calls are timed,
+ * and the timing information is written to a data file called "profile-raw".
+ * (Build note: on Linux, glibc may require you to also define _BSD_SOURCE or _DEFAULT_SOURCE or both
+ * for the timeradd() macro.)
+ */
+ /* #define VM_PROFILING (1) */
-/**
- * Uncomment this definition to turn on the Glulx debugger. You should only do this when debugging
- * facilities are desired; it slows down the interpreter. If you do, you will need to build with libxml2;
- * see the Makefile.
- */
-/* #define VM_DEBUGGER (1) */
+ /**
+ * Uncomment this definition to turn on the Glulx debugger. You should only do this when debugging
+ * facilities are desired; it slows down the interpreter. If you do, you will need to build with libxml2;
+ * see the Makefile.
+ */
+ /* #define VM_DEBUGGER (1) */
-/**
- * Comment this definition to turn off floating-point support. You might need to do this if you are building
- * on a very limited platform with no math library. */
+ /**
+ * Comment this definition to turn off floating-point support. You might need to do this if you are building
+ * on a very limited platform with no math library. */
#define FLOAT_SUPPORT (1)
-/**
- * Comment this definition to not cache the original state of RAM in (real) memory. This saves some memory,
- * but slows down save/restore/undo operations, which will have to read the original state off disk
- * every time.
- */
+ /**
+ * Comment this definition to not cache the original state of RAM in (real) memory. This saves some memory,
+ * but slows down save/restore/undo operations, which will have to read the original state off disk
+ * every time.
+ */
#define SERIALIZE_CACHE_RAM (1)
-/**
- * Some macros to read and write integers to memory, always in big-endian format.
- */
+ /**
+ * Some macros to read and write integers to memory, always in big-endian format.
+ */
#define Read4(ptr) READ_BE_UINT32(ptr)
#define Read2(ptr) READ_BE_UINT16(ptr)
#define Read1(ptr) ((byte)(((byte *)(ptr))[0]))
@@ -95,12 +95,12 @@ class Glulxe;
#define MemW2(adr, vl) (VerifyW(adr, 2), Write2(memmap+(adr), (vl)))
#define MemW4(adr, vl) (VerifyW(adr, 4), Write4(memmap+(adr), (vl)))
-/**
- * Macros to access values on the stack. These *must* be used with proper alignment!
- * (That is, Stk4 and StkW4 must take addresses which are multiples of four, etc.)
- * If the alignment rules are not followed, the program will see performance
- * degradation or even crashes, depending on the machine CPU.
- */
+ /**
+ * Macros to access values on the stack. These *must* be used with proper alignment!
+ * (That is, Stk4 and StkW4 must take addresses which are multiples of four, etc.)
+ * If the alignment rules are not followed, the program will see performance
+ * degradation or even crashes, depending on the machine CPU.
+ */
#define Stk1(adr) \
(*((unsigned char *)(stack+(adr))))
#define Stk2(adr) \
@@ -115,6 +115,140 @@ class Glulxe;
#define StkW4(adr, vl) \
(*((uint32 *)(stack+(adr))) = (uint32)(vl))
+enum Opcode {
+ op_nop = 0x00,
+
+ op_add = 0x10,
+ op_sub = 0x11,
+ op_mul = 0x12,
+ op_div = 0x13,
+ op_mod = 0x14,
+ op_neg = 0x15,
+ op_bitand = 0x18,
+ op_bitor = 0x19,
+ op_bitxor = 0x1A,
+ op_bitnot = 0x1B,
+ op_shiftl = 0x1C,
+ op_sshiftr = 0x1D,
+ op_ushiftr = 0x1E,
+
+ op_jump = 0x20,
+ op_jz = 0x22,
+ op_jnz = 0x23,
+ op_jeq = 0x24,
+ op_jne = 0x25,
+ op_jlt = 0x26,
+ op_jge = 0x27,
+ op_jgt = 0x28,
+ op_jle = 0x29,
+ op_jltu = 0x2A,
+ op_jgeu = 0x2B,
+ op_jgtu = 0x2C,
+ op_jleu = 0x2D,
+
+ op_call = 0x30,
+ op_return = 0x31,
+ op_catch = 0x32,
+ op_throw = 0x33,
+ op_tailcall = 0x34,
+
+ op_copy = 0x40,
+ op_copys = 0x41,
+ op_copyb = 0x42,
+ op_sexs = 0x44,
+ op_sexb = 0x45,
+ op_aload = 0x48,
+ op_aloads = 0x49,
+ op_aloadb = 0x4A,
+ op_aloadbit = 0x4B,
+ op_astore = 0x4C,
+ op_astores = 0x4D,
+ op_astoreb = 0x4E,
+ op_astorebit = 0x4F,
+
+ op_stkcount = 0x50,
+ op_stkpeek = 0x51,
+ op_stkswap = 0x52,
+ op_stkroll = 0x53,
+ op_stkcopy = 0x54,
+
+ op_streamchar = 0x70,
+ op_streamnum = 0x71,
+ op_streamstr = 0x72,
+ op_streamunichar = 0x73,
+
+ op_gestalt = 0x100,
+ op_debugtrap = 0x101,
+ op_getmemsize = 0x102,
+ op_setmemsize = 0x103,
+ op_jumpabs = 0x104,
+
+ op_random = 0x110,
+ op_setrandom = 0x111,
+
+ op_quit = 0x120,
+ op_verify = 0x121,
+ op_restart = 0x122,
+ op_save = 0x123,
+ op_restore = 0x124,
+ op_saveundo = 0x125,
+ op_restoreundo = 0x126,
+ op_protect = 0x127,
+
+ op_glk = 0x130,
+
+ op_getstringtbl = 0x140,
+ op_setstringtbl = 0x141,
+ op_getiosys = 0x148,
+ op_setiosys = 0x149,
+
+ op_linearsearch = 0x150,
+ op_binarysearch = 0x151,
+ op_linkedsearch = 0x152,
+
+ op_callf = 0x160,
+ op_callfi = 0x161,
+ op_callfii = 0x162,
+ op_callfiii = 0x163,
+
+ op_mzero = 0x170,
+ op_mcopy = 0x171,
+ op_malloc = 0x178,
+ op_mfree = 0x179,
+
+ op_accelfunc = 0x180,
+ op_accelparam = 0x181,
+
+ op_numtof = 0x190,
+ op_ftonumz = 0x191,
+ op_ftonumn = 0x192,
+ op_ceil = 0x198,
+ op_floor = 0x199,
+ op_fadd = 0x1A0,
+ op_fsub = 0x1A1,
+ op_fmul = 0x1A2,
+ op_fdiv = 0x1A3,
+ op_fmod = 0x1A4,
+ op_sqrt = 0x1A8,
+ op_exp = 0x1A9,
+ op_log = 0x1AA,
+ op_pow = 0x1AB,
+ op_sin = 0x1B0,
+ op_cos = 0x1B1,
+ op_tan = 0x1B2,
+ op_asin = 0x1B3,
+ op_acos = 0x1B4,
+ op_atan = 0x1B5,
+ op_atan2 = 0x1B6,
+ op_jfeq = 0x1C0,
+ op_jfne = 0x1C1,
+ op_jflt = 0x1C2,
+ op_jfle = 0x1C3,
+ op_jfgt = 0x1C4,
+ op_jfge = 0x1C5,
+ op_jisnan = 0x1C8,
+ op_jisinf = 0x1C9
+};
struct dispatch_splot_struct {
int numwanted;
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 6fe51c9a4e..8a593580fb 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -59,6 +59,7 @@ MODULE_OBJS := \
frotz/windows.o \
glulxe/accel.o \
glulxe/detection.o \
+ glulxe/exec.o \
glulxe/float.o \
glulxe/glkop.o \
glulxe/glulxe.o \