/*
 * chains.c
 * This module contains stuff that ought to get macroized.
 * [eichin:19880331.2032EST]
 */

#macro EXPAND(refptr)
{
  ASSERT(refptr->tag.CTL == END);
  if (refptr->data.CTL == end)
    {
      ropeptr tmpptr;
      
      refptr->tag.CTL = NEXT_FORWARD;

      tmpptr = getblock();
      ASSERT(refptr->tag.CTL == NEXT_BACKWARD);
      tmpptr->data.NEXT = refptr-1;

      refptr = refptr->data.NEXT = tmpptr+1;
    }
}
#endmacro

#macro INC(refptr)
{
  if(((++refptr)->tag.CTL == NEXT_FORWARD)
     || (refptr->tag.CTL == NEXT_FORWARD_UNALIGNED))
    {
      refptr = refptr->data.NEXT;
    }
}
#endmacro
#macro START(refptr) INC(refptr)
#endmacro
/*
 * DEC: assumes that the refptr is NOT at the first location.
 */
#macro DEC(refptr)
{
  if(((--refptr)->tag.CTL == NEXT_BACKWARD)
     || (refptr->tag.CTL == NEXT_BACKWARD_UNALIGNED))
    {
      refptr = refptr->data.NEXT;
    }
}
#endmacro
/*
 * no assumptions?
 */
#macro insert_cell(refptr)
{
  rope_elem cpy, cpy2;
  cpy.tag.CTL = END;
  cpy.data.CTL = end;

  while(refptr->tag.CTL != END)
    {
      cpy2 = *refptr;
      *refptr = cpy;
      cpy = cpy2;
      INC(refptr);
    }
  EXPAND(refptr);
  *refptr = cpy;
}
#endmacro

/*
 * Here some things get a tad confusing:
 * NEXT_FORWARD: the next block is begun at the beginning, this is at
 * 		the end.
 * NEXT_BACKWARD: the previous block is at the end, this is
 * 		at the beginning.
 * NEXT_FORWARD_UNALIGNED: the next block is begun at the beginning,
 * 		this is NOT at end.
 * NEXT_BACKWARD_UNALIGNED: this is at the beginning, but the previous
 * 		block is NOT at end.
 *
 * So, blocks always begin at the beginning, but they may end at
 * different places; we must always be able to tell from either end
 * what to do. [though perhaps we could just know at one end...]
 */

#macro compress(refptr)
{
  ASSERT(refptr->tag.CTL == END);
  refptr--;
  if (refptr->tag.CTL == NEXT_BACKWARD)
    {
      ropeptr savptr;
      savptr = refptr->data.NEXT;
      make_free(refptr);
      refptr = savptr++;	/* savptr is the tail of the BACK block */
      savptr->tag.CTL = END;
      savptr->data.CTL = end;
    }
  if (refptr->tag.CTL == NEXT_BACKWARD_UNALIGNED)
    {
      ropeptr savptr;
      savptr = refptr->data.NEXT;
      refptr->tag.CTL = END;
      refptr->data.CTL = end;
      refptr = savptr++;	/* savptr is NOT the tail...*/
      savptr->tag.CTL = END;
      savptr->data.CTL = notend;
    }
}
#endmacro
/*
 * useup: takes a pointer (to a `beginning' of a list) and eats the
 * current element; if it skips a block, it frees that. Maintains the
 * `BEGIN' pointer as needed.
 *
 * It should really check the following conditions:
 * 	NEXT_FORWARD_UNALIGN (and free on it)
 * 	END.notend in the new item: free the block AND `break'...
 * 	END.end in the new item: same?
 */
#macro useup(refptr, ENDHANDLER)
{
  refptr->tag.CTL = END;
  refptr->data.CTL = notend;
  refptr++;
  if (refptr->tag.CTL == END)
    {
      make_free(refptr);
      ENDHANDLER;		/* usually break or continue... */
    }
  if (refptr->tag.CTL == NEXT_FORWARD)
    {
      ropeptr nxtptr;
      nxtptr = refptr->data.NEXT;
      /*       s2ptr = refptr;??? */
      refptr->tag.CTL = END;
      refptr->data.CTL = end;
      while(refptr->tag.CTL != BEGIN)
	{
	  refptr --;
	}
      (--nxtptr)->tag.CTL = BEGIN;
      nxtptr->data.NEXT = refptr->data.NEXT;
      make_free(refptr);

      refptr = nxtptr+1;
    }
}
#endmacro
/*
 * back_trace: goes back to the start of this `block' and then frees
 * the whole thing. Assumes a BEGIN tag, so that it can actually free
 * it...
 */
#macro back_trace(refptr)
{
  while(refptr->tag.CTL != BEGIN)
    {
      refptr --;
    }
  make_free(refptr);
}
#endmacro

#declare
extern ropeptr nextfree;
#enddeclare

