/* xldmem - xlisp dynamic memory management routines */
/* Copyright (c) 1989, by David Michael Betz.                            */
/* You may give out copies of this software; for conditions see the file */
/* COPYING included with this distribution.                              */

#include "xlisp.h"

typedef LVAL (*subrfun)(V);

static LVAL finalize_registered, finalize_pending;
static VOID check_finalize(V);
static VOID do_finalize(V);
static VOID check_weak_boxes(V);

#ifdef NEWGC

/* gc tuning parameters */
/**** these should probably be in special variables */
#define ALLOC_THRESHOLD_INCREMENT 0x20000L
#define GC_EXPAND_FRAC 0.2
#define FULL_GC_FRAC 0.85

static double gc_expand_frac = GC_EXPAND_FRAC;
static double full_gc_frac = FULL_GC_FRAC;

/* position offsets for doubly linked list heads in segment */
#define IMMEDIATE_OFFSET 0
#define ALLOCATED_OFFSET 1
#define IMMEDIATE_TENURED_OFFSET 2
#define ALLOCATED_TENURED_OFFSET 3
#define IMMEDIATE_TO_NEW_OFFSET 4
#define ALLOCATED_TO_NEW_OFFSET 5
#define NUM_OFFSETS 6
#define TENURED_OFFSET(n) (type_info[(int)ntype(n)].tenured_offset)
#define TO_NEW_OFFSET(n)  (type_info[(int)ntype(n)].to_new_offset)

/* limits for segment size - max is 2^15 - 5 for 16-bit shorts */
#define MIN_ANODES 1
#define MAX_ANODES ((1 << 15) - NUM_OFFSETS - 1)

/* macro to compute the size of a segment */
#define segsize(n) (sizeof(SEGMENT)+((n)-1)*sizeof(struct node))

#ifdef TIMES
static unsigned long gctime;    /* calcuate time in garbage collector */
#endif

/* macros to convert between size in bytes and vector size */
#define btow_size(n) (((unsigned)(n)+(sizeof(LVAL)-1))/(unsigned)sizeof(LVAL))
#define wtob_size(n) (((long) (n)) * sizeof(LVAL))

/* variables local to xldmem.c and xlimage.c */
SEGMENT *segs,*lastseg,*fixseg,*charseg,*lastpermseg;
int anodes,nsegs;

/* local variables */
static long minor_gccalls;
static long nperm,ntenured,alloc_threshold,threshold_crossing_gccount;
static SEGMENT *curseg;
static LVAL curbase, curtop;

/* structure for holding information about types */
struct {
  int has_children, allocated;
  int tenured_offset, to_new_offset;
} type_info[NUMTYPES];

/* macros using the structure */
#define node_has_children(node) (type_info[(int)ntype(node)].has_children)
#define type_is_allocated(type) (type_info[(int)type].allocated)
#define node_is_allocated(node) (type_info[(int)ntype(node)].allocated)

/* macros to check/set whether a node is in new or old space */
static int old_code = 0;
#define is_old_node(x) (ngcflag1(x)==old_code)
#define is_new_node(x) (ngcflag1(x)!=old_code)
#define set_old_node(x) (old_code ? setngcflag1(x) : unsetngcflag1(x))
#define set_new_node(x) (old_code ? unsetngcflag1(x) : setngcflag1(x))

/* macros to check/set whether a node is in the to new list */
#define is_to_new_node(x) (ngcflag2(x))
#define set_to_new_node(x) (setngcflag2(x))
#define unset_to_new_node(x) (unsetngcflag2(x))

/* macros to access and set the offsets of a node */
#define NEXT_NODE_OFFSET(x) ((x)->n_gc.n_gc_offsets.forward)
#define SET_NEXT_NODE_OFFSET(x,p) ((x)->n_gc.n_gc_offsets.forward = (p))
#define LAST_NODE_OFFSET(x) ((x)->n_gc.n_gc_offsets.backward)
#define SET_LAST_NODE_OFFSET(x,p) ((x)->n_gc.n_gc_offsets.backward = (p))
#define NODE_BASE_OFFSET(x) ((x)->n_base_offset)
#define SET_NODE_BASE_OFFSET(x,p) ((x)->n_base_offset = (p))

/* macros to access and set the forwarding pointer of a node */
#define NEXT_NODE_PTR(x) ((x)->n_gc.n_next)
#define SET_NEXT_NODE_PTR(x,p) ((x)->n_gc.n_next = (p))

/* macros to access and set the base (first node in seg.) pointer of a node */
#define NODE_BASE(x) ((x) - NODE_BASE_OFFSET(x))
#define SET_NODE_BASE(x, p) SET_NODE_BASE_OFFSET(x,(x) - (p))

/* macros to access and set linked list pointers */
#define NEXT_NODE(x) (NODE_BASE(x) + NEXT_NODE_OFFSET(x))
#define SET_NEXT_NODE(x,p) SET_NEXT_NODE_OFFSET(x,(p)-NODE_BASE(x))
#define LAST_NODE(x) (NODE_BASE(x) + LAST_NODE_OFFSET(x))
#define SET_LAST_NODE(x,p) SET_LAST_NODE_OFFSET(x,(p)-NODE_BASE(x))

/* macro to unlink a node from its current linked list */
#define UNLINK_NODE(x) {\
  register LVAL __x, base; \
  register  short lnox, nnox; \
  __x = (x); base = NODE_BASE(__x); \
  lnox = LAST_NODE_OFFSET(__x); nnox = NEXT_NODE_OFFSET(__x); \
  SET_NEXT_NODE_OFFSET(base + lnox,nnox); \
  SET_LAST_NODE_OFFSET(base + nnox,lnox); \
}

/* macro to link a node x after a node p in a linked list in the same seg. */
#define LINK_NODE(x,p) {\
  register LVAL __x, __p; \
  register short ox, op, nnop; \
  __x = (x); __p = (p); \
  nnop = NEXT_NODE_OFFSET(__p); \
  ox = NODE_BASE_OFFSET(__x); op = NODE_BASE_OFFSET(__p);\
  SET_NEXT_NODE_OFFSET(__p,ox); \
  SET_LAST_NODE_OFFSET(__x,op); \
  SET_NEXT_NODE_OFFSET(__x,nnop); \
  SET_LAST_NODE_OFFSET(NODE_BASE(__x) + nnop,ox); \
}

/* macro to link a node to the base with ffset b */
#define LINK_NODE_TO_BASE(x,b) LINK_NODE(x,NODE_BASE(x)+(b))

/* macro to move a node from one linked list to another in its segment */
#define MOVE_NODE(x,p) { UNLINK_NODE(x); LINK_NODE(x,p); }

/* macro to move a node to the list with base offset b */
#define MOVE_NODE_TO_BASE(x,b) MOVE_NODE(x,NODE_BASE(x)+(b))

/* macro to move all the nodes in a list except the base to another list */
#define MOVE_LIST(a,b) {\
  register LVAL nna, lna, nnb; \
  nna = NEXT_NODE(a); lna = LAST_NODE(a); nnb = NEXT_NODE(b); \
  if (a != nna) { \
    SET_NEXT_NODE(a,a); \
    SET_LAST_NODE(a,a); \
    SET_NEXT_NODE(b,nna); \
    SET_LAST_NODE(nna,b); \
    SET_NEXT_NODE(lna,nnb); \
    SET_LAST_NODE(nnb,lna); \
  } \
}

/* macro to find the next free node; also advances the pointer */
#define NEXT_FREE_NODE() \
  (curtop = curbase + NEXT_NODE_OFFSET(curtop), \
   (curtop == curbase) ? NIL : curtop)

/* macro to handle creating references from old to new space */
/* This assumes NIL is marked as 'old' and 'to_new' */
#define check_old_to_new(x,y) { \
  if (is_old_node(x) && is_new_node(y)) { \
    if (! is_to_new_node(x)) { \
      MOVE_NODE_TO_BASE(x, TO_NEW_OFFSET(x)); \
      set_to_new_node(x); \
    } \
  } \
}

/* forward declarations */
LOCAL LVAL newnode P1H(int);
LOCAL LVAL allocvector P2H(int, unsigned);
LOCAL int  addseg(V);
LOCAL VOID stats(V);
LOCAL VOID check_alloc_threshold P1H(long);
LOCAL VOID adjust_alloc_threshold(V);
LOCAL VOID expand_node_space(V);
LOCAL VOID morevmem P1H(int);
LOCAL VOID ggc P1H(int);
LOCAL VOID clean_free_allocated_node P1H(LVAL);
LOCAL char *IViewNewAData P4H(int, int, long *, int);

/* xlminit - initialize the dynamic memory module */
VOID xlminit(V)
{
    LVAL p;
    int i;

    /* initialize our internal variables */
    segs = lastseg = curseg = lastpermseg = NULL;
    nnodes = nfree = total = gccalls = minor_gccalls = nperm = ntenured = 0L;
    alloc_threshold = ALLOC_THRESHOLD_INCREMENT;
    threshold_crossing_gccount = 0L;
    nsegs = 0;
    anodes = NNODES;
    gctime = 0L;

    for (i = 0; i < NUMTYPES; i++) {
      type_info[i].has_children = FALSE;
      type_info[i].allocated = TRUE;
    }
    type_info[FREE].allocated = FALSE;
    type_info[SUBR].allocated = FALSE;
    type_info[FSUBR].allocated = FALSE;
    type_info[CONS].allocated = FALSE;
    type_info[CONS].has_children = TRUE;
    type_info[SYMBOL].has_children = TRUE;
    type_info[FIXNUM].allocated = FALSE;
    type_info[FLONUM].allocated = FALSE;
#ifdef BIGNUMS
    type_info[RATIO].allocated = FALSE;
    type_info[RATIO].has_children = TRUE;
#endif
    type_info[OBJECT].has_children = TRUE;
    type_info[VECTOR].has_children = TRUE;
    type_info[CLOSURE].has_children = TRUE;
    type_info[CHAR].allocated = FALSE;
    type_info[USTREAM].allocated = FALSE;
    type_info[USTREAM].has_children = TRUE;
    type_info[COMPLEX].allocated = FALSE;
    type_info[COMPLEX].has_children = TRUE;
    type_info[RNDSTATE].allocated = FALSE;
    type_info[RNDSTATE].has_children = TRUE;
    type_info[NATPTR].allocated = FALSE;
    type_info[NATPTR].has_children = TRUE;
    type_info[WEAKBOX].allocated = FALSE;
    type_info[WEAKBOX].has_children = FALSE;
    type_info[DARRAY].allocated = FALSE;
    type_info[DARRAY].has_children = TRUE;
    type_info[STRUCT].has_children = TRUE;
#ifdef BYTECODE
    type_info[BCCLOSURE].allocated = FALSE;
    type_info[BCCLOSURE].has_children = TRUE;
    type_info[CPSNODE].has_children = TRUE;
    type_info[BCODE].has_children = TRUE;
#endif /*BYTECODE */
#ifdef PACKAGES
    type_info[PACKAGE].has_children = TRUE;
#endif /* PACKAGES */
    for (i = 0; i < NUMTYPES; i++) {
      if (type_info[i].allocated) {
	type_info[i].tenured_offset = ALLOCATED_TENURED_OFFSET;
	type_info[i].to_new_offset = ALLOCATED_TO_NEW_OFFSET;
      }
      else {
	type_info[i].tenured_offset = IMMEDIATE_TENURED_OFFSET;
	type_info[i].to_new_offset = IMMEDIATE_TO_NEW_OFFSET;
      }
    }

    /* allocate the fixnum segment */
    if ((fixseg = newsegment(SFIXSIZE)) == NULL)
	xlfatal("insufficient memory");

    /* initialize the fixnum segment */
    p = &fixseg->sg_nodes[0] + NUM_OFFSETS;
    for (i = SFIXMIN; i <= SFIXMAX; ++i) {
	setntype(p, FIXNUM);
	p->n_fixnum = i;
	set_old_node(p);
	++p;
	nperm++;
    }

    /* allocate the character segment */
    if ((charseg = newsegment(CHARSIZE)) == NULL)
	xlfatal("insufficient memory");

    /* initialize the character segment */
    p = &charseg->sg_nodes[0] + NUM_OFFSETS;
    for (i = CHARMIN; i <= CHARMAX; ++i) {
	setntype(p, CHAR);
	p->n_chcode = i;
	set_old_node(p);
	++p;
	nperm++;
    }

    /* set pointer to last permanent segment */
    lastpermseg = lastseg;

    /* allocate an initial segment */
    if (! addseg())
	xlfatal("insufficient memory");
    curseg = lastpermseg->sg_next;
    curtop = curbase = &curseg->sg_nodes[0];
    
    /* initialize structures that are marked by the collector */
    obarray = NULL;
    xlenv = xlfenv = xldenv = NIL;
    s_gcflag = s_gchook = NULL;

    /* allocate the evaluation stack */
    if ((xlstkbase = (LVAL **)malloc(EDEPTH * sizeof(LVAL *))) == NULL)
	xlfatal("insufficient memory");
    xlstack = xlstktop = xlstkbase + EDEPTH;

    /* allocate the argument stack */
    if ((xlargstkbase = (LVAL *)malloc(ADEPTH * sizeof(LVAL))) == NULL)
	xlfatal("insufficient memory");
    xlargstktop = xlargstkbase + ADEPTH;
    xlfp = xlsp = xlargstkbase;
    *xlsp++ = NIL;

#ifdef MULVALS
    /* allocate the result array */
    if ((xlresults = (LVAL *)malloc(MULVALLIMIT * sizeof(LVAL))) == NULL)
	xlfatal("insufficient memory");
    for (i = 0; i < MULVALLIMIT; i++)
      xlresults[i] = NIL;
#endif /* MULVALS */

    /* we have to make a NIL symbol before continuing */
    /**** this is a real hack */
    p = xlmakesym("NIL");
    MEMCPY(NIL, p, sizeof(struct node));    /* we point to this! */
    defconstant(NIL, NIL);
    set_old_node(NIL);
    set_to_new_node(NIL);
    setfunction(NIL, NIL);
    setntype(p, FREE);                      /* don't collect "garbage" */

    finalize_registered = NIL;
    finalize_pending = NIL;
}

