1 /* funcs.c: Glulxe function-handling functions.
2 Designed by Andrew Plotkin <erkyrath@eblong.com>
3 http://eblong.com/zarf/glulx/index.html
10 This writes a new call frame onto the stack, at stackptr. It leaves
11 frameptr pointing to the frame (ie, the original stackptr value.)
12 argc and argv are an array of arguments. Note that if argc is zero,
15 void enter_function(glui32 addr, glui32 argc, glui32 *argv)
18 acceleration_func accelfunc;
21 glui32 modeaddr, opaddr, val;
24 accelfunc = accel_get_func(addr);
26 profile_in(addr, TRUE);
27 val = accelfunc(argc, argv);
33 profile_in(addr, FALSE);
35 /* Check the Glulx type identifier byte. */
36 functype = Mem1(addr);
37 if (functype != 0xC0 && functype != 0xC1) {
38 if (functype >= 0xC0 && functype <= 0xDF)
39 fatal_error_i("Call to unknown type of function.", addr);
41 fatal_error_i("Call to non-function.", addr);
45 /* Bump the frameptr to the top. */
48 /* Go through the function's locals-format list, copying it to the
49 call frame. At the same time, we work out how much space the locals
50 will actually take up. (Including padding.) */
54 /* Grab two bytes from the locals-format list. These are
55 unsigned (0..255 range). */
61 /* Copy them into the call frame. */
62 StkW1(frameptr+8+2*ix, loctype);
63 StkW1(frameptr+8+2*ix+1, locnum);
66 /* If the type is zero, we're done, except possibly for two more
67 zero bytes in the call frame (to ensure 4-byte alignment.) */
69 /* Make sure ix is even. */
71 StkW1(frameptr+8+2*ix, 0);
72 StkW1(frameptr+8+2*ix+1, 0);
78 /* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2
84 else if (loctype == 2) {
88 else if (loctype == 1) {
92 fatal_error("Illegal local type in locals-format list.");
95 /* Add the length of the locals themselves. */
96 locallen += (loctype * locnum);
99 /* Pad the locals to 4-byte alignment. */
103 /* We now know how long the locals-frame and locals segments are. */
104 localsbase = frameptr+8+2*ix;
105 valstackbase = localsbase+locallen;
107 /* Test for stack overflow. */
108 /* This really isn't good enough; if the format list overflowed the
109 stack, we've already written outside the stack array. */
110 if (valstackbase >= stacksize)
111 fatal_error("Stack overflow in function call.");
113 /* Fill in the beginning of the stack frame. */
114 StkW4(frameptr+4, 8+2*ix);
115 StkW4(frameptr, 8+2*ix+locallen);
117 /* Set the stackptr and PC. */
118 stackptr = valstackbase;
121 /* Zero out all the locals. */
122 for (jx=0; jx<locallen; jx++)
123 StkW1(localsbase+jx, 0);
125 if (functype == 0xC0) {
126 /* Push the function arguments on the stack. The locals have already
128 if (stackptr+4*(argc+1) >= stacksize)
129 fatal_error("Stack overflow in function arguments.");
130 for (ix=0; ix<argc; ix++) {
131 val = argv[(argc-1)-ix];
132 StkW4(stackptr, val);
135 StkW4(stackptr, argc);
139 /* Copy in function arguments. This is a bit gross, since we have to
140 follow the locals format. If there are fewer arguments than locals,
141 that's fine -- we've already zeroed out this space. If there are
142 more arguments than locals, the extras are silently dropped. */
143 modeaddr = frameptr+8;
147 loctype = Stk1(modeaddr);
149 locnum = Stk1(modeaddr);
156 while (ix < argc && locnum) {
164 else if (loctype == 2) {
167 while (ix < argc && locnum) {
168 val = argv[ix] & 0xFFFF;
175 else if (loctype == 1) {
176 while (ix < argc && locnum) {
177 val = argv[ix] & 0xFF;
189 Pop the current call frame off the stack. This is very simple.
191 void leave_function()
198 Push the magic four values on the stack: result destination,
201 void push_callstub(glui32 desttype, glui32 destaddr)
203 if (stackptr+16 > stacksize)
204 fatal_error("Stack overflow in callstub.");
205 StkW4(stackptr+0, desttype);
206 StkW4(stackptr+4, destaddr);
207 StkW4(stackptr+8, pc);
208 StkW4(stackptr+12, frameptr);
213 Remove the magic four values from the stack, and use them. The
214 returnvalue, whatever it is, is put at the result destination;
215 the PC and frameptr registers are set.
217 void pop_callstub(glui32 returnvalue)
219 glui32 desttype, destaddr;
220 glui32 newpc, newframeptr;
223 fatal_error("Stack underflow in callstub.");
226 newframeptr = Stk4(stackptr+12);
227 newpc = Stk4(stackptr+8);
228 destaddr = Stk4(stackptr+4);
229 desttype = Stk4(stackptr+0);
232 frameptr = newframeptr;
234 /* Recompute valstackbase and localsbase */
235 valstackbase = frameptr + Stk4(frameptr);
236 localsbase = frameptr + Stk4(frameptr+4);
241 fatal_error("String-terminator call stub at end of function call.");
245 /* This call stub was pushed during a string-decoding operation!
246 We have to restart it. (Note that the return value is discarded.) */
247 stream_string(pc, 0xE1, destaddr);
251 /* This call stub was pushed during a number-printing operation.
252 Restart that. (Return value discarded.) */
253 stream_num(pc, TRUE, destaddr);
257 /* This call stub was pushed during a C-string printing operation.
258 We have to restart it. (Note that the return value is discarded.) */
259 stream_string(pc, 0xE0, destaddr);
263 /* This call stub was pushed during a Unicode printing operation.
264 We have to restart it. (Note that the return value is discarded.) */
265 stream_string(pc, 0xE2, destaddr);
269 /* We're back in the original frame, so we can store the returnvalue.
270 (If we tried to do this before resetting frameptr, a result
271 destination on the stack would go astray.) */
272 store_operand(desttype, destaddr, returnvalue);
277 /* pop_callstub_string():
278 Remove the magic four values, but interpret them as a string restart
279 state. Returns zero if it's a termination stub, or returns the
280 restart address. The bitnum is extra.
282 glui32 pop_callstub_string(int *bitnum)
284 glui32 desttype, destaddr, newpc;
287 fatal_error("Stack underflow in callstub.");
290 newpc = Stk4(stackptr+8);
291 destaddr = Stk4(stackptr+4);
292 desttype = Stk4(stackptr+0);
296 if (desttype == 0x11) {
299 if (desttype == 0x10) {
304 fatal_error("Function-terminator call stub at end of string.");