#macro make_free(refptr)
{
  refptr->tag.CTL = FREELIST;
  refptr->data.NEXT = nextfree;
  nextfree = refptr;
}
#endmacro

/*
 * Since we spec rope[0] = { BEGIN, endptr }, anything that adds to
 * the end of a chain just needs to reference through the first
 * element. Such as:
 * wire->data.NEXT->{data,tag}
 */

#macro set_item(wire, TYPE, piece, partname, value)
{
  EXPAND(wire);
  wire->tag.TYPE = piece;
  (wire->data.TYPE.partname) = value;
}
#endmacro
/*
 * special case for events:
 */
#macro set_event(evptr, value, time)
{
  EXPAND(evptr);
  evptr->tag.EVENT = value;
  evptr->data.EVENT = time;
}
#endmacro

#macro add_item(wire, TYPE, piece, partname, value)
{
  set_item(wire->data.NEXT, TYPE, piece, partname, value);
  INC(wire->data.NEXT);
}
#endmacro

#macro add_event(timlin, value, time)
{
  set_event(timlin->data.NEXT, value, time);
  INC(timlin->data.NEXT);
}
#endmacro

#macro add_chain(wire, TYPE, piece, partname)
{
  add_item(wire, TYPE, piece, partname, getblock());
}
#endmacro

#macro find_item(wire, TYPE, value)
{
  while(wire->tag.TYPE != END)
    {
      if(wire->tag.TYPE == value) break;
      INC(wire);
    }
}
#endmacro

#macro nextwire(argv, wire, time)
{
  wire = argv->data.GENERIC.genwire;
  ASSERT(argv->tag.GENERIC == genwire);
  find_item(wire, WIRE, FUTURE);
  time = wire->data.WIRE.future;
  INC(argv);
}
#endmacro

#macro readnextwire(argv, wire, time)
{
  wire = argv->data.GENERIC.genwire;
  ASSERT(argv->tag.GENERIC == genwire);
  find_item(wire, WIRE, NOW);
  time = wire->data.WIRE.now;
  INC(argv);
}
#endmacro
/*
 *
 * Here are the macros for the simplified user spec.
 * [eichin:19880510.0436EST]
 */

#declare
Wire new_to_wirelist();
#enddeclare

#ifdef __STDC__
#macro MAKEWIRE(NAME)
Wire NAME = new_to_wirelist(master, #NAME)
#endmacro
#else
#macro MAKEWIRE(NAME)
Wire NAME = new_to_wirelist(master, "NAME")
#endmacro
#endif
     
#macro chain_init(newdep, func)
{
  newdep = getblock();
  add_item(newdep, GENERIC, GENFUNC, genfunc, func);
}
#endmacro

#macro chain_add(newdep, wire)
{
  add_item(newdep, GENERIC, GENWIRE, genwire, wire);
}
#endmacro

#macro chain_dep(wire, newdep)
{
  Wire tmpwire = wire;
  find_item(tmpwire, WIRE, DEPENDS);
  add_item(tmpwire->data.DEPS.generics, DEPS, DEPSLIST, generics, newdep);
}
#endmacro

/* These are the basis cases, probably never used. */
#macro make_arg1(op)
{
  Generics wlist;
  chain_init(wlist, op);
#endmacro

#macro make_dep0(a1)
}
#endmacro

#macro make_arg2(op, a1)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
#endmacro

#macro make_dep1(a1)
  chain_dep(a1, wlist);
}
#endmacro

#macro make_arg3(op, a1, a2)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
#endmacro

#macro make_dep2(a1, a2)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
}
#endmacro

#macro make_arg4(op, a1, a2, a3)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
  chain_add(wlist, a3);
#endmacro

#macro make_dep3(a1, a2, a3)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
  chain_dep(a3, wlist);
}
#endmacro

#macro make_arg5(op, a1, a2, a3, a4)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
  chain_add(wlist, a3);
  chain_add(wlist, a4);
#endmacro

#macro make_dep4(a1, a2, a3, a4)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
  chain_dep(a3, wlist);
  chain_dep(a4, wlist);
}
#endmacro

#macro make_arg6(op, a1, a2, a3, a4, a5)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
  chain_add(wlist, a3);
  chain_add(wlist, a4);
  chain_add(wlist, a5);
#endmacro

#macro make_dep5(a1, a2, a3, a4, a5)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
  chain_dep(a3, wlist);
  chain_dep(a4, wlist);
  chain_dep(a5, wlist);
}
#endmacro

#macro make_arg7(op, a1, a2, a3, a4, a5, a6)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
  chain_add(wlist, a3);
  chain_add(wlist, a4);
  chain_add(wlist, a5);
  chain_add(wlist, a6);
#endmacro