/* The next three functions are function versions of rplaca, rplacd and */
/* setelement that check for creating references from old to new space. */
/**** all other files need to be checked carefully to see where the     */
/**** macro versions Rplaca, Rplacd and Setelement can be used safely   */

LVAL rplaca P2C(LVAL, x, LVAL, y) { check_old_to_new(x,y); return Rplaca(x,y); }
LVAL rplacd P2C(LVAL, x, LVAL, y) { check_old_to_new(x,y); return Rplacd(x,y); }

LVAL setelement P3C(LVAL, x, int, i, LVAL, v)
{
  check_old_to_new(x,v);
  return Setelement(x,i,v);
}

/* cons - construct a new cons node */
LVAL cons P2C(LVAL, x, LVAL, y)
{
    LVAL nnode;

    /* get a free node */
    if ((nnode = NEXT_FREE_NODE()) == NIL) {
      curtop = LAST_NODE(curtop); /* back up the curtop pointer */
      xlstkcheck(2);
      xlprotect(x);
      xlprotect(y);
      nnode = newnode(CONS);
      xlpopn(2);
    }
    else {
      setntype(nnode, CONS);
      unset_to_new_node(nnode);
      nfree--;
    }

    /* initialize the new node */
    Rplaca(nnode,x);
    Rplacd(nnode,y);

    /* return the new node */
    return (nnode);
}

/* cvstring - convert a string to a string node */
LVAL cvstring P1C(char *, str)
{
    LVAL val;
    val = newstring(STRLEN(str));
    STRCPY(getstring(val),str);
    return (val);
}

/* newstring - allocate and initialize a new string */
LVAL newstring P1C(unsigned, size)
{
    LVAL val;
    val = allocvector(STRING,btow_size(size+1));
    val->n_strlen = size;
    return (val);
}

#ifdef BIGNUMS
/* newbignum - allocate a new bignum */
LVAL newbignum P1C(unsigned, size)
{
  /* size of the sign field not included in n_vsize */
  BIGNUMDATA *x;
  LVAL val;
  xlsave1(val);
  val = allocvector(BIGNUM,btow_size((size+1)*sizeof(BIGNUMDATA)));
  val->n_bsize = size;
  x = getbignumarray(val);
  size++;
  while (size--) *x++ = 0;	/* set value to zero */
  xlpop();
  return val;
}
#endif	

/* cvsymbol - convert a string to a symbol */
LVAL cvsymbol P1C(char *, pname)
{
    LVAL val;
    xlsave1(val);
    val = allocvector(SYMBOL,SYMSIZE);
    setvalue(val,s_unbound);
    setfunction(val,s_unbound);
    setpname(val,cvstring(pname));
    setsnormal(val); /* L. Tierney */
    xlpop();
    return (val);
}

/* cvsubr - convert a function to a subr or fsubr */
LVAL cvsubr P3C(subrfun, fcn, int, type, int, offset)
{
    LVAL val;
    val = newnode(type);
    val->n_subr = fcn;
    val->n_offset = offset;
    return (val);
}

/* cvfile - convert a file pointer to a stream */
LVAL cvfile P2C(FILEP, fp, int, iomode)
{
    LVAL val;
    val = newnode(STREAM);
    setfile(val,fp);
    setsavech(val,'\0');
    val->n_sflags = iomode;
    val->n_cpos = 0;
    return (val);
}

/* cvfixnum - convert an integer to a fixnum node */
LVAL cvfixnum P1C(FIXTYPE, n)
{
    LVAL val;
    if (n >= SFIXMIN && n <= SFIXMAX)
	return (&fixseg->sg_nodes[(int)n-SFIXMIN+NUM_OFFSETS]);
    if ((val = NEXT_FREE_NODE()) == NIL) {
      curtop = LAST_NODE(curtop); /* back up the curtop pointer */
      val = newnode(FIXNUM);
    }
    else {
      setntype(val, FIXNUM);
      nfree--;
    }
    val->n_fixnum = n;
    return (val);
}

/* cvflonum - convert a floating point number to a flonum node */
LVAL cvflonum P1C(FLOTYPE, n)
{
    LVAL val;
    if ((val = NEXT_FREE_NODE()) == NIL) {
      curtop = LAST_NODE(curtop); /* back up the curtop pointer */
      val = newnode(FLONUM);
    }
    else {
      setntype(val, FLONUM);
      nfree--;
    }
    val->n_flonum = n;
    return (val);
}

/* cvchar - convert an integer to a character node */
LVAL cvchar P1C(int, n)
{
    if (n >= CHARMIN && n <= CHARMAX)
	return (&charseg->sg_nodes[n-CHARMIN+NUM_OFFSETS]);
    xlerror("character code out of range",cvfixnum((FIXTYPE)n));
    return (NIL);   /* never executed but gets rid of warning message */
}

#ifdef BIGNUMS
/* cvbratio - convert a pair of bignums into a ratio node */
LVAL cvbratio P2C(LVAL, num, LVAL, denom)
{
  FIXTYPE nu, d;
  int fixtyped;
  LVAL n,m,r;

  if (cvtbigfixnum(num, &nu) && cvtbigfixnum(denom, &d))
    return cvratio(nu,d);
  xlstkcheck(5);
  xlprotect(num);
  xlprotect(denom);
  xlsave(n);
  xlsave(m);
  xlsave(r);

  if (zeropbignum(num)) {	/* zero is fixnum zero */
    xlpopn(5);
    return cvfixnum((FIXTYPE) 0);
  }
  if (getbignumsign(denom)) {	/* denominator must be positive */
    denom = copybignum(denom, 0);
    num = copybignum(num,!getbignumsign(num)); /* final sign */
  }
  n = copybignum(num, 0);	/* abs of numerator */
  m = denom;
  for (;;) {			/* get gcd */
    divbignum(m, n, &r);	/* use remainder only */
    if (zeropbignum(r)) break;
    m = n;
    n = r;
  }
  if ((!cvtbigfixnum(n, &d)) || d != 1) { /* can reduce */
    denom = divbignum(denom, n, &r);
    num = divbignum(num, n, &r);
  }
  if ((fixtyped = cvtbigfixnum(denom, &d)) != 0 && d == 1) {
    /* reduced to an integer */
    xlpopn(5);
    if (cvtbigfixnum(num, &nu)) return cvfixnum(nu);
    return num;
  }
  /* got value to return */
  r = newnode(RATIO);
  r->n_cdr = r->n_car = NIL; /* in case of garbage collect */
  r->n_cdr = (fixtyped ? cvfixnum(d) : denom);
  r->n_car = (cvtbigfixnum(num, &nu) ? cvfixnum(nu) : num);
  xlpopn(5);
  return (r);
}

/* cvratio - convert an integer pair to a ratio node */
LVAL cvratio P2C(FIXTYPE, num, FIXTYPE, denom)
{
  LVAL val;
  unsigned long n, m, r, nu, de;
  int sign;

  if (num == 0) return cvfixnum((FIXTYPE) 0); /* zero is int zero */
  if (denom < 0) {		/* denominator must be positive */
    if (denom == -1 && num == MINFIX) {
      xlsave1(val);
      val = cvtulongbignum((unsigned long)MAXFIX+1, FALSE);
      xlpop();
      return val;
    }
    denom = -denom;
    sign = num >= 0;
  }
  else
    sign = num < 0;
	
  if (num < 0) num = -num;
  n = nu = (unsigned long)(long)num;
  m = de = (unsigned long)(long)denom; /* reduce the ratio: compute GCD */
  for (;;) {
    if ((r = m % n) == 0) break;
    m = n;
    n = r;
  }
  if (n != 1) {
    de /= n;
    nu /= n;
  }
  if (de == 1)
    return cvfixnum(sign ? -(long)nu : (long)nu); /* reduced to integer */
  xlsave1(val);
  val = newnode(RATIO);
  val->n_cdr = val->n_car = NIL; /* in case of garbage collect */
  if ((nu == (unsigned long)MAXFIX+1 && sign==0))
    val->n_car = cvtulongbignum(nu, sign);
  else
    val->n_car = cvfixnum(sign ? -(long)nu : (long)nu);
  if (de == (unsigned long)MAXFIX+1)
    val->n_cdr = cvtulongbignum(de, FALSE);
  else
    val->n_cdr = cvfixnum(de);
  xlpop();
  return (val);
}
#endif

/* newustream - create a new unnamed stream */
LVAL newustream(V)
{
    LVAL val;
    val = newnode(USTREAM);
    sethead(val,NIL);
    settail(val,NIL);
    return (val);
}

/* newobject - allocate and initialize a new object */
LVAL newobject P2C(LVAL, cls, int, size)
{
    LVAL val;
    val = allocvector(OBJECT,size+1);
    setelement(val,0,cls);
    return (val);
}

/* newclosure - allocate and initialize a new closure */
LVAL newclosure P4C(LVAL, name, LVAL, type, LVAL, env, LVAL, fenv)
{
    LVAL val;
    val = allocvector(CLOSURE,CLOSIZE);
    setname(val,name);
    settype(val,type);
    setenvi(val,env);
    setfenv(val,fenv);
    return (val);
}

/* newstruct - allocate and initialize a new structure node */
LVAL newstruct P2C(LVAL, type, int, size)
{
    LVAL val;
    val = allocvector(STRUCT,size+1);
    setelement(val,0,type);
    return (val);
}

#ifdef BYTECODE
/* newcpsnode - allocate and initialize a new CPS node for compiler */
LVAL newcpsnode P1C(LVAL, type)
{
    LVAL val;
    val = allocvector(CPSNODE,CPSNODESIZE);
    setcpstype(val,type);
    return (val);
}

