diff options
author | Paul Gilbert | 2019-04-14 21:19:44 -0700 |
---|---|---|
committer | Paul Gilbert | 2019-04-17 20:46:06 -0700 |
commit | 4f1387317f2dc2dbff2b25708aef22e11d80d617 (patch) | |
tree | 3dec691b60e2f3b5f8fb672c778beb3295e11493 | |
parent | 6307fd08ce7cbb0b939f520f9741aef884f66f88 (diff) | |
download | scummvm-rg350-4f1387317f2dc2dbff2b25708aef22e11d80d617.tar.gz scummvm-rg350-4f1387317f2dc2dbff2b25708aef22e11d80d617.tar.bz2 scummvm-rg350-4f1387317f2dc2dbff2b25708aef22e11d80d617.zip |
GLK: GLULXE: Added funcs methods
-rw-r--r-- | engines/glk/glulxe/funcs.cpp | 305 | ||||
-rw-r--r-- | engines/glk/glulxe/glulxe.h | 25 | ||||
-rw-r--r-- | engines/glk/module.mk | 1 |
3 files changed, 330 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); /**@}*/ diff --git a/engines/glk/module.mk b/engines/glk/module.mk index 8a593580fb..1f026b2a18 100644 --- a/engines/glk/module.mk +++ b/engines/glk/module.mk @@ -61,6 +61,7 @@ MODULE_OBJS := \ glulxe/detection.o \ glulxe/exec.o \ glulxe/float.o \ + glulxe/funcs.o \ glulxe/glkop.o \ glulxe/glulxe.o \ magnetic/detection.o \ |