-- StackImpl.mesa  
--   Edited by Sweet, 5-Mar-82 21:15:48
--   Edited by Satterthwaite, December 16, 1982 10:08 am

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [codeptr, tempcontext, tempstart, warnStackOverflow],
  CodeDefs: TYPE USING [
    Base, BYTE, CCIndex, codeType, DataStackIndex, EvalStackSize, LabelCCNull,
    Lexeme, StackBackup, StackIndex, StackItem, StackLocRec, StackNull, StackPos,
    TempAddr, VarComponent],
  FOpCodes: TYPE USING [
    qBNDCK, qDDUP, qDEXCH, qDIS, qDUP, qEXCH, qLI, qLLK, qNILCK, qNILCKL],
  Log: TYPE USING [Warning],
  P5: TYPE USING [GenTempLex, PopEffect, PushEffect],
  P5L: TYPE USING [LoadComponent, StoreComponent],
  P5U: TYPE USING [CreateLabel, DeleteCell, FreeChunk, GetChunk, Out0, Out1],
  Stack: TYPE USING [],
  Symbols: TYPE USING [Base, BitAddress, ContextLevel, ctxType, lZ, seType];

StackImpl: PROGRAM
    IMPORTS CPtr: Code, Log, P5, P5L, P5U
    EXPORTS Stack =
  BEGIN OPEN CodeDefs;

  cb: CodeDefs.Base;
  seb, ctxb: Symbols.Base;
  uBound: StackPos;

  StackImplNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];
    ctxb ← base[Symbols.ctxType];
    cb ← base[codeType];
    END;

  stking: PUBLIC BOOL;
  stkHead: StackIndex ← StackNull;
  stkPtr: StackIndex;

  StackModelingError: PUBLIC SIGNAL = CODE;

  StkError: PRIVATE PROC =
    BEGIN SIGNAL StackModelingError END;

  Above: PUBLIC PROC [s: StackIndex, count: CARDINAL←1, nullOk: BOOL←FALSE]
      RETURNS [StackIndex] =
    BEGIN
    THROUGH [0..count) DO
      IF s = StackNull THEN StkError[];
      s ← cb[s].uplink;
      ENDLOOP;
    IF s = StackNull AND ~nullOk THEN StkError[];
    RETURN [s]
    END;

  Also: PUBLIC PROC [place: StackBackup, n: CARDINAL←1] =
    BEGIN
    s: StackIndex ← Top[n];
    fp: POINTER TO frame StackBackup;
    forget: BOOL ← FALSE;
    IF n = 1 THEN {
      WITH ss: cb[s] SELECT FROM
        data => ss.backup ← place;
        ENDCASE => StkError[];
      RETURN};
    WITH pp: place SELECT FROM
      frame => fp ← @pp;
      none => forget ← TRUE;
      ENDCASE => StkError[];
    THROUGH [0..n) DO
      WITH ss: cb[s] SELECT FROM
	data => ss.backup ← place;
	ENDCASE => StkError[];
      IF ~forget THEN fp.tOffset ← fp.tOffset+1;
      s ← cb[s].uplink;
      ENDLOOP;
    END;

  Check: PUBLIC PROC [b: BYTE] =
    BEGIN
    pusheffect: CARDINAL = P5.PushEffect[b];
    popeffect: CARDINAL = P5.PopEffect[b];
    extra: CARDINAL ← 0;
    s: StackIndex ← stkPtr;
    THROUGH [0..popeffect) DO s ← cb[s].downlink ENDLOOP;
    WHILE s # stkHead DO
      WITH ss: cb[s] SELECT FROM
        data => IF ss.loaded THEN extra ← extra + 1;
	ENDCASE;
      s ← cb[s].downlink;
      ENDLOOP;
    IF extra + pusheffect > uBound THEN DumpAndComplain[];
    SELECT b FROM
      FOpCodes.qNILCK => Load[Top[1],1];
      FOpCodes.qNILCKL => Load[Top[2],2];
      FOpCodes.qBNDCK => {Load[Top[2],2]; Decr[1]};
      ENDCASE => 
	BEGIN
	IF popeffect # 0 THEN LoadToDepth[popeffect];
	Incr[pusheffect];
	END;
    END;

  Clear: PUBLIC PROC =
    BEGIN
    saveStking: BOOL = stking;
    stking ← FALSE; -- Off[];
    WHILE stkPtr # stkHead DO
      WITH cc: cb[stkPtr] SELECT FROM
	data => IF cc.loaded THEN P5U.Out0[FOpCodes.qDIS];
	ENDCASE => StkError[]; -- shouldn't go over a mark
      DelStackItem[stkPtr];
      ENDLOOP;
    stking ← saveStking;
    END;
    
  ComponentForBackup: PUBLIC PROC [sb: StackBackup, words: CARDINAL←1] RETURNS [VarComponent] =
    BEGIN
    WITH bb: sb SELECT FROM
      frame => RETURN [[wSize: words, space: 
        frame[wd: bb.tOffset, level: bb.tLevel, immutable: TRUE]]];
      link => 
        IF words # 1 THEN StkError[] 
	ELSE RETURN [[wSize: 1, space: link[bb.link]]];
      const => 
        IF words # 1 THEN StkError[] 
	ELSE RETURN [[wSize: 1, space: const[d1: bb.value]]];
      ENDCASE => StkError[];
    ERROR; -- can't get here, but it makes the compiler happy
    END;

  DataIndex: PUBLIC PROC [s: StackIndex] RETURNS [DataStackIndex] =
    BEGIN
    IF s = StackNull THEN RETURN[LOOPHOLE[s]];
    WITH cb[s] SELECT FROM
      data => RETURN [LOOPHOLE[s]];
      ENDCASE => StkError[];
    ERROR; -- to remove compiler warning
    END;

  Decr: PUBLIC PROC [count: CARDINAL←1] =
    BEGIN
    THROUGH [0..count) DO
      IF cb[stkPtr].tag = mark THEN StkError[];
      DelStackItem[stkPtr]; -- won't delete stkHead (which is mark anyway)
      ENDLOOP;
    END;

  DeleteToMark: PUBLIC PROC =
    BEGIN
    ResetToMark[]; 
    DelStackItem[stkPtr];
    END;

  DelStackItem: PRIVATE PROC [s: StackIndex] =
    BEGIN
    up: StackIndex = cb[s].uplink;
    down: StackIndex = cb[s].downlink;
    WITH ss: cb[s] SELECT FROM
      mark =>
	BEGIN
	IF s = stkHead THEN StkError[]; -- fell off the end
	IF CPtr.codeptr = ss.label THEN CPtr.codeptr ← cb[ss.label].blink;
	P5U.DeleteCell[ss.label];
	END;
      ENDCASE;
    P5U.FreeChunk[s, StackItem.SIZE];
    IF up # StackNull THEN cb[up].downlink ← down
    ELSE stkPtr ← down;
    cb[down].uplink ← up;
    END;

  Depth: PUBLIC PROC RETURNS [d: StackPos] =
    BEGIN
    d ← 0;
    FOR s: StackIndex ← stkPtr, cb[s].downlink UNTIL s = stkHead DO
      WITH ss: cb[s] SELECT FROM
	data => IF ss.loaded THEN d ← d+1;
	ENDCASE;
      ENDLOOP;
    END;

  Dump: PUBLIC PROC =
    BEGIN
    extra: CARDINAL ← 0;
    s: StackIndex ← stkPtr;
    wa: CARDINAL;
    savec: CodeDefs.CCIndex = CPtr.codeptr;
    target: CodeDefs.CCIndex = cb[savec].flink;
    next: CodeDefs.CCIndex;
    saveStking: BOOL = stking;
    stking ← FALSE; -- Off[];
    WHILE s # stkHead DO
      WITH ss: cb[s] SELECT FROM
	data => IF ss.backup.where = none THEN extra ← extra + 1;
	ENDCASE;
      s ← cb[s].downlink;
      ENDLOOP;
    IF extra # 0 THEN
      BEGIN
      tlex: Lexeme.se = P5.GenTempLex[extra];
      a: Symbols.BitAddress = seb[tlex.lexsei].idValue;
      wa ← a.wd + extra-1;
      END;
    s ← stkPtr;
    WHILE s # stkHead DO
      WITH ss: cb[s] SELECT FROM
	data => IF ss.loaded THEN wa ← Store[s, wa];
	mark => CPtr.codeptr ← ss.label;
	ENDCASE;
      s ← cb[s].downlink;
      ENDLOOP;
    CPtr.codeptr ← savec;
    UNTIL (next ← cb[CPtr.codeptr].flink) = target DO
      CPtr.codeptr ← next;
      ENDLOOP;
    stking ← saveStking;
    END;
    
  DumpAndComplain: PRIVATE PROC = {
    Dump[];
    IF CPtr.warnStackOverflow THEN Log.Warning[other--awfulCode--]};

  Dup: PUBLIC PROC [load: BOOL←FALSE] =
    BEGIN
    oldTop: DataStackIndex = DataIndex[stkPtr];
    ds: DataStackIndex;
    saveStking: BOOL = stking;
    stking ← FALSE; -- Off[];
    IF Depth[]+1 > uBound THEN DumpAndComplain[];
    Incr[1]; ds ← LOOPHOLE[stkPtr]; -- Incr adds data ones
    cb[ds].backup ← cb[oldTop].backup;
    IF cb[oldTop].loaded THEN P5U.Out0[FOpCodes.qDUP]
    ELSE {
      cb[ds].loaded ← FALSE;
      IF cb[oldTop].backup.where = none THEN StkError[]
      ELSE IF load THEN LoadItem[stkPtr]};
    stking ← saveStking;
    END;

  DDup: PUBLIC PROC [load: BOOL←FALSE] =
    BEGIN
    old2: DataStackIndex = DataIndex[stkPtr];
    old1: DataStackIndex = DataIndex[cb[old2].downlink];
    ds1, ds2: DataStackIndex;
    saveStking: BOOL = stking;
    stking ← FALSE; -- Off[];
    IF Depth[]+2 > uBound THEN DumpAndComplain[];
    Incr[1]; ds1 ← LOOPHOLE[stkPtr]; -- Incr adds data ones
    Incr[1]; ds2 ← LOOPHOLE[stkPtr];
    cb[ds1].backup ← cb[old1].backup;
    cb[ds2].backup ← cb[old2].backup;
    IF cb[old1].loaded AND cb[old2].loaded THEN P5U.Out0[FOpCodes.qDDUP]
    ELSE {
      cb[ds1].loaded ← FALSE;
      cb[ds2].loaded ← FALSE;
      IF load THEN {LoadItem[ds1]; LoadItem[ds2]}};
    stking ← saveStking;
    END;

  Exchange: PUBLIC PROC =
    BEGIN
    st1: DataStackIndex = DataIndex[stkPtr];
    st2: DataStackIndex = DataIndex[cb[st1].downlink];
    IF cb[st1].loaded THEN Load[st2, 1]
    ELSE
      BEGIN
      t: StackIndex = cb[st2].downlink;
      cb[t].uplink ← st1; cb[st1].downlink ← t;
      cb[st1].uplink ← st2; cb[st2].downlink ← st1;
      cb[st2].uplink ← StackNull;
      stkPtr ← st2;
      END;
    END;

  DExchange: PUBLIC PROC =
    BEGIN
    st1: DataStackIndex = DataIndex[stkPtr];
    st2: DataStackIndex = DataIndex[cb[st1].downlink];
    st3: DataStackIndex = DataIndex[cb[st2].downlink];
    st4: DataStackIndex = DataIndex[cb[st3].downlink];
    IF cb[st1].loaded OR cb[st2].loaded THEN Load[st3, 2]
    ELSE
      BEGIN
      t: StackIndex = cb[st4].downlink;
      cb[t].uplink ← st2; cb[st2].downlink ← t;
      cb[st1].uplink ← st4; cb[st4].downlink ← st1;
      cb[st3].uplink ← StackNull;
      stkPtr ← st3;
      END;
    END;

  Forget: PUBLIC PROC [s: StackIndex, count: CARDINAL←1] =
    BEGIN
    next: StackIndex;
    THROUGH [0..count) DO
      IF s = StackNull THEN StkError[];
      next ← cb[s].uplink;
      DelStackItem[s];
      s ← next;
      ENDLOOP;
    END;

  Incr: PUBLIC PROC [count: CARDINAL←1] =
    BEGIN
    s: StackIndex;
    THROUGH [0..count) DO
      cb[stkPtr].uplink ← s ← P5U.GetChunk[StackItem.SIZE];
      cb[s] ← [downlink: stkPtr, info: data[]];
      stkPtr ← s;
      ENDLOOP;
    END;

  Init: PUBLIC PROC = 
    BEGIN
    uBound ← EvalStackSize - 1; -- might want to store into temp outside first page
    stkHead ← P5U.GetChunk[StackItem.SIZE];
    cb[stkHead] ← [downlink: stkHead, info: mark[LabelCCNull]];
    stkPtr ← stkHead;
    stking ← FALSE;
    END;

  KeepOnly: PUBLIC PROC [s: StackIndex, count: CARDINAL] =
    BEGIN -- used when taking initial field of larger stacked record
    n: CARDINAL ← 0;
    THROUGH [0..count) DO
      IF s = StackNull THEN StkError[];
      s ← cb[s].uplink;
      ENDLOOP;
    WHILE s # StackNull DO
      n ← n+1;
      s ← cb[s].uplink;
      ENDLOOP;
    IF n # 0 THEN Pop[n];
    END;

  Load: PUBLIC PROC [s: StackIndex, count: CARDINAL←1] =
    BEGIN
    loc: StackLocRec ← Loc[s, count];
    first: DataStackIndex = DataIndex[s];
    last: DataStackIndex ← DataIndex[Above[first, count-1]];
    ts: StackIndex;
    saveStking: BOOL = stking;
    stking ← FALSE; -- Off[];
    BEGIN -- to set up linkToTop label
    WITH ll: loc SELECT FROM
      onStack =>
	BEGIN
	ad: CARDINAL;
	IF ll.depth = 0 THEN GO TO done;
	ad ← 0;
	ts ← stkPtr;
	THROUGH [0..ll.depth) DO
	  WITH ss: cb[ts] SELECT FROM
	    data => IF ss.loaded THEN ad ← ad+1;
	    ENDCASE => NULL;
	  ts ← cb[ts].downlink;
	  ENDLOOP;
	IF ad = 0 THEN GO TO linkToTop;
	IF ad = 1 AND count = 1 THEN {P5U.Out0[FOpCodes.qEXCH]; GO TO linkToTop};
	IF ad = 2 AND count = 2 THEN {P5U.Out0[FOpCodes.qDEXCH]; GO TO linkToTop};
	StoreItems[cb[last].uplink, ll.depth];
	GO TO linkToTop;
	END;
      contig, stored =>
	BEGIN
	IF Depth[] + count > uBound THEN DumpAndComplain[];
	ts ← first;
	THROUGH [0..count) DO
	  LoadItem[ts];
	  ts ← cb[ts].uplink;
	  ENDLOOP;
	GO TO linkToTop;
	END;
      ENDCASE =>
	BEGIN -- usually some things in temps with some loaded above
	toLoad: CARDINAL ← count;
	extra: CARDINAL;
	xs: StackIndex ← first;
	ds: DataStackIndex;
	THROUGH [0..count) DO
	  ds ← DataIndex[xs];
	  IF cb[ds].loaded THEN toLoad ← toLoad-1;
	  xs ← cb[ds].uplink;
	  ENDLOOP;
	IF Depth[] + toLoad > uBound THEN DumpAndComplain[];
	IF toLoad = count-1 AND count <= 3 AND cb[last].loaded
	 AND ds # StackNull THEN
	  BEGIN
	  ts ← first;
	  THROUGH [0..toLoad) DO
	    LoadItem[ts];
	    P5U.Out0[FOpCodes.qEXCH];
	    ts ← cb[ts].uplink;
	    ENDLOOP;
	  GO TO linkToTop;
	  END;
	IF toLoad = count-2 AND count <= 6 AND count MOD 2 = 0 
	 AND cb[last].loaded 
	 AND cb[LOOPHOLE[cb[last].downlink, DataStackIndex]].loaded
	 AND ds # StackNull THEN
	  BEGIN
	  ts ← first;
	  THROUGH [0..toLoad/2) DO
	    LoadItem[ts];
	    ts ← cb[ts].uplink;
	    LoadItem[ts];
	    P5U.Out0[FOpCodes.qDEXCH];
	    ts ← cb[ts].uplink;
	    ENDLOOP;
	  GO TO linkToTop;
	  END;
	xs ← first; extra ← count;
	THROUGH [0..count) DO
	  ds ← DataIndex[xs];
	  IF ~cb[ds].loaded THEN EXIT;
	  extra ← extra-1;
	  xs ← cb[ds].uplink;
	  ENDLOOP;
	StoreItems[ds, VDepthOf[ds]+1]; -- in the unlikely case stuff is above
	ts ← ds;
	THROUGH [0..extra) DO
	  LoadItem[ts];
	  ts ← cb[ts].uplink;
	  ENDLOOP;
	GO TO linkToTop;
	END;
    EXITS
      linkToTop =>
	BEGIN
	rest: StackIndex = Above[first, count, TRUE];
	IF rest # StackNull THEN
	  BEGIN
	  down: StackIndex = cb[first].downlink;
	  cb[stkPtr].uplink ← first;
	  cb[first].downlink ← stkPtr;
	  cb[rest].downlink ← down;
	  cb[down].uplink ← rest;
	  cb[last].uplink ← StackNull;
	  stkPtr ← last;
	  END;
	END;
      done => NULL;
    END;
    stking ← saveStking;
    END;

  LoadItem: PRIVATE PROC [s: StackIndex] =
    BEGIN -- stking is off when called
    ds: DataStackIndex = DataIndex[s];
    sb: StackBackup = cb[ds].backup;
    IF cb[ds].loaded THEN RETURN;
    WITH bb: sb SELECT FROM
      frame =>
	BEGIN
	var: VarComponent ← [
	  wSize: 1, 
	  space: frame[level: bb.tLevel, wd: bb.tOffset, immutable: TRUE]];
	P5L.LoadComponent[var];
	END;
      link => P5U.Out1[FOpCodes.qLLK, bb.link];
      const => P5U.Out1[FOpCodes.qLI, bb.value];
      faddr =>
	BEGIN
	var: VarComponent ← [
	  wSize: 1, 
	  space: faddr[level: bb.tLevel, wd: bb.tOffset]];
	P5L.LoadComponent[var];
	END;
      ENDCASE => StkError[];
    cb[ds].loaded ← TRUE;
    END;

  LoadToDepth: PRIVATE PROC [n: StackPos] =
    BEGIN
    IF n = 0 THEN RETURN;
    Load[Top[n], n];
    Decr[n];
    END;

  Loc: PUBLIC PROC [s: StackIndex, count: CARDINAL←1] RETURNS [StackLocRec] =
    BEGIN
    ds: DataStackIndex ← DataIndex[s];
    sb: StackBackup;
    contig: BOOL ← TRUE;
    i: CARDINAL ← 0;
    off: TempAddr;
    lvl: Symbols.ContextLevel;
    IF cb[ds].loaded THEN
      BEGIN
      d: StackPos ← 0;
      THROUGH (0..count) DO
	ds ← DataIndex[cb[ds].uplink];
	IF ~cb[ds].loaded THEN RETURN[[mixed[]]];
	ENDLOOP;
      s ← ds;
      WHILE s # stkPtr DO -- note: this counts marks, used by Load
	d ← d+1;
	s ← cb[s].uplink;
	ENDLOOP;
      RETURN[[onStack[d]]];
      END;
    DO 
      sb ← cb[ds].backup;
      WITH bb: sb SELECT FROM
	frame => 
	  IF i = 0 THEN {lvl ← bb.tLevel; off ← bb.tOffset}
	  ELSE {IF bb.tLevel # lvl OR bb.tOffset # off+i THEN contig ← FALSE};
	link, const, faddr => 
	  IF count = 1 THEN RETURN [[contig[sb]]]
	  ELSE contig ← FALSE;
	ENDCASE;
      i ← i+1;
      IF i = count THEN EXIT;
      ds ← DataIndex[cb[ds].uplink];
      IF cb[ds].loaded THEN RETURN[[mixed[]]];
      ENDLOOP;
    IF contig THEN RETURN [[contig[[frame[tOffset: off, tLevel: lvl]]]]]
    ELSE RETURN [[stored[]]];
    END;

  Mark: PUBLIC PROC =
    BEGIN
    down: StackIndex = stkPtr;
    stkPtr ← P5U.GetChunk[StackItem.SIZE];
    cb[stkPtr] ← [downlink: down, info: mark[P5U.CreateLabel[]]];
    cb[down].uplink ← stkPtr;
    END;

  MoveToTemp: PUBLIC PROC [
	firstIndex: StackIndex, count: CARDINAL, preChaff: CARDINAL]
      RETURNS [VarComponent] =
    BEGIN -- store "count" words from stack into contiguous temps
    -- and pop off preChaff words ahead of firstIndex
    s: StackIndex;
    tStart, tempPrev: TempAddr;
    ctlvl: Symbols.ContextLevel = ctxb[CPtr.tempcontext].level;
    lvlPrev: Symbols.ContextLevel;
    first: BOOL ← TRUE;
    remaining: CARDINAL ← count;
    saveStking: BOOL = stking;
    above: StackIndex = Above[s: firstIndex, count: count, nullOk: TRUE];

    PutBackJunk: PROC =
      BEGIN
      cb[stkPtr].uplink ← above;
      cb[above].downlink ← stkPtr;
      UNTIL cb[stkPtr].uplink = StackNull DO stkPtr ← cb[stkPtr].uplink ENDLOOP;
      END;

    stking ← FALSE; -- Stack.Off[];
    IF above # StackNull THEN
      BEGIN -- unlikely
      StoreItems[above, VDepthOf[above]+1];
      stkPtr ← cb[above].downlink;
      cb[stkPtr].uplink ← StackNull; -- temporarily unlink
      END;
    IF count = 1 THEN
      BEGIN -- trade space for clarity
      ds: DataStackIndex = DataIndex[firstIndex];
      sb: StackBackup ← cb[ds].backup;
      var: VarComponent;
      IF cb[ds].loaded THEN {StoreItems[ds, 1]; sb ← cb[ds].backup};
      WITH bb: sb SELECT FROM
	frame => var ← [wSize: 1, space:
	  frame[wd: bb.tOffset, immutable: TRUE, level: bb.tLevel]];
	link =>
	  var ← [wSize: 1, space: link[wd: bb.link]];
	faddr => var ← [wSize: 1, space:
	  faddr[wd: bb.tOffset, level: bb.tLevel]];
	const =>
	  var ← [wSize: 1, space: const[d1: bb.value]];
	ENDCASE;
      DelStackItem[firstIndex];
      IF preChaff # 0 THEN Pop[preChaff];
      stking ← saveStking;
      IF above # StackNull THEN PutBackJunk[];
      RETURN[var]
      END;
    
    BEGIN -- to set up moveRest label
    BEGIN -- to set up moveAll label
    FOR s ← firstIndex, cb[s].uplink WHILE s # StackNull DO
      ds: DataStackIndex = DataIndex[s];
      sb: StackBackup = cb[ds].backup;
      WITH bb: sb SELECT FROM
	frame => 
	  BEGIN
	  IF first THEN
	    BEGIN
	    tStart ← bb.tOffset;
	    lvlPrev ← bb.tLevel;
	    first ← FALSE;
	    END
	  ELSE
	    BEGIN
	    IF bb.tLevel # lvlPrev OR bb.tOffset # tempPrev+1 THEN
	      GO TO moveAll; -- not worth a check for hole after prev
	    END;
	  tempPrev ← bb.tOffset;
	  remaining ← remaining-1;
	  END;
	link, const, faddr, none => 
	  IF first OR lvlPrev # ctlvl OR tempPrev # CPtr.tempstart-1 THEN GO TO moveAll
	  ELSE GO TO moveRest;
	ENDCASE => StkError[];
      ENDLOOP;
    EXITS
      moveAll =>
	BEGIN
	remaining ← count;
	tStart ← CPtr.tempstart;
	lvlPrev ← ctlvl;
	GO TO moveRest;
	END;
    END;
    EXITS
      moveRest =>
	BEGIN
	n: CARDINAL ← remaining;
	k: CARDINAL;
	tlex: Lexeme.se = P5.GenTempLex[remaining];
	a: Symbols.BitAddress = seb[tlex.lexsei].idValue;
	wa: CARDINAL ← a.wd + remaining - 1;
	WHILE n > 0 DO
	  k ← MIN[n, 2];
	  Load[Top[k], k];
	  THROUGH [0..k) DO
	    wa ← Store[stkPtr, wa, TRUE];
	    DelStackItem[stkPtr]; -- this updates stkPtr
	    ENDLOOP;
	  n ← n - k;
	  ENDLOOP;
	END;
    END;
    
    IF remaining < count THEN Pop[count-remaining];
    IF preChaff # 0 THEN Pop[preChaff];
      
    stking ← saveStking;
    IF above # StackNull THEN PutBackJunk[];
    RETURN [[wSize: count, space: frame[wd: tStart, immutable: TRUE, level: lvlPrev]]];
    END;

  New: PUBLIC PROC RETURNS [old: StackIndex] =
    BEGIN
    old ← cb[stkHead].uplink;
    cb[stkHead].uplink ← StackNull;
    stkPtr ← stkHead;
    END;

  Pop: PUBLIC PROC [count: CARDINAL←1] =
    BEGIN
    saveStking: BOOL = stking;
    stking ← FALSE; -- Off[];
    THROUGH [0..count) DO
      ds: DataStackIndex = DataIndex[stkPtr];
      IF cb[ds].loaded THEN P5U.Out0[FOpCodes.qDIS];
      DelStackItem[stkPtr];
      ENDLOOP;
    stking ← saveStking;
    END;

  Prefix: PUBLIC PROC [sti: StackIndex] =
    BEGIN
    ts, bs: StackIndex;
    IF sti = StackNull THEN RETURN;
    FOR ts ← sti, cb[ts].uplink UNTIL cb[ts].uplink = StackNull DO
      ENDLOOP;
    bs ← cb[stkHead].uplink;
    cb[ts].uplink ← bs;
    IF bs = StackNull THEN stkPtr ← ts ELSE cb[bs].downlink ← ts;
    cb[stkHead].uplink ← sti; cb[sti].downlink ← stkHead;
    END;

  Require: PUBLIC PROC [n: StackPos] =
    BEGIN
    extra: CARDINAL ← 0;
    s: StackIndex ← stkPtr;
    THROUGH [0..n) DO  s ← cb[s].downlink ENDLOOP;
    WHILE s # stkHead DO
      WITH ss: cb[s] SELECT FROM
        data => IF ss.loaded THEN extra ← extra + 1;
	ENDCASE;
      s ← cb[s].downlink;
      ENDLOOP;
    IF extra # 0 THEN Dump[];
    END;

  Reset: PUBLIC PROC =
    BEGIN
    WHILE stkPtr # stkHead DO DelStackItem[stkPtr] ENDLOOP;
    END;

  ResetToMark: PUBLIC PROC =
    BEGIN
    n: CARDINAL ← 0;
    FOR s: StackIndex ← stkPtr, cb[s].downlink DO
      WITH cb[s] SELECT FROM
	mark => IF s = stkHead THEN StkError[] ELSE EXIT;
	ENDCASE => n ← n+1;
      ENDLOOP;
    IF n # 0 THEN LoadToDepth[n];
    END;

  Restore: PUBLIC PROC [s: StackIndex] =
    BEGIN
    Reset[]; -- free all but head
    cb[stkHead].uplink ← s;
    stkPtr ← stkHead;
    UNTIL s = StackNull DO
      stkPtr ← s;
      s ← cb[stkPtr].uplink;
      ENDLOOP;
    END;

  RoomFor: PUBLIC PROC [n: CARDINAL] RETURNS [BOOL] =
    BEGIN
    RETURN [Depth[]+n <= uBound]
    END;

  Store: PRIVATE PROC [
      s: StackIndex,
      addr: TempAddr,
      storeNew: BOOL ← FALSE] RETURNS [nextAddr: TempAddr] =
    BEGIN -- stack is off when called
    --    Store the top element at addr 
    --      if storeNew = FALSE and in memory, then generate POP instead
    lvl: Symbols.ContextLevel;
    ds: DataStackIndex = DataIndex[s];
    sb: StackBackup = cb[ds].backup;
    IF ~cb[ds].loaded THEN RETURN[addr];
    IF storeNew OR sb.where = none THEN
      BEGIN
      lvl ← ctxb[CPtr.tempcontext].level;
      StoreWord[addr, lvl];
      cb[ds].backup ← [frame[tLevel: lvl, tOffset: addr]];
      addr ← addr-1;
      END
    ELSE P5U.Out0[FOpCodes.qDIS];
    cb[ds].loaded ← FALSE;
    RETURN [addr];
    END;

  StoreItems: PRIVATE PROC [start: StackIndex, count: CARDINAL] =
    BEGIN -- not necessarily contiguously
    needed: CARDINAL ← 0;
    s, last: DataStackIndex;
    ts: StackIndex;
    wa: CARDINAL;
    ts ← start;
    THROUGH [0..count) DO
      IF ts = StackNull THEN StkError[];
      s ← DataIndex[ts];
      IF cb[s].loaded AND cb[s].backup.where = none THEN needed ← needed + 1;
      last ← s;
      ts ← cb[s].uplink;
      ENDLOOP;
    IF needed # 0 THEN
      BEGIN
      tlex: Lexeme.se ← P5.GenTempLex[needed];
      a: Symbols.BitAddress ← seb[tlex.lexsei].idValue;
      wa ← a.wd + needed - 1;
      END;
    ts ← last;
    THROUGH [0..count) DO
      s ← DataIndex[ts];
      IF cb[s].loaded THEN wa ← Store[s, wa, FALSE];
      ts ← cb[s].downlink;
      ENDLOOP;
    END;

  StoreWord: PRIVATE PROC [offset: TempAddr, lvl: Symbols.ContextLevel] =
    BEGIN
    var: VarComponent = [wSize: 1, space: frame[wd: offset, level: lvl]];
    P5L.StoreComponent[var];
    END;

  TempStore: PUBLIC PROC [count: CARDINAL ← 1] RETURNS [VarComponent] =
    BEGIN -- store top of stack into contiguous temps
    RETURN [MoveToTemp[Top[count], count, 0]];
    END;

  Top: PUBLIC PROC [count: CARDINAL←1] RETURNS [s: StackIndex] =
    BEGIN
    s ← stkPtr;
    THROUGH (0..count) DO s ← cb[s].downlink ENDLOOP;
    IF s = stkHead THEN StkError[];
    RETURN
    END;

  UnMark: PUBLIC PROC =
    BEGIN
    n: CARDINAL ← 0;
    FOR s: StackIndex ← stkPtr, cb[s].downlink DO
      WITH cb[s] SELECT FROM
	mark =>
	  BEGIN
	  IF s = stkHead THEN StkError[]; -- fell off the end
	  DelStackItem[s];
	  LoadToDepth[n]; -- make sure loaded, also forget from where
	  Incr[n]; -- remember how many things loaded
	  RETURN
	  END;
	ENDCASE => n ← n+1;
      ENDLOOP;
    END;

  VDepth: PUBLIC PROC RETURNS [StackPos] =
    BEGIN
    RETURN [VDepthOf[stkHead]];
    END;

  VDepthOf: PUBLIC PROC [s: StackIndex] RETURNS [d: StackPos] =
    BEGIN
    d ← 0;
    IF s = StackNull THEN StkError[];
    DO
      s ← cb[s].uplink;
      IF s = StackNull THEN RETURN;
      IF cb[s].tag # mark THEN d ← d+1;
      ENDLOOP;
    END;

  END.