/* newbcode - allocate and initialize a new byte code vector */
LVAL newbcode P5C(LVAL, code, LVAL, jtab, LVAL, lits, LVAL, idx, LVAL, env)
{
    LVAL val;
    val = allocvector(BCODE,BCODESIZE);
    setbccode(val, code);
    setbcjtab(val, jtab);
    setbclits(val, lits);
    setbcidx(val, idx);
    setbcenv(val, env);
    return (val);
}
#endif /* BYTECODE */

#ifdef PACKAGES
/* newpackage - allocate and initialize a new package */
LVAL newpackage(V)
{
  LVAL val;
  xlsave1(val);
  val = allocvector(PACKAGE,PACKSIZE);
  setintsyms(val, newvector(HSIZE));
  setextsyms(val, newvector(HSIZE));
  xlpop();
  return (val);
}
#endif /* PACKAGES */

/* newvector - allocate and initialize a new vector node */
LVAL newvector P1C(unsigned, size)
{
  return(allocvector(VECTOR,size));
}

/* allocvector - allocate and initialize a new vector node */
LOCAL LVAL allocvector P2C(int, type, unsigned, size)
{
  LVAL vect, *p;
  int i;
  unsigned long bsize; /* changed to unsigned long - L. Tierney */

  xlsave1(vect);
  vect = newnode(type);
  vect->n_vsize = 0;
  if (size != 0) {
    bsize = wtob_size(size);
    check_alloc_threshold(bsize);
    if ((vect->n_vdata = (LVAL *)VALLOC(size)) == NULL) {
      ggc(FALSE);
      if ((vect->n_vdata = (LVAL *)VALLOC(size)) == NULL) {
	ggc(TRUE);
	if ((vect->n_vdata = (LVAL *)VALLOC(size)) == NULL)
	  xlabort("insufficient vector space");
      }
    }
    vect->n_vsize = size;
    total += bsize;
  }

  /* set all the elements to NIL, except for STRINGs and TVECs */
  if (type != STRING && type != TVEC)
    for (i = 0, p = vect->n_vdata; i < size; i++)
      *p++ = NIL;

  xlpop();
  return (vect);
}

#ifdef XLISP_STAT
/* Added for internal allocated storage - L. Tierney */
#include "xlstat.h"

LOCAL char *IViewNewAData P4C(int, n, int, m, long *, size, int, reloc)
{
  char *addr;
  
  addr = (reloc) ? StRCalloc(n, m): StCalloc(n, m);
  *size = (reloc) ? StRSize(addr) : ((long) m) * ((long) n);
  return(addr);
}

/* newadata(n, m, reloc) - convert a string to a string node */
LVAL newadata P3C(int, n, int, m, int, reloc)
{
    LVAL val;
    long size;
    
    /*if (reloc) xlfail("relocatable allocated data not supported");*/

    size = ((long) m) * ((long) n);
    check_alloc_threshold(size);

    xlsave1(val);
    val = newnode(ADATA);
    setadreloc(val, reloc);

    /**** check what happens when asked for zero length */
    if ((val->n_adaddr = IViewNewAData(n, m, &size, reloc)) == NULL) {
      ggc(FALSE);  
      if ((val->n_adaddr = IViewNewAData(n, m, &size, reloc)) == NULL) {
	ggc(TRUE);  
	if ((val->n_adaddr = IViewNewAData(n, m, &size, reloc)) == NULL)
	  xlabort("insufficient memory");
      }
    }
    val->n_adsize = size;
    total += size;

    xlpop();
    return (val);
}

VOID reallocaddata P3C(LVAL, val, int, n, int, m)
{
  char *addr;
  
  check_alloc_threshold(((long) n) * ((long) m)); /**** overestimate */
  if (! adatap(val) || ! getadreloc(val)) xlfail("not relocatable");
  addr = StRRealloc(getadaddr(val), n, m);
  if (addr == NULL) xlabort("allocation failed");
  val->n_adaddr = addr;
  total -= getadsize(val);
  val->n_adsize = StRSize(addr);
  total += getadsize(val);
}

VOID freeadata P1C(LVAL, val)
{
  if (! adatap(val)) xlfail("not a data object");
  if (getadreloc(val)) StRFree(getadaddr(val));
  else StFree(getadaddr(val));
  val->n_adaddr = NULL;
  total -= getadsize(val);
  val->n_adsize = 0;
  adjust_alloc_threshold();
}

/* Added for internal allocated storage - L. Tierney */
#endif /* XLISP_STAT */

#ifdef DODO
/* find_free_node - search all segments for the first free node, NIL if none */
LOCAL LVAL find_free_node(V)
{
  LVAL val;

  val = NEXT_FREE_NODE();
  while (val == NIL && curseg != NULL) {
    curseg = curseg->sg_next;
    if (curseg != NULL) {
      curtop = curbase = &curseg->sg_nodes[0];
      val = NEXT_FREE_NODE();
    }
  }
  return(val);
}
#else
/* macro version */
#define FIND_FREE_NODE(val) { \
  val = NEXT_FREE_NODE(); \
  while (val == NIL && curseg != NULL) { \
    curseg = curseg->sg_next; \
    if (curseg != NULL) { \
      curtop = curbase = &curseg->sg_nodes[0]; \
      val = NEXT_FREE_NODE(); \
    } \
  } \
}
#endif /* DODO */

/**** should interrupts be disabled here? */
/* newnode - allocate a new node */
LOCAL LVAL newnode P1C(int, type)
{
  register LVAL nnode;

#ifdef DODO
  if ((nnode = find_free_node()) == NIL) {
    ggc(FALSE);
    if (nnodes - nfree > nnodes * full_gc_frac) {
      ggc(TRUE);
      expand_node_space();
    }
    if ((nnode = find_free_node()) == NIL)
      xlabort("insufficient node space");
  }
#else
  FIND_FREE_NODE(nnode);
  if (nnode == NIL) {
    ggc(FALSE);
    if (nnodes - nfree > nnodes * full_gc_frac) {
      ggc(TRUE);
      expand_node_space();
    }
    FIND_FREE_NODE(nnode);
    if (nnode == NIL)
      xlabort("insufficient node space");
  }
#endif /* DODO */
  nfree--;

  /* move allocated node from immediate list to allocated list */
  if (type_is_allocated(type)) {
    nnode->n_vdata = NULL;
    curtop = LAST_NODE(nnode); /* back up the curtop pointer */
    MOVE_NODE_TO_BASE(nnode, ALLOCATED_OFFSET);
  }

  /* initialize the new node */
  setntype(nnode, type);
  unset_to_new_node(nnode);

  /* return the new node */
  return (nnode);
}

/* macro to move a node to the tenured list -- node is already marked as old */
#define TENURE_NODE(n) { LINK_NODE_TO_BASE(n,TENURED_OFFSET(n)); ntenured++; }

/* macro to either tenure a node or place it on the forwarded list */
#ifdef NULLPTRDEBUG
static int hit_null_pointer;

#define forward_node(n) \
{\
  register LVAL tmp = (n); \
  if (tmp == NULL) { \
    hit_null_pointer = TRUE; \
  } \
  else { \
    if (is_new_node(tmp)) {\
      UNLINK_NODE(tmp); \
      if (node_has_children(tmp)) { \
        SET_NEXT_NODE_PTR(tmp, forwarded_nodes); \
        forwarded_nodes = tmp; \
        } \
      else { \
        TENURE_NODE(tmp); \
      } \
      set_old_node(tmp); \
    } \
  } \
}
#else
#define forward_node(n) \
{\
  register LVAL tmp = (n); \
  if (is_new_node(tmp)) {\
    UNLINK_NODE(tmp); \
    if (node_has_children(tmp)) { \
      SET_NEXT_NODE_PTR(tmp, forwarded_nodes); \
      forwarded_nodes = tmp; \
      } \
    else { \
      TENURE_NODE(tmp); \
    } \
    set_old_node(tmp); \
  }\
}
#endif /* NULLPTRDEBUG */

/* macro to forward all children of a node -- node type must have children */
#ifdef BYTECODE
#define CASE_BCCLOSURE case BCCLOSURE:
#else
#define CASE_BCCLOSURE
#endif
#ifdef BIGNUMS
#define CASE_RATIO case RATIO:
#else
#define CASE_RATIO
#endif

#define forward_children(node) \
{ \
  register LVAL temp; \
  register int i, n; \
  temp = (node); \
  switch (ntype(temp)) { \
  case CONS: \
  case USTREAM: \
  case COMPLEX: \
  case RNDSTATE: \
  CASE_RATIO \
  CASE_BCCLOSURE \
  case DARRAY: \
    forward_node(car(temp)); \
    forward_node(cdr(temp)); \
    break; \
  case NATPTR: \
    forward_node(cdr(temp)); \
    break; \
  default: \
    for (i = 0, n = getsize(temp); --n >= 0; ++i) \
      forward_node(getelement(temp,i)); \
    break; \
  } \
}

/* clean_free_allocated_node - do finalization for free allocated node */
LOCAL VOID clean_free_allocated_node P1C(LVAL, tmp)
{
  switch (ntype(tmp)) {
  case FREE: /**** I'm not sure I need this case */
    break;
  case STRING:
  case TVEC:
    if (getstring(tmp) != NULL) {
      long size = btow_size(getslength(tmp) + 1);
      total -= size * sizeof(LVAL);
      VRELEASE(getstring(tmp), size);
    }
    break;
#ifdef BIGNUMS
  case BIGNUM:
    if (getbignumarray(tmp) != NULL) {
      long size = btow_size((1+(long)getbignumsize(tmp))*sizeof(BIGNUMDATA));
      total -= size * sizeof(LVAL);
      VRELEASE(getstring(tmp), size);
    }
    break;
#endif
#ifdef XLISP_STAT
  case ADATA:
    if (getadaddr(tmp) != NULL) {
      total -= getadsize(tmp);
      if (getadreloc(tmp)) StRFree(getadaddr(tmp));
      else StFree(getadaddr(tmp));
    }
    break;
#endif /* XLISP_STAT */
  case STREAM:
    if (getfile(tmp) != CLOSED
	&& getfile(tmp) != STDIN
	&& getfile(tmp) != STDOUT
	&& getfile(tmp) != CONSOLE)/* taa fix - dont close stdio */
      OSCLOSE(getfile(tmp));
    break;
  default:
    if (tmp->n_vsize) {
      VRELEASE((ALLOCTYPE *) tmp->n_vdata, tmp->n_vsize);
      total -= wtob_size(tmp->n_vsize);
    }
    break;
  }
  setntype(tmp, FREE);
}

/* gc - garbage collect (only called here and in xlimage.c) */
VOID gc(V) { ggc(TRUE); }

