/*
 * object.c
 *
 * Object manipulation routines.
 *
 */

#include "ztypes.h"
#include "object.h"
#include "operand.h"

zbyte_t h_type;
unsigned h_objects_offset;

/*
 * get_object_address
 *
 * Calculate the address of an object in the data area.
 *
 */

unsigned object_address(unsigned obj)
{
    /* Address calculation is object table base + size of default properties area +
       object number-1 * object size */

    if (h_type <= V3)
        return (zword_t)(h_objects_offset + ((P3_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * O3_SIZE));
    else
        return (zword_t)(h_objects_offset + ((P4_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * O4_SIZE));

}/* object_address */

/*
 * insert_obj
 *
 * Insert object 1 as the child of object 2 after first removing it from its
 * previous parent. The object is inserted at the front of the child object
 * chain.
 *
 */

void z_insert_obj(unsigned obj1, unsigned obj2)
{
    unsigned obj1_addr, obj2_addr, child;

    /* Get addresses of both objects */

    obj1_addr = object_address(obj1);
    obj2_addr = object_address(obj2);

    /* Remove object 1 from current parent */

    z_remove_obj(obj1);

    if (h_type <= V3)
    {
        set_byte(obj1_addr + O3_PARENT, obj2);    /* Parent of 1 = 2 */
        child=get_byte(obj2_addr + O3_CHILD);     /* Temp = 1st child of 2 */
        set_byte(obj2_addr + O3_CHILD, obj1);     /* 1st child of 2 = 1 */
        set_byte(obj1_addr + O3_SIBLING, child);  /* Sibling of 1 = Temp */
    }
    else
    {
        set_word(obj1_addr + O4_PARENT, obj2);    /* Parent of 1 = 2 */
        child=get_word(obj2_addr + O4_CHILD);     /* Temp = 1st child of 2 */
        set_word(obj2_addr + O4_CHILD, obj1);     /* 1st child of 2 = 1 */
        set_word(obj1_addr + O4_SIBLING, child);  /* Sibling of 1 = Temp */
    }
}/* insert_obj */

/*
 * remove_obj
 *
 * Remove an object by unlinking from the its parent object and from its
 * siblings.
 *
 */

void z_remove_obj(unsigned obj)
{
    unsigned obj_addr, parent_addr, sibling_addr, parent, younger_sibling, older_sibling;

    /* Get address of object to be removed */

    obj_addr = object_address(obj);

    if (h_type <= V3)
    {
	/* Get parent of object, and return if no parent */

	parent=get_byte(obj_addr + O3_PARENT);
	if (parent == 0)
	    return;

	/* Get (older) sibling of object and set both parent and sibling
	   pointers to 0 */

    	set_byte(obj_addr + O3_PARENT, 0);

	older_sibling = get_byte(obj_addr + O3_SIBLING);
	set_byte(obj_addr + O3_SIBLING, 0);

	/* Get first child of parent (the youngest sibling of the object) */

	parent_addr = object_address(parent);
	younger_sibling = get_byte(parent_addr + O3_CHILD);

	/* Remove object from the list of siblings */

	if (younger_sibling == obj)
	    set_byte(parent_addr + O3_CHILD, older_sibling);
	else
	{
	    do
	    {
		sibling_addr=object_address(younger_sibling);
		younger_sibling=get_byte(sibling_addr + O3_SIBLING);
	    }
	    while (younger_sibling != obj);
	    set_byte(sibling_addr + O3_SIBLING, older_sibling);
	}
    }
    else
    {
	/* Get parent of object, and return if no parent */

	parent=get_word(obj_addr + O4_PARENT);
	if (parent == 0)
	    return;

	/* Get (older) sibling of object and set both parent and sibling
	   pointers to 0 */

	set_word(obj_addr + O4_PARENT, 0);

	older_sibling=get_word(obj_addr + O4_SIBLING);
	set_word(obj_addr + O4_SIBLING, 0);

	/* Get first child of parent (the youngest sibling of the object) */

	parent_addr = object_address(parent);
	younger_sibling=get_word(parent_addr + O4_CHILD);

	/* Remove object from the list of siblings */

	if (younger_sibling == obj)
	    set_word(parent_addr + O4_CHILD, older_sibling);
	else
	{
	    do
	    {
		sibling_addr = object_address(younger_sibling);
		younger_sibling=get_word(sibling_addr + O4_SIBLING);
	    }
	    while (younger_sibling != obj);
	    set_word(sibling_addr + O4_SIBLING, older_sibling);
	}
    }
}/* remove_obj */

/*
 * get_parent
 *
 * Load the parent object pointer of an object
 *
 */

void z_get_parent(unsigned obj)
{
    if (h_type <= V3)
    	store_operand(get_byte(object_address(obj) + O3_PARENT));
    else
    	store_operand(get_word(object_address(obj) + O4_PARENT));

}/* get_parent */

/*
 * get_child
 *
 * Load the child object pointer of an object and jump if the child pointer is
 * not NULL.
 *
 */

void z_get_child(unsigned obj)
{
    unsigned child;

    if (h_type <= V3)
    	child = get_byte(object_address(obj) + O3_CHILD);
    else
    	child = get_word(object_address(obj) + O4_CHILD);

    store_operand(child);

    conditional_jump(child != 0);

}/* get_child */

/*
 * get_sibling
 *
 * Load the next child object pointer of an object and jump if the next child
 * pointer is not NULL.
 *
 */

void z_get_sibling(unsigned obj)
{
    unsigned next;

    if (h_type <= V3)
    	next = get_byte(object_address(obj) + O3_SIBLING);
    else
    	next = get_word(object_address(obj) + O4_SIBLING);

    store_operand(next);

    conditional_jump(next != 0);

}/* get_sibling */

/*
 * jin
 *
 * Jump if object 2 is the parent of object 1
 *
 */

void z_jin(unsigned obj1, unsigned obj2)
{
    unsigned parent;

    if (h_type <= V3)
    	parent = get_byte(object_address(obj1) + O3_PARENT);
    else
    	parent = get_word(object_address(obj1) + O4_PARENT);

    conditional_jump(parent == obj2);

}/* jin */

/*
 * test_attr
 *
 * Test if an attribute bit is set.
 *
 */

void z_test_attr(unsigned obj, unsigned bit)
{
    unsigned objp;
    unsigned value;

    /* Get attribute address */

    objp = object_address(obj) + (bit >> 3);

    /* Load attribute byte */

    value = get_byte (objp);

    /* Test attribute */

    conditional_jump((value >> (7 - (bit & 7))) & 1);

}/* test_attr */

/*
 * set_attr
 *
 * Set an attribute bit.
 *
 */

void z_set_attr(unsigned obj, unsigned bit)
{
    unsigned objp;
    unsigned value;

    /* Get attribute address */

    objp = object_address (obj) + (bit >> 3);

    /* Load attribute byte */

    value = get_byte (objp);

    /* Set attribute bit */

    value |= 0x80 >> (bit & 7);

    /* Store attribute byte */

    set_byte(objp, value);

}/* set_attr */

/*
 * clear_attr
 *
 * Clear an attribute bit
 *
 */

void z_clear_attr(unsigned obj, unsigned bit)
{
    unsigned objp;
    unsigned value;

    /* Get attribute address */

    objp = object_address(obj) + (bit >> 3);

    /* Load attribute byte */

    value = get_byte(objp);

    /* Clear attribute bit */

    value &= ~(0x80 >> (bit & 7));

    /* Store attribute byte */

    set_byte(objp, value);

}/* clear_attr */

