aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/frotz/processor_objects.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/frotz/processor_objects.cpp')
-rw-r--r--engines/glk/frotz/processor_objects.cpp732
1 files changed, 732 insertions, 0 deletions
diff --git a/engines/glk/frotz/processor_objects.cpp b/engines/glk/frotz/processor_objects.cpp
new file mode 100644
index 0000000000..7e1fe6de0c
--- /dev/null
+++ b/engines/glk/frotz/processor_objects.cpp
@@ -0,0 +1,732 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define MAX_OBJECT 2000
+
+enum O1 {
+ O1_PARENT = 4,
+ O1_SIBLING = 5,
+ O1_CHILD = 6,
+ O1_PROPERTY_OFFSET = 7,
+ O1_SIZE = 9
+};
+
+enum O4 {
+ O4_PARENT = 6,
+ O4_SIBLING = 8,
+ O4_CHILD = 10,
+ O4_PROPERTY_OFFSET = 12,
+ O4_SIZE = 14
+};
+
+zword Processor::object_address(zword obj) {
+ // Check object number
+ if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
+ print_string("@Attempt to address illegal object ");
+ print_num(obj);
+ print_string(". This is normally fatal.");
+ new_line();
+ runtimeError(ERR_ILL_OBJ);
+ }
+
+ // Return object address
+ if (h_version <= V3)
+ return h_objects + ((obj - 1) * O1_SIZE + 62);
+ else
+ return h_objects + ((obj - 1) * O4_SIZE + 126);
+}
+
+zword Processor::object_name(zword object) {
+ zword obj_addr;
+ zword name_addr;
+
+ obj_addr = object_address(object);
+
+ // The object name address is found at the start of the properties
+ if (h_version <= V3)
+ obj_addr += O1_PROPERTY_OFFSET;
+ else
+ obj_addr += O4_PROPERTY_OFFSET;
+
+ LOW_WORD(obj_addr, name_addr);
+
+ return name_addr;
+}
+
+zword Processor::first_property(zword obj) {
+ zword prop_addr;
+ zbyte size;
+
+ // Fetch address of object name
+ prop_addr = object_name (obj);
+
+ // Get length of object name
+ LOW_BYTE(prop_addr, size);
+
+ // Add name length to pointer
+ return prop_addr + 1 + 2 * size;
+}
+
+zword Processor::next_property(zword prop_addr) {
+ zbyte value;
+
+ // Load the current property id
+ LOW_BYTE(prop_addr, value);
+ prop_addr++;
+
+ // Calculate the length of this property
+ if (h_version <= V3)
+ value >>= 5;
+ else if (!(value & 0x80))
+ value >>= 6;
+ else {
+ LOW_BYTE(prop_addr, value);
+ value &= 0x3f;
+
+ if (value == 0)
+ // demanded by Spec 1.0
+ value = 64;
+ }
+
+ // Add property length to current property pointer
+ return prop_addr + value + 1;
+}
+
+void Processor::unlink_object(zword object) {
+ zword obj_addr;
+ zword parent_addr;
+ zword sibling_addr;
+
+ if (object == 0) {
+ runtimeError(ERR_REMOVE_OBJECT_0);
+ return;
+ }
+
+ obj_addr = object_address(object);
+
+ if (h_version <= V3) {
+
+ zbyte parent;
+ zbyte younger_sibling;
+ zbyte older_sibling;
+ zbyte zero = 0;
+
+ // Get parent of object, and return if no parent
+ obj_addr += O1_PARENT;
+ LOW_BYTE(obj_addr, parent);
+ if (!parent)
+ return;
+
+ // Get (older) sibling of object and set both parent and sibling pointers to 0
+ SET_BYTE(obj_addr, zero);
+ obj_addr += O1_SIBLING - O1_PARENT;
+ LOW_BYTE(obj_addr, older_sibling);
+ SET_BYTE(obj_addr, zero);
+
+ // Get first child of parent (the youngest sibling of the object)
+ parent_addr = object_address(parent) + O1_CHILD;
+ LOW_BYTE(parent_addr, younger_sibling);
+
+ // Remove object from the list of siblings
+ if (younger_sibling == object)
+ SET_BYTE(parent_addr, older_sibling);
+ else {
+ do {
+ sibling_addr = object_address(younger_sibling) + O1_SIBLING;
+ LOW_BYTE(sibling_addr, younger_sibling);
+ } while (younger_sibling != object);
+ SET_BYTE(sibling_addr, older_sibling);
+ }
+ } else {
+ zword parent;
+ zword younger_sibling;
+ zword older_sibling;
+ zword zero = 0;
+
+ // Get parent of object, and return if no parent
+ obj_addr += O4_PARENT;
+ LOW_WORD(obj_addr, parent);
+ if (!parent)
+ return;
+
+ // Get (older) sibling of object and set both parent and sibling pointers to 0
+ SET_WORD(obj_addr, zero);
+ obj_addr += O4_SIBLING - O4_PARENT;
+ LOW_WORD(obj_addr, older_sibling);
+ SET_WORD(obj_addr, zero);
+
+ // Get first child of parent (the youngest sibling of the object)
+ parent_addr = object_address(parent) + O4_CHILD;
+ LOW_WORD(parent_addr, younger_sibling);
+
+ // Remove object from the list of siblings
+ if (younger_sibling == object) {
+ SET_WORD(parent_addr, older_sibling);
+ } else {
+ do {
+ sibling_addr = object_address(younger_sibling) + O4_SIBLING;
+ LOW_WORD(sibling_addr, younger_sibling);
+ } while (younger_sibling != object);
+ SET_WORD(sibling_addr, older_sibling);
+ }
+ }
+}
+
+void Processor::z_clear_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (_storyId == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute assignment display a short note
+ if (_attribute_assignment) {
+ stream_mssg_on();
+ print_string("@clear_attr ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_num(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_CLEAR_ATTR_0);
+ return;
+ }
+
+ // Get attribute address
+ obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+ // Clear attribute bit
+ LOW_BYTE(obj_addr, value);
+ value &= ~(0x80 >> (zargs[1] & 7));
+ SET_BYTE(obj_addr, value);
+}
+
+void Processor::z_jin() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@jin ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_object(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_JIN_0);
+ branch(0 == zargs[1]);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte parent;
+
+ // Get parent id from object
+ obj_addr += O1_PARENT;
+ LOW_BYTE(obj_addr, parent);
+
+ // Branch if the parent is obj2
+ branch(parent == zargs[1]);
+
+ } else {
+ zword parent;
+
+ // Get parent id from object
+ obj_addr += O4_PARENT;
+ LOW_WORD(obj_addr, parent);
+
+ // Branch if the parent is obj2
+ branch(parent == zargs[1]);
+ }
+}
+
+void Processor::z_get_child() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@get_child ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_CHILD_0);
+ store(0);
+ branch(false);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte child;
+
+ // Get child id from object
+ obj_addr += O1_CHILD;
+ LOW_BYTE(obj_addr, child);
+
+ // Store child id and branch
+ store(child);
+ branch(child);
+ } else {
+ zword child;
+
+ // Get child id from object
+ obj_addr += O4_CHILD;
+ LOW_WORD(obj_addr, child);
+
+ // Store child id and branch
+ store(child);
+ branch(child);
+ }
+}
+
+void Processor::z_get_next_prop() {
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_NEXT_PROP_0);
+ store(0);
+ return;
+ }
+
+ // Property id is in bottom five (six) bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ if (zargs[1] != 0) {
+ // Scan down the property list
+ do {
+ LOW_BYTE(prop_addr, value);
+ prop_addr = next_property(prop_addr);
+ } while ((value & mask) > zargs[1]);
+
+ // Exit if the property does not exist
+ if ((value & mask) != zargs[1])
+ runtimeError(ERR_NO_PROP);
+ }
+
+ // Return the property id
+ LOW_BYTE(prop_addr, value);
+ store((zword) (value & mask));
+}
+
+void Processor::z_get_parent() {
+ zword obj_addr;
+
+ // If we are monitoring object locating display a short note
+ if (_object_locating) {
+ stream_mssg_on();
+ print_string("@get_parent ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_PARENT_0);
+ store(0);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte parent;
+
+ // Get parent id from object
+ obj_addr += O1_PARENT;
+ LOW_BYTE(obj_addr, parent);
+
+ // Store parent
+ store(parent);
+
+ } else {
+ zword parent;
+
+ // Get parent id from object
+ obj_addr += O4_PARENT;
+ LOW_WORD(obj_addr, parent);
+
+ // Store parent
+ store(parent);
+ }
+}
+
+void Processor::z_get_prop() {
+ zword prop_addr;
+ zword wprop_val;
+ zbyte bprop_val;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_PROP_0);
+ store(0);
+ return;
+ }
+
+ // Property id is in bottom five (six) bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ // Scan down the property list
+ for (;;) {
+ LOW_BYTE(prop_addr, value);
+ if ((value & mask) <= zargs[1])
+ break;
+ prop_addr = next_property(prop_addr);
+ }
+
+ if ((value & mask) == zargs[1]) {
+ // property found
+
+ // Load property(byte or word sized)
+ prop_addr++;
+
+ if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+ LOW_BYTE(prop_addr, bprop_val);
+ wprop_val = bprop_val;
+ } else {
+ LOW_WORD(prop_addr, wprop_val);
+ }
+ } else {
+ // property not found
+
+ // Load default value
+ prop_addr = h_objects + 2 * (zargs[1] - 1);
+ LOW_WORD(prop_addr, wprop_val);
+ }
+
+ // Store the property value
+ store(wprop_val);
+}
+
+void Processor::z_get_prop_addr() {
+ zword prop_addr;
+ zbyte value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_PROP_ADDR_0);
+ store(0);
+ return;
+ }
+
+ if (_storyId == BEYOND_ZORK)
+ if (zargs[0] > MAX_OBJECT)
+ { store(0); return; }
+
+ // Property id is in bottom five (six) bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ // Scan down the property list
+ for (;;) {
+ LOW_BYTE(prop_addr, value);
+ if ((value & mask) <= zargs[1])
+ break;
+ prop_addr = next_property(prop_addr);
+ }
+
+ // Calculate the property address or return zero
+ if ((value & mask) == zargs[1]) {
+
+ if (h_version >= V4 && (value & 0x80))
+ prop_addr++;
+ store((zword) (prop_addr + 1));
+
+ } else {
+ store(0);
+ }
+}
+
+void Processor::z_get_prop_len() {
+ zword addr;
+ zbyte value;
+
+ // Back up the property pointer to the property id
+ addr = zargs[0] - 1;
+ LOW_BYTE(addr, value);
+
+ // Calculate length of property
+ if (h_version <= V3)
+ value = (value >> 5) + 1;
+ else if (!(value & 0x80))
+ value = (value >> 6) + 1;
+ else {
+ value &= 0x3f;
+
+ if (value == 0)
+ value = 64; // demanded by Spec 1.0
+ }
+
+ // Store length of property
+ store(value);
+}
+
+void Processor::z_get_sibling() {
+ zword obj_addr;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_GET_SIBLING_0);
+ store(0);
+ branch(false);
+ return;
+ }
+
+ obj_addr = object_address(zargs[0]);
+
+ if (h_version <= V3) {
+ zbyte sibling;
+
+ // Get sibling id from object
+ obj_addr += O1_SIBLING;
+ LOW_BYTE(obj_addr, sibling);
+
+ // Store sibling and branch
+ store(sibling);
+ branch(sibling);
+
+ } else {
+ zword sibling;
+
+ // Get sibling id from object
+ obj_addr += O4_SIBLING;
+ LOW_WORD(obj_addr, sibling);
+
+ // Store sibling and branch
+ store(sibling);
+ branch(sibling);
+ }
+}
+
+void Processor::z_insert_obj() {
+ zword obj1 = zargs[0];
+ zword obj2 = zargs[1];
+ zword obj1_addr;
+ zword obj2_addr;
+
+ // If we are monitoring object movements display a short note
+ if (_object_movement) {
+ stream_mssg_on();
+ print_string("@move_obj ");
+ print_object(obj1);
+ print_string(" ");
+ print_object(obj2);
+ stream_mssg_off();
+ }
+
+ if (obj1 == 0) {
+ runtimeError(ERR_MOVE_OBJECT_0);
+ return;
+ }
+
+ if (obj2 == 0) {
+ runtimeError(ERR_MOVE_OBJECT_TO_0);
+ return;
+ }
+
+ // Get addresses of both objects
+ obj1_addr = object_address(obj1);
+ obj2_addr = object_address(obj2);
+
+ // Remove object 1 from current parent
+ unlink_object(obj1);
+
+ // Make object 1 first child of object 2
+ if (h_version <= V3) {
+ zbyte child;
+
+ obj1_addr += O1_PARENT;
+ SET_BYTE(obj1_addr, obj2);
+ obj2_addr += O1_CHILD;
+ LOW_BYTE(obj2_addr, child);
+ SET_BYTE(obj2_addr, obj1);
+ obj1_addr += O1_SIBLING - O1_PARENT;
+ SET_BYTE(obj1_addr, child);
+
+ } else {
+ zword child;
+
+ obj1_addr += O4_PARENT;
+ SET_WORD(obj1_addr, obj2);
+ obj2_addr += O4_CHILD;
+ LOW_WORD(obj2_addr, child);
+ SET_WORD(obj2_addr, obj1);
+ obj1_addr += O4_SIBLING - O4_PARENT;
+ SET_WORD(obj1_addr, child);
+ }
+}
+
+void Processor::z_put_prop() {
+ zword prop_addr;
+ zword value;
+ zbyte mask;
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_PUT_PROP_0);
+ return;
+ }
+
+ // Property id is in bottom five or six bits
+ mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+ // Load address of first property
+ prop_addr = first_property(zargs[0]);
+
+ // Scan down the property list
+ for (;;) {
+ LOW_BYTE(prop_addr, value);
+ if ((value & mask) <= zargs[1])
+ break;
+
+ prop_addr = next_property(prop_addr);
+ }
+
+ // Exit if the property does not exist
+ if ((value & mask) != zargs[1])
+ runtimeError(ERR_NO_PROP);
+
+ // Store the new property value (byte or word sized)
+ prop_addr++;
+
+ if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+ zbyte v = zargs[2];
+ SET_BYTE(prop_addr, v);
+ } else {
+ zword v = zargs[2];
+ SET_WORD(prop_addr, v);
+ }
+}
+
+void Processor::z_remove_obj() {
+ // If we are monitoring object movements display a short note
+ if (_object_movement) {
+ stream_mssg_on();
+ print_string("@remove_obj ");
+ print_object(zargs[0]);
+ stream_mssg_off();
+ }
+
+ // Call unlink_object to do the job
+ unlink_object(zargs[0]);
+}
+
+void Processor::z_set_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (_storyId == SHERLOCK)
+ if (zargs[1] == 48)
+ return;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute assignment display a short note
+ if (_attribute_assignment) {
+ stream_mssg_on();
+ print_string("@set_attr ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_num(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_SET_ATTR_0);
+ return;
+ }
+
+ // Get attribute address
+ obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+ // Load attribute byte
+ LOW_BYTE(obj_addr, value);
+
+ // Set attribute bit
+ value |= 0x80 >> (zargs[1] & 7);
+
+ // Store attribute byte
+ SET_BYTE(obj_addr, value);
+}
+
+void Processor::z_test_attr() {
+ zword obj_addr;
+ zbyte value;
+
+ if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+ runtimeError(ERR_ILL_ATTR);
+
+ // If we are monitoring attribute testing display a short note
+ if (_attribute_testing) {
+ stream_mssg_on();
+ print_string("@test_attr ");
+ print_object(zargs[0]);
+ print_string(" ");
+ print_num(zargs[1]);
+ stream_mssg_off();
+ }
+
+ if (zargs[0] == 0) {
+ runtimeError(ERR_TEST_ATTR_0);
+ branch(false);
+ return;
+ }
+
+ // Get attribute address
+ obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+ // Load attribute byte
+ LOW_BYTE(obj_addr, value);
+
+ // Test attribute
+ branch(value & (0x80 >> (zargs[1] & 7)));
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle