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