X-Git-Url: https://git.stderr.nl/gitweb?p=projects%2Fchimara%2Fchimara.git;a=blobdiff_plain;f=interpreters%2Fbocfel%2Fobjects.c;fp=interpreters%2Fbocfel%2Fobjects.c;h=ae68839c51f6ec74e6d24b5b9f8bbb311084cffc;hp=0000000000000000000000000000000000000000;hb=aa30979369091c96bca34499c28cb01bc16efb1d;hpb=61180dab8f5c29f5a29b83fcb7d62942f7a741d1 diff --git a/interpreters/bocfel/objects.c b/interpreters/bocfel/objects.c new file mode 100644 index 0000000..ae68839 --- /dev/null +++ b/interpreters/bocfel/objects.c @@ -0,0 +1,436 @@ +/*- + * Copyright 2009-2012 Chris Spiegel. + * + * This file is part of Bocfel. + * + * Bocfel is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version + * 2 or 3, as published by the Free Software Foundation. + * + * Bocfel is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Bocfel. If not, see . + */ + +#include +#include + +#include "objects.h" +#include "branch.h" +#include "memory.h" +#include "process.h" +#include "screen.h" +#include "util.h" +#include "zterp.h" + +static uint16_t OBJECT(uint16_t n) +{ + /* Use 32-bit arithmetic to detect 16-bit overflow. */ + uint32_t base = header.objects, obj = n, addr; + int objsize; + + if(zversion <= 3) + { + ZASSERT(n <= 255, "illegal object %u referenced", (unsigned)n); + addr = base + (31 * 2) + (9 * (obj - 1)); + objsize = 9; + } + else + { + addr = base + (63 * 2) + (14 * (obj - 1)); + objsize = 14; + } + + ZASSERT(addr + objsize < header.static_start, "object %u out of range", (unsigned)n); + + return addr; +} + +#define OFFSET_PARENT (zversion <= 3 ? 4 : 6) +#define OFFSET_SIBLING (zversion <= 3 ? 5 : 8) +#define OFFSET_CHILD (zversion <= 3 ? 6 : 10) +#define OFFSET_PROP (zversion <= 3 ? 7 : 12) + +#define PARENT(object) RELATION(object, OFFSET_PARENT) +#define SIBLING(object) RELATION(object, OFFSET_SIBLING) +#define CHILD(object) RELATION(object, OFFSET_CHILD) + +#define SET_PARENT(obj1, obj2) SET_OBJECT(obj1, obj2, OFFSET_PARENT) +#define SET_SIBLING(obj1, obj2) SET_OBJECT(obj1, obj2, OFFSET_SIBLING) +#define SET_CHILD(obj1, obj2) SET_OBJECT(obj1, obj2, OFFSET_CHILD) + +static uint16_t PROPADDR(uint16_t n) +{ + return WORD(OBJECT(n) + OFFSET_PROP); +} + +static uint16_t RELATION(uint16_t object, int offset) +{ + return zversion <= 3 ? BYTE(OBJECT(object) + offset) : WORD(OBJECT(object) + offset); +} + +/* + * the 32 attribute flags parent sibling child properties + * ---32 bits in 4 bytes--- ---3 bytes------------------ ---2 bytes-- + * + * the 48 attribute flags parent sibling child properties + * ---48 bits in 6 bytes--- ---3 words, i.e. 6 bytes---- ---2 bytes-- + */ +static void SET_OBJECT(uint16_t obj1, uint16_t obj2, int offset) +{ + if(zversion <= 3) STORE_BYTE(OBJECT(obj1) + offset, obj2); + else STORE_WORD(OBJECT(obj1) + offset, obj2); +} + +static void remove_obj(uint16_t object) +{ + uint16_t parent = PARENT(object); + + if(parent != 0) + { + uint16_t child = CHILD(parent); + + /* Direct child */ + if(child == object) + { + /* parent->child = parent->child->sibling */ + SET_CHILD(parent, SIBLING(child)); + } + else + { + while(SIBLING(child) != object) + { + /* child = child->sibling */ + child = SIBLING(child); + } + + /* Now the sibling of child is the object to remove. */ + + /* child->sibling = child->sibling->sibling */ + SET_SIBLING(child, SIBLING(SIBLING(child))); + } + + /* object->parent = 0 */ + SET_PARENT(object, 0); + + /* object->sibling = 0 */ + SET_SIBLING(object, 0); + } +} + +static uint16_t property_length(uint16_t propaddr) +{ + uint16_t length; + /* The address is to the data; the size byte is right before. */ + uint8_t byte = user_byte(propaddr - 1); + + if(zversion <= 3) + { + length = (byte >> 5) + 1; + } + else + { + if(byte & 0x80) + { + length = byte & 0x3f; + if(length == 0) length = 64; + } + else + { + length = (byte & 0x40) ? 2 : 1; + } + } + + return length; +} + +static uint8_t PROPERTY(uint16_t addr) +{ + uint8_t propnum; + + if(zversion <= 3) + { + propnum = user_byte(addr - 1) & 0x1f; + } + else + { + if(user_byte(addr - 1) & 0x80) propnum = user_byte(addr - 2) & 0x3f; + else propnum = user_byte(addr - 1) & 0x3f; + } + + return propnum; +} + +static uint16_t advance_prop_addr(uint16_t propaddr) +{ + uint8_t size; + + size = user_byte(propaddr++); + + if(size == 0) return 0; + + if(zversion >= 4 && (size & 0x80)) propaddr++; + + return propaddr; +} + +static uint16_t first_property(uint16_t object) +{ + uint16_t propaddr = PROPADDR(object); + + propaddr += (2 * user_byte(propaddr)) + 1; + + return advance_prop_addr(propaddr); +} + +static uint16_t next_property(uint16_t propaddr) +{ + propaddr += property_length(propaddr); + + return advance_prop_addr(propaddr); +} + +#define FOR_EACH_PROPERTY(object, addr) for(uint16_t addr = first_property(object); addr != 0; addr = next_property(addr)) + +static int find_prop(uint16_t object, uint16_t property, uint16_t *propaddr, uint16_t *length) +{ + FOR_EACH_PROPERTY(object, addr) + { + if(PROPERTY(addr) == property) + { + *propaddr = addr; + *length = property_length(addr); + return 1; + } + } + + return 0; +} + +static void check_attr(uint16_t attr) +{ + ZASSERT(attr <= (zversion <= 3 ? 31 : 47), "invalid attribute: %u", (unsigned)attr); +} + +static int is_zero(int is_store, int is_jump) +{ + if(zargs[0] == 0) + { + if(is_store) store(0); + if(is_jump) branch_if(0); + + return 1; + } + + return 0; +} + +#define check_zero(store, jump) do { if(is_zero(store, jump)) return; } while(0) + +/* Attributes are stored at the very beginning of an object, so the + * address OBJECT() returns refers directly to the attributes. The + * leftmost bit is attribute 0. Thus these attribute functions need to + * find out first which byte of the attributes to look at; this is done + * by dividing by 8. Attributes 0-7 will be in byte 0, 8-15 in byte 1, + * and so on. Then the particular bit is found. Attributes 0..7 are + * bits 7..0, attributes 8..15 are 7..0, and so on. Taking the + * remainder of the attribute divided by 8 gives the bit position, + * counting from the left, of the attribute. + */ +#define ATTR_BIT(num) (0x80U >> ((num) % 8)) +void ztest_attr(void) +{ + check_zero(0, 1); + check_attr(zargs[1]); + + uint16_t addr = OBJECT(zargs[0]) + (zargs[1] / 8); + + branch_if(BYTE(addr) & ATTR_BIT(zargs[1])); +} + +void zset_attr(void) +{ + check_zero(0, 0); + check_attr(zargs[1]); + + uint16_t addr = OBJECT(zargs[0]) + (zargs[1] / 8); + + STORE_BYTE(addr, BYTE(addr) | ATTR_BIT(zargs[1])); +} + +void zclear_attr(void) +{ + check_zero(0, 0); + check_attr(zargs[1]); + + uint16_t addr = OBJECT(zargs[0]) + (zargs[1] / 8); + + STORE_BYTE(addr, BYTE(addr) & ~ATTR_BIT(zargs[1])); +} +#undef ATTR_BIT + +void zremove_obj(void) +{ + check_zero(0, 0); + + remove_obj(zargs[0]); +} + +void zinsert_obj(void) +{ + check_zero(0, 0); + + remove_obj(zargs[0]); + + SET_SIBLING(zargs[0], CHILD(zargs[1])); + SET_CHILD(zargs[1], zargs[0]); + SET_PARENT(zargs[0], zargs[1]); +} + +void zget_sibling(void) +{ + check_zero(1, 1); + + uint16_t sibling = SIBLING(zargs[0]); + + store(sibling); + branch_if(sibling != 0); +} + +void zget_child(void) +{ + check_zero(1, 1); + + uint16_t child = CHILD(zargs[0]); + + store(child); + branch_if(child != 0); +} + +void zget_parent(void) +{ + check_zero(1, 0); + + store(PARENT(zargs[0])); +} + +void zput_prop(void) +{ + check_zero(0, 0); + + uint16_t propaddr, length; + int found; + + found = find_prop(zargs[0], zargs[1], &propaddr, &length); + + ZASSERT(found, "broken story: no prop"); + ZASSERT(length == 1 || length == 2, "broken story: property too long: %u", (unsigned)length); + + if(length == 1) user_store_byte(propaddr, zargs[2] & 0xff); + else user_store_word(propaddr, zargs[2]); +} + +void zget_prop(void) +{ + check_zero(1, 0); + + uint16_t propaddr, length; + + if(find_prop(zargs[0], zargs[1], &propaddr, &length)) + { + if (length == 1) store(user_byte(propaddr)); + else if(length == 2) store(user_word(propaddr)); + + /* If the length is > 2, the story file is misbehaving. At least + * Christminster does this, and Frotz and Nitfol allow it, reading a + * word, so do that here. + */ + else store(user_word(propaddr)); + } + else + { + uint32_t i; + + ZASSERT(zargs[1] < (zversion <= 3 ? 32 : 64), "invalid property: %u", (unsigned)zargs[1]); + + i = header.objects + (2 * (zargs[1] - 1)); + store(WORD(i)); + } +} + +void zget_prop_len(void) +{ + /* Z-spec 1.1 says @get_prop_len 0 must yield 0. */ + if(zargs[0] == 0) store(0); + else store(property_length(zargs[0])); +} + +void zget_prop_addr(void) +{ + check_zero(1, 0); + + uint16_t propaddr, length; + + if(find_prop(zargs[0], zargs[1], &propaddr, &length)) store(propaddr); + else store(0); +} + +void zget_next_prop(void) +{ + check_zero(1, 0); + + uint16_t object = zargs[0], property = zargs[1]; + int next = 0; + int found_prop = 0; + + FOR_EACH_PROPERTY(object, propaddr) + { + uint8_t propnum = PROPERTY(propaddr); + + if(property == 0 || next) + { + found_prop = propnum; + break; + } + + if(propnum == property) next = 1; + } + + store(found_prop); +} + +void zjin(void) +{ + /* @jin 0 0 is not defined, since @jin requires an object (§15) and + * object 0 is not actually an object (§12.3). However, many + * interpreters yield a true value for this, and Torbjorn Andersson’s + * strictz tester expects it to be true, so go with the flow. + */ + if(zargs[0] == 0 && zargs[1] == 0) + { + branch_if(1); + return; + } + + check_zero(0, 1); + + branch_if(PARENT(zargs[0]) == zargs[1]); +} + +void print_object(uint16_t obj, void (*outc)(uint8_t)) +{ + if(obj == 0) return; + + print_handler(PROPADDR(obj) + 1, outc); +} + +void zprint_obj(void) +{ + check_zero(0, 0); + + print_object(zargs[0], NULL); +}