1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/glulx/glulx.h"
24
25 namespace Glk {
26 namespace Glulx {
27
enter_function(uint funcaddr,uint argc,uint * argv)28 void Glulx::enter_function(uint funcaddr, uint argc, uint *argv) {
29 uint ix, jx;
30 acceleration_func accelFunc;
31 int locallen;
32 int functype;
33 uint modeaddr, opaddr, val;
34 int loctype, locnum;
35 uint addr = funcaddr;
36
37 accelFunc = accel_get_func(addr);
38 if (accelFunc) {
39 profile_in(addr, stackptr, true);
40 val = (this->*accelFunc)(argc, argv);
41 profile_out(stackptr);
42 pop_callstub(val);
43 return;
44 }
45
46 profile_in(addr, stackptr, false);
47
48 /* Check the Glulx type identifier byte. */
49 functype = Mem1(addr);
50 if (functype != 0xC0 && functype != 0xC1) {
51 if (functype >= 0xC0 && functype <= 0xDF)
52 fatal_error_i("Call to unknown type of function.", addr);
53 else
54 fatal_error_i("Call to non-function.", addr);
55 }
56 addr++;
57
58 /* Bump the frameptr to the top. */
59 frameptr = stackptr;
60
61 /* Go through the function's locals-format list, copying it to the
62 call frame. At the same time, we work out how much space the locals
63 will actually take up. (Including padding.) */
64 ix = 0;
65 locallen = 0;
66 while (1) {
67 /* Grab two bytes from the locals-format list. These are
68 unsigned (0..255 range). */
69 loctype = Mem1(addr);
70 addr++;
71 locnum = Mem1(addr);
72 addr++;
73
74 /* Copy them into the call frame. */
75 StkW1(frameptr + 8 + 2 * ix, loctype);
76 StkW1(frameptr + 8 + 2 * ix + 1, locnum);
77 ix++;
78
79 /* If the type is zero, we're done, except possibly for two more
80 zero bytes in the call frame (to ensure 4-byte alignment.) */
81 if (loctype == 0) {
82 /* Make sure ix is even. */
83 if (ix & 1) {
84 StkW1(frameptr + 8 + 2 * ix, 0);
85 StkW1(frameptr + 8 + 2 * ix + 1, 0);
86 ix++;
87 }
88 break;
89 }
90
91 /* Pad to 4-byte or 2-byte alignment if these locals are 4 or 2
92 bytes long. */
93 if (loctype == 4) {
94 while (locallen & 3)
95 locallen++;
96 } else if (loctype == 2) {
97 while (locallen & 1)
98 locallen++;
99 } else if (loctype == 1) {
100 /* no padding */
101 } else {
102 fatal_error("Illegal local type in locals-format list.");
103 }
104
105 /* Add the length of the locals themselves. */
106 locallen += (loctype * locnum);
107 }
108
109 /* Pad the locals to 4-byte alignment. */
110 while (locallen & 3)
111 locallen++;
112
113 /* We now know how long the locals-frame and locals segments are. */
114 localsbase = frameptr + 8 + 2 * ix;
115 valstackbase = localsbase + locallen;
116
117 /* Test for stack overflow. */
118 /* This really isn't good enough; if the format list overflowed the
119 stack, we've already written outside the stack array. */
120 if (valstackbase >= stacksize)
121 fatal_error("Stack overflow in function call.");
122
123 /* Fill in the beginning of the stack frame. */
124 StkW4(frameptr + 4, 8 + 2 * ix);
125 StkW4(frameptr, 8 + 2 * ix + locallen);
126
127 /* Set the stackptr and PC. */
128 stackptr = valstackbase;
129 pc = addr;
130
131 /* Zero out all the locals. */
132 for (jx = 0; jx < (uint)locallen; jx++)
133 StkW1(localsbase + jx, 0);
134
135 if (functype == 0xC0) {
136 /* Push the function arguments on the stack. The locals have already
137 been zeroed. */
138 if (stackptr + 4 * (argc + 1) >= stacksize)
139 fatal_error("Stack overflow in function arguments.");
140 for (ix = 0; ix < argc; ix++) {
141 val = argv[(argc - 1) - ix];
142 StkW4(stackptr, val);
143 stackptr += 4;
144 }
145 StkW4(stackptr, argc);
146 stackptr += 4;
147 } else {
148 /* Copy in function arguments. This is a bit gross, since we have to
149 follow the locals format. If there are fewer arguments than locals,
150 that's fine -- we've already zeroed out this space. If there are
151 more arguments than locals, the extras are silently dropped. */
152 modeaddr = frameptr + 8;
153 opaddr = localsbase;
154 ix = 0;
155 while (ix < argc) {
156 loctype = Stk1(modeaddr);
157 modeaddr++;
158 locnum = Stk1(modeaddr);
159 modeaddr++;
160 if (loctype == 0)
161 break;
162 if (loctype == 4) {
163 while (opaddr & 3)
164 opaddr++;
165 while (ix < argc && locnum) {
166 val = argv[ix];
167 StkW4(opaddr, val);
168 opaddr += 4;
169 ix++;
170 locnum--;
171 }
172 } else if (loctype == 2) {
173 while (opaddr & 1)
174 opaddr++;
175 while (ix < argc && locnum) {
176 val = argv[ix] & 0xFFFF;
177 StkW2(opaddr, val);
178 opaddr += 2;
179 ix++;
180 locnum--;
181 }
182 } else if (loctype == 1) {
183 while (ix < argc && locnum) {
184 val = argv[ix] & 0xFF;
185 StkW1(opaddr, val);
186 opaddr += 1;
187 ix++;
188 locnum--;
189 }
190 }
191 }
192 }
193
194 /* If the debugger is compiled in, check for a breakpoint on this
195 function. (Checking the function address, not the starting PC.) */
196 debugger_check_func_breakpoint(funcaddr);
197 }
198
leave_function()199 void Glulx::leave_function() {
200 profile_out(stackptr);
201 stackptr = frameptr;
202 }
203
push_callstub(uint desttype,uint destaddr)204 void Glulx::push_callstub(uint desttype, uint destaddr) {
205 if (stackptr + 16 > stacksize)
206 fatal_error("Stack overflow in callstub.");
207 StkW4(stackptr + 0, desttype);
208 StkW4(stackptr + 4, destaddr);
209 StkW4(stackptr + 8, pc);
210 StkW4(stackptr + 12, frameptr);
211 stackptr += 16;
212 }
213
pop_callstub(uint returnvalue)214 void Glulx::pop_callstub(uint returnvalue) {
215 uint desttype, destaddr;
216 uint newpc, newframeptr;
217
218 if (stackptr < 16)
219 fatal_error("Stack underflow in callstub.");
220 stackptr -= 16;
221
222 newframeptr = Stk4(stackptr + 12);
223 newpc = Stk4(stackptr + 8);
224 destaddr = Stk4(stackptr + 4);
225 desttype = Stk4(stackptr + 0);
226
227 pc = newpc;
228 frameptr = newframeptr;
229
230 /* Recompute valstackbase and localsbase */
231 valstackbase = frameptr + Stk4(frameptr);
232 localsbase = frameptr + Stk4(frameptr + 4);
233
234 switch (desttype) {
235
236 case 0x11:
237 fatal_error("String-terminator call stub at end of function call.");
238 break;
239
240 case 0x10:
241 /* This call stub was pushed during a string-decoding operation!
242 We have to restart it. (Note that the return value is discarded.) */
243 stream_string(pc, 0xE1, destaddr);
244 break;
245
246 case 0x12:
247 /* This call stub was pushed during a number-printing operation.
248 Restart that. (Return value discarded.) */
249 stream_num(pc, true, destaddr);
250 break;
251
252 case 0x13:
253 /* This call stub was pushed during a C-string printing operation.
254 We have to restart it. (Note that the return value is discarded.) */
255 stream_string(pc, 0xE0, destaddr);
256 break;
257
258 case 0x14:
259 /* This call stub was pushed during a Unicode printing operation.
260 We have to restart it. (Note that the return value is discarded.) */
261 stream_string(pc, 0xE2, destaddr);
262 break;
263
264 default:
265 /* We're back in the original frame, so we can store the returnvalue.
266 (If we tried to do this before resetting frameptr, a result
267 destination on the stack would go astray.) */
268 store_operand(desttype, destaddr, returnvalue);
269 break;
270 }
271 }
272
pop_callstub_string(int * bitnum)273 uint Glulx::pop_callstub_string(int *bitnum) {
274 uint desttype, destaddr, newpc;
275
276 if (stackptr < 16)
277 fatal_error("Stack underflow in callstub.");
278 stackptr -= 16;
279
280 newpc = Stk4(stackptr + 8);
281 destaddr = Stk4(stackptr + 4);
282 desttype = Stk4(stackptr + 0);
283
284 pc = newpc;
285
286 if (desttype == 0x11) {
287 return 0;
288 }
289 if (desttype == 0x10) {
290 *bitnum = destaddr;
291 return pc;
292 }
293
294 fatal_error("Function-terminator call stub at end of string.");
295 return 0;
296 }
297
298 } // End of namespace Glulx
299 } // End of namespace Glk
300