1 /* object.c - Object manipulation opcodes
2 * Copyright (c) 1995-1997 Stefan Jokisch
3 *
4 * This file is part of Frotz.
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "frotz.h"
22
23 f_setup_t f_setup;
24 z_header_t z_header;
25
26 #define MAX_OBJECT 2000
27
28 #define O1_PARENT 4
29 #define O1_SIBLING 5
30 #define O1_CHILD 6
31 #define O1_PROPERTY_OFFSET 7
32 #define O1_SIZE 9
33
34 #define O4_PARENT 6
35 #define O4_SIBLING 8
36 #define O4_CHILD 10
37 #define O4_PROPERTY_OFFSET 12
38 #define O4_SIZE 14
39
40
41 /*
42 * object_address
43 *
44 * Calculate the address of an object.
45 *
46 */
object_address(zword obj)47 static zword object_address(zword obj)
48 {
49 /* Check object number */
50 if (obj > ((z_header.version <= V3) ? 255 : MAX_OBJECT)) {
51 print_string("@Attempt to address illegal object ");
52 print_num(obj);
53 print_string(". This is normally fatal.");
54 new_line();
55 runtime_error (ERR_ILL_OBJ);
56 }
57
58 /* Return object address */
59 if (z_header.version <= V3)
60 return z_header.objects + ((obj - 1) * O1_SIZE + 62);
61 else
62 return z_header.objects + ((obj - 1) * O4_SIZE + 126);
63 } /* object_address */
64
65
66 /*
67 * object_name
68 *
69 * Return the address of the given object's name.
70 *
71 */
object_name(zword object)72 zword object_name(zword object)
73 {
74 zword obj_addr;
75 zword name_addr;
76
77 obj_addr = object_address(object);
78
79 /* The object name address is found at the start of the properties */
80 if (z_header.version <= V3)
81 obj_addr += O1_PROPERTY_OFFSET;
82 else
83 obj_addr += O4_PROPERTY_OFFSET;
84 LOW_WORD(obj_addr, name_addr)
85
86 return name_addr;
87 } /* object_name */
88
89
90 /*
91 * first_property
92 *
93 * Calculate the start address of the property list associated with
94 * an object.
95 *
96 */
first_property(zword obj)97 static zword first_property(zword obj)
98 {
99 zword prop_addr;
100 zbyte size;
101
102 /* Fetch address of object name */
103 prop_addr = object_name (obj);
104
105 /* Get length of object name */
106 LOW_BYTE(prop_addr, size)
107
108 /* Add name length to pointer */
109 return prop_addr + 1 + 2 * size;
110 } /* first_property */
111
112
113 /*
114 * next_property
115 *
116 * Calculate the address of the next property in a property list.
117 *
118 */
next_property(zword prop_addr)119 static zword next_property(zword prop_addr)
120 {
121 zbyte value;
122
123 /* Load the current property id */
124 LOW_BYTE(prop_addr, value)
125 prop_addr++;
126
127 /* Calculate the length of this property */
128
129 if (z_header.version <= V3)
130 value >>= 5;
131 else if (!(value & 0x80))
132 value >>= 6;
133 else {
134 LOW_BYTE(prop_addr, value)
135 value &= 0x3f;
136 if (value == 0) value = 64; /* demanded by Spec 1.0 */
137 }
138
139 /* Add property length to current property pointer */
140 return prop_addr + value + 1;
141 } /* next_property */
142
143
144 /*
145 * unlink_object
146 *
147 * Unlink an object from its parent and siblings.
148 *
149 */
unlink_object(zword object)150 static void unlink_object(zword object)
151 {
152 zword obj_addr;
153 zword parent_addr;
154 zword sibling_addr;
155
156 if (object == 0) {
157 runtime_error(ERR_REMOVE_OBJECT_0);
158 return;
159 }
160
161 obj_addr = object_address (object);
162
163 if (z_header.version <= V3) {
164 zbyte parent;
165 zbyte younger_sibling;
166 zbyte older_sibling;
167 zbyte zero = 0;
168
169 /* Get parent of object, and return if no parent */
170 obj_addr += O1_PARENT;
171 LOW_BYTE(obj_addr, parent)
172 if (!parent)
173 return;
174
175 /* Get (older) sibling of object and set both
176 * parent and sibling pointers to 0 */
177 SET_BYTE(obj_addr, zero)
178 obj_addr += O1_SIBLING - O1_PARENT;
179 LOW_BYTE(obj_addr, older_sibling)
180 SET_BYTE(obj_addr, zero)
181
182 /* Get first child of parent (the youngest sibling
183 * of the object) */
184 parent_addr = object_address(parent) + O1_CHILD;
185 LOW_BYTE(parent_addr, younger_sibling)
186
187 /* Remove object from the list of siblings */
188 if (younger_sibling == object)
189 SET_BYTE(parent_addr, older_sibling)
190 else {
191 do {
192 sibling_addr = object_address(younger_sibling)
193 + O1_SIBLING;
194 LOW_BYTE(sibling_addr, younger_sibling)
195 } while (younger_sibling != object);
196 SET_BYTE(sibling_addr, older_sibling)
197 }
198 } else {
199 zword parent;
200 zword younger_sibling;
201 zword older_sibling;
202 zword zero = 0;
203
204 /* Get parent of object, and return if no parent */
205 obj_addr += O4_PARENT;
206 LOW_WORD(obj_addr, parent)
207 if (!parent)
208 return;
209
210 /* Get (older) sibling of object and set both parent
211 * and sibling pointers to 0 */
212 SET_WORD(obj_addr, zero)
213 obj_addr += O4_SIBLING - O4_PARENT;
214 LOW_WORD(obj_addr, older_sibling)
215 SET_WORD(obj_addr, zero)
216
217 /* Get first child of parent (the youngest sibling
218 * of the object) */
219 parent_addr = object_address(parent) + O4_CHILD;
220 LOW_WORD(parent_addr, younger_sibling)
221
222 /* Remove object from the list of siblings */
223 if (younger_sibling == object)
224 SET_WORD(parent_addr, older_sibling)
225 else {
226 do {
227 sibling_addr = object_address(younger_sibling)
228 + O4_SIBLING;
229 LOW_WORD(sibling_addr, younger_sibling)
230 } while (younger_sibling != object);
231 SET_WORD(sibling_addr, older_sibling)
232 }
233 }
234 } /* unlink_object */
235
236
237 /*
238 * z_clear_attr, clear an object attribute.
239 *
240 * zargs[0] = object
241 * zargs[1] = number of attribute to be cleared
242 *
243 */
z_clear_attr(void)244 void z_clear_attr(void)
245 {
246 zword obj_addr;
247 zbyte value;
248
249 if (story_id == SHERLOCK)
250 if (zargs[1] == 48)
251 return;
252
253 if (zargs[1] > ((z_header.version <= V3) ? 31 : 47))
254 runtime_error(ERR_ILL_ATTR);
255
256 /* If we are monitoring attribute assignment display a short note */
257
258 if (f_setup.attribute_assignment) {
259 stream_mssg_on();
260 print_string("@clear_attr ");
261 print_object(zargs[0]);
262 print_string(" ");
263 print_num(zargs[1]);
264 stream_mssg_off();
265 }
266
267 if (zargs[0] == 0) {
268 runtime_error(ERR_CLEAR_ATTR_0);
269 return;
270 }
271
272 /* Get attribute address */
273 obj_addr = object_address(zargs[0]) + zargs[1] / 8;
274
275 /* Clear attribute bit */
276 LOW_BYTE(obj_addr, value)
277 value &= ~(0x80 >> (zargs[1] & 7));
278 SET_BYTE(obj_addr, value)
279 } /* z_clear_attr */
280
281
282 /*
283 * z_jin, branch if the first object is inside the second.
284 *
285 * zargs[0] = first object
286 * zargs[1] = second object
287 *
288 */
z_jin(void)289 void z_jin(void)
290 {
291 zword obj_addr;
292
293 /* If we are monitoring object locating display a short note */
294 if (f_setup.object_locating) {
295 stream_mssg_on();
296 print_string("@jin ");
297 print_object(zargs[0]);
298 print_string(" ");
299 print_object(zargs[1]);
300 stream_mssg_off();
301 }
302
303 if (zargs[0] == 0) {
304 runtime_error(ERR_JIN_0);
305 branch(0 == zargs[1]);
306 return;
307 }
308
309 obj_addr = object_address(zargs[0]);
310
311 if (z_header.version <= V3) {
312 zbyte parent;
313
314 /* Get parent id from object */
315 obj_addr += O1_PARENT;
316 LOW_BYTE(obj_addr, parent)
317
318 /* Branch if the parent is obj2 */
319 branch (parent == zargs[1]);
320 } else {
321 zword parent;
322 /* Get parent id from object */
323 obj_addr += O4_PARENT;
324 LOW_WORD(obj_addr, parent)
325
326 /* Branch if the parent is obj2 */
327 branch (parent == zargs[1]);
328 }
329 } /* z_jin */
330
331
332 /*
333 * z_get_child, store the child of an object.
334 *
335 * zargs[0] = object
336 *
337 */
z_get_child(void)338 void z_get_child(void)
339 {
340 zword obj_addr;
341
342 /* If we are monitoring object locating display a short note */
343
344 if (f_setup.object_locating) {
345 stream_mssg_on();
346 print_string("@get_child ");
347 print_object(zargs[0]);
348 stream_mssg_off();
349 }
350
351 if (zargs[0] == 0) {
352 runtime_error(ERR_GET_CHILD_0);
353 store(0);
354 branch(FALSE);
355 return;
356 }
357
358 obj_addr = object_address (zargs[0]);
359
360 if (z_header.version <= V3) {
361 zbyte child;
362
363 /* Get child id from object */
364 obj_addr += O1_CHILD;
365 LOW_BYTE(obj_addr, child)
366
367 /* Store child id and branch */
368 store(child);
369 branch(child);
370 } else {
371 zword child;
372
373 /* Get child id from object */
374 obj_addr += O4_CHILD;
375 LOW_WORD(obj_addr, child)
376
377 /* Store child id and branch */
378 store(child);
379 branch(child);
380 }
381 } /* z_get_child */
382
383
384 /*
385 * z_get_next_prop, store the number of the first or next property.
386 *
387 * zargs[0] = object
388 * zargs[1] = address of current property (0 gets the first property)
389 *
390 */
z_get_next_prop(void)391 void z_get_next_prop(void)
392 {
393 zword prop_addr;
394 zbyte value;
395 zbyte mask;
396
397 if (zargs[0] == 0) {
398 runtime_error(ERR_GET_NEXT_PROP_0);
399 store(0);
400 return;
401 }
402
403 /* Property id is in bottom five (six) bits */
404 mask = (z_header.version <= V3) ? 0x1f : 0x3f;
405
406 /* Load address of first property */
407 prop_addr = first_property(zargs[0]);
408
409 if (zargs[1] != 0) {
410 /* Scan down the property list */
411 do {
412 LOW_BYTE(prop_addr, value)
413 prop_addr = next_property(prop_addr);
414 } while ((value & mask) > zargs[1]);
415
416 /* Exit if the property does not exist */
417 if ((value & mask) != zargs[1])
418 runtime_error(ERR_NO_PROP);
419 }
420
421 /* Return the property id */
422 LOW_BYTE(prop_addr, value)
423 store((zword) (value & mask));
424 } /* z_get_next_prop */
425
426
427 /*
428 * z_get_parent, store the parent of an object.
429 *
430 * zargs[0] = object
431 *
432 */
z_get_parent(void)433 void z_get_parent(void)
434 {
435 zword obj_addr;
436
437 /* If we are monitoring object locating display a short note */
438
439 if (f_setup.object_locating) {
440 stream_mssg_on();
441 print_string("@get_parent ");
442 print_object(zargs[0]);
443 stream_mssg_off();
444 }
445
446 if (zargs[0] == 0) {
447 runtime_error(ERR_GET_PARENT_0);
448 store(0);
449 return;
450 }
451
452 obj_addr = object_address (zargs[0]);
453
454 if (z_header.version <= V3) {
455 zbyte parent;
456
457 /* Get parent id from object */
458 obj_addr += O1_PARENT;
459 LOW_BYTE(obj_addr, parent)
460
461 /* Store parent */
462 store(parent);
463 } else {
464 zword parent;
465
466 /* Get parent id from object */
467 obj_addr += O4_PARENT;
468 LOW_WORD (obj_addr, parent)
469
470 /* Store parent */
471 store (parent);
472 }
473 } /* z_get_parent */
474
475
476 /*
477 * z_get_prop, store the value of an object property.
478 *
479 * zargs[0] = object
480 * zargs[1] = number of property to be examined
481 *
482 */
z_get_prop(void)483 void z_get_prop(void)
484 {
485 zword prop_addr;
486 zword wprop_val;
487 zbyte bprop_val;
488 zbyte value;
489 zbyte mask;
490
491 if (zargs[0] == 0) {
492 runtime_error(ERR_GET_PROP_0);
493 store(0);
494 return;
495 }
496
497 /* Property id is in bottom five (six) bits */
498 mask = (z_header.version <= V3) ? 0x1f : 0x3f;
499
500 /* Load address of first property */
501 prop_addr = first_property(zargs[0]);
502
503 /* Scan down the property list */
504 for (;;) {
505 LOW_BYTE(prop_addr, value)
506 if ((value & mask) <= zargs[1])
507 break;
508 prop_addr = next_property(prop_addr);
509 }
510
511 if ((value & mask) == zargs[1]) { /* property found */
512 /* Load property (byte or word sized) */
513 prop_addr++;
514 if ((z_header.version <= V3 && !(value & 0xe0)) ||
515 (z_header.version >= V4 && !(value & 0xc0))) {
516 LOW_BYTE(prop_addr, bprop_val)
517 wprop_val = bprop_val;
518 } else
519 LOW_WORD(prop_addr, wprop_val)
520 } else { /* property not found */
521 /* Load default value */
522 prop_addr = z_header.objects + 2 * (zargs[1] - 1);
523 LOW_WORD(prop_addr, wprop_val)
524 }
525 /* Store the property value */
526 store (wprop_val);
527 } /* z_get_prop */
528
529
530 /*
531 * z_get_prop_addr, store the address of an object property.
532 *
533 * zargs[0] = object
534 * zargs[1] = number of property to be examined
535 *
536 */
z_get_prop_addr(void)537 void z_get_prop_addr(void)
538 {
539 zword prop_addr;
540 zbyte value;
541 zbyte mask;
542
543 if (zargs[0] == 0) {
544 runtime_error(ERR_GET_PROP_ADDR_0);
545 store(0);
546 return;
547 }
548
549 if (story_id == BEYOND_ZORK) {
550 if (zargs[0] > MAX_OBJECT) {
551 store (0);
552 return;
553 }
554 }
555
556 /* Property id is in bottom five (six) bits */
557 mask = (z_header.version <= V3) ? 0x1f : 0x3f;
558
559 /* Load address of first property */
560 prop_addr = first_property(zargs[0]);
561
562 /* Scan down the property list */
563 for (;;) {
564 LOW_BYTE(prop_addr, value)
565 if ((value & mask) <= zargs[1])
566 break;
567 prop_addr = next_property(prop_addr);
568 }
569
570 /* Calculate the property address or return zero */
571 if ((value & mask) == zargs[1]) {
572 if (z_header.version >= V4 && (value & 0x80))
573 prop_addr++;
574 store ((zword) (prop_addr + 1));
575 } else
576 store (0);
577 } /* z_get_prop_addr */
578
579
580 /*
581 * z_get_prop_len, store the length of an object property.
582 *
583 * zargs[0] = address of property to be examined
584 *
585 */
z_get_prop_len(void)586 void z_get_prop_len(void)
587 {
588 zword addr;
589 zbyte value;
590
591 if (zargs[0] == 0) {
592 store(0); /* demanded by Spec 1.1 */
593 return;
594 }
595
596 /* Back up the property pointer to the property id */
597 addr = zargs[0] - 1;
598 LOW_BYTE(addr, value)
599
600 /* Calculate length of property */
601 if (z_header.version <= V3)
602 value = (value >> 5) + 1;
603 else if (!(value & 0x80))
604 value = (value >> 6) + 1;
605 else {
606 value &= 0x3f;
607 if (value == 0) value = 64; /* demanded by Spec 1.0 */
608
609 }
610 /* Store length of property */
611 store(value);
612 } /* z_get_prop_len */
613
614
615 /*
616 * z_get_sibling, store the sibling of an object.
617 *
618 * zargs[0] = object
619 *
620 */
z_get_sibling(void)621 void z_get_sibling(void)
622 {
623 zword obj_addr;
624
625 if (zargs[0] == 0) {
626 runtime_error(ERR_GET_SIBLING_0);
627 store(0);
628 branch(FALSE);
629 return;
630 }
631
632 obj_addr = object_address(zargs[0]);
633
634 if (z_header.version <= V3) {
635 zbyte sibling;
636
637 /* Get sibling id from object */
638 obj_addr += O1_SIBLING;
639 LOW_BYTE(obj_addr, sibling)
640
641 /* Store sibling and branch */
642 store(sibling);
643 branch(sibling);
644 } else {
645 zword sibling;
646
647 /* Get sibling id from object */
648 obj_addr += O4_SIBLING;
649 LOW_WORD(obj_addr, sibling)
650
651 /* Store sibling and branch */
652 store(sibling);
653 branch(sibling);
654 }
655
656 } /* z_get_sibling */
657
658
659 /*
660 * z_insert_obj, make an object the first child of another object.
661 *
662 * zargs[0] = object to be moved
663 * zargs[1] = destination object
664 *
665 */
z_insert_obj(void)666 void z_insert_obj(void)
667 {
668 zword obj1 = zargs[0];
669 zword obj2 = zargs[1];
670 zword obj1_addr;
671 zword obj2_addr;
672
673 /* If we are monitoring object movements display a short note */
674 if (f_setup.object_movement) {
675 stream_mssg_on();
676 print_string("@move_obj ");
677 print_object(obj1);
678 print_string(" ");
679 print_object(obj2);
680 stream_mssg_off();
681 }
682
683 if (obj1 == 0) {
684 runtime_error(ERR_MOVE_OBJECT_0);
685 return;
686 }
687
688 if (obj2 == 0) {
689 runtime_error(ERR_MOVE_OBJECT_TO_0);
690 return;
691 }
692
693 /* Get addresses of both objects */
694 obj1_addr = object_address(obj1);
695 obj2_addr = object_address(obj2);
696
697 /* Remove object 1 from current parent */
698 unlink_object(obj1);
699
700 /* Make object 1 first child of object 2 */
701 if (z_header.version <= V3) {
702 zbyte child;
703
704 obj1_addr += O1_PARENT;
705 SET_BYTE(obj1_addr, obj2)
706 obj2_addr += O1_CHILD;
707 LOW_BYTE(obj2_addr, child)
708 SET_BYTE(obj2_addr, obj1)
709 obj1_addr += O1_SIBLING - O1_PARENT;
710 SET_BYTE(obj1_addr, child)
711 } else {
712 zword child;
713
714 obj1_addr += O4_PARENT;
715 SET_WORD(obj1_addr, obj2)
716 obj2_addr += O4_CHILD;
717 LOW_WORD(obj2_addr, child)
718 SET_WORD(obj2_addr, obj1)
719 obj1_addr += O4_SIBLING - O4_PARENT;
720 SET_WORD(obj1_addr, child)
721 }
722 } /* z_insert_obj */
723
724
725 /*
726 * z_put_prop, set the value of an object property.
727 *
728 * zargs[0] = object
729 * zargs[1] = number of property to set
730 * zargs[2] = value to set property to
731 *
732 */
z_put_prop(void)733 void z_put_prop(void)
734 {
735 zword prop_addr;
736 zword value;
737 zbyte mask;
738
739 if (zargs[0] == 0) {
740 runtime_error(ERR_PUT_PROP_0);
741 return;
742 }
743
744 /* Property id is in bottom five or six bits */
745 mask = (z_header.version <= V3) ? 0x1f : 0x3f;
746
747 /* Load address of first property */
748 prop_addr = first_property(zargs[0]);
749
750 /* Scan down the property list */
751 for (;;) {
752 LOW_BYTE(prop_addr, value)
753 if ((value & mask) <= zargs[1])
754 break;
755 prop_addr = next_property (prop_addr);
756 }
757
758 /* Exit if the property does not exist */
759 if ((value & mask) != zargs[1])
760 runtime_error(ERR_NO_PROP);
761
762 /* Store the new property value (byte or word sized) */
763 prop_addr++;
764
765 if ((z_header.version <= V3 && !(value & 0xe0)) ||
766 (z_header.version >= V4 && !(value & 0xc0))) {
767 zbyte v = zargs[2];
768 SET_BYTE(prop_addr, v)
769 } else {
770 zword v = zargs[2];
771 SET_WORD(prop_addr, v)
772 }
773 } /* z_put_prop */
774
775
776 /*
777 * z_remove_obj, unlink an object from its parent and siblings.
778 *
779 * zargs[0] = object
780 *
781 */
z_remove_obj(void)782 void z_remove_obj (void)
783 {
784 /* If we are monitoring object movements display a short note */
785 if (f_setup.object_movement) {
786 stream_mssg_on();
787 print_string("@remove_obj ");
788 print_object(zargs[0]);
789 stream_mssg_off();
790 }
791
792 /* Call unlink_object to do the job */
793 unlink_object(zargs[0]);
794 } /* z_remove_obj */
795
796
797 /*
798 * z_set_attr, set an object attribute.
799 *
800 * zargs[0] = object
801 * zargs[1] = number of attribute to set
802 *
803 */
z_set_attr(void)804 void z_set_attr(void)
805 {
806 zword obj_addr;
807 zbyte value;
808
809 if (story_id == SHERLOCK)
810 if (zargs[1] == 48)
811 return;
812
813 if (zargs[1] > ((z_header.version <= V3) ? 31 : 47))
814 runtime_error(ERR_ILL_ATTR);
815
816 /* If we are monitoring attribute assignment display a short note */
817 if (f_setup.attribute_assignment) {
818 stream_mssg_on();
819 print_string("@set_attr ");
820 print_object(zargs[0]);
821 print_string(" ");
822 print_num(zargs[1]);
823 stream_mssg_off();
824 }
825
826 if (zargs[0] == 0) {
827 runtime_error(ERR_SET_ATTR_0);
828 return;
829 }
830
831 /* Get attribute address */
832 obj_addr = object_address(zargs[0]) + zargs[1] / 8;
833
834 /* Load attribute byte */
835 LOW_BYTE(obj_addr, value)
836
837 /* Set attribute bit */
838 value |= 0x80 >> (zargs[1] & 7);
839
840 /* Store attribute byte */
841 SET_BYTE(obj_addr, value)
842 } /* z_set_attr */
843
844
845 /*
846 * z_test_attr, branch if an object attribute is set.
847 *
848 * zargs[0] = object
849 * zargs[1] = number of attribute to test
850 *
851 */
z_test_attr(void)852 void z_test_attr(void)
853 {
854 zword obj_addr;
855 zbyte value;
856
857 if (zargs[1] > ((z_header.version <= V3) ? 31 : 47))
858 runtime_error(ERR_ILL_ATTR);
859
860 /* If we are monitoring attribute testing display a short note */
861 if (f_setup.attribute_testing) {
862 stream_mssg_on();
863 print_string("@test_attr ");
864 print_object(zargs[0]);
865 print_string(" ");
866 print_num(zargs[1]);
867 stream_mssg_off();
868 }
869 if (zargs[0] == 0) {
870 runtime_error(ERR_TEST_ATTR_0);
871 branch(FALSE);
872 return;
873 }
874
875 /* Get attribute address */
876 obj_addr = object_address(zargs[0]) + zargs[1] / 8;
877
878 /* Load attribute byte */
879 LOW_BYTE(obj_addr, value)
880
881 /* Test attribute */
882 branch (value & (0x80 >> (zargs[1] & 7)));
883
884 } /* z_test_attr */
885