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