/* ggc - generational garbage collector */
LOCAL VOID ggc P1C(int, full_gc)
{
  register LVAL **p, *ap, tmp, forwarded_nodes;
  char buf[STRMAX+1];
  LVAL *newfp, fun;
  register SEGMENT *sg;
#ifdef TIMES
    unsigned long gccount = run_tick_count();
#endif

  /**** make sure interrupts are turned off */
  
#ifdef STSZ
#if (GCSTMARGIN>0)
    if (STACKREPORT(fun)<GCSTMARGIN) { /* Do not try gc with less */
        dbgputstr("Insufficient stack left for GC  ");
        if (batchmode) xlfatal("uncaught error");
        xltoplevel(TRUE);
    }
#endif
#endif

  set_gc_cursor(TRUE); /* L. Tierney */
  
  /* print the start of the gc message */
  if (s_gcflag != NULL && getvalue(s_gcflag) != NIL) {
    /* print message on a fresh line */
    xlfreshline(getvalue(s_debugio));
    sprintf(buf,"[ gc: total %ld, ",nnodes);
    dbgputstr(buf); /* TAA MOD -- was std output */
  }

#ifdef NULLPTRDEBUG
  hit_null_pointer = FALSE;
#endif /* NULLPTRDEBUG */

  /* reset the current segment pointers */
  curseg = lastpermseg->sg_next;
  curtop = curbase = &curseg->sg_nodes[0];

  /* make tenured space into new space for a full gc */
  if (full_gc) {
    /* change the old node code */
    old_code = (old_code == 1) ? 0 : 1;

    /* mark NIL and the fixed segments as old */
    set_old_node(NIL);
    set_to_new_node(NIL);
    for (sg = segs; sg != NULL; sg = sg->sg_next) {
      int n;

      for (tmp = &sg->sg_nodes[0], n = sg->sg_size; --n >= 0; tmp++)
	set_old_node(tmp);

      if (sg == lastpermseg) break;
    }

    for (sg = lastpermseg->sg_next; sg != NULL; sg = sg->sg_next) {
      register LVAL base;
      int n;

      /**** mark the list heads as old -- probably not necessary */
      for (tmp = &sg->sg_nodes[0], n = NUM_OFFSETS; --n >= 0; tmp++)
	set_old_node(tmp);

      /* mark the nodes in the immediate and allocated lists as new; */
      base = &sg->sg_nodes[0] + IMMEDIATE_OFFSET;
      for (tmp = NEXT_NODE(base); tmp != base; tmp = NEXT_NODE(tmp))
	set_new_node(tmp);
      base = &sg->sg_nodes[0] + ALLOCATED_OFFSET;
      for (tmp = NEXT_NODE(base); tmp != base; tmp = NEXT_NODE(tmp))
	set_new_node(tmp);
    
      /* move the tenured and to new lists to the new lists */
      base = &sg->sg_nodes[0];
      MOVE_LIST(base + IMMEDIATE_TENURED_OFFSET, base + IMMEDIATE_OFFSET);
      MOVE_LIST(base + IMMEDIATE_TO_NEW_OFFSET, base + IMMEDIATE_OFFSET);
      MOVE_LIST(base + ALLOCATED_TENURED_OFFSET, base + ALLOCATED_OFFSET);
      MOVE_LIST(base + ALLOCATED_TO_NEW_OFFSET, base + ALLOCATED_OFFSET);
    }
    ntenured = 0;
  }

  /* reset the forwarded node list */
  forwarded_nodes = NIL;

  /* forward the children of NIL */
  forward_children(NIL);

  /* forward the unbound marker */
  if (s_unbound != NIL)
    forward_node(s_unbound);

  /* forward the obarray and the current environment */
  if (obarray != NULL)
    forward_node(obarray);
  forward_node(xlenv);
  forward_node(xlfenv);
  forward_node(xldenv);

  /* forward the evaluation stack */
  for (p = xlstack; p < xlstktop; ++p)
    forward_node(**p);

  /* forward the argument stack */
  for (ap = xlargstkbase; ap < xlsp; ++ap)
    forward_node(*ap);

#ifdef MULVALS
  /* forward the result array */
    {
      int i;
      for (i = 0; i < xlnumresults; i++)
	forward_node(xlresults[i]);
    }
#endif /* MULVALS */

  /* forward the children of tenured nodes pointing to new ones */
  for (sg = lastpermseg->sg_next; sg != NULL; sg = sg->sg_next) {
    register LVAL base;
    base  = &sg->sg_nodes[0] + IMMEDIATE_TO_NEW_OFFSET;
    for (tmp = NEXT_NODE(base); tmp != base; tmp = NEXT_NODE(tmp))
      forward_children(tmp);
    base  = &sg->sg_nodes[0] + ALLOCATED_TO_NEW_OFFSET;
    for (tmp = NEXT_NODE(base); tmp != base; tmp = NEXT_NODE(tmp))
      forward_children(tmp);
  }

  /* forward the children of forwarded nodes */
  while ((tmp = forwarded_nodes) != NIL) {
    forwarded_nodes = NEXT_NODE_PTR(tmp);
    forward_children(tmp);
    TENURE_NODE(tmp);
    unset_to_new_node(tmp);
  }
  check_finalize();
  if (finalize_registered != NIL || finalize_pending != NIL) {
    forward_node(finalize_registered);
    forward_node(finalize_pending);
    while ((tmp = forwarded_nodes) != NIL) {
      forwarded_nodes = NEXT_NODE_PTR(tmp);
      forward_children(tmp);
      TENURE_NODE(tmp);
      unset_to_new_node(tmp);
    }
  }
  check_weak_boxes();

  /* clean out free allocated nodes */
  for (sg = lastpermseg->sg_next; sg != NULL; sg = sg->sg_next) {
    register LVAL ibase, abase;
    ibase = &sg->sg_nodes[0];
    abase = ibase + ALLOCATED_OFFSET;
    for (tmp = NEXT_NODE(abase); tmp != abase; tmp = NEXT_NODE(tmp)) {
      clean_free_allocated_node(tmp);
    }
    MOVE_LIST(abase, ibase);
  }

  /* adjust the threshold for allocations to trigger a gc */
  adjust_alloc_threshold();

  /* update the statistics */
  nfree = nnodes - nperm - ntenured;
  ++gccalls;
  if (! full_gc) minor_gccalls++;

#ifdef NULLPTRDEBUG
  if (hit_null_pointer)
    xlfail("GC hit a null pointer -- probably time to bail out");
#endif /* NULLPTRDEBUG */

/**** move gc cursor thing to before hook? */
  if (! null(finalize_pending))
    do_finalize();

  /* call the *gc-hook* if necessary */
  if (s_gchook != NULL && ((fun = getvalue(s_gchook)) != NIL) ) {

    /* rebind hook function to NIL  TAA MOD */
    tmp = xldenv;
    xldbind(s_gchook,NIL);

    newfp = xlsp;
    pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
    pusharg(fun);
    pusharg(cvfixnum((FIXTYPE)2));
    pusharg(cvfixnum((FIXTYPE)nnodes));
    pusharg(cvfixnum((FIXTYPE)nfree));
    xlfp = newfp;
    xlapply(2);

    /* unbind the symbol TAA MOD */
    xlunbind(tmp);
  }

  /* print the end of the gc message */
  if (s_gcflag != NULL && getvalue(s_gcflag) != NIL) {
    sprintf(buf,"%ld free ]\n",nfree);
    dbgputstr(buf); /* TAA MOD -- was std output */
  }
  set_gc_cursor(FALSE); /* L. Tierney */
#ifdef TIMES
    gctime += run_tick_count() - gccount;
#endif
}

/* addseg - add a segment to the available memory */
LOCAL int addseg(V)
{
    SEGMENT *newseg;

    /* allocate the new segment */
    if (anodes == 0 || (newseg = newsegment(anodes)) == NULL)
	return (FALSE);
    adjust_alloc_threshold();
    nfree += (long)anodes;

    /* return successfully */
    return (TRUE);
}

/* newsegment - create a new segment (only called here and in xlimage.c) */
SEGMENT *newsegment P1C(int, n)
{
    SEGMENT *newseg;
    LVAL p, base;
    int m;

#ifdef MACINTOSH /* L. Tierney */
    maximum_memory();
#endif /* MACINTOSH */

    n += NUM_OFFSETS;

    /* allocate the new segment */
    if ((newseg = (SEGMENT *)CALLOC(1,segsize(n))) == NULL)
	return (NULL);

    /* initialize the new segment */
    newseg->sg_size = n;
    newseg->sg_next = NULL;
    if (segs != NULL)
	lastseg->sg_next = newseg;
    else
	segs = newseg;
    lastseg = newseg;

    /* initialize the list header nodes */
    base = &newseg->sg_nodes[0];
    for (p = base, m = NUM_OFFSETS; --m >= 0; ++p) {
      set_old_node(p);
      SET_NODE_BASE(p, base);
      SET_NEXT_NODE(p, p);
      SET_LAST_NODE(p, p);
    }

    /* add each new node to the segment's immediate free list */
    for (p = base + NUM_OFFSETS, m = n - NUM_OFFSETS; --m >= 0; ++p) {
      setntype(p, FREE);
      set_new_node(p);
      unset_to_new_node(p);
      SET_NODE_BASE(p, base);
      LINK_NODE(p, base);
    }

    /* update the statistics */
    total += (long)segsize(n);
    nnodes += (long)n;
    ++nsegs;

    /* return the new segment */
    return (newseg);
}

/* stats - print memory statistics */
LOCAL VOID stats(V)
{
  long major_gccalls = gccalls - minor_gccalls;

  sprintf(buf,"Nodes:             %ld\n",nnodes); stdputstr(buf);
  sprintf(buf,"Free nodes:        %ld\n",nfree);  stdputstr(buf);
  sprintf(buf,"Segments:          %d\n",nsegs);   stdputstr(buf);
  sprintf(buf,"Allocate:          %d\n",anodes);  stdputstr(buf);
  sprintf(buf,"Total:             %ld\n",total);  stdputstr(buf);
  sprintf(buf,"Major Collections: %ld\n",major_gccalls); stdputstr(buf);
  sprintf(buf,"Minor Collections: %ld\n",minor_gccalls); stdputstr(buf);
/**** drop after debugging is done */
  sprintf(buf,"Threshold crosses: %ld\n", threshold_crossing_gccount);
  stdputstr(buf);
#ifdef TIMES
    sprintf(buf,"Time (sec):  %ld\n",gctime/ticks_per_second());
    stdputstr(buf);
#endif
}

/* xgc - xlisp function to force garbage collection */
LVAL xgc(V)
{
    /* make sure there aren't any arguments */
    xllastarg();

    /* garbage collect */
    gc();

    /* return nil */
    return (NIL);
}

/* xexpand - xlisp function to force memory expansion */
LVAL xexpand(V)
{
    LVAL num;
    FIXTYPE n,i;

    /* get the new number to allocate */
    if (moreargs()) {
	num = xlgafixnum();
	n = getfixnum(num);
        /* make sure there aren't any more arguments */
        xllastarg();
    }
    else
	n = 1;

    /* allocate more segments */
    for (i = 0; i < n; i++)
	if (!addseg())
	    break;

    /* return the number of segments added */
    return (cvfixnum((FIXTYPE)i));
}

/* xalloc - xlisp function to set the number of nodes to allocate */
LVAL xalloc(V)
{
    FIXTYPE n;  /* TAA MOD -- prevent overflow */
    int oldn;

#ifdef DEBUG
    /**** drop after debugging is done */
    if (symbolp(peekarg(0))) {
      LVAL arg;
  
      if (xlgetkeyarg(xlenter(":GC-EXPAND-FRACTION"), &arg)) {
	if (floatp(arg)) {
	  gc_expand_frac = getflonum(arg);
	  return(arg);
	}
      }
      if (xlgetkeyarg(xlenter(":FULL-GC-FRACTION"), &arg)) {
	if (floatp(arg)) {
	  full_gc_frac = getflonum(arg);
	  return(arg);
	}
      }
      return(NIL);
    }
#endif /* DEBUG */

    /* get the new number to allocate */
    n = getfixnum(xlgafixnum());

    /* make sure there aren't any more arguments */
    if (xlargc > 1) xltoomany();    /* but one more is OK, TAA MOD */

    /* set the new number of nodes to allocate */
    if (n > MAX_ANODES || n < MIN_ANODES)
      xlfail("alloc out of range");
    oldn = anodes;
    anodes = (int)n;

    /* return the old number */
    return (cvfixnum((FIXTYPE)oldn));
}

/* xmem - xlisp function to print memory statistics */
LVAL xmem(V)
{
    /* allow one argument for compatiblity with common lisp */
    if (xlargc > 1) xltoomany();    /* TAA Mod */

    /* print the statistics */
    stats();

    /* return nil */
    return (NIL);
}

#ifdef SAVERESTORE
/* xsave - save the memory image */
LVAL xsave(V)
{
    char *name;

    /* get the file name */
    name = getstring(xlgetfname());
    xllastarg();

    /* save the memory image */
    return (xlisave(name) ? s_true : NIL);
}

