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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 /* zchar obj_num[10]; */
48 /* Check object number */
50 if (obj
> ((h_version
<= V3
) ? 255 : MAX_OBJECT
)) {
51 print_string("@Attempt to address illegal object ");
53 print_string(". This is normally fatal.");
55 runtime_error (ERR_ILL_OBJ
);
58 /* Return object address */
61 return h_objects
+ ((obj
- 1) * O1_SIZE
+ 62);
63 return h_objects
+ ((obj
- 1) * O4_SIZE
+ 126);
70 * Return the address of the given object's name.
74 zword
object_name (zword object
)
79 obj_addr
= object_address (object
);
81 /* The object name address is found at the start of the properties */
84 obj_addr
+= O1_PROPERTY_OFFSET
;
86 obj_addr
+= O4_PROPERTY_OFFSET
;
88 LOW_WORD (obj_addr
, name_addr
)
97 * Calculate the start address of the property list associated with
102 static zword
first_property (zword obj
)
107 /* Fetch address of object name */
109 prop_addr
= object_name (obj
);
111 /* Get length of object name */
113 LOW_BYTE (prop_addr
, size
)
115 /* Add name length to pointer */
117 return prop_addr
+ 1 + 2 * size
;
119 }/* first_property */
124 * Calculate the address of the next property in a property list.
128 static zword
next_property (zword prop_addr
)
132 /* Load the current property id */
134 LOW_BYTE (prop_addr
, value
)
137 /* Calculate the length of this property */
141 else if (!(value
& 0x80))
145 LOW_BYTE (prop_addr
, value
)
148 if (value
== 0) value
= 64; /* demanded by Spec 1.0 */
152 /* Add property length to current property pointer */
154 return prop_addr
+ value
+ 1;
161 * Unlink an object from its parent and siblings.
165 static void unlink_object (zword object
)
172 runtime_error (ERR_REMOVE_OBJECT_0
);
176 obj_addr
= object_address (object
);
178 if (h_version
<= V3
) {
181 zbyte younger_sibling
;
185 /* Get parent of object, and return if no parent */
187 obj_addr
+= O1_PARENT
;
188 LOW_BYTE (obj_addr
, parent
)
192 /* Get (older) sibling of object and set both parent and sibling
195 SET_BYTE (obj_addr
, zero
)
196 obj_addr
+= O1_SIBLING
- O1_PARENT
;
197 LOW_BYTE (obj_addr
, older_sibling
)
198 SET_BYTE (obj_addr
, zero
)
200 /* Get first child of parent (the youngest sibling of the object) */
202 parent_addr
= object_address (parent
) + O1_CHILD
;
203 LOW_BYTE (parent_addr
, younger_sibling
)
205 /* Remove object from the list of siblings */
207 if (younger_sibling
== object
)
208 SET_BYTE (parent_addr
, older_sibling
)
211 sibling_addr
= object_address (younger_sibling
) + O1_SIBLING
;
212 LOW_BYTE (sibling_addr
, younger_sibling
)
213 } while (younger_sibling
!= object
);
214 SET_BYTE (sibling_addr
, older_sibling
)
220 zword younger_sibling
;
224 /* Get parent of object, and return if no parent */
226 obj_addr
+= O4_PARENT
;
227 LOW_WORD (obj_addr
, parent
)
231 /* Get (older) sibling of object and set both parent and sibling
234 SET_WORD (obj_addr
, zero
)
235 obj_addr
+= O4_SIBLING
- O4_PARENT
;
236 LOW_WORD (obj_addr
, older_sibling
)
237 SET_WORD (obj_addr
, zero
)
239 /* Get first child of parent (the youngest sibling of the object) */
241 parent_addr
= object_address (parent
) + O4_CHILD
;
242 LOW_WORD (parent_addr
, younger_sibling
)
244 /* Remove object from the list of siblings */
246 if (younger_sibling
== object
)
247 SET_WORD (parent_addr
, older_sibling
)
250 sibling_addr
= object_address (younger_sibling
) + O4_SIBLING
;
251 LOW_WORD (sibling_addr
, younger_sibling
)
252 } while (younger_sibling
!= object
);
253 SET_WORD (sibling_addr
, older_sibling
)
261 * z_clear_attr, clear an object attribute.
264 * zargs[1] = number of attribute to be cleared
268 void z_clear_attr (void)
273 if (story_id
== SHERLOCK
)
277 if (zargs
[1] > ((h_version
<= V3
) ? 31 : 47))
278 runtime_error (ERR_ILL_ATTR
);
280 /* If we are monitoring attribute assignment display a short note */
282 if (f_setup
.attribute_assignment
) {
284 print_string ("@clear_attr ");
285 print_object (zargs
[0]);
287 print_num (zargs
[1]);
292 runtime_error (ERR_CLEAR_ATTR_0
);
296 /* Get attribute address */
298 obj_addr
= object_address (zargs
[0]) + zargs
[1] / 8;
300 /* Clear attribute bit */
302 LOW_BYTE (obj_addr
, value
)
303 value
&= ~(0x80 >> (zargs
[1] & 7));
304 SET_BYTE (obj_addr
, value
)
309 * z_jin, branch if the first object is inside the second.
311 * zargs[0] = first object
312 * zargs[1] = second object
320 /* If we are monitoring object locating display a short note */
322 if (f_setup
.object_locating
) {
324 print_string ("@jin ");
325 print_object (zargs
[0]);
327 print_object (zargs
[1]);
332 runtime_error (ERR_JIN_0
);
333 branch (0 == zargs
[1]);
337 obj_addr
= object_address (zargs
[0]);
339 if (h_version
<= V3
) {
343 /* Get parent id from object */
345 obj_addr
+= O1_PARENT
;
346 LOW_BYTE (obj_addr
, parent
)
348 /* Branch if the parent is obj2 */
350 branch (parent
== zargs
[1]);
356 /* Get parent id from object */
358 obj_addr
+= O4_PARENT
;
359 LOW_WORD (obj_addr
, parent
)
361 /* Branch if the parent is obj2 */
363 branch (parent
== zargs
[1]);
370 * z_get_child, store the child of an object.
376 void z_get_child (void)
380 /* If we are monitoring object locating display a short note */
382 if (f_setup
.object_locating
) {
384 print_string ("@get_child ");
385 print_object (zargs
[0]);
390 runtime_error (ERR_GET_CHILD_0
);
396 obj_addr
= object_address (zargs
[0]);
398 if (h_version
<= V3
) {
402 /* Get child id from object */
404 obj_addr
+= O1_CHILD
;
405 LOW_BYTE (obj_addr
, child
)
407 /* Store child id and branch */
416 /* Get child id from object */
418 obj_addr
+= O4_CHILD
;
419 LOW_WORD (obj_addr
, child
)
421 /* Store child id and branch */
431 * z_get_next_prop, store the number of the first or next property.
434 * zargs[1] = address of current property (0 gets the first property)
438 void z_get_next_prop (void)
445 runtime_error (ERR_GET_NEXT_PROP_0
);
450 /* Property id is in bottom five (six) bits */
452 mask
= (h_version
<= V3
) ? 0x1f : 0x3f;
454 /* Load address of first property */
456 prop_addr
= first_property (zargs
[0]);
460 /* Scan down the property list */
463 LOW_BYTE (prop_addr
, value
)
464 prop_addr
= next_property (prop_addr
);
465 } while ((value
& mask
) > zargs
[1]);
467 /* Exit if the property does not exist */
469 if ((value
& mask
) != zargs
[1])
470 runtime_error (ERR_NO_PROP
);
474 /* Return the property id */
476 LOW_BYTE (prop_addr
, value
)
477 store ((zword
) (value
& mask
));
479 }/* z_get_next_prop */
482 * z_get_parent, store the parent of an object.
488 void z_get_parent (void)
492 /* If we are monitoring object locating display a short note */
494 if (f_setup
.object_locating
) {
496 print_string ("@get_parent ");
497 print_object (zargs
[0]);
502 runtime_error (ERR_GET_PARENT_0
);
507 obj_addr
= object_address (zargs
[0]);
509 if (h_version
<= V3
) {
513 /* Get parent id from object */
515 obj_addr
+= O1_PARENT
;
516 LOW_BYTE (obj_addr
, parent
)
526 /* Get parent id from object */
528 obj_addr
+= O4_PARENT
;
529 LOW_WORD (obj_addr
, parent
)
540 * z_get_prop, store the value of an object property.
543 * zargs[1] = number of property to be examined
547 void z_get_prop (void)
556 runtime_error (ERR_GET_PROP_0
);
561 /* Property id is in bottom five (six) bits */
563 mask
= (h_version
<= V3
) ? 0x1f : 0x3f;
565 /* Load address of first property */
567 prop_addr
= first_property (zargs
[0]);
569 /* Scan down the property list */
572 LOW_BYTE (prop_addr
, value
)
573 if ((value
& mask
) <= zargs
[1])
575 prop_addr
= next_property (prop_addr
);
578 if ((value
& mask
) == zargs
[1]) { /* property found */
580 /* Load property (byte or word sized) */
584 if ((h_version
<= V3
&& !(value
& 0xe0)) || (h_version
>= V4
&& !(value
& 0xc0))) {
586 LOW_BYTE (prop_addr
, bprop_val
)
587 wprop_val
= bprop_val
;
589 } else LOW_WORD (prop_addr
, wprop_val
)
591 } else { /* property not found */
593 /* Load default value */
595 prop_addr
= h_objects
+ 2 * (zargs
[1] - 1);
596 LOW_WORD (prop_addr
, wprop_val
)
600 /* Store the property value */
607 * z_get_prop_addr, store the address of an object property.
610 * zargs[1] = number of property to be examined
614 void z_get_prop_addr (void)
621 runtime_error (ERR_GET_PROP_ADDR_0
);
626 if (story_id
== BEYOND_ZORK
)
627 if (zargs
[0] > MAX_OBJECT
)
628 { store (0); return; }
630 /* Property id is in bottom five (six) bits */
632 mask
= (h_version
<= V3
) ? 0x1f : 0x3f;
634 /* Load address of first property */
636 prop_addr
= first_property (zargs
[0]);
638 /* Scan down the property list */
641 LOW_BYTE (prop_addr
, value
)
642 if ((value
& mask
) <= zargs
[1])
644 prop_addr
= next_property (prop_addr
);
647 /* Calculate the property address or return zero */
649 if ((value
& mask
) == zargs
[1]) {
651 if (h_version
>= V4
&& (value
& 0x80))
653 store ((zword
) (prop_addr
+ 1));
657 }/* z_get_prop_addr */
660 * z_get_prop_len, store the length of an object property.
662 * zargs[0] = address of property to be examined
666 void z_get_prop_len (void)
671 /* Back up the property pointer to the property id */
674 LOW_BYTE (addr
, value
)
676 /* Calculate length of property */
679 value
= (value
>> 5) + 1;
680 else if (!(value
& 0x80))
681 value
= (value
>> 6) + 1;
686 if (value
== 0) value
= 64; /* demanded by Spec 1.0 */
690 /* Store length of property */
694 }/* z_get_prop_len */
697 * z_get_sibling, store the sibling of an object.
703 void z_get_sibling (void)
708 runtime_error (ERR_GET_SIBLING_0
);
714 obj_addr
= object_address (zargs
[0]);
716 if (h_version
<= V3
) {
720 /* Get sibling id from object */
722 obj_addr
+= O1_SIBLING
;
723 LOW_BYTE (obj_addr
, sibling
)
725 /* Store sibling and branch */
734 /* Get sibling id from object */
736 obj_addr
+= O4_SIBLING
;
737 LOW_WORD (obj_addr
, sibling
)
739 /* Store sibling and branch */
749 * z_insert_obj, make an object the first child of another object.
751 * zargs[0] = object to be moved
752 * zargs[1] = destination object
756 void z_insert_obj (void)
758 zword obj1
= zargs
[0];
759 zword obj2
= zargs
[1];
763 /* If we are monitoring object movements display a short note */
765 if (f_setup
.object_movement
) {
767 print_string ("@move_obj ");
775 runtime_error (ERR_MOVE_OBJECT_0
);
780 runtime_error (ERR_MOVE_OBJECT_TO_0
);
784 /* Get addresses of both objects */
786 obj1_addr
= object_address (obj1
);
787 obj2_addr
= object_address (obj2
);
789 /* Remove object 1 from current parent */
791 unlink_object (obj1
);
793 /* Make object 1 first child of object 2 */
795 if (h_version
<= V3
) {
799 obj1_addr
+= O1_PARENT
;
800 SET_BYTE (obj1_addr
, obj2
)
801 obj2_addr
+= O1_CHILD
;
802 LOW_BYTE (obj2_addr
, child
)
803 SET_BYTE (obj2_addr
, obj1
)
804 obj1_addr
+= O1_SIBLING
- O1_PARENT
;
805 SET_BYTE (obj1_addr
, child
)
811 obj1_addr
+= O4_PARENT
;
812 SET_WORD (obj1_addr
, obj2
)
813 obj2_addr
+= O4_CHILD
;
814 LOW_WORD (obj2_addr
, child
)
815 SET_WORD (obj2_addr
, obj1
)
816 obj1_addr
+= O4_SIBLING
- O4_PARENT
;
817 SET_WORD (obj1_addr
, child
)
824 * z_put_prop, set the value of an object property.
827 * zargs[1] = number of property to set
828 * zargs[2] = value to set property to
832 void z_put_prop (void)
839 runtime_error (ERR_PUT_PROP_0
);
843 /* Property id is in bottom five or six bits */
845 mask
= (h_version
<= V3
) ? 0x1f : 0x3f;
847 /* Load address of first property */
849 prop_addr
= first_property (zargs
[0]);
851 /* Scan down the property list */
854 LOW_BYTE (prop_addr
, value
)
855 if ((value
& mask
) <= zargs
[1])
857 prop_addr
= next_property (prop_addr
);
860 /* Exit if the property does not exist */
862 if ((value
& mask
) != zargs
[1])
863 runtime_error (ERR_NO_PROP
);
865 /* Store the new property value (byte or word sized) */
869 if ((h_version
<= V3
&& !(value
& 0xe0)) || (h_version
>= V4
&& !(value
& 0xc0))) {
871 SET_BYTE (prop_addr
, v
)
874 SET_WORD (prop_addr
, v
)
880 * z_remove_obj, unlink an object from its parent and siblings.
886 void z_remove_obj (void)
889 /* If we are monitoring object movements display a short note */
891 if (f_setup
.object_movement
) {
893 print_string ("@remove_obj ");
894 print_object (zargs
[0]);
898 /* Call unlink_object to do the job */
900 unlink_object (zargs
[0]);
905 * z_set_attr, set an object attribute.
908 * zargs[1] = number of attribute to set
912 void z_set_attr (void)
917 if (story_id
== SHERLOCK
)
921 if (zargs
[1] > ((h_version
<= V3
) ? 31 : 47))
922 runtime_error (ERR_ILL_ATTR
);
924 /* If we are monitoring attribute assignment display a short note */
926 if (f_setup
.attribute_assignment
) {
928 print_string ("@set_attr ");
929 print_object (zargs
[0]);
931 print_num (zargs
[1]);
936 runtime_error (ERR_SET_ATTR_0
);
940 /* Get attribute address */
942 obj_addr
= object_address (zargs
[0]) + zargs
[1] / 8;
944 /* Load attribute byte */
946 LOW_BYTE (obj_addr
, value
)
948 /* Set attribute bit */
950 value
|= 0x80 >> (zargs
[1] & 7);
952 /* Store attribute byte */
954 SET_BYTE (obj_addr
, value
)
959 * z_test_attr, branch if an object attribute is set.
962 * zargs[1] = number of attribute to test
966 void z_test_attr (void)
971 if (zargs
[1] > ((h_version
<= V3
) ? 31 : 47))
972 runtime_error (ERR_ILL_ATTR
);
974 /* If we are monitoring attribute testing display a short note */
976 if (f_setup
.attribute_testing
) {
978 print_string ("@test_attr ");
979 print_object (zargs
[0]);
981 print_num (zargs
[1]);
986 runtime_error (ERR_TEST_ATTR_0
);
991 /* Get attribute address */
993 obj_addr
= object_address (zargs
[0]) + zargs
[1] / 8;
995 /* Load attribute byte */
997 LOW_BYTE (obj_addr
, value
)
1001 branch (value
& (0x80 >> (zargs
[1] & 7)));