Add Bocfel interpreter
[projects/chimara/chimara.git] / interpreters / bocfel / objects.c
1 /*-
2  * Copyright 2009-2012 Chris Spiegel.
3  *
4  * This file is part of Bocfel.
5  *
6  * Bocfel is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version
8  * 2 or 3, as published by the Free Software Foundation.
9  *
10  * Bocfel is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Bocfel.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stddef.h>
20 #include <stdint.h>
21
22 #include "objects.h"
23 #include "branch.h"
24 #include "memory.h"
25 #include "process.h"
26 #include "screen.h"
27 #include "util.h"
28 #include "zterp.h"
29
30 static uint16_t OBJECT(uint16_t n)
31 {
32   /* Use 32-bit arithmetic to detect 16-bit overflow. */
33   uint32_t base = header.objects, obj = n, addr;
34   int objsize;
35
36   if(zversion <= 3)
37   {
38     ZASSERT(n <= 255, "illegal object %u referenced", (unsigned)n);
39     addr = base + (31 * 2) + (9 * (obj - 1));
40     objsize = 9;
41   }
42   else
43   {
44     addr = base + (63 * 2) + (14 * (obj - 1));
45     objsize = 14;
46   }
47
48   ZASSERT(addr + objsize < header.static_start, "object %u out of range", (unsigned)n);
49
50   return addr;
51 }
52
53 #define OFFSET_PARENT   (zversion <= 3 ? 4 :  6)
54 #define OFFSET_SIBLING  (zversion <= 3 ? 5 :  8)
55 #define OFFSET_CHILD    (zversion <= 3 ? 6 : 10)
56 #define OFFSET_PROP     (zversion <= 3 ? 7 : 12)
57
58 #define PARENT(object)          RELATION(object, OFFSET_PARENT)
59 #define SIBLING(object)         RELATION(object, OFFSET_SIBLING)
60 #define CHILD(object)           RELATION(object, OFFSET_CHILD)
61
62 #define SET_PARENT(obj1, obj2)  SET_OBJECT(obj1, obj2, OFFSET_PARENT)
63 #define SET_SIBLING(obj1, obj2) SET_OBJECT(obj1, obj2, OFFSET_SIBLING)
64 #define SET_CHILD(obj1, obj2)   SET_OBJECT(obj1, obj2, OFFSET_CHILD)
65
66 static uint16_t PROPADDR(uint16_t n)
67 {
68   return WORD(OBJECT(n) + OFFSET_PROP);
69 }
70
71 static uint16_t RELATION(uint16_t object, int offset)
72 {
73   return zversion <= 3 ? BYTE(OBJECT(object) + offset) : WORD(OBJECT(object) + offset);
74 }
75
76 /*
77  * the 32 attribute flags     parent     sibling     child   properties
78  * ---32 bits in 4 bytes---   ---3 bytes------------------  ---2 bytes--
79  *
80  * the 48 attribute flags     parent    sibling   child     properties
81  * ---48 bits in 6 bytes---   ---3 words, i.e. 6 bytes----  ---2 bytes--
82  */
83 static void SET_OBJECT(uint16_t obj1, uint16_t obj2, int offset)
84 {
85   if(zversion <= 3) STORE_BYTE(OBJECT(obj1) + offset, obj2);
86   else              STORE_WORD(OBJECT(obj1) + offset, obj2);
87 }
88
89 static void remove_obj(uint16_t object)
90 {
91   uint16_t parent = PARENT(object);
92
93   if(parent != 0)
94   {
95     uint16_t child = CHILD(parent);
96
97     /* Direct child */
98     if(child == object)
99     {
100       /* parent->child = parent->child->sibling */
101       SET_CHILD(parent, SIBLING(child));
102     }
103     else
104     {
105       while(SIBLING(child) != object)
106       {
107         /* child = child->sibling */
108         child = SIBLING(child);
109       }
110
111       /* Now the sibling of child is the object to remove. */
112
113       /* child->sibling = child->sibling->sibling */
114       SET_SIBLING(child, SIBLING(SIBLING(child)));
115     }
116
117     /* object->parent = 0 */
118     SET_PARENT(object, 0);
119
120     /* object->sibling = 0 */
121     SET_SIBLING(object, 0);
122   }
123 }
124
125 static uint16_t property_length(uint16_t propaddr)
126 {
127   uint16_t length;
128   /* The address is to the data; the size byte is right before. */
129   uint8_t byte = user_byte(propaddr - 1);
130
131   if(zversion <= 3)
132   {
133     length = (byte >> 5) + 1;
134   }
135   else
136   {
137     if(byte & 0x80)
138     {
139       length = byte & 0x3f;
140       if(length == 0) length = 64;
141     }
142     else
143     {
144       length = (byte & 0x40) ? 2 : 1;
145     }
146   }
147
148   return length;
149 }
150
151 static uint8_t PROPERTY(uint16_t addr)
152 {
153   uint8_t propnum;
154
155   if(zversion <= 3)
156   {
157     propnum = user_byte(addr - 1) & 0x1f;
158   }
159   else
160   {
161     if(user_byte(addr - 1) & 0x80) propnum = user_byte(addr - 2) & 0x3f;
162     else                           propnum = user_byte(addr - 1) & 0x3f;
163   }
164
165   return propnum;
166 }
167
168 static uint16_t advance_prop_addr(uint16_t propaddr)
169 {
170   uint8_t size;
171
172   size = user_byte(propaddr++);
173
174   if(size == 0) return 0;
175
176   if(zversion >= 4 && (size & 0x80)) propaddr++;
177
178   return propaddr;
179 }
180
181 static uint16_t first_property(uint16_t object)
182 {
183   uint16_t propaddr = PROPADDR(object);
184
185   propaddr += (2 * user_byte(propaddr)) + 1;
186
187   return advance_prop_addr(propaddr);
188 }
189
190 static uint16_t next_property(uint16_t propaddr)
191 {
192   propaddr += property_length(propaddr);
193
194   return advance_prop_addr(propaddr);
195 }
196
197 #define FOR_EACH_PROPERTY(object, addr) for(uint16_t addr = first_property(object); addr != 0; addr = next_property(addr))
198
199 static int find_prop(uint16_t object, uint16_t property, uint16_t *propaddr, uint16_t *length)
200 {
201   FOR_EACH_PROPERTY(object, addr)
202   {
203     if(PROPERTY(addr) == property)
204     {
205       *propaddr = addr;
206       *length = property_length(addr);
207       return 1;
208     }
209   }
210
211   return 0;
212 }
213
214 static void check_attr(uint16_t attr)
215 {
216   ZASSERT(attr <= (zversion <= 3 ? 31 : 47), "invalid attribute: %u", (unsigned)attr);
217 }
218
219 static int is_zero(int is_store, int is_jump)
220 {
221   if(zargs[0] == 0)
222   {
223     if(is_store) store(0);
224     if(is_jump)  branch_if(0);
225
226     return 1;
227   }
228
229   return 0;
230 }
231
232 #define check_zero(store, jump) do { if(is_zero(store, jump)) return; } while(0)
233
234 /* Attributes are stored at the very beginning of an object, so the
235  * address OBJECT() returns refers directly to the attributes.  The
236  * leftmost bit is attribute 0.  Thus these attribute functions need to
237  * find out first which byte of the attributes to look at; this is done
238  * by dividing by 8.  Attributes 0-7 will be in byte 0, 8-15 in byte 1,
239  * and so on.  Then the particular bit is found.  Attributes 0..7 are
240  * bits 7..0, attributes 8..15 are 7..0, and so on.  Taking the
241  * remainder of the attribute divided by 8 gives the bit position,
242  * counting from the left, of the attribute.
243  */
244 #define ATTR_BIT(num)           (0x80U >> ((num) % 8))
245 void ztest_attr(void)
246 {
247   check_zero(0, 1);
248   check_attr(zargs[1]);
249
250   uint16_t addr = OBJECT(zargs[0]) + (zargs[1] / 8);
251
252   branch_if(BYTE(addr) & ATTR_BIT(zargs[1]));
253 }
254
255 void zset_attr(void)
256 {
257   check_zero(0, 0);
258   check_attr(zargs[1]);
259
260   uint16_t addr = OBJECT(zargs[0]) + (zargs[1] / 8);
261
262   STORE_BYTE(addr, BYTE(addr) | ATTR_BIT(zargs[1]));
263 }
264
265 void zclear_attr(void)
266 {
267   check_zero(0, 0);
268   check_attr(zargs[1]);
269
270   uint16_t addr = OBJECT(zargs[0]) + (zargs[1] / 8);
271
272   STORE_BYTE(addr, BYTE(addr) & ~ATTR_BIT(zargs[1]));
273 }
274 #undef ATTR_BIT
275
276 void zremove_obj(void)
277 {
278   check_zero(0, 0);
279
280   remove_obj(zargs[0]);
281 }
282
283 void zinsert_obj(void)
284 {
285   check_zero(0, 0);
286
287   remove_obj(zargs[0]);
288
289   SET_SIBLING(zargs[0], CHILD(zargs[1]));
290   SET_CHILD(zargs[1], zargs[0]);
291   SET_PARENT(zargs[0], zargs[1]);
292 }
293
294 void zget_sibling(void)
295 {
296   check_zero(1, 1);
297
298   uint16_t sibling = SIBLING(zargs[0]);
299
300   store(sibling);
301   branch_if(sibling != 0);
302 }
303
304 void zget_child(void)
305 {
306   check_zero(1, 1);
307
308   uint16_t child = CHILD(zargs[0]);
309
310   store(child);
311   branch_if(child != 0);
312 }
313
314 void zget_parent(void)
315 {
316   check_zero(1, 0);
317
318   store(PARENT(zargs[0]));
319 }
320
321 void zput_prop(void)
322 {
323   check_zero(0, 0);
324
325   uint16_t propaddr, length;
326   int found;
327
328   found = find_prop(zargs[0], zargs[1], &propaddr, &length);
329
330   ZASSERT(found, "broken story: no prop");
331   ZASSERT(length == 1 || length == 2, "broken story: property too long: %u", (unsigned)length);
332
333   if(length == 1) user_store_byte(propaddr, zargs[2] & 0xff);
334   else            user_store_word(propaddr, zargs[2]);
335 }
336
337 void zget_prop(void)
338 {
339   check_zero(1, 0);
340
341   uint16_t propaddr, length;
342
343   if(find_prop(zargs[0], zargs[1], &propaddr, &length))
344   {
345     if     (length == 1) store(user_byte(propaddr));
346     else if(length == 2) store(user_word(propaddr));
347
348     /* If the length is > 2, the story file is misbehaving.  At least
349      * Christminster does this, and Frotz and Nitfol allow it, reading a
350      * word, so do that here.
351      */
352     else                 store(user_word(propaddr));
353   }
354   else
355   {
356     uint32_t i;
357
358     ZASSERT(zargs[1] < (zversion <= 3 ? 32 : 64), "invalid property: %u", (unsigned)zargs[1]);
359
360     i = header.objects + (2 * (zargs[1] - 1));
361     store(WORD(i));
362   }
363 }
364
365 void zget_prop_len(void)
366 {
367   /* Z-spec 1.1 says @get_prop_len 0 must yield 0. */
368   if(zargs[0] == 0) store(0);
369   else              store(property_length(zargs[0]));
370 }
371
372 void zget_prop_addr(void)
373 {
374   check_zero(1, 0);
375
376   uint16_t propaddr, length;
377
378   if(find_prop(zargs[0], zargs[1], &propaddr, &length)) store(propaddr);
379   else store(0);
380 }
381
382 void zget_next_prop(void)
383 {
384   check_zero(1, 0);
385
386   uint16_t object = zargs[0], property = zargs[1];
387   int next = 0;
388   int found_prop = 0;
389
390   FOR_EACH_PROPERTY(object, propaddr)
391   {
392     uint8_t propnum = PROPERTY(propaddr);
393
394     if(property == 0 || next)
395     {
396       found_prop = propnum;
397       break;
398     }
399
400     if(propnum == property) next = 1;
401   }
402
403   store(found_prop);
404 }
405
406 void zjin(void)
407 {
408   /* @jin 0 0 is not defined, since @jin requires an object (§15) and
409    * object 0 is not actually an object (§12.3).  However, many
410    * interpreters yield a true value for this, and Torbjorn Andersson’s
411    * strictz tester expects it to be true, so go with the flow.
412    */
413   if(zargs[0] == 0 && zargs[1] == 0)
414   {
415     branch_if(1);
416     return;
417   }
418
419   check_zero(0, 1);
420
421   branch_if(PARENT(zargs[0]) == zargs[1]);
422 }
423
424 void print_object(uint16_t obj, void (*outc)(uint8_t))
425 {
426   if(obj == 0) return;
427
428   print_handler(PROPADDR(obj) + 1, outc);
429 }
430
431 void zprint_obj(void)
432 {
433   check_zero(0, 0);
434
435   print_object(zargs[0], NULL);
436 }