aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/glulxe
diff options
context:
space:
mode:
authorPaul Gilbert2019-04-14 21:19:44 -0700
committerPaul Gilbert2019-04-17 20:46:06 -0700
commit4f1387317f2dc2dbff2b25708aef22e11d80d617 (patch)
tree3dec691b60e2f3b5f8fb672c778beb3295e11493 /engines/glk/glulxe
parent6307fd08ce7cbb0b939f520f9741aef884f66f88 (diff)
downloadscummvm-rg350-4f1387317f2dc2dbff2b25708aef22e11d80d617.tar.gz
scummvm-rg350-4f1387317f2dc2dbff2b25708aef22e11d80d617.tar.bz2
scummvm-rg350-4f1387317f2dc2dbff2b25708aef22e11d80d617.zip
GLK: GLULXE: Added funcs methods
Diffstat (limited to 'engines/glk/glulxe')
-rw-r--r--engines/glk/glulxe/funcs.cpp305
-rw-r--r--engines/glk/glulxe/glulxe.h25
2 files changed, 329 insertions, 1 deletions
diff --git a/engines/glk/glulxe/funcs.cpp b/engines/glk/glulxe/funcs.cpp
new file mode 100644
index 0000000000..cb427e24fe
--- /dev/null
+++ b/engines/glk/glulxe/funcs.cpp
@@ -0,0 +1,305 @@
+/* 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::enter_function(uint funcaddr, uint argc, uint *argv) {
+ uint ix, jx;
+ acceleration_func accelFunc;
+ int locallen;
+ int functype;
+ uint modeaddr, opaddr, val;
+ int loctype, locnum;
+ uint addr = funcaddr;
+
+ accelFunc = accel_get_func(addr);
+ if (accelFunc) {
+ profile_in(addr, stackptr, TRUE);
+ val = (this->*accelFunc)(argc, argv);
+ profile_out(stackptr);
+ pop_callstub(val);
+ return;
+ }
+
+ profile_in(addr, stackptr, FALSE);
+
+ /* Check the Glulx type identifier byte. */
+ functype = Mem1(addr);
+ if (functype != 0xC0 && functype != 0xC1) {
+ if (functype >= 0xC0 && functype <= 0xDF)
+ fatal_error_i("Call to unknown type of function.", addr);
+ else
+ fatal_error_i("Call to non-function.", addr);
+ }
+ addr++;
+
+ /* Bump the frameptr to the top. */
+ frameptr = stackptr;
+
+ /* Go through the function's locals-format list, copying it to the
+ call frame. At the same time, we work out how much space the locals
+ will actually take up. (Including padding.) */
+ ix = 0;
+ locallen = 0;
+ while (1) {
+ /* Grab two bytes from the locals-format list. These are
+ unsigned (0..255 range). */
+ loctype = Mem1(addr);
+ addr++;
+ locnum = Mem1(addr);
+ addr++;
+
+ /* Copy them into the call frame. */
+ StkW1(frameptr+8+2*ix, loctype);
+ StkW1(frameptr+8+2*ix+1, locnum);
+ ix++;
+
+ /* If the type is zero, we're done, except possibly for two more
+ zero bytes in the call frame (to ensure 4-byte alignment.) */
+ if (loctype == 0) {
+ /* Make sure ix is even. */
+ if (ix & 1) {
+ StkW1(frameptr+8+2*ix, 0);
+ StkW1(frameptr+8+2*ix+1, 0);
+ ix++;
+ }
+ break;
+ }
+
+ /* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2
+ bytes long. */
+ if (loctype == 4) {
+ while (locallen & 3)
+ locallen++;
+ }
+ else if (loctype == 2) {
+ while (locallen & 1)
+ locallen++;
+ }
+ else if (loctype == 1) {
+ /* no padding */
+ }
+ else {
+ fatal_error("Illegal local type in locals-format list.");
+ }
+
+ /* Add the length of the locals themselves. */
+ locallen += (loctype * locnum);
+ }
+
+ /* Pad the locals to 4-byte alignment. */
+ while (locallen & 3)
+ locallen++;
+
+ /* We now know how long the locals-frame and locals segments are. */
+ localsbase = frameptr+8+2*ix;
+ valstackbase = localsbase+locallen;
+
+ /* Test for stack overflow. */
+ /* This really isn't good enough; if the format list overflowed the
+ stack, we've already written outside the stack array. */
+ if (valstackbase >= stacksize)
+ fatal_error("Stack overflow in function call.");
+
+ /* Fill in the beginning of the stack frame. */
+ StkW4(frameptr+4, 8+2*ix);
+ StkW4(frameptr, 8+2*ix+locallen);
+
+ /* Set the stackptr and PC. */
+ stackptr = valstackbase;
+ pc = addr;
+
+ /* Zero out all the locals. */
+ for (jx=0; jx < (uint)locallen; jx++)
+ StkW1(localsbase+jx, 0);
+
+ if (functype == 0xC0) {
+ /* Push the function arguments on the stack. The locals have already
+ been zeroed. */
+ if (stackptr+4*(argc+1) >= stacksize)
+ fatal_error("Stack overflow in function arguments.");
+ for (ix=0; ix<argc; ix++) {
+ val = argv[(argc-1)-ix];
+ StkW4(stackptr, val);
+ stackptr += 4;
+ }
+ StkW4(stackptr, argc);
+ stackptr += 4;
+ }
+ else {
+ /* Copy in function arguments. This is a bit gross, since we have to
+ follow the locals format. If there are fewer arguments than locals,
+ that's fine -- we've already zeroed out this space. If there are
+ more arguments than locals, the extras are silently dropped. */
+ modeaddr = frameptr+8;
+ opaddr = localsbase;
+ ix = 0;
+ while (ix < argc) {
+ loctype = Stk1(modeaddr);
+ modeaddr++;
+ locnum = Stk1(modeaddr);
+ modeaddr++;
+ if (loctype == 0)
+ break;
+ if (loctype == 4) {
+ while (opaddr & 3)
+ opaddr++;
+ while (ix < argc && locnum) {
+ val = argv[ix];
+ StkW4(opaddr, val);
+ opaddr += 4;
+ ix++;
+ locnum--;
+ }
+ }
+ else if (loctype == 2) {
+ while (opaddr & 1)
+ opaddr++;
+ while (ix < argc && locnum) {
+ val = argv[ix] & 0xFFFF;
+ StkW2(opaddr, val);
+ opaddr += 2;
+ ix++;
+ locnum--;
+ }
+ }
+ else if (loctype == 1) {
+ while (ix < argc && locnum) {
+ val = argv[ix] & 0xFF;
+ StkW1(opaddr, val);
+ opaddr += 1;
+ ix++;
+ locnum--;
+ }
+ }
+ }
+ }
+
+ /* If the debugger is compiled in, check for a breakpoint on this
+ function. (Checking the function address, not the starting PC.) */
+ debugger_check_func_breakpoint(funcaddr);
+}
+
+void Glulxe::leave_function() {
+ profile_out(stackptr);
+ stackptr = frameptr;
+}
+
+void Glulxe::push_callstub(uint desttype, uint destaddr) {
+ if (stackptr+16 > stacksize)
+ fatal_error("Stack overflow in callstub.");
+ StkW4(stackptr+0, desttype);
+ StkW4(stackptr+4, destaddr);
+ StkW4(stackptr+8, pc);
+ StkW4(stackptr+12, frameptr);
+ stackptr += 16;
+}
+
+void Glulxe::pop_callstub(uint returnvalue) {
+ uint desttype, destaddr;
+ uint newpc, newframeptr;
+
+ if (stackptr < 16)
+ fatal_error("Stack underflow in callstub.");
+ stackptr -= 16;
+
+ newframeptr = Stk4(stackptr+12);
+ newpc = Stk4(stackptr+8);
+ destaddr = Stk4(stackptr+4);
+ desttype = Stk4(stackptr+0);
+
+ pc = newpc;
+ frameptr = newframeptr;
+
+ /* Recompute valstackbase and localsbase */
+ valstackbase = frameptr + Stk4(frameptr);
+ localsbase = frameptr + Stk4(frameptr+4);
+
+ switch (desttype) {
+
+ case 0x11:
+ fatal_error("String-terminator call stub at end of function call.");
+ break;
+
+ case 0x10:
+ /* This call stub was pushed during a string-decoding operation!
+ We have to restart it. (Note that the return value is discarded.) */
+ stream_string(pc, 0xE1, destaddr);
+ break;
+
+ case 0x12:
+ /* This call stub was pushed during a number-printing operation.
+ Restart that. (Return value discarded.) */
+ stream_num(pc, true, destaddr);
+ break;
+
+ case 0x13:
+ /* This call stub was pushed during a C-string printing operation.
+ We have to restart it. (Note that the return value is discarded.) */
+ stream_string(pc, 0xE0, destaddr);
+ break;
+
+ case 0x14:
+ /* This call stub was pushed during a Unicode printing operation.
+ We have to restart it. (Note that the return value is discarded.) */
+ stream_string(pc, 0xE2, destaddr);
+ break;
+
+ default:
+ /* We're back in the original frame, so we can store the returnvalue.
+ (If we tried to do this before resetting frameptr, a result
+ destination on the stack would go astray.) */
+ store_operand(desttype, destaddr, returnvalue);
+ break;
+ }
+}
+
+uint Glulxe::pop_callstub_string(int *bitnum) {
+ uint desttype, destaddr, newpc;
+
+ if (stackptr < 16)
+ fatal_error("Stack underflow in callstub.");
+ stackptr -= 16;
+
+ newpc = Stk4(stackptr+8);
+ destaddr = Stk4(stackptr+4);
+ desttype = Stk4(stackptr+0);
+
+ pc = newpc;
+
+ if (desttype == 0x11) {
+ return 0;
+ }
+ if (desttype == 0x10) {
+ *bitnum = destaddr;
+ return pc;
+ }
+
+ fatal_error("Function-terminator call stub at end of string.");
+ return 0;
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index fb7f63b3d6..2eed876ffe 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -317,10 +317,33 @@ public:
* @{
*/
+ /**
+ * This writes a new call frame onto the stack, at stackptr. It leaves frameptr pointing
+ * to the frame (ie, the original stackptr value.) argc and argv are an array of arguments.
+ * Note that if argc is zero, argv may be NULL.
+ */
void enter_function(uint addr, uint argc, uint *argv);
- void leave_function(void);
+
+ /**
+ * Pop the current call frame off the stack. This is very simple.
+ */
+ void leave_function();
+
+ /**
+ * Push the magic four values on the stack: result destination, PC, and frameptr.
+ */
void push_callstub(uint desttype, uint destaddr);
+
+ /**
+ * Remove the magic four values from the stack, and use them. The returnvalue, whatever it is,
+ * is put at the result destination; the PC and frameptr registers are set.
+ */
void pop_callstub(uint returnvalue);
+
+ /**
+ * Remove the magic four values, but interpret them as a string restart state.
+ * Returns zero if it's a termination stub, or returns the restart address. The bitnum is extra.
+ */
uint pop_callstub_string(int *bitnum);
/**@}*/