/* xrestore - restore a saved memory image */
LVAL xrestore(V)
{
#ifdef XLISP_STAT
    xlfail("restore not available");
    return(NIL); /* never returns */
#else
    extern XL_JMP_BUF top_level;
    char *name;

    /* get the file name */
    name = getstring(xlgetfname());
    xllastarg();

    /* restore the saved memory image */
    if (!xlirestore(name))
	return (NIL);

    /* return directly to the top level */
    dbgputstr("[ returning to the top level ]\n");  /* TAA MOD --was std out*/
    XL_LONGJMP(top_level,1);
    return (NIL);   /* never executed, but avoids warning message */
#endif /* XLISP_STAT */
}

VOID sweep_free_nodes(V)
{
  SEGMENT *sg;
  LVAL p;
  int n;
  /**** use free list = immediate list */
  for (sg = segs->sg_next; sg != NULL; sg = sg->sg_next) {
    p = &sg->sg_nodes[0] + NUM_OFFSETS;
    for (n = sg->sg_size - NUM_OFFSETS; --n >= 0; ++p)
      if (! is_old_node(p))
	setntype(p, FREE);
  }
}

VOID initialize_node P1C(LVAL, node)
{
  /* set gc flags */
  set_old_node(node); /*** new??? */
  unset_to_new_node(node);

  /* move allocated node from immediate list to allocated list */
  if (node_is_allocated(node))
    MOVE_NODE_TO_BASE(node, ALLOCATED_OFFSET);
}
  
#endif /* SAVERESTORE */

/* From XLISP-STAT, Copyright (c) 1988 Luke Tierney */

LVAL newicomplex P2C(FIXTYPE, real, FIXTYPE, imag)
{
  LVAL val, r, i;
  
  if (imag == 0) val = cvfixnum(real);
  else {
    xlstkcheck(2);
    xlsave(r);
    xlsave(i);
    r = cvfixnum(real);
    i = cvfixnum(imag);
    val = cons(r, i);
    setntype(val, COMPLEX);
    xlpopn(2);
  }
  return(val);
}

LVAL newdcomplex P2C(double, real, double, imag)
{
  LVAL val, r, i;
  
  xlstkcheck(2);
  xlsave(r);
  xlsave(i);
  r = cvflonum((FLOTYPE) real);
  i = cvflonum((FLOTYPE) imag);
  val = cons(r, i);
  setntype(val, COMPLEX);
  xlpopn(2);
  return(val);
}

#ifdef BIGNUMS
/* newcomplex - allocate and initialize a new object */
LVAL newcomplex P2C(LVAL, real, LVAL, imag)
{
  LVAL val;
  xlstkcheck(2);
  xlprotect(real);
  xlprotect(imag);
	
  if (! rationalp(real) || ! rationalp(imag)) {
    if (! floatp(real)) real = cvflonum(makefloat(real));
    if (! floatp(imag)) imag = cvflonum(makefloat(imag));
  }
  if (fixp(imag) && getfixnum(imag) == 0)
    val = real;
  else {
    val = newnode(COMPLEX);
    getreal(val) = real;
    getimag(val) = imag;
  }
  xlpopn(2);
  return(val);
}
#else
/* newcomplex - allocate and initialize a new object */
LVAL newcomplex P2C(LVAL, real, LVAL, imag)
{
  if (fixp(real) && fixp(imag))
    return(newicomplex(getfixnum(real), getfixnum(imag)));
  else
    return(newdcomplex(makefloat(real), makefloat(imag)));
}
#endif
#else

/* node flags */
#define MARK    0x40
#define LEFT    0x80
#define node_marked(n) ((ntype(n) & MARK))
#define unmark_node(n) (ntype(n) &= ~MARK)
#define mark_node(n) (ntype(n) |= MARK)
#define mark_and_type(n) (mark_node(n) & TYPEFIELD)
#define unmarked_array_node(n) ((ntype(n) & (ARRAY|MARK)) == ARRAY)
#define came_from_left(n) (ntype(n) & LEFT)
#define set_left(n) (ntype(n) |= LEFT)
#define unset_left(n) (ntype(n) &= ~LEFT)
#define is_array_type(x) (((x) & ARRAY) != 0)
#define gcntype(n) (ntype(n)&TYPEFIELD)

/* expansion parameters */
#define ALLOC_THRESHOLD_INCREMENT 0x20000L
#define GC_EXPAND_FRAC 0.2

/* macro to compute the size of a segment */
#define segsize(n) (sizeof(SEGMENT)+((n)-1)*sizeof(struct node))

#ifdef TIMES
static unsigned long gctime;    /* calcuate time in garbage collector */
#endif

/* macros to convert between size in bytes and vector size */
#define btow_size(n) (((unsigned)(n)+(sizeof(LVAL)-1))/(unsigned)sizeof(LVAL))
#define wtob_size(n) (((long) (n)) * sizeof(LVAL))

/* variables local to xldmem.c and xlimage.c */
SEGMENT *segs,*lastseg,*fixseg,*charseg;
int anodes,nsegs;
LVAL fnodes = NIL;

/* local variables */
static long alloc_threshold,threshold_crossing_gccount;
static double gc_expand_frac = GC_EXPAND_FRAC;

/* forward declarations */
#ifdef JMAC
LOCAL LVAL Newnode P1H(int);
#else
LOCAL LVAL newnode P1H(int);
#endif
LOCAL LVAL allocvector P2H(int, unsigned);
LOCAL VOID mark P1H(LVAL);
LOCAL VOID sweep(V);
LOCAL int  addseg(V);
LOCAL VOID stats(V);
LOCAL VOID check_alloc_threshold P1H(long);
LOCAL VOID adjust_alloc_threshold(V);
LOCAL VOID expand_node_space(V);
LOCAL VOID morevmem P1H(int);
LOCAL char *IViewNewAData P4H(int, int, long *, int);

#ifdef JMAC
LVAL _nnode = NIL;
FIXTYPE _tfixed = 0;
int _tint = 0;

#define newnode(type) (((_nnode = fnodes) != NIL) ? \
            ((fnodes = cdr(_nnode)), \
             nfree--, \
             (_nnode->n_type = type), \
             rplacd(_nnode,NIL), \
             _nnode) \
            : Newnode(type))

#endif

/* $putpatch.c$: "MODULE_XLDMEM_C_GLOBALS" */

/* xlminit - initialize the dynamic memory module */
VOID xlminit(V)
{
    LVAL p;
    int i;

    /* initialize our internal variables */
    segs = lastseg = NULL;
    nnodes = nfree = total = gccalls = 0L;
    alloc_threshold = ALLOC_THRESHOLD_INCREMENT;
    threshold_crossing_gccount = 0L;
    nsegs = 0;
    anodes = NNODES;
    gctime = 0L;
    fnodes = NIL;

    /* allocate the fixnum segment */
    if ((fixseg = newsegment(SFIXSIZE)) == NULL)
	xlfatal("insufficient memory");

    /* initialize the fixnum segment */
    p = &fixseg->sg_nodes[0];
    for (i = SFIXMIN; i <= SFIXMAX; ++i) {
	setntype(p, FIXNUM);
	p->n_fixnum = i;
	++p;
    }

    /* allocate the character segment */
    if ((charseg = newsegment(CHARSIZE)) == NULL)
	xlfatal("insufficient memory");

    /* initialize the character segment */
    p = &charseg->sg_nodes[0];
    for (i = CHARMIN; i <= CHARMAX; ++i) {
	setntype(p, CHAR);
	p->n_chcode = i;
	++p;
    }

    /* initialize structures that are marked by the collector */
    obarray = NULL;
    xlenv = xlfenv = xldenv = NIL;
    s_gcflag = s_gchook = NULL;

    /* $putpatch.c$: "MODULE_XLDMEM_C_XLMINIT" */

    /* allocate the evaluation stack */
    if ((xlstkbase = (LVAL **)malloc(EDEPTH * sizeof(LVAL *))) == NULL)
	xlfatal("insufficient memory");
    xlstack = xlstktop = xlstkbase + EDEPTH;

    /* allocate the argument stack */
    if ((xlargstkbase = (LVAL *)malloc(ADEPTH * sizeof(LVAL))) == NULL)
	xlfatal("insufficient memory");
    xlargstktop = xlargstkbase + ADEPTH;
    xlfp = xlsp = xlargstkbase;
    *xlsp++ = NIL;

#ifdef MULVALS
    /* allocate the result array */
    if ((xlresults = (LVAL *)malloc(MULVALLIMIT * sizeof(LVAL))) == NULL)
	xlfatal("insufficient memory");
    for (i = 0; i < MULVALLIMIT; i++)
      xlresults[i] = NIL;
    xlnumresults = 0;
#endif /* MULVALS */

    /* we have to make a NIL symbol before continuing */
    p = xlmakesym("NIL");
    MEMCPY(NIL, p, sizeof(struct node));    /* we point to this! */
    defconstant(NIL, NIL);
    setfunction(NIL, NIL);
    setntype(p, FREE);                      /* don't collect "garbage" */

    finalize_registered = NIL;
    finalize_pending = NIL;
}

/* cons - construct a new cons node */
LVAL cons P2C(LVAL, x, LVAL, y)
{
    LVAL nnode;

    /* get a free node */
    if ((nnode = fnodes) == NIL) {
      xlstkcheck(2);
      xlprotect(x);
      xlprotect(y);
      nnode = newnode(CONS);
      xlpopn(2);
    }
    else {
      fnodes = cdr(fnodes);
      setntype(nnode, CONS);
      nfree--;
    }

    /* initialize the new node */
    rplaca(nnode,x);
    rplacd(nnode,y);

    /* return the new node */
    return (nnode);
}

/* cvstring - convert a string to a string node */
LVAL cvstring P1C(char *, str)
{
    LVAL val;
    val = newstring(STRLEN(str));
    STRCPY(getstring(val),str);
    return (val);
}

/* newstring - allocate and initialize a new string */
LVAL newstring P1C(unsigned, size)
{
    LVAL val;
    val = allocvector(STRING,btow_size(size+1));
    val->n_strlen = size;
    return (val);
}

#ifdef BIGNUMS
/* newbignum - allocate a new bignum */
LVAL newbignum P1C(unsigned, size)
{
  /* size of the sign field not included in n_vsize */
  BIGNUMDATA *x;
  LVAL val;
  xlsave1(val);
  val = allocvector(BIGNUM,btow_size((size+1)*sizeof(BIGNUMDATA)));
  val->n_bsize = size;
  x = getbignumarray(val);
  size++;
  while (size--) *x++ = 0;	/* set value to zero */
  xlpop();
  return val;
}
#endif	

/* cvsymbol - convert a string to a symbol */
LVAL cvsymbol P1C(char *, pname)
{
    LVAL val;
    xlsave1(val);
    val = allocvector(SYMBOL,SYMSIZE);
    setvalue(val,s_unbound);
    setfunction(val,s_unbound);
    setpname(val,cvstring(pname));
    setsnormal(val); /* L. Tierney */
    xlpop();
    return (val);
}

/* cvsubr - convert a function to a subr or fsubr */
LVAL cvsubr P3C(subrfun, fcn, int, type, int, offset)
{
    LVAL val;
    val = newnode(type);
    val->n_subr = fcn;
    val->n_offset = offset;
    return (val);
}

/* cvfile - convert a file pointer to a stream */
LVAL cvfile P2C(FILEP, fp, int, iomode)
{
    LVAL val;
    val = newnode(STREAM);
    setfile(val,fp);
    setsavech(val,'\0');
    val->n_sflags = iomode;
    val->n_cpos = 0;
    return (val);
}

