/*
  Object
*/

#include "object.h"

#include "head.h"
#include "jump.h"
#include "mem.h"
#include "print.h"
#include "var.h"

#define std_attr    	0
#define std_locn    	4
#define std_link    	5
#define std_hold    	6
#define std_prop    	7
#define std_size	9

#define plus_attr    	0
#define plus_locn    	6
#define plus_link    	8
#define plus_hold    	10
#define plus_prop    	12
#define plus_size	14

#define std_base	0x35
#define plus_base	0x70

#define FIRST_ATTR      ((byte) 0x80)

#define static

static long_word std_addr(word obj)
{
  return hd_object() + (long_word) obj * std_size + std_base;
}

static long_word plus_addr(word obj)
{
  return hd_object() + (long_word) obj * plus_size + plus_base;
}

/* Support */

static word get_locn(word obj)
{
  if(hd_plus())
    return rd_word_addr(plus_addr(obj) + plus_locn);
  else
    return rd_byte_addr(std_addr(obj) + std_locn);
}

static void set_locn(word obj, word locn)
{
  if(hd_plus())
    wr_word_addr(plus_addr(obj) + plus_locn, locn);
  else
    wr_byte_addr(std_addr(obj) + std_locn, locn);
}

static word get_link(word obj)
{
  if(hd_plus())
    return rd_word_addr(plus_addr(obj) + plus_link);
  else
    return rd_byte_addr(std_addr(obj) + std_link);
}

static void set_link(word obj, word link)
{
  if(hd_plus())
    wr_word_addr(plus_addr(obj) + plus_link, link);
  else
    wr_byte_addr(std_addr(obj) + std_link, link);
}

static word get_hold(word obj)
{
  if(hd_plus())
    return rd_word_addr(plus_addr(obj) + plus_hold);
  else
    return rd_byte_addr(std_addr(obj) + std_hold);
}

static void set_hold(word obj, word hold)
{
  if(hd_plus())
    wr_word_addr(plus_addr(obj) + plus_hold, hold);
  else
    wr_byte_addr(std_addr(obj) + std_hold, hold);
}

/* Main routines */

void obj_remove(word obj1)
{
  word obj2 = get_locn(obj1);
  if(obj2)
  {
    if(get_hold(obj2) == obj1)
    {
      set_hold(obj2, get_link(obj1));
    }
    else
    {
      obj2 = get_hold(obj2);
      while(get_link(obj2) != obj1)
        obj2 = get_link(obj2);
      set_link(obj2, get_link(obj1));
    }
    set_locn(obj1, 0);
    set_link(obj1, 0);
  }
}

void obj_transfer(word obj1, word obj2)
{
  /* Move obj1 into obj2 */
  obj_remove(obj1);
  set_link(obj1, get_hold(obj2));
  set_locn(obj1, obj2);
  set_hold(obj2, obj1);
}

void obj_holds(word obj)
{
  word hold = get_hold(obj);
  store(hold);
  ret_value(hold != 0);
}

void obj_link(word obj)
{
  word link = get_link(obj);
  store(link);
  ret_value(link != 0);
}

static long_word obj_attr(word obj, word attr)
{
  word off = (word) attr >> 3;
  return hd_plus() ? plus_addr(obj) + plus_attr + off
                   : std_addr(obj) + std_attr + off;
}

void obj_test(word obj, word attr)
{
  long_word a = obj_attr(obj, attr);
  byte mask   = ((byte) FIRST_ATTR) >> (attr % 8);
  ret_value((mask & rd_byte_addr(a)) != 0);
}

void obj_set(word obj, word attr)
{
  long_word a = obj_attr(obj, attr);
  byte mask   = ((byte) FIRST_ATTR) >> (attr % 8);
  wr_byte_addr(a, rd_byte_addr(a) | mask);
}

void obj_clr(word obj, word attr)
{
  long_word a = obj_attr(obj, attr);
  byte mask   = ((byte) FIRST_ATTR) >> (attr % 8);
  wr_byte_addr(a, rd_byte_addr(a) & ~mask);
}

void obj_loc(word obj)
{
  store(get_locn(obj));
}

void obj_check(word obj1, word obj2)
{
  ret_value(get_locn(obj1) == obj2);
}

void obj_print(word obj)
{
  print1(obj_prop(obj) + 1);
}

word obj_prop(word obj)
{
  return rd_word_addr(hd_plus() ? plus_addr(obj) + plus_prop
                                : std_addr(obj) + std_prop);
}
