Bug fixes
[rodin/chimara.git] / interpreters / glulxe / accel.c
1 /* accel.c: Glulxe code for accelerated functions
2     Designed by Andrew Plotkin <erkyrath@eblong.com>
3     http://eblong.com/zarf/glulx/index.html
4 */
5
6 #include "glk.h"
7 #include "glulxe.h"
8
9 /* Git passes along function arguments in reverse order. To make our lives
10    more interesting. */
11 #ifdef ARGS_REVERSED
12 #define ARG(argv, argc, ix) (argv[(argc-1)-ix])
13 #else
14 #define ARG(argv, argc, ix) (argv[ix])
15 #endif
16
17 /* Any function can be called with any number of arguments. This macro
18    lets us snarf a given argument, or zero if it wasn't supplied. */
19 #define ARG_IF_GIVEN(argv, argc, ix)  ((argc > ix) ? (ARG(argv, argc, ix)) : 0)
20
21 static void accel_error(char *msg);
22 static glui32 func_1_z__region(glui32 argc, glui32 *argv);
23 static glui32 func_2_cp__tab(glui32 argc, glui32 *argv);
24 static glui32 func_3_ra__pr(glui32 argc, glui32 *argv);
25 static glui32 func_4_rl__pr(glui32 argc, glui32 *argv);
26 static glui32 func_5_oc__cl(glui32 argc, glui32 *argv);
27 static glui32 func_6_rv__pr(glui32 argc, glui32 *argv);
28 static glui32 func_7_op__pr(glui32 argc, glui32 *argv);
29
30 static int obj_in_class(glui32 obj);
31 static glui32 get_prop(glui32 obj, glui32 id);
32
33 /* Parameters, set by @accelparam. */
34 static glui32 classes_table = 0;     /* class object array */
35 static glui32 indiv_prop_start = 0;  /* first individual prop ID */
36 static glui32 class_metaclass = 0;   /* "Class" class object */
37 static glui32 object_metaclass = 0;  /* "Object" class object */
38 static glui32 routine_metaclass = 0; /* "Routine" class object */
39 static glui32 string_metaclass = 0;  /* "String" class object */
40 static glui32 self = 0;              /* address of global "self" */
41 static glui32 num_attr_bytes = 0;    /* number of attributes / 8 */
42 static glui32 cpv__start = 0;        /* array of common prop defaults */
43
44 typedef struct accelentry_struct {
45     glui32 addr;
46     acceleration_func func;
47     struct accelentry_struct *next;
48 } accelentry_t;
49
50 #define ACCEL_HASH_SIZE (511)
51
52 static accelentry_t **accelentries = NULL;
53
54 void init_accel()
55 {
56     accelentries = NULL;
57 }
58
59 acceleration_func accel_find_func(glui32 index)
60 {
61     switch (index) {
62         case 0: return NULL; /* 0 always means no acceleration */
63         case 1: return func_1_z__region;
64         case 2: return func_2_cp__tab;
65         case 3: return func_3_ra__pr;
66         case 4: return func_4_rl__pr;
67         case 5: return func_5_oc__cl;
68         case 6: return func_6_rv__pr;
69         case 7: return func_7_op__pr;
70     }
71     return NULL;
72 }
73
74 acceleration_func accel_get_func(glui32 addr)
75 {
76     int bucknum;
77     accelentry_t *ptr;
78
79     if (!accelentries)
80         return NULL;
81
82     bucknum = (addr % ACCEL_HASH_SIZE);
83     for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
84         if (ptr->addr == addr)
85             return ptr->func;
86     }
87     return NULL;
88 }
89
90 void accel_set_func(glui32 index, glui32 addr)
91 {
92     int bucknum;
93     accelentry_t *ptr;
94     int functype;
95     acceleration_func new_func = NULL;
96
97     /* Check the Glulx type identifier byte. */
98     functype = Mem1(addr);
99     if (functype != 0xC0 && functype != 0xC1) {
100         fatal_error_i("Attempt to accelerate non-function.", addr);
101     }
102
103     if (!accelentries) {
104         accelentries = (accelentry_t **)glulx_malloc(ACCEL_HASH_SIZE 
105             * sizeof(accelentry_t *));
106         if (!accelentries) 
107             fatal_error("Cannot malloc acceleration table.");
108         for (bucknum=0; bucknum<ACCEL_HASH_SIZE; bucknum++)
109             accelentries[bucknum] = NULL;
110     }
111
112     new_func = accel_find_func(index);
113
114     bucknum = (addr % ACCEL_HASH_SIZE);
115     for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
116         if (ptr->addr == addr)
117             break;
118     }
119     if (!ptr) {
120         if (!new_func) {
121             return; /* no need for a new entry */
122         }
123         ptr = (accelentry_t *)glulx_malloc(sizeof(accelentry_t));
124         if (!ptr)
125             fatal_error("Cannot malloc acceleration entry.");
126         ptr->addr = addr;
127         ptr->func = NULL;
128         ptr->next = accelentries[bucknum];
129         accelentries[bucknum] = ptr;
130     }
131
132     ptr->func = new_func;
133 }
134
135 void accel_set_param(glui32 index, glui32 val)
136 {
137     switch (index) {
138         case 0: classes_table = val; break;
139         case 1: indiv_prop_start = val; break;
140         case 2: class_metaclass = val; break;
141         case 3: object_metaclass = val; break;
142         case 4: routine_metaclass = val; break;
143         case 5: string_metaclass = val; break;
144         case 6: self = val; break;
145         case 7: num_attr_bytes = val; break;
146         case 8: cpv__start = val; break;
147     }
148 }
149
150 static void accel_error(char *msg)
151 {
152     glk_put_char('\n');
153     glk_put_string(msg);
154     glk_put_char('\n');
155 }
156
157 static int obj_in_class(glui32 obj)
158 {
159     /* This checks whether obj is contained in Class, not whether
160        it is a member of Class. */
161     return (Mem4(obj + 13 + num_attr_bytes) == class_metaclass);
162 }
163
164 static glui32 get_prop(glui32 obj, glui32 id)
165 {
166     glui32 cla = 0;
167     glui32 prop;
168     glui32 call_argv[2];
169
170     if (id & 0xFFFF0000) {
171         cla = Mem4(classes_table+((id & 0xFFFF) * 4));
172         ARG(call_argv, 2, 0) = obj;
173         ARG(call_argv, 2, 1) = cla;
174         if (func_5_oc__cl(2, call_argv) == 0)
175             return 0;
176
177         id >>= 16;
178         obj = cla;
179     }
180
181     ARG(call_argv, 2, 0) = obj;
182     ARG(call_argv, 2, 1) = id;
183     prop = func_2_cp__tab(2, call_argv);
184     if (prop == 0)
185         return 0;
186
187     if (obj_in_class(obj) && (cla == 0)) {
188         if ((id < indiv_prop_start) || (id >= indiv_prop_start+8))
189             return 0;
190     }
191
192     if (Mem4(self) != obj) {
193         if (Mem1(prop + 9) & 1)
194             return 0;
195     }
196     return prop;
197 }
198
199 static glui32 func_1_z__region(glui32 argc, glui32 *argv)
200 {
201     glui32 addr;
202     glui32 tb;
203
204     if (argc < 1)
205         return 0;
206
207     addr = ARG(argv, argc, 0);
208     if (addr < 36)
209         return 0;
210     if (addr >= endmem)
211         return 0;
212
213     tb = Mem1(addr);
214     if (tb >= 0xE0) {
215         return 3;
216     }
217     if (tb >= 0xC0) {
218         return 2;
219     }
220     if (tb >= 0x70 && tb <= 0x7F && addr >= ramstart) {
221         return 1;
222     }
223     return 0;
224 }
225
226 static glui32 func_2_cp__tab(glui32 argc, glui32 *argv)
227 {
228     glui32 obj;
229     glui32 id;
230     glui32 otab, max;
231
232     obj = ARG_IF_GIVEN(argv, argc, 0);
233     id = ARG_IF_GIVEN(argv, argc, 1);
234
235     if (func_1_z__region(1, &obj) != 1) {
236         accel_error("[** Programming error: tried to find the \".\" of (something) **]");
237         return 0;
238     }
239
240     otab = Mem4(obj + 16);
241     if (!otab)
242         return 0;
243
244     max = Mem4(otab);
245     otab += 4;
246     /* @binarysearch id 2 otab 10 max 0 0 res; */
247     return binary_search(id, 2, otab, 10, max, 0, 0);
248 }
249
250 static glui32 func_3_ra__pr(glui32 argc, glui32 *argv)
251 {
252     glui32 obj;
253     glui32 id;
254     glui32 prop;
255
256     obj = ARG_IF_GIVEN(argv, argc, 0);
257     id = ARG_IF_GIVEN(argv, argc, 1);
258
259     prop = get_prop(obj, id);
260     if (prop == 0)
261         return 0;
262
263     return Mem4(prop + 4);
264 }
265
266 static glui32 func_4_rl__pr(glui32 argc, glui32 *argv)
267 {
268     glui32 obj;
269     glui32 id;
270     glui32 prop;
271
272     obj = ARG_IF_GIVEN(argv, argc, 0);
273     id = ARG_IF_GIVEN(argv, argc, 1);
274
275     prop = get_prop(obj, id);
276     if (prop == 0)
277         return 0;
278
279     return 4 * Mem2(prop + 2);
280 }
281
282 static glui32 func_5_oc__cl(glui32 argc, glui32 *argv)
283 {
284     glui32 obj;
285     glui32 cla;
286     glui32 zr, prop, inlist, inlistlen, jx;
287
288     obj = ARG_IF_GIVEN(argv, argc, 0);
289     cla = ARG_IF_GIVEN(argv, argc, 1);
290
291     zr = func_1_z__region(1, &obj);
292     if (zr == 3)
293         return (cla == string_metaclass) ? 1 : 0;
294     if (zr == 2)
295         return (cla == routine_metaclass) ? 1 : 0;
296     if (zr != 1)
297         return 0;
298
299     if (cla == class_metaclass) {
300         if (obj_in_class(obj))
301             return 1;
302         if (obj == class_metaclass)
303             return 1;
304         if (obj == string_metaclass)
305             return 1;
306         if (obj == routine_metaclass)
307             return 1;
308         if (obj == object_metaclass)
309             return 1;
310         return 0;
311     }
312     if (cla == object_metaclass) {
313         if (obj_in_class(obj))
314             return 0;
315         if (obj == class_metaclass)
316             return 0;
317         if (obj == string_metaclass)
318             return 0;
319         if (obj == routine_metaclass)
320             return 0;
321         if (obj == object_metaclass)
322             return 0;
323         return 1;
324     }
325     if ((cla == string_metaclass) || (cla == routine_metaclass))
326         return 0;
327
328     if (!obj_in_class(cla)) {
329         accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
330         return 0;
331     }
332
333     prop = get_prop(obj, 2);
334     if (prop == 0)
335        return 0;
336
337     inlist = Mem4(prop + 4);
338     if (inlist == 0)
339        return 0;
340
341     inlistlen = Mem2(prop + 2);
342     for (jx = 0; jx < inlistlen; jx++) {
343         if (Mem4(inlist + (4 * jx)) == cla)
344             return 1;
345     }
346     return 0;
347 }
348
349 static glui32 func_6_rv__pr(glui32 argc, glui32 *argv)
350 {
351     glui32 id;
352     glui32 addr;
353
354     id = ARG_IF_GIVEN(argv, argc, 1);
355
356     addr = func_3_ra__pr(argc, argv);
357
358     if (addr == 0) {
359         if ((id > 0) && (id < indiv_prop_start))
360             return Mem4(cpv__start + (4 * id));
361
362         accel_error("[** Programming error: tried to read (something) **]");
363         return 0;
364     }
365
366     return Mem4(addr);
367 }
368
369 static glui32 func_7_op__pr(glui32 argc, glui32 *argv)
370 {
371     glui32 obj;
372     glui32 id;
373     glui32 zr;
374
375     obj = ARG_IF_GIVEN(argv, argc, 0);
376     id = ARG_IF_GIVEN(argv, argc, 1);
377
378     zr = func_1_z__region(1, &obj);
379     if (zr == 3) {
380         /* print is INDIV_PROP_START+6 */
381         if (id == indiv_prop_start+6)
382             return 1;
383         /* print_to_array is INDIV_PROP_START+7 */
384         if (id == indiv_prop_start+7)
385             return 1;
386         return 0;
387     }
388     if (zr == 2) {
389         /* call is INDIV_PROP_START+5 */
390         return ((id == indiv_prop_start+5) ? 1 : 0);
391     }
392     if (zr != 1)
393         return 0;
394
395     if ((id >= indiv_prop_start) && (id < indiv_prop_start+8)) {
396         if (obj_in_class(obj))
397             return 1;
398     }
399
400     return ((func_3_ra__pr(argc, argv)) ? 1 : 0);
401 }