1 /* object.c - Object manipulation opcodes
2 * Copyright (c) 1995-1997 Stefan Jokisch
4 * This file is part of Frotz.
6 * Frotz is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Frotz is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 #define MAX_OBJECT 2000
28 #define O1_PROPERTY_OFFSET 7
34 #define O4_PROPERTY_OFFSET 12
40 * Calculate the address of an object.
44 static zword object_address (zword obj)
46 /* Check object number */
48 if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
49 print_string("@Attempt to address illegal object ");
51 print_string(". This is normally fatal.");
53 runtime_error (ERR_ILL_OBJ);
56 /* Return object address */
59 return h_objects + ((obj - 1) * O1_SIZE + 62);
61 return h_objects + ((obj - 1) * O4_SIZE + 126);
68 * Return the address of the given object's name.
72 zword object_name (zword object)
77 obj_addr = object_address (object);
79 /* The object name address is found at the start of the properties */
82 obj_addr += O1_PROPERTY_OFFSET;
84 obj_addr += O4_PROPERTY_OFFSET;
86 LOW_WORD (obj_addr, name_addr)
95 * Calculate the start address of the property list associated with
100 static zword first_property (zword obj)
105 /* Fetch address of object name */
107 prop_addr = object_name (obj);
109 /* Get length of object name */
111 LOW_BYTE (prop_addr, size)
113 /* Add name length to pointer */
115 return prop_addr + 1 + 2 * size;
117 }/* first_property */
122 * Calculate the address of the next property in a property list.
126 static zword next_property (zword prop_addr)
130 /* Load the current property id */
132 LOW_BYTE (prop_addr, value)
135 /* Calculate the length of this property */
139 else if (!(value & 0x80))
143 LOW_BYTE (prop_addr, value)
146 if (value == 0) value = 64; /* demanded by Spec 1.0 */
150 /* Add property length to current property pointer */
152 return prop_addr + value + 1;
159 * Unlink an object from its parent and siblings.
163 static void unlink_object (zword object)
170 runtime_error (ERR_REMOVE_OBJECT_0);
174 obj_addr = object_address (object);
176 if (h_version <= V3) {
179 zbyte younger_sibling;
183 /* Get parent of object, and return if no parent */
185 obj_addr += O1_PARENT;
186 LOW_BYTE (obj_addr, parent)
190 /* Get (older) sibling of object and set both parent and sibling
193 SET_BYTE (obj_addr, zero)
194 obj_addr += O1_SIBLING - O1_PARENT;
195 LOW_BYTE (obj_addr, older_sibling)
196 SET_BYTE (obj_addr, zero)
198 /* Get first child of parent (the youngest sibling of the object) */
200 parent_addr = object_address (parent) + O1_CHILD;
201 LOW_BYTE (parent_addr, younger_sibling)
203 /* Remove object from the list of siblings */
205 if (younger_sibling == object)
206 SET_BYTE (parent_addr, older_sibling)
209 sibling_addr = object_address (younger_sibling) + O1_SIBLING;
210 LOW_BYTE (sibling_addr, younger_sibling)
211 } while (younger_sibling != object);
212 SET_BYTE (sibling_addr, older_sibling)
218 zword younger_sibling;
222 /* Get parent of object, and return if no parent */
224 obj_addr += O4_PARENT;
225 LOW_WORD (obj_addr, parent)
229 /* Get (older) sibling of object and set both parent and sibling
232 SET_WORD (obj_addr, zero)
233 obj_addr += O4_SIBLING - O4_PARENT;
234 LOW_WORD (obj_addr, older_sibling)
235 SET_WORD (obj_addr, zero)
237 /* Get first child of parent (the youngest sibling of the object) */
239 parent_addr = object_address (parent) + O4_CHILD;
240 LOW_WORD (parent_addr, younger_sibling)
242 /* Remove object from the list of siblings */
244 if (younger_sibling == object)
245 SET_WORD (parent_addr, older_sibling)
248 sibling_addr = object_address (younger_sibling) + O4_SIBLING;
249 LOW_WORD (sibling_addr, younger_sibling)
250 } while (younger_sibling != object);
251 SET_WORD (sibling_addr, older_sibling)
259 * z_clear_attr, clear an object attribute.
262 * zargs[1] = number of attribute to be cleared
266 void z_clear_attr (void)
271 if (story_id == SHERLOCK)
275 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
276 runtime_error (ERR_ILL_ATTR);
278 /* If we are monitoring attribute assignment display a short note */
280 if (option_attribute_assignment) {
282 print_string ("@clear_attr ");
283 print_object (zargs[0]);
285 print_num (zargs[1]);
290 runtime_error (ERR_CLEAR_ATTR_0);
294 /* Get attribute address */
296 obj_addr = object_address (zargs[0]) + zargs[1] / 8;
298 /* Clear attribute bit */
300 LOW_BYTE (obj_addr, value)
301 value &= ~(0x80 >> (zargs[1] & 7));
302 SET_BYTE (obj_addr, value)
307 * z_jin, branch if the first object is inside the second.
309 * zargs[0] = first object
310 * zargs[1] = second object
318 /* If we are monitoring object locating display a short note */
320 if (option_object_locating) {
322 print_string ("@jin ");
323 print_object (zargs[0]);
325 print_object (zargs[1]);
330 runtime_error (ERR_JIN_0);
331 branch (0 == zargs[1]);
335 obj_addr = object_address (zargs[0]);
337 if (h_version <= V3) {
341 /* Get parent id from object */
343 obj_addr += O1_PARENT;
344 LOW_BYTE (obj_addr, parent)
346 /* Branch if the parent is obj2 */
348 branch (parent == zargs[1]);
354 /* Get parent id from object */
356 obj_addr += O4_PARENT;
357 LOW_WORD (obj_addr, parent)
359 /* Branch if the parent is obj2 */
361 branch (parent == zargs[1]);
368 * z_get_child, store the child of an object.
374 void z_get_child (void)
378 /* If we are monitoring object locating display a short note */
380 if (option_object_locating) {
382 print_string ("@get_child ");
383 print_object (zargs[0]);
388 runtime_error (ERR_GET_CHILD_0);
394 obj_addr = object_address (zargs[0]);
396 if (h_version <= V3) {
400 /* Get child id from object */
402 obj_addr += O1_CHILD;
403 LOW_BYTE (obj_addr, child)
405 /* Store child id and branch */
414 /* Get child id from object */
416 obj_addr += O4_CHILD;
417 LOW_WORD (obj_addr, child)
419 /* Store child id and branch */
429 * z_get_next_prop, store the number of the first or next property.
432 * zargs[1] = address of current property (0 gets the first property)
436 void z_get_next_prop (void)
443 runtime_error (ERR_GET_NEXT_PROP_0);
448 /* Property id is in bottom five (six) bits */
450 mask = (h_version <= V3) ? 0x1f : 0x3f;
452 /* Load address of first property */
454 prop_addr = first_property (zargs[0]);
458 /* Scan down the property list */
461 LOW_BYTE (prop_addr, value)
462 prop_addr = next_property (prop_addr);
463 } while ((value & mask) > zargs[1]);
465 /* Exit if the property does not exist */
467 if ((value & mask) != zargs[1])
468 runtime_error (ERR_NO_PROP);
472 /* Return the property id */
474 LOW_BYTE (prop_addr, value)
475 store ((zword) (value & mask));
477 }/* z_get_next_prop */
480 * z_get_parent, store the parent of an object.
486 void z_get_parent (void)
490 /* If we are monitoring object locating display a short note */
492 if (option_object_locating) {
494 print_string ("@get_parent ");
495 print_object (zargs[0]);
500 runtime_error (ERR_GET_PARENT_0);
505 obj_addr = object_address (zargs[0]);
507 if (h_version <= V3) {
511 /* Get parent id from object */
513 obj_addr += O1_PARENT;
514 LOW_BYTE (obj_addr, parent)
524 /* Get parent id from object */
526 obj_addr += O4_PARENT;
527 LOW_WORD (obj_addr, parent)
538 * z_get_prop, store the value of an object property.
541 * zargs[1] = number of property to be examined
545 void z_get_prop (void)
554 runtime_error (ERR_GET_PROP_0);
559 /* Property id is in bottom five (six) bits */
561 mask = (h_version <= V3) ? 0x1f : 0x3f;
563 /* Load address of first property */
565 prop_addr = first_property (zargs[0]);
567 /* Scan down the property list */
570 LOW_BYTE (prop_addr, value)
571 if ((value & mask) <= zargs[1])
573 prop_addr = next_property (prop_addr);
576 if ((value & mask) == zargs[1]) { /* property found */
578 /* Load property (byte or word sized) */
582 if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
584 LOW_BYTE (prop_addr, bprop_val)
585 wprop_val = bprop_val;
587 } else LOW_WORD (prop_addr, wprop_val)
589 } else { /* property not found */
591 /* Load default value */
593 prop_addr = h_objects + 2 * (zargs[1] - 1);
594 LOW_WORD (prop_addr, wprop_val)
598 /* Store the property value */
605 * z_get_prop_addr, store the address of an object property.
608 * zargs[1] = number of property to be examined
612 void z_get_prop_addr (void)
619 runtime_error (ERR_GET_PROP_ADDR_0);
624 if (story_id == BEYOND_ZORK)
625 if (zargs[0] > MAX_OBJECT)
626 { store (0); return; }
628 /* Property id is in bottom five (six) bits */
630 mask = (h_version <= V3) ? 0x1f : 0x3f;
632 /* Load address of first property */
634 prop_addr = first_property (zargs[0]);
636 /* Scan down the property list */
639 LOW_BYTE (prop_addr, value)
640 if ((value & mask) <= zargs[1])
642 prop_addr = next_property (prop_addr);
645 /* Calculate the property address or return zero */
647 if ((value & mask) == zargs[1]) {
649 if (h_version >= V4 && (value & 0x80))
651 store ((zword) (prop_addr + 1));
655 }/* z_get_prop_addr */
658 * z_get_prop_len, store the length of an object property.
660 * zargs[0] = address of property to be examined
664 void z_get_prop_len (void)
669 /* Back up the property pointer to the property id */
672 LOW_BYTE (addr, value)
674 /* Calculate length of property */
677 value = (value >> 5) + 1;
678 else if (!(value & 0x80))
679 value = (value >> 6) + 1;
684 if (value == 0) value = 64; /* demanded by Spec 1.0 */
688 /* Store length of property */
692 }/* z_get_prop_len */
695 * z_get_sibling, store the sibling of an object.
701 void z_get_sibling (void)
706 runtime_error (ERR_GET_SIBLING_0);
712 obj_addr = object_address (zargs[0]);
714 if (h_version <= V3) {
718 /* Get sibling id from object */
720 obj_addr += O1_SIBLING;
721 LOW_BYTE (obj_addr, sibling)
723 /* Store sibling and branch */
732 /* Get sibling id from object */
734 obj_addr += O4_SIBLING;
735 LOW_WORD (obj_addr, sibling)
737 /* Store sibling and branch */
747 * z_insert_obj, make an object the first child of another object.
749 * zargs[0] = object to be moved
750 * zargs[1] = destination object
754 void z_insert_obj (void)
756 zword obj1 = zargs[0];
757 zword obj2 = zargs[1];
761 /* If we are monitoring object movements display a short note */
763 if (option_object_movement) {
765 print_string ("@move_obj ");
773 runtime_error (ERR_MOVE_OBJECT_0);
778 runtime_error (ERR_MOVE_OBJECT_TO_0);
782 /* Get addresses of both objects */
784 obj1_addr = object_address (obj1);
785 obj2_addr = object_address (obj2);
787 /* Remove object 1 from current parent */
789 unlink_object (obj1);
791 /* Make object 1 first child of object 2 */
793 if (h_version <= V3) {
797 obj1_addr += O1_PARENT;
798 SET_BYTE (obj1_addr, obj2)
799 obj2_addr += O1_CHILD;
800 LOW_BYTE (obj2_addr, child)
801 SET_BYTE (obj2_addr, obj1)
802 obj1_addr += O1_SIBLING - O1_PARENT;
803 SET_BYTE (obj1_addr, child)
809 obj1_addr += O4_PARENT;
810 SET_WORD (obj1_addr, obj2)
811 obj2_addr += O4_CHILD;
812 LOW_WORD (obj2_addr, child)
813 SET_WORD (obj2_addr, obj1)
814 obj1_addr += O4_SIBLING - O4_PARENT;
815 SET_WORD (obj1_addr, child)
822 * z_put_prop, set the value of an object property.
825 * zargs[1] = number of property to set
826 * zargs[2] = value to set property to
830 void z_put_prop (void)
837 runtime_error (ERR_PUT_PROP_0);
841 /* Property id is in bottom five or six bits */
843 mask = (h_version <= V3) ? 0x1f : 0x3f;
845 /* Load address of first property */
847 prop_addr = first_property (zargs[0]);
849 /* Scan down the property list */
852 LOW_BYTE (prop_addr, value)
853 if ((value & mask) <= zargs[1])
855 prop_addr = next_property (prop_addr);
858 /* Exit if the property does not exist */
860 if ((value & mask) != zargs[1])
861 runtime_error (ERR_NO_PROP);
863 /* Store the new property value (byte or word sized) */
867 if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
869 SET_BYTE (prop_addr, v)
872 SET_WORD (prop_addr, v)
878 * z_remove_obj, unlink an object from its parent and siblings.
884 void z_remove_obj (void)
887 /* If we are monitoring object movements display a short note */
889 if (option_object_movement) {
891 print_string ("@remove_obj ");
892 print_object (zargs[0]);
896 /* Call unlink_object to do the job */
898 unlink_object (zargs[0]);
903 * z_set_attr, set an object attribute.
906 * zargs[1] = number of attribute to set
910 void z_set_attr (void)
915 if (story_id == SHERLOCK)
919 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
920 runtime_error (ERR_ILL_ATTR);
922 /* If we are monitoring attribute assignment display a short note */
924 if (option_attribute_assignment) {
926 print_string ("@set_attr ");
927 print_object (zargs[0]);
929 print_num (zargs[1]);
934 runtime_error (ERR_SET_ATTR_0);
938 /* Get attribute address */
940 obj_addr = object_address (zargs[0]) + zargs[1] / 8;
942 /* Load attribute byte */
944 LOW_BYTE (obj_addr, value)
946 /* Set attribute bit */
948 value |= 0x80 >> (zargs[1] & 7);
950 /* Store attribute byte */
952 SET_BYTE (obj_addr, value)
957 * z_test_attr, branch if an object attribute is set.
960 * zargs[1] = number of attribute to test
964 void z_test_attr (void)
969 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
970 runtime_error (ERR_ILL_ATTR);
972 /* If we are monitoring attribute testing display a short note */
974 if (option_attribute_testing) {
976 print_string ("@test_attr ");
977 print_object (zargs[0]);
979 print_num (zargs[1]);
984 runtime_error (ERR_TEST_ATTR_0);
989 /* Get attribute address */
991 obj_addr = object_address (zargs[0]) + zargs[1] / 8;
993 /* Load attribute byte */
995 LOW_BYTE (obj_addr, value)
999 branch (value & (0x80 >> (zargs[1] & 7)));