#include "glulxe.h"
void enter_function(glui32 funcaddr, glui32 argc, glui32 *argv)
{
int ix, jx;
int locallen;
int functype;
glui32 modeaddr, opaddr, val;
int loctype, locnum;
glui32 addr = funcaddr;
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++;
frameptr = stackptr;
ix = 0;
locallen = 0;
while (1) {
loctype = Mem1(addr);
addr++;
locnum = Mem1(addr);
addr++;
StkW1(frameptr+8+2*ix, loctype);
StkW1(frameptr+8+2*ix+1, locnum);
ix++;
if (loctype == 0) {
if (ix & 1) {
StkW1(frameptr+8+2*ix, 0);
StkW1(frameptr+8+2*ix+1, 0);
ix++;
}
break;
}
if (loctype == 4) {
while (locallen & 3)
locallen++;
}
else if (loctype == 2) {
while (locallen & 1)
locallen++;
}
else if (loctype == 1) {
}
else {
fatal_error("Illegal local type in locals-format list.");
}
locallen += (loctype * locnum);
}
while (locallen & 3)
locallen++;
localsbase = frameptr+8+2*ix;
valstackbase = localsbase+locallen;
if (valstackbase >= stacksize)
trap(TRAP_STACK_EXHAUSTED);
StkW4(frameptr+4, 8+2*ix);
StkW4(frameptr, 8+2*ix+locallen);
stackptr = valstackbase;
pc = addr;
for (jx=0; jx<locallen; jx++)
StkW1(localsbase+jx, 0);
if (functype == 0xC0) {
if (stackptr+4*(argc+1) >= stacksize)
trap(TRAP_STACK_EXHAUSTED);
for (ix=0; ix<argc; ix++) {
val = argv[(argc-1)-ix];
StkW4(stackptr, val);
stackptr += 4;
}
StkW4(stackptr, argc);
stackptr += 4;
}
else {
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--;
}
}
}
}
}
void leave_function()
{
stackptr = frameptr;
}
void push_callstub(glui32 desttype, glui32 destaddr)
{
if (stackptr+16 > stacksize)
trap(TRAP_STACK_EXHAUSTED);
StkW4(stackptr+0, desttype);
StkW4(stackptr+4, destaddr);
StkW4(stackptr+8, pc);
StkW4(stackptr+12, frameptr);
stackptr += 16;
}
void pop_callstub(glui32 returnvalue)
{
glui32 desttype, destaddr;
glui32 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;
valstackbase = frameptr + Stk4(frameptr);
localsbase = frameptr + Stk4(frameptr+4);
switch (desttype) {
case 0x11:
case 0x10:
case 0x12:
case 0x13:
case 0x14:
fatal_error("Unexpected callstub type.");
break;
default:
store_operand(desttype, destaddr, returnvalue);
break;
}
}
glui32 pop_callstub_string(int *bitnum)
{
glui32 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;
}