#ifdef JMAC
/* cvfixnum - convert an integer to a fixnum node */
LVAL Cvfixnum P1C(FIXTYPE, n)
{
    LVAL val;
    val = newnode(FIXNUM);
    val->n_fixnum = n;
    return (val);
}
#else
/* cvfixnum - convert an integer to a fixnum node */
LVAL cvfixnum P1C(FIXTYPE, n)
{
    LVAL val;
    if (n >= SFIXMIN && n <= SFIXMAX)
	return (&fixseg->sg_nodes[(int)n-SFIXMIN]);
    if ((val = fnodes) == NIL) {
      val = newnode(FIXNUM);
    }
    else {
      fnodes = cdr(fnodes);
      setntype(val, FIXNUM);
      nfree--;
    }
    val->n_fixnum = n;
    return (val);
}
#endif

/* cvflonum - convert a floating point number to a flonum node */
LVAL cvflonum P1C(FLOTYPE, n)
{
    LVAL val;
    if ((val = fnodes) == NIL) {
      val = newnode(FLONUM);
    }
    else {
      fnodes = cdr(fnodes);
      setntype(val, FLONUM);
      nfree--;
    }
    val->n_flonum = n;
    return (val);
}

/* cvchar - convert an integer to a character node */
#ifdef JMAC
LVAL Cvchar P1C(int, n)
{
    xlerror("character code out of range",cvfixnum((FIXTYPE)n));
    return(NIL);    /* never executed */
}
#else
LVAL cvchar P1C(int, n)
{
    if (n >= CHARMIN && n <= CHARMAX)
	return (&charseg->sg_nodes[n-CHARMIN]);
    xlerror("character code out of range",cvfixnum((FIXTYPE)n));
    return NIL;   /* never executed but gets rid of warning message */
}
#endif

#ifdef BIGNUMS
/* cvbratio - convert a pair of bignums into a ratio node */
LVAL cvbratio P2C(LVAL, num, LVAL, denom)
{
  FIXTYPE nu, d;
  int fixtyped;
  LVAL n,m,r;

  if (cvtbigfixnum(num, &nu) && cvtbigfixnum(denom, &d))
    return cvratio(nu,d);
  xlstkcheck(5);
  xlprotect(num);
  xlprotect(denom);
  xlsave(n);
  xlsave(m);
  xlsave(r);

  if (zeropbignum(num)) {	/* zero is fixnum zero */
    xlpopn(5);
    return cvfixnum((FIXTYPE) 0);
  }
  if (getbignumsign(denom)) {	/* denominator must be positive */
    denom = copybignum(denom, 0);
    num = copybignum(num,!getbignumsign(num)); /* final sign */
  }
  n = copybignum(num, 0);	/* abs of numerator */
  m = denom;
  for (;;) {			/* get gcd */
    divbignum(m, n, &r);	/* use remainder only */
    if (zeropbignum(r)) break;
    m = n;
    n = r;
  }
  if ((!cvtbigfixnum(n, &d)) || d != 1) { /* can reduce */
    denom = divbignum(denom, n, &r);
    num = divbignum(num, n, &r);
  }
  if ((fixtyped = cvtbigfixnum(denom, &d)) != 0 && d == 1) {
    /* reduced to an integer */
    xlpopn(5);
    if (cvtbigfixnum(num, &nu)) return cvfixnum(nu);
    return num;
  }
  /* got value to return */
  r = newnode(RATIO);
  r->n_cdr = r->n_car = NIL; /* in case of garbage collect */
  r->n_cdr = (fixtyped ? cvfixnum(d) : denom);
  r->n_car = (cvtbigfixnum(num, &nu) ? cvfixnum(nu) : num);
  xlpopn(5);
  return (r);
}

/* cvratio - convert an integer pair to a ratio node */
LVAL cvratio P2C(FIXTYPE, num, FIXTYPE, denom)
{
  LVAL val;
  unsigned long n, m, r, nu, de;
  int sign;

  if (num == 0) return cvfixnum((FIXTYPE) 0); /* zero is int zero */
  if (denom < 0) {		/* denominator must be positive */
    if (denom == -1 && num == MINFIX) {
      xlsave1(val);
      val = cvtulongbignum((unsigned long)MAXFIX+1, FALSE);
      xlpop();
      return val;
    }
    denom = -denom;
    sign = num >= 0;
  }
  else
    sign = num < 0;
	
  if (num < 0) num = -num;
  n = nu = (unsigned long)(long)num;
  m = de = (unsigned long)(long)denom; /* reduce the ratio: compute GCD */
  for (;;) {
    if ((r = m % n) == 0) break;
    m = n;
    n = r;
  }
  if (n != 1) {
    de /= n;
    nu /= n;
  }
  if (de == 1)
    return cvfixnum(sign ? -(long)nu : (long)nu); /* reduced to integer */
  xlsave1(val);
  val = newnode(RATIO);
  val->n_cdr = val->n_car = NIL; /* in case of garbage collect */
  if ((nu == (unsigned long)MAXFIX+1 && sign==0))
    val->n_car = cvtulongbignum(nu, sign);
  else
    val->n_car = cvfixnum(sign ? -(long)nu : (long)nu);
  if (de == (unsigned long)MAXFIX+1)
    val->n_cdr = cvtulongbignum(de, FALSE);
  else
    val->n_cdr = cvfixnum(de);
  xlpop();
  return (val);
}
#endif

/* newustream - create a new unnamed stream */
LVAL newustream(V)
{
    LVAL val;
    val = newnode(USTREAM);
    sethead(val,NIL);
    settail(val,NIL);
    return (val);
}

/* newobject - allocate and initialize a new object */
LVAL newobject P2C(LVAL, cls, int, size)
{
    LVAL val;
    val = allocvector(OBJECT,size+1);
    setelement(val,0,cls);
    return (val);
}

/* newclosure - allocate and initialize a new closure */
LVAL newclosure P4C(LVAL, name, LVAL, type, LVAL, env, LVAL, fenv)
{
    LVAL val;
    val = allocvector(CLOSURE,CLOSIZE);
    setname(val,name);
    settype(val,type);
    setenvi(val,env);
    setfenv(val,fenv);
    return (val);
}

/* newstruct - allocate and initialize a new structure node */
LVAL newstruct P2C(LVAL, type, int, size)
{
    LVAL val;
    val = allocvector(STRUCT,size+1);
    setelement(val,0,type);
    return (val);
}

#ifdef BYTECODE
/* newcpsnode - allocate and initialize a new CPS node for compiler */
LVAL newcpsnode P1C(LVAL, type)
{
    LVAL val;
    val = allocvector(CPSNODE,CPSNODESIZE);
    setcpstype(val,type);
    return (val);
}

/* newbcode - allocate and initialize a new byte code vector */
LVAL newbcode P5C(LVAL, code, LVAL, jtab, LVAL, lits, LVAL, idx, LVAL, env)
{
    LVAL val;
    val = allocvector(BCODE,BCODESIZE);
    setbccode(val, code);
    setbcjtab(val, jtab);
    setbclits(val, lits);
    setbcidx(val, idx);
    setbcenv(val, env);
    return (val);
}
#endif /* BYTECODE */

#ifdef PACKAGES
/* newpackage - allocate and initialize a new package */
LVAL newpackage(V)
{
  LVAL val;
  xlsave1(val);
  val = allocvector(PACKAGE,PACKSIZE);
  setintsyms(val, newvector(HSIZE));
  setextsyms(val, newvector(HSIZE));
  xlpop();
  return (val);
}
#endif /* PACKAGES */

/* newvector - allocate and initialize a new vector node */
LVAL newvector P1C(unsigned, size)
{
  return(allocvector(VECTOR,size));
}

/* allocvector - allocate and initialize a new vector node */
LOCAL LVAL allocvector P2C(int, type, unsigned, size)
{
  LVAL vect, *p;
  int i;
  unsigned long bsize; /* changed to unsigned long - L. Tierney */

  xlsave1(vect);
  vect = newnode(type);
  vect->n_vsize = 0;
  if (size != 0) {
    bsize = wtob_size(size);
    check_alloc_threshold(bsize);
    if ((vect->n_vdata = (LVAL *)VALLOC(size)) == NULL) {
      gc();
      if ((vect->n_vdata = (LVAL *)VALLOC(size)) == NULL)
	xlabort("insufficient vector space");
    }
    vect->n_vsize = size;
    total += bsize;
  }

  /* set all the elements to NIL, except for STRINGs and TVECs */
  if (type != STRING && type != TVEC)
    for (i = 0, p = vect->n_vdata; i < size; i++)
      *p++ = NIL;

  xlpop();
  return (vect);
}

#ifdef XLISP_STAT
/* Added for internal allocated storage - L. Tierney */
#include "xlstat.h"

LOCAL char *IViewNewAData P4C(int, n, int, m, long *, size, int, reloc)
{
  char *addr;
  
  addr = (reloc) ? StRCalloc(n, m): StCalloc(n, m);
  *size = (reloc) ? StRSize(addr) : ((long) m) * ((long) n);
  return(addr);
}

/* newadata(n, m, reloc) - convert a string to a string node */
LVAL newadata P3C(int, n, int, m, int, reloc)
{
    LVAL val;
    long size;
    
    /*if (reloc) xlfail("relocatable allocated data not supported");*/

    size = ((long) m) * ((long) n);
    check_alloc_threshold(size);

    xlsave1(val);
    val = newnode(ADATA);
    setadreloc(val, reloc);

    /**** check what happens when asked for zero length */
    if ((val->n_adaddr = IViewNewAData(n, m, &size, reloc)) == NULL) {
	gc();  
	if ((val->n_adaddr = IViewNewAData(n, m, &size, reloc)) == NULL)
	    xlabort("insufficient memory");
    }
    val->n_adsize = size;
    total += size;

    xlpop();
    return (val);
}

VOID reallocaddata P3C(LVAL, val, int, n, int, m)
{
  char *addr;
  
  check_alloc_threshold(((long) n) * ((long) m)); /**** overestimate */
  if (! adatap(val) || ! getadreloc(val)) xlfail("not relocatable");
  addr = StRRealloc(getadaddr(val), n, m);
  if (addr == NULL) xlabort("allocation failed");
  val->n_adaddr = addr;
  total -= getadsize(val);
  val->n_adsize = StRSize(addr);
  total += getadsize(val);
}

VOID freeadata P1C(LVAL, val)
{
  if (! adatap(val)) xlfail("not a data object");
  if (getadreloc(val)) StRFree(getadaddr(val));
  else StFree(getadaddr(val));
  val->n_adaddr = NULL;
  total -= getadsize(val);
  val->n_adsize = 0;
  adjust_alloc_threshold();
}

/* Added for internal allocated storage - L. Tierney */
#endif /* XLISP_STAT */

/* newnode - allocate a new node */
#ifdef JMAC
LOCAL LVAL Newnode P1C(int, type)
{
    LVAL nnode;
  
    /* get a free node */
    gc();
    expand_node_space();
    if ((nnode = fnodes) == NIL)
        xlabort("insufficient node space");

    /* unlink the node from the free list */
    fnodes = cdr(nnode);
    nfree -= 1L;
    
    /* initialize the new node */
    nnode->n_type = type;
    rplacd(nnode,NIL);

    /* return the new node */
    return (nnode);
    }
#else
LOCAL LVAL newnode P1C(int, type)
{
    LVAL nnode;

    /* get a free node */
    if ((nnode = fnodes) == NIL) {
        gc();
        expand_node_space();
	if ((nnode = fnodes) == NIL)
	    xlabort("insufficient node space");
    }

    /* unlink the node from the free list */
    fnodes = cdr(nnode);
    nfree -= 1L;

    /* initialize the new node */
    setntype(nnode, type);
    rplacd(nnode,NIL);

    /* return the new node */
    return (nnode);
}
#endif