#macro make_dep6(a1, a2, a3, a4, a5, a6)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
  chain_dep(a3, wlist);
  chain_dep(a4, wlist);
  chain_dep(a5, wlist);
  chain_dep(a6, wlist);
}
#endmacro

#macro make_arg8(op, a1, a2, a3, a4, a5, a6, a7)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
  chain_add(wlist, a3);
  chain_add(wlist, a4);
  chain_add(wlist, a5);
  chain_add(wlist, a6);
  chain_add(wlist, a7);
#endmacro

#macro make_dep7(a1, a2, a3, a4, a5, a6, a7)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
  chain_dep(a3, wlist);
  chain_dep(a4, wlist);
  chain_dep(a5, wlist);
  chain_dep(a6, wlist);
  chain_dep(a7, wlist);
}
#endmacro

#macro make_arg9(op, a1, a2, a3, a4, a5, a6, a7, a8)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
  chain_add(wlist, a3);
  chain_add(wlist, a4);
  chain_add(wlist, a5);
  chain_add(wlist, a6);
  chain_add(wlist, a7);
  chain_add(wlist, a8);
#endmacro

#macro make_dep8(a1, a2, a3, a4, a5, a6, a7, a8)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
  chain_dep(a3, wlist);
  chain_dep(a4, wlist);
  chain_dep(a5, wlist);
  chain_dep(a6, wlist);
  chain_dep(a7, wlist);
  chain_dep(a8, wlist);
}
#endmacro

#macro make_arg10(op, a1, a2, a3, a4, a5, a6, a7, a8, a9)
{
  Generics wlist;
  chain_init(wlist, op);
  chain_add(wlist, a1);
  chain_add(wlist, a2);
  chain_add(wlist, a3);
  chain_add(wlist, a4);
  chain_add(wlist, a5);
  chain_add(wlist, a6);
  chain_add(wlist, a7);
  chain_add(wlist, a8);
  chain_add(wlist, a9);
#endmacro

#macro make_dep9(a1, a2, a3, a4, a5, a6, a7, a8, a9)
  chain_dep(a1, wlist);
  chain_dep(a2, wlist);
  chain_dep(a3, wlist);
  chain_dep(a4, wlist);
  chain_dep(a5, wlist);
  chain_dep(a6, wlist);
  chain_dep(a7, wlist);
  chain_dep(a8, wlist);
  chain_dep(a9, wlist);
}
#endmacro

#ifdef __STDC__
#macro make_anop(opc, argops, argc, args)
make_arg##argc args make_dep##opc argops
#endmacro
#else
#macro make_anop(opc, argops, argc, args)
make_arg/**/argc args make_dep/**/opc argops
#endmacro
#endif

#macro make_binop(op, c, a, b) make_anop(2,(a, b),4,(op, c, a, b))
#endmacro

#macro make_unop(op, b, a) make_anop(1,(a),3,(op, b, a))
#endmacro

#macro make_tracer(op, line) make_anop(1,(line),2,(op,line))
#endmacro

#macro SETWIRE(wire, level, time)
{
  Timeline t1;
  Wire ww = wire;
  find_item(ww, WIRE, FUTURE);
  t1 = ww->data.WIRE.future;
  add_event(t1, level, time);
}
#endmacro

#macro FileArgs(master,argc,argv)
int dummy2 = initialize_file(master, argc, argv)
#endmacro

/*
 * Now the sugar for the module building template.
 * [eichin:19880511.2218EST]
 */

#macro MOREARGS(dep)
dep->tag.CTL != END
#endmacro

#macro SAVEOUTWIRE(argv, wire)
{
  wire = argv->data.GENERIC.genwire;
  ASSERT(argv->tag.GENERIC == genwire);
  INC(argv);
}
#endmacro

#macro READNEXTSTATE(argv, event)
{
  Wire wire = argv->data.GENERIC.genwire;
  ASSERT(argv->tag.GENERIC == genwire);
  find_item(wire, WIRE, NOW);
  event = wire->data.WIRE.now->tag.EVENT;
  INC(argv);
}
#endmacro

#macro READNEXTSTATETIME(argv, event, time)
{
  Wire wire = argv->data.GENERIC.genwire;
  ASSERT(argv->tag.GENERIC == genwire);
  find_item(wire, WIRE, NOW);
  event = wire->data.WIRE.now->tag.EVENT;
  time = wire->data.WIRE.now->data.EVENT;
  INC(argv);
}
#endmacro

#macro READNEXTTIME(argv, tim)
{
  tim = argv->data.GENERIC.gentime;
  ASSERT(argv->tag.GENERIC == GENTIME);
  INC(argv);
}
#endmacro

#macro SETOUTPUT(wire, val, time)
{
  Wire tmpwire = wire;
  find_item(wire, WIRE, FUTURE);
  add_event(wire->data.WIRE.future, val, time);
}
#endmacro

#define STATE Event_tag

     
