1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/frotz/processor.h"
24
25 namespace Glk {
26 namespace Frotz {
27
28 #define MAX_OBJECT 2000
29
30 enum O1 {
31 O1_PARENT = 4,
32 O1_SIBLING = 5,
33 O1_CHILD = 6,
34 O1_PROPERTY_OFFSET = 7,
35 O1_SIZE = 9
36 };
37
38 enum O4 {
39 O4_PARENT = 6,
40 O4_SIBLING = 8,
41 O4_CHILD = 10,
42 O4_PROPERTY_OFFSET = 12,
43 O4_SIZE = 14
44 };
45
object_address(zword obj)46 zword Processor::object_address(zword obj) {
47 // Check object number
48 if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
49 print_string("@Attempt to address illegal object ");
50 print_num(obj);
51 print_string(". This is normally fatal.");
52 new_line();
53 runtimeError(ERR_ILL_OBJ);
54 }
55
56 // Return object address
57 if (h_version <= V3)
58 return h_objects + ((obj - 1) * O1_SIZE + 62);
59 else
60 return h_objects + ((obj - 1) * O4_SIZE + 126);
61 }
62
object_name(zword object)63 zword Processor::object_name(zword object) {
64 zword obj_addr;
65 zword name_addr;
66
67 obj_addr = object_address(object);
68
69 // The object name address is found at the start of the properties
70 if (h_version <= V3)
71 obj_addr += O1_PROPERTY_OFFSET;
72 else
73 obj_addr += O4_PROPERTY_OFFSET;
74
75 LOW_WORD(obj_addr, name_addr);
76
77 return name_addr;
78 }
79
first_property(zword obj)80 zword Processor::first_property(zword obj) {
81 zword prop_addr;
82 zbyte size;
83
84 // Fetch address of object name
85 prop_addr = object_name (obj);
86
87 // Get length of object name
88 LOW_BYTE(prop_addr, size);
89
90 // Add name length to pointer
91 return prop_addr + 1 + 2 * size;
92 }
93
next_property(zword prop_addr)94 zword Processor::next_property(zword prop_addr) {
95 zbyte value;
96
97 // Load the current property id
98 LOW_BYTE(prop_addr, value);
99 prop_addr++;
100
101 // Calculate the length of this property
102 if (h_version <= V3)
103 value >>= 5;
104 else if (!(value & 0x80))
105 value >>= 6;
106 else {
107 LOW_BYTE(prop_addr, value);
108 value &= 0x3f;
109
110 if (value == 0)
111 // demanded by Spec 1.0
112 value = 64;
113 }
114
115 // Add property length to current property pointer
116 return prop_addr + value + 1;
117 }
118
unlink_object(zword object)119 void Processor::unlink_object(zword object) {
120 zword obj_addr;
121 zword parent_addr;
122 zword sibling_addr;
123
124 if (object == 0) {
125 runtimeError(ERR_REMOVE_OBJECT_0);
126 return;
127 }
128
129 obj_addr = object_address(object);
130
131 if (h_version <= V3) {
132
133 zbyte parent;
134 zbyte younger_sibling;
135 zbyte older_sibling;
136 zbyte zero = 0;
137
138 // Get parent of object, and return if no parent
139 obj_addr += O1_PARENT;
140 LOW_BYTE(obj_addr, parent);
141 if (!parent)
142 return;
143
144 // Get (older) sibling of object and set both parent and sibling pointers to 0
145 SET_BYTE(obj_addr, zero);
146 obj_addr += O1_SIBLING - O1_PARENT;
147 LOW_BYTE(obj_addr, older_sibling);
148 SET_BYTE(obj_addr, zero);
149
150 // Get first child of parent (the youngest sibling of the object)
151 parent_addr = object_address(parent) + O1_CHILD;
152 LOW_BYTE(parent_addr, younger_sibling);
153
154 // Remove object from the list of siblings
155 if (younger_sibling == object)
156 SET_BYTE(parent_addr, older_sibling);
157 else {
158 do {
159 sibling_addr = object_address(younger_sibling) + O1_SIBLING;
160 LOW_BYTE(sibling_addr, younger_sibling);
161 } while (younger_sibling != object);
162 SET_BYTE(sibling_addr, older_sibling);
163 }
164 } else {
165 zword parent;
166 zword younger_sibling;
167 zword older_sibling;
168 zword zero = 0;
169
170 // Get parent of object, and return if no parent
171 obj_addr += O4_PARENT;
172 LOW_WORD(obj_addr, parent);
173 if (!parent)
174 return;
175
176 // Get (older) sibling of object and set both parent and sibling pointers to 0
177 SET_WORD(obj_addr, zero);
178 obj_addr += O4_SIBLING - O4_PARENT;
179 LOW_WORD(obj_addr, older_sibling);
180 SET_WORD(obj_addr, zero);
181
182 // Get first child of parent (the youngest sibling of the object)
183 parent_addr = object_address(parent) + O4_CHILD;
184 LOW_WORD(parent_addr, younger_sibling);
185
186 // Remove object from the list of siblings
187 if (younger_sibling == object) {
188 SET_WORD(parent_addr, older_sibling);
189 } else {
190 do {
191 sibling_addr = object_address(younger_sibling) + O4_SIBLING;
192 LOW_WORD(sibling_addr, younger_sibling);
193 } while (younger_sibling != object);
194 SET_WORD(sibling_addr, older_sibling);
195 }
196 }
197 }
198
z_clear_attr()199 void Processor::z_clear_attr() {
200 zword obj_addr;
201 zbyte value;
202
203 if (_storyId == SHERLOCK)
204 if (zargs[1] == 48)
205 return;
206
207 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
208 runtimeError(ERR_ILL_ATTR);
209
210 // If we are monitoring attribute assignment display a short note
211 if (_attribute_assignment) {
212 stream_mssg_on();
213 print_string("@clear_attr ");
214 print_object(zargs[0]);
215 print_string(" ");
216 print_num(zargs[1]);
217 stream_mssg_off();
218 }
219
220 if (zargs[0] == 0) {
221 runtimeError(ERR_CLEAR_ATTR_0);
222 return;
223 }
224
225 // Get attribute address
226 obj_addr = object_address(zargs[0]) + zargs[1] / 8;
227
228 // Clear attribute bit
229 LOW_BYTE(obj_addr, value);
230 value &= ~(0x80 >> (zargs[1] & 7));
231 SET_BYTE(obj_addr, value);
232 }
233
z_jin()234 void Processor::z_jin() {
235 zword obj_addr;
236
237 // If we are monitoring object locating display a short note
238 if (_object_locating) {
239 stream_mssg_on();
240 print_string("@jin ");
241 print_object(zargs[0]);
242 print_string(" ");
243 print_object(zargs[1]);
244 stream_mssg_off();
245 }
246
247 if (zargs[0] == 0) {
248 runtimeError(ERR_JIN_0);
249 branch(0 == zargs[1]);
250 return;
251 }
252
253 obj_addr = object_address(zargs[0]);
254
255 if (h_version <= V3) {
256 zbyte parent;
257
258 // Get parent id from object
259 obj_addr += O1_PARENT;
260 LOW_BYTE(obj_addr, parent);
261
262 // Branch if the parent is obj2
263 branch(parent == zargs[1]);
264
265 } else {
266 zword parent;
267
268 // Get parent id from object
269 obj_addr += O4_PARENT;
270 LOW_WORD(obj_addr, parent);
271
272 // Branch if the parent is obj2
273 branch(parent == zargs[1]);
274 }
275 }
276
z_get_child()277 void Processor::z_get_child() {
278 zword obj_addr;
279
280 // If we are monitoring object locating display a short note
281 if (_object_locating) {
282 stream_mssg_on();
283 print_string("@get_child ");
284 print_object(zargs[0]);
285 stream_mssg_off();
286 }
287
288 if (zargs[0] == 0) {
289 runtimeError(ERR_GET_CHILD_0);
290 store(0);
291 branch(false);
292 return;
293 }
294
295 obj_addr = object_address(zargs[0]);
296
297 if (h_version <= V3) {
298 zbyte child;
299
300 // Get child id from object
301 obj_addr += O1_CHILD;
302 LOW_BYTE(obj_addr, child);
303
304 // Store child id and branch
305 store(child);
306 branch(child);
307 } else {
308 zword child;
309
310 // Get child id from object
311 obj_addr += O4_CHILD;
312 LOW_WORD(obj_addr, child);
313
314 // Store child id and branch
315 store(child);
316 branch(child);
317 }
318 }
319
z_get_next_prop()320 void Processor::z_get_next_prop() {
321 zword prop_addr;
322 zbyte value;
323 zbyte mask;
324
325 if (zargs[0] == 0) {
326 runtimeError(ERR_GET_NEXT_PROP_0);
327 store(0);
328 return;
329 }
330
331 // Property id is in bottom five (six) bits
332 mask = (h_version <= V3) ? 0x1f : 0x3f;
333
334 // Load address of first property
335 prop_addr = first_property(zargs[0]);
336
337 if (zargs[1] != 0) {
338 // Scan down the property list
339 do {
340 LOW_BYTE(prop_addr, value);
341 prop_addr = next_property(prop_addr);
342 } while ((value & mask) > zargs[1]);
343
344 // Exit if the property does not exist
345 if ((value & mask) != zargs[1])
346 runtimeError(ERR_NO_PROP);
347 }
348
349 // Return the property id
350 LOW_BYTE(prop_addr, value);
351 store((zword)(value & mask));
352 }
353
z_get_parent()354 void Processor::z_get_parent() {
355 zword obj_addr;
356
357 // If we are monitoring object locating display a short note
358 if (_object_locating) {
359 stream_mssg_on();
360 print_string("@get_parent ");
361 print_object(zargs[0]);
362 stream_mssg_off();
363 }
364
365 if (zargs[0] == 0) {
366 runtimeError(ERR_GET_PARENT_0);
367 store(0);
368 return;
369 }
370
371 obj_addr = object_address(zargs[0]);
372
373 if (h_version <= V3) {
374 zbyte parent;
375
376 // Get parent id from object
377 obj_addr += O1_PARENT;
378 LOW_BYTE(obj_addr, parent);
379
380 // Store parent
381 store(parent);
382
383 } else {
384 zword parent;
385
386 // Get parent id from object
387 obj_addr += O4_PARENT;
388 LOW_WORD(obj_addr, parent);
389
390 // Store parent
391 store(parent);
392 }
393 }
394
z_get_prop()395 void Processor::z_get_prop() {
396 zword prop_addr;
397 zword wprop_val;
398 zbyte bprop_val;
399 zbyte value;
400 zbyte mask;
401
402 if (zargs[0] == 0) {
403 runtimeError(ERR_GET_PROP_0);
404 store(0);
405 return;
406 }
407
408 // Property id is in bottom five (six) bits
409 mask = (h_version <= V3) ? 0x1f : 0x3f;
410
411 // Load address of first property
412 prop_addr = first_property(zargs[0]);
413
414 // Scan down the property list
415 for (;;) {
416 LOW_BYTE(prop_addr, value);
417 if ((value & mask) <= zargs[1])
418 break;
419 prop_addr = next_property(prop_addr);
420 }
421
422 if ((value & mask) == zargs[1]) {
423 // property found
424
425 // Load property(byte or word sized)
426 prop_addr++;
427
428 if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
429 LOW_BYTE(prop_addr, bprop_val);
430 wprop_val = bprop_val;
431 } else {
432 LOW_WORD(prop_addr, wprop_val);
433 }
434 } else {
435 // property not found
436
437 // Load default value
438 prop_addr = h_objects + 2 * (zargs[1] - 1);
439 LOW_WORD(prop_addr, wprop_val);
440 }
441
442 // Store the property value
443 store(wprop_val);
444 }
445
z_get_prop_addr()446 void Processor::z_get_prop_addr() {
447 zword prop_addr;
448 zbyte value;
449 zbyte mask;
450
451 if (zargs[0] == 0) {
452 runtimeError(ERR_GET_PROP_ADDR_0);
453 store(0);
454 return;
455 }
456
457 if (_storyId == BEYOND_ZORK)
458 if (zargs[0] > MAX_OBJECT)
459 { store(0); return; }
460
461 // Property id is in bottom five (six) bits
462 mask = (h_version <= V3) ? 0x1f : 0x3f;
463
464 // Load address of first property
465 prop_addr = first_property(zargs[0]);
466
467 // Scan down the property list
468 for (;;) {
469 LOW_BYTE(prop_addr, value);
470 if ((value & mask) <= zargs[1])
471 break;
472 prop_addr = next_property(prop_addr);
473 }
474
475 // Calculate the property address or return zero
476 if ((value & mask) == zargs[1]) {
477
478 if (h_version >= V4 && (value & 0x80))
479 prop_addr++;
480 store((zword)(prop_addr + 1));
481
482 } else {
483 store(0);
484 }
485 }
486
z_get_prop_len()487 void Processor::z_get_prop_len() {
488 zword addr;
489 zbyte value;
490
491 // Back up the property pointer to the property id
492 addr = zargs[0] - 1;
493 LOW_BYTE(addr, value);
494
495 // Calculate length of property
496 if (h_version <= V3)
497 value = (value >> 5) + 1;
498 else if (!(value & 0x80))
499 value = (value >> 6) + 1;
500 else {
501 value &= 0x3f;
502
503 if (value == 0)
504 value = 64; // demanded by Spec 1.0
505 }
506
507 // Store length of property
508 store(value);
509 }
510
z_get_sibling()511 void Processor::z_get_sibling() {
512 zword obj_addr;
513
514 if (zargs[0] == 0) {
515 runtimeError(ERR_GET_SIBLING_0);
516 store(0);
517 branch(false);
518 return;
519 }
520
521 obj_addr = object_address(zargs[0]);
522
523 if (h_version <= V3) {
524 zbyte sibling;
525
526 // Get sibling id from object
527 obj_addr += O1_SIBLING;
528 LOW_BYTE(obj_addr, sibling);
529
530 // Store sibling and branch
531 store(sibling);
532 branch(sibling);
533
534 } else {
535 zword sibling;
536
537 // Get sibling id from object
538 obj_addr += O4_SIBLING;
539 LOW_WORD(obj_addr, sibling);
540
541 // Store sibling and branch
542 store(sibling);
543 branch(sibling);
544 }
545 }
546
z_insert_obj()547 void Processor::z_insert_obj() {
548 zword obj1 = zargs[0];
549 zword obj2 = zargs[1];
550 zword obj1_addr;
551 zword obj2_addr;
552
553 // If we are monitoring object movements display a short note
554 if (_object_movement) {
555 stream_mssg_on();
556 print_string("@move_obj ");
557 print_object(obj1);
558 print_string(" ");
559 print_object(obj2);
560 stream_mssg_off();
561 }
562
563 if (obj1 == 0) {
564 runtimeError(ERR_MOVE_OBJECT_0);
565 return;
566 }
567
568 if (obj2 == 0) {
569 runtimeError(ERR_MOVE_OBJECT_TO_0);
570 return;
571 }
572
573 // Get addresses of both objects
574 obj1_addr = object_address(obj1);
575 obj2_addr = object_address(obj2);
576
577 // Remove object 1 from current parent
578 unlink_object(obj1);
579
580 // Make object 1 first child of object 2
581 if (h_version <= V3) {
582 zbyte child;
583
584 obj1_addr += O1_PARENT;
585 SET_BYTE(obj1_addr, obj2);
586 obj2_addr += O1_CHILD;
587 LOW_BYTE(obj2_addr, child);
588 SET_BYTE(obj2_addr, obj1);
589 obj1_addr += O1_SIBLING - O1_PARENT;
590 SET_BYTE(obj1_addr, child);
591
592 } else {
593 zword child;
594
595 obj1_addr += O4_PARENT;
596 SET_WORD(obj1_addr, obj2);
597 obj2_addr += O4_CHILD;
598 LOW_WORD(obj2_addr, child);
599 SET_WORD(obj2_addr, obj1);
600 obj1_addr += O4_SIBLING - O4_PARENT;
601 SET_WORD(obj1_addr, child);
602 }
603 }
604
z_put_prop()605 void Processor::z_put_prop() {
606 zword prop_addr;
607 zword value;
608 zbyte mask;
609
610 if (zargs[0] == 0) {
611 runtimeError(ERR_PUT_PROP_0);
612 return;
613 }
614
615 // Property id is in bottom five or six bits
616 mask = (h_version <= V3) ? 0x1f : 0x3f;
617
618 // Load address of first property
619 prop_addr = first_property(zargs[0]);
620
621 // Scan down the property list
622 for (;;) {
623 LOW_BYTE(prop_addr, value);
624 if ((value & mask) <= zargs[1])
625 break;
626
627 prop_addr = next_property(prop_addr);
628 }
629
630 // Exit if the property does not exist
631 if ((value & mask) != zargs[1])
632 runtimeError(ERR_NO_PROP);
633
634 // Store the new property value (byte or word sized)
635 prop_addr++;
636
637 if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
638 zbyte v = zargs[2];
639 SET_BYTE(prop_addr, v);
640 } else {
641 zword v = zargs[2];
642 SET_WORD(prop_addr, v);
643 }
644 }
645
z_remove_obj()646 void Processor::z_remove_obj() {
647 // If we are monitoring object movements display a short note
648 if (_object_movement) {
649 stream_mssg_on();
650 print_string("@remove_obj ");
651 print_object(zargs[0]);
652 stream_mssg_off();
653 }
654
655 // Call unlink_object to do the job
656 unlink_object(zargs[0]);
657 }
658
z_set_attr()659 void Processor::z_set_attr() {
660 zword obj_addr;
661 zbyte value;
662
663 if (_storyId == SHERLOCK)
664 if (zargs[1] == 48)
665 return;
666
667 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
668 runtimeError(ERR_ILL_ATTR);
669
670 // If we are monitoring attribute assignment display a short note
671 if (_attribute_assignment) {
672 stream_mssg_on();
673 print_string("@set_attr ");
674 print_object(zargs[0]);
675 print_string(" ");
676 print_num(zargs[1]);
677 stream_mssg_off();
678 }
679
680 if (zargs[0] == 0) {
681 runtimeError(ERR_SET_ATTR_0);
682 return;
683 }
684
685 // Get attribute address
686 obj_addr = object_address(zargs[0]) + zargs[1] / 8;
687
688 // Load attribute byte
689 LOW_BYTE(obj_addr, value);
690
691 // Set attribute bit
692 value |= 0x80 >> (zargs[1] & 7);
693
694 // Store attribute byte
695 SET_BYTE(obj_addr, value);
696 }
697
z_test_attr()698 void Processor::z_test_attr() {
699 zword obj_addr;
700 zbyte value;
701
702 if (zargs[1] > ((h_version <= V3) ? 31 : 47))
703 runtimeError(ERR_ILL_ATTR);
704
705 // If we are monitoring attribute testing display a short note
706 if (_attribute_testing) {
707 stream_mssg_on();
708 print_string("@test_attr ");
709 print_object(zargs[0]);
710 print_string(" ");
711 print_num(zargs[1]);
712 stream_mssg_off();
713 }
714
715 if (zargs[0] == 0) {
716 runtimeError(ERR_TEST_ATTR_0);
717 branch(false);
718 return;
719 }
720
721 // Get attribute address
722 obj_addr = object_address(zargs[0]) + zargs[1] / 8;
723
724 // Load attribute byte
725 LOW_BYTE(obj_addr, value);
726
727 // Test attribute
728 branch(value & (0x80 >> (zargs[1] & 7)));
729 }
730
731 } // End of namespace Frotz
732 } // End of namespace Glk
733