/* gc - garbage collect (only called here and in xlimage.c) */
VOID gc(V)
{
    LVAL **p,*ap,tmp;
    FRAMEP newfp;
    LVAL fun;
#ifdef TIMES
    unsigned long gccount = run_tick_count();
#endif

#ifdef STSZ
#if (GCSTMARGIN>0)
    if (STACKREPORT(fun)<GCSTMARGIN) { /* Do not try gc with less */
        dbgputstr("Insufficient stack left for GC  ");
        if (batchmode) xlfatal("uncaught error");
        xltoplevel(TRUE);
    }
#endif
#endif

    set_gc_cursor(TRUE); /* L. Tierney */

    /* print the start of the gc message */
    if (s_gcflag != NULL && getvalue(s_gcflag) != NIL) {
        /* print message on a fresh line */
        xlfreshline(getvalue(s_debugio));
	sprintf(buf,"[ gc: total %ld, ",nnodes);
	dbgputstr(buf); /* TAA MOD -- was std output */
    }

    /* $putpatch.c$: "MODULE_XLDMEM_C_GC" */

    /* mark the obarray, the argument list and the current environment */
    if (obarray != NULL)
	mark(obarray);
    if (xlenv != NIL)
	mark(xlenv);
    if (xlfenv != NIL)
	mark(xlfenv);
    if (xldenv != NIL)
	mark(xldenv);

    mark(NIL);
    mark(s_unbound);    /* TAA Mod 1/92 */

    /* mark the evaluation stack */
    for (p = xlstack; p < xlstktop; ++p)
	if ((tmp = **p) != NIL)
	    mark(tmp);

    /* mark the argument stack */
    for (ap = xlargstkbase; ap < xlsp; ++ap)
	if ((tmp = *ap) != NIL)
	    mark(tmp);

#ifdef MULVALS
    /* mark the results */
    {
      int i;
      for (i = 0; i < xlnumresults; i++)
	if ((tmp = xlresults[i]) != NIL)
	  mark(tmp);
    }
#endif /* MULVALS */

    /* sweep memory collecting all unmarked nodes */
    sweep();

    unmark_node(NIL);

    adjust_alloc_threshold();

    /* count the gc call */
    ++gccalls;

    /* call the *gc-hook* if necessary */
    if (s_gchook != NULL && ((fun = getvalue(s_gchook)) != NIL) ) {

        /* rebind hook function to NIL  TAA MOD */
        tmp = xldenv;
        xldbind(s_gchook,NIL);

	newfp = xlsp;
	pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
	pusharg(fun);
	pusharg(cvfixnum((FIXTYPE)2));
	pusharg(cvfixnum((FIXTYPE)nnodes));
	pusharg(cvfixnum((FIXTYPE)nfree));
	xlfp = newfp;
	xlapply(2);

        /* unbind the symbol TAA MOD */
        xlunbind(tmp);
    }

    /* print the end of the gc message */
    if (s_gcflag != NULL && getvalue(s_gcflag) != NIL) {
	sprintf(buf,"%ld free ]\n",nfree);
	dbgputstr(buf); /* TAA MOD -- was std output */
    }

    set_gc_cursor(FALSE); /* L. Tierney */

#ifdef TIMES
    gctime += run_tick_count() - gccount;
#endif
}

/* mark - mark all accessible nodes */
LOCAL VOID mark P1C(LVAL, ptr)
{
  register LVAL this,prev,tmp;
  int type,i,n;

#ifdef STSZ /* can't recover from here */
#if (GCSTMARGIN>0)
    if (STACKREPORT(n) < GCMARGLO)
        xlfatal("Insufficient stack during GC");
#endif
#endif

  /* initialize */
  prev = NIL;
  this = ptr;

  /* mark this list */
  for (;;) {

    /* descend as far as we can */
    while (! node_marked(this)) {

      type = mark_and_type(this);

      /* check cons type nodes */
      if (type >= CONS && type < ARRAY) {
	if ((tmp = car(this)) != NIL) {
	  set_left(this);
	  rplaca(this,prev);
	}
	else if ((tmp = cdr(this)) != NIL)
	  rplacd(this,prev);
	else 				/* both sides nil */
	  break;
	prev = this;			/* step down the branch */
	this = tmp;
      }
      /* $putpatch.c$: "MODULE_XLDMEM_C_MARK" */
      else if (is_array_type(type)) {
	  for (i = 0, n = getsize(this); i < n; i++) 
	    if ((tmp = getelement(this,i)) != NIL)
	      mark(tmp);
	  break;
      }
      else if (type == NATPTR) {
	mark(cdr(this));
	break;
      }
      else
	break;
    }

    /* backup to a point where we can continue descending */
    for (;;) {

      /* make sure there is a previous node */
      if (prev != NIL) {
	if (came_from_left(prev)) {	/* came from left side */
	  unset_left(prev);
	  tmp = car(prev);
	  rplaca(prev,this);
	  if ((this = cdr(prev)) != NIL) {
	    rplacd(prev,tmp);
	    break;
	  }
	}
	else {				/* came from right side */
	  tmp = cdr(prev);
	  rplacd(prev,this);
	}
	this = prev;			/* step back up the branch */
	prev = tmp;
      }

      /* no previous node, must be done */
      else
	return;
    }
  }
}

/* sweep - sweep all unmarked nodes and add them to the free list */
LOCAL VOID sweep(V)
{
  SEGMENT *seg;
  LVAL p;
  int n;

  /* empty the free list */
  fnodes = NIL;
  nfree = 0L;

  /* add all unmarked nodes */
  for (seg = segs; seg != NULL; seg = seg->sg_next) {
    if (seg == fixseg || seg == charseg) {
      /* remove marks from segments */
      p = &seg->sg_nodes[0];
      for (n = seg->sg_size; --n >= 0;)
	unmark_node(p++);
      continue;
    }
    p = &seg->sg_nodes[0];

    for (n = seg->sg_size; --n >= 0; p++)
      if (node_marked(p))
	unmark_node(p);
      else {
	switch (gcntype(p)) {
	case STRING:
	case TVEC:
	  if (getstring(p) != NULL) {
	    long size = btow_size(getslength(p) + 1);
	    total -= size * sizeof(LVAL);
	    VRELEASE(getstring(p), size);
	  }
	  break;
#ifdef BIGNUMS
  case BIGNUM:
	  if (getbignumarray(p) != NULL) {
	    long size = btow_size((1+(long)getbignumsize(p))
				  *sizeof(BIGNUMDATA));
	    total -= size * sizeof(LVAL);
	    VRELEASE(getstring(p), size);
	  }
	  break;
#endif
#ifdef XLISP_STAT
	case ADATA:
	  if (getadaddr(p) != NULL) {
	    total -= getadsize(p);
	    if (getadreloc(p)) StRFree(getadaddr(p));
	    else StFree(getadaddr(p));
	  }
	  break;
#endif /* XLISP_STAT */
	case STREAM:
	  if (getfile(p) != CLOSED
	      && getfile(p) != STDIN
	      && getfile(p) != STDOUT
	      && getfile(p) != CONSOLE)/* taa fix - dont close stdio */
	    OSCLOSE(getfile(p));
	  break;
        /* $putpatch.c$: "MODULE_XLDMEM_C_SWEEP" */
	case SYMBOL:
	case OBJECT:
	case VECTOR:
	case CLOSURE:
	case STRUCT:
#ifdef BYTECODE
	case CPSNODE:
	case BCODE:
#endif /*BYTECODE */
#ifdef PACKAGES
	case PACKAGE:
#endif /* PACKAGES */
	  if (p->n_vsize) {
	    total -= wtob_size(p->n_vsize);
	    VRELEASE((char *) p->n_vdata, p->n_vsize);
	  }
	  break;
	}
	setntype(p, FREE);
	rplaca(p,NIL);
	rplacd(p,fnodes);
	fnodes = p;
	nfree++;
      }
  }
}

/* addseg - add a segment to the available memory */
LOCAL int addseg(V)
{
    SEGMENT *newseg;
    LVAL p;
    int n;

    /* allocate the new segment */
    if (anodes == 0 || (newseg = newsegment(anodes)) == NULL)
	return (FALSE);
    adjust_alloc_threshold();

    /* add each new node to the free list */
    p = &newseg->sg_nodes[0];
    for (n = anodes; --n >= 0; ++p) {
	rplacd(p,fnodes);
	fnodes = p;
    }
    nfree += (long)anodes;

    /* return successfully */
    return (TRUE);
}

/* newsegment - create a new segment (only called here and in xlimage.c) */
SEGMENT *newsegment P1C(int, n)
{
    SEGMENT *newseg;

#ifdef MACINTOSH /* L. Tierney */
    maximum_memory();
#endif /* MACINTOSH */

    /* allocate the new segment */
    if ((newseg = (SEGMENT *)CALLOC(1,segsize(n))) == NULL)
	return (NULL);

    /* initialize the new segment */
    newseg->sg_size = n;
    newseg->sg_next = NULL;
    if (segs != NULL)
	lastseg->sg_next = newseg;
    else
	segs = newseg;
    lastseg = newseg;

    /* update the statistics */
    total += (long)segsize(n);
    nnodes += (long)n;
    ++nsegs;

    /* return the new segment */
    return (newseg);
}

/* stats - print memory statistics */
LOCAL VOID stats(V)
{
    sprintf(buf,"Nodes:        %ld\n",nnodes); stdputstr(buf);
    sprintf(buf,"Free nodes:   %ld\n",nfree);  stdputstr(buf);
    sprintf(buf,"Segments:     %d\n",nsegs);   stdputstr(buf);
    sprintf(buf,"Allocate:     %d\n",anodes);  stdputstr(buf);
    sprintf(buf,"Total:        %ld\n",total);  stdputstr(buf);
    sprintf(buf,"Collections:  %ld\n",gccalls); stdputstr(buf);
/**** drop after debugging is done */
    sprintf(buf,"Threshold crosses: %ld\n", threshold_crossing_gccount);
    stdputstr(buf);
#ifdef TIMES
    sprintf(buf,"Time (sec):  %ld\n",gctime/ticks_per_second());
    stdputstr(buf);
#endif
}

/* xgc - xlisp function to force garbage collection */
LVAL xgc(V)
{
    /* make sure there aren't any arguments */
    xllastarg();

    /* garbage collect */
    gc();

    /* return nil */
    return (NIL);
}

/* xexpand - xlisp function to force memory expansion */
LVAL xexpand(V)
{
    LVAL num;
    FIXTYPE n,i;

    /* get the new number to allocate */
    if (moreargs()) {
	num = xlgafixnum();
	n = getfixnum(num);
        /* make sure there aren't any more arguments */
        xllastarg();
    }
    else
	n = 1;

    /* allocate more segments */
    for (i = 0; i < n; i++)
	if (!addseg())
	    break;

    /* return the number of segments added */
    return (cvfixnum((FIXTYPE)i));
}

/* xalloc - xlisp function to set the number of nodes to allocate */
LVAL xalloc(V)
{
    FIXTYPE n;  /* TAA MOD -- prevent overflow */
    int oldn;

#ifdef DEBUG
    /**** drop after debugging is done */
    if (symbolp(peekarg(0))) {
      LVAL arg;
  
      if (xlgetkeyarg(xlenter(":GC-EXPAND-FRACTION"), &arg)) {
	if (floatp(arg)) {
	  gc_expand_frac = getflonum(arg);
	  return(arg);
	}
      }
      return(NIL);
    }
#endif /* DEBUG */

    /* get the new number to allocate */
    n = getfixnum(xlgafixnum());

    /* make sure there aren't any more arguments */
    if (xlargc > 1) xltoomany();    /* but one more is OK, TAA MOD */

#ifdef DODO
    /**** put something like this in after debugging */
    /**** also need to be able to expand up to some level, */
    /**** adjust expansion parameters */
    /* Place limits on argument by clipping to reasonable values  TAA MOD */
    if (n > ((long)MAXSLEN - sizeof(SEGMENT))/sizeof(struct node)) 
        n = ((long)MAXSLEN - sizeof(SEGMENT))/sizeof(struct node);
    else if (n < 1000) 
        n = 1000;   /* arbitrary */
#endif /* DODO */

    /* set the new number of nodes to allocate */
    oldn = anodes;
    anodes = (int)n;

    /* return the old number */
    return (cvfixnum((FIXTYPE)oldn));
}

/* xmem - xlisp function to print memory statistics */
LVAL xmem(V)
{
    /* allow one argument for compatiblity with common lisp */
    if (xlargc > 1) xltoomany();    /* TAA Mod */

    /* print the statistics */
    stats();

    /* return nil */
    return (NIL);
}

#ifdef SAVERESTORE
/* xsave - save the memory image */
LVAL xsave(V)
{
    char *name;

    /* get the file name */
    name = getstring(xlgetfname());
    xllastarg();

    /* save the memory image */
    return (xlisave(name) ? s_true : NIL);
}

/* xrestore - restore a saved memory image */
LVAL xrestore(V)
{
#ifdef XLISP_STAT
    xlfail("restore not available");
    return(NIL); /* never returns */
#else
    char *name;

    /* get the file name */
    name = getstring(xlgetfname());
    xllastarg();

    /* restore the saved memory image */
    if (!xlirestore(name))
	return (NIL);

    /* return directly to the top level */
    dbgputstr("[ returning to the top level ]\n");  /* TAA MOD --was std out*/
    XL_LONGJMP(top_level,1);
    return (NIL);   /* never executed, but avoids warning message */
#endif /* XLISP_STAT */
}
#endif /* SAVERESTORE */

/* From XLISP-STAT, Copyright (c) 1988 Luke Tierney */

LVAL newicomplex P2C(FIXTYPE, real, FIXTYPE, imag)
{
  LVAL val, r, i;
  
  if (imag == 0) val = cvfixnum(real);
  else {
    xlstkcheck(2);
    xlsave(r);
    xlsave(i);
    r = cvfixnum(real);
    i = cvfixnum(imag);
    val = cons(r, i);
    setntype(val, COMPLEX);
    xlpopn(2);
  }
  return(val);
}

LVAL newdcomplex P2C(double, real, double, imag)
{
  LVAL val, r, i;
  
  xlstkcheck(2);
  xlsave(r);
  xlsave(i);
  r = cvflonum((FLOTYPE) real);
  i = cvflonum((FLOTYPE) imag);
  val = cons(r, i);
  setntype(val, COMPLEX);
  xlpopn(2);
  return(val);
}

#ifdef BIGNUMS
/* newcomplex - allocate and initialize a new object */
LVAL newcomplex P2C(LVAL, real, LVAL, imag)
{
  LVAL val;
  xlstkcheck(2);
  xlprotect(real);
  xlprotect(imag);
	
  if (! rationalp(real) || ! rationalp(imag)) {
    if (! floatp(real)) real = cvflonum(makefloat(real));
    if (! floatp(imag)) imag = cvflonum(makefloat(imag));
  }
  if (fixp(imag) && getfixnum(imag) == 0)
    val = real;
  else {
    val = newnode(COMPLEX);
    getreal(val) = real;
    getimag(val) = imag;
  }
  xlpopn(2);
  return(val);
}
#else
/* newcomplex - allocate and initialize a new object */
LVAL newcomplex P2C(LVAL, real, LVAL, imag)
{
  if (fixp(real) && fixp(imag))
    return(newicomplex(getfixnum(real), getfixnum(imag)));
  else
    return(newdcomplex(makefloat(real), makefloat(imag)));
}
#endif
#endif /* NEWGC */

#ifdef TIMES
/* gc_tick_count - total number of ticks spent in gc */
unsigned long gc_tick_count(V) { return(gctime); }
#endif

/* check_alloc_threshold - do gc if alloc would push total over threshold */
LOCAL VOID check_alloc_threshold P1C(long, size)
{
  if (alloc_threshold < total + size) {
    long tmp;
    static int bumped = 0;
#ifdef NEWGC
    tmp = (total + size + ALLOC_THRESHOLD_INCREMENT - 1);
    tmp /= ALLOC_THRESHOLD_INCREMENT;
    tmp *= ALLOC_THRESHOLD_INCREMENT;
    alloc_threshold = tmp;
    ggc(FALSE);
    bumped++;
    if (bumped > 4 || alloc_threshold < total + size) {
      tmp = (total + size + ALLOC_THRESHOLD_INCREMENT - 1);
      tmp /= ALLOC_THRESHOLD_INCREMENT;
      tmp *= ALLOC_THRESHOLD_INCREMENT;
      alloc_threshold = tmp;
      ggc(TRUE);
      bumped = 0;
    }
#else
    tmp = (total + size + ALLOC_THRESHOLD_INCREMENT - 1);
    tmp /= ALLOC_THRESHOLD_INCREMENT;
    tmp *= ALLOC_THRESHOLD_INCREMENT;
    alloc_threshold = tmp;
    gc();
#endif /* NEWGC */
    threshold_crossing_gccount++;
  }
}

/* adjust_alloc_threshold - adjust threshold up or down based on total */
LOCAL VOID adjust_alloc_threshold(V)
{
  long n, m;

  n = (total + ALLOC_THRESHOLD_INCREMENT - 1) / ALLOC_THRESHOLD_INCREMENT;
  m = alloc_threshold / ALLOC_THRESHOLD_INCREMENT;
  if (n > m) alloc_threshold = n * ALLOC_THRESHOLD_INCREMENT;
  else if (m > n + 2) alloc_threshold = (n + 2) * ALLOC_THRESHOLD_INCREMENT;
}

/* expand_node_space allocate new nodes if necessary */
LOCAL VOID expand_node_space(V)
{
  long needed;

  /* find the number of additional nodes needed */
  needed = (long) (gc_expand_frac * (nnodes - nfree)) + 1;

  /* allocate additional segments if necessary */
  if (anodes > 0)
      while (needed > nfree)
	if (! addseg())
	  break;
}   

/**** This scheme assumes sizeof(void *) <= 8 */
/**** For the MS Windoes version, malloc should be tuned properly */
/**** Should be integrated properly with GC; use know sizes for symbol, etc. */

#define PAGEUNITS 9
#define MEMUNITS 10
static ALLOCTYPE *memarray[MEMUNITS] = { NULL };

/* morevmem - allocate another page of storage to nunits */
LOCAL VOID morevmem P1C(int, nunits)
{
  ALLOCTYPE *p;
  unsigned long nbytes, nalloc;
  int n;

  nalloc = (8 << ((nunits < PAGEUNITS) ? PAGEUNITS : nunits));
  n = (nunits < PAGEUNITS) ? (1 << (PAGEUNITS - nunits)) : 1;
  nbytes = (8 << nunits);
  p = malloc(nalloc);
  if (p == NULL) xlabort("insufficient vector space");
  if (p != NULL) {
    while (n-- > 0) {
      *((char **) p) = memarray[nunits];
      memarray[nunits] = p;
      p = ((char *) p) + nbytes;
    }
  }
}

/* VALLOC - allocate space for a vector */
ALLOCTYPE *VALLOC P1C(unsigned long, size)
{
  ALLOCTYPE *p;
  unsigned long nbytes;
  int nunits;

  if (size > MAXVECLEN) xlfail("allocation too large"); /**** check this */

  /* find number of bytes, rounded to next multiple of 8 */
  nbytes = (size * sizeof(LVAL) + 7) & ~7;
  
  /* find memory unit -- max(0, ceiling(log2(nbytes)) - 3) */
  if (nbytes == 0) nunits = 0;
  else {
    register unsigned long shiftr = (nbytes - 1) >> 2;
    nunits = 0;
    while (shiftr >>= 1) nunits++;
  }

  if (nbytes == 0)
    p = NULL;
  else if (nunits < MEMUNITS) {
    char **pt;
    if (memarray[nunits] == NULL) morevmem(nunits);
    pt = (char **) memarray[nunits];
    if (pt != NULL) memarray[nunits] = pt[0];
    p = (char *) pt;
  }
  else p = malloc(size * sizeof(LVAL));
  if (p == NULL) xlabort("insufficient vector space");

  MEMSET((char *) p, 0, wtob_size(size));
  return(p);
}

/* VRELEASE - release space for a vector */
VOID VRELEASE P2C(ALLOCTYPE *, p, unsigned long, size)
{
  unsigned long nbytes;
  int nunits;

  if (p != NULL) {
    /* find number of bytes, rounded to next multiple of 8 */
    nbytes = (size * sizeof(LVAL) + 7) & ~7;
  
    /* find memory unit -- max(0, ceiling(log2(nbytes)) - 3) */
    if (nbytes == 0) nunits = 0;
    else {
      register unsigned long shiftr = (nbytes - 1) >> 2;
      nunits = 0;
      while (shiftr >>= 1) nunits++;
    }

    if (nunits < MEMUNITS) {
      *((char **) p) = memarray[nunits];
      memarray[nunits] = p;
    }
    else free(p);
  }
}

LVAL newtvec P2C(int, n, int, m)
{
  unsigned size;
  LVAL val;

  size = n * m;
  val = allocvector(TVEC,btow_size(size+1));
  val->n_strlen = size;
  return (val);
}

LVAL newrndstate P2C(LVAL, gen, LVAL, data)
{
  LVAL val;
  val = cons(gen, data);
  setntype(val, RNDSTATE);
  return(val);
}

#ifdef BYTECODE
LVAL newbcclosure P2C(LVAL, type, LVAL, code)
{
  LVAL val;
  val = cons(type, code);
  setntype(val, BCCLOSURE);
  return(val);
}
#endif /* BYTECODE */

static VOID check_finalize(V)
{
  LVAL last = NIL, next = finalize_registered, head, tail;

  while (consp(next)) {
    if (is_new_node(car(car(next)))) {
      head = next;
      tail = cdr(next);
      if (null(last))
        finalize_registered = tail;
      else
        Rplacd(last, tail);
      next = tail;
      Rplacd(head, finalize_pending);
      finalize_pending = head;
    }
    else {
      last = next;
      next = cdr(next);
    }
  }
}

static VOID do_finalize(V)
{
  CONTEXT cntxt;
  LVAL next;

  xlsave1(next);
  xlbegin(&cntxt,CF_UNWIND|CF_ERROR,NIL);
  XL_SETJMP(cntxt.c_jmpbuf);
  while (consp(finalize_pending)) {
    next = finalize_pending;
    finalize_pending = cdr(next);
    xlapp1(cdr(car(next)), car(car(next)));
  }
  xlend(&cntxt);
  xlpop();
}

static void check_weak_boxes()
{
  LVAL last = NIL, next = xlweakboxes;

  while (ntype(next) == WEAKBOX) {
    if (is_new_node(next)) {
      /* delete box from weak box list */
      LVAL tail = cdr(next);
      if (null(last))
        xlweakboxes = tail;
      else
        Rplacd(last, tail);
      next = tail;
    }
    else {
      if (is_new_node(car(next)))
	Rplaca(next, NIL);
      last = next;
      next = cdr(next);
    }
  }
}

LVAL xregfinal(V)
{
  LVAL arg, fun;
  arg = xlgetarg();
  fun = xlgetarg();
  xllastarg();
  finalize_registered = cons(cons(arg, fun), finalize_registered);
  return NIL;
}

LVAL xmkweakbox(V)
{
  LVAL val = xlgetarg();
  LVAL box = consa(val);
  xllastarg();
  ntype(box) = WEAKBOX;
  Rplacd(box, xlweakboxes);
  xlweakboxes = box;
  return box;
}

#define weakboxp(x) (ntype(x) == WEAKBOX)
#define xlgaweakbox()    (testarg(typearg(weakboxp)))
LVAL xweakboxval(V)
{
  LVAL box = xlgaweakbox();
  xllastarg();
  return car(box);
}

LVAL newnatptr P2C(ALLOCTYPE *, p, LVAL, v)
{
  LVAL val;
  xlprot1(v);
  val = newnode(NATPTR);
  car(val) = (LVAL) p;
  cdr(val) = v;
  xlpop();
  return val;
}


syntax highlighted by Code2HTML, v. 0.9.1