-- StackImpl.mesa  Edited by Sweet,  January 10, 1980  11:20 AM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [BYTE],
  Code: FROM "code" USING [
    codeptr, dStar, stking, tempcontext, tempstart],
  CodeDefs: FROM "codedefs" USING [
    CCIndex, CCNull, EvalStackSize, LabelCCNull, Lexeme, StackIndex, 
    StackItem, StackLocRec, StackNull, StackPos, TempAddr, VarComponent],
  FOpCodes: FROM "fopcodes" USING [qDUP, qEXCH, qLLK, qPOP],
  P5: FROM "p5" USING [GenTempLex, MinimalStack, PopEffect, PushEffect],
  P5L: FROM "p5l" USING [LoadComponent, StoreComponent],
  P5U: FROM "p5u" USING [
    CreateLabel, DeleteCell, FreeChunk, GetChunk, Out0, Out1],
  Stack: FROM "stack",
  Symbols: FROM "symbols" USING [
    BitAddress, ContextLevel, ctxType, lZ, seType],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType];

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

  CPtr: POINTER TO FRAME [Code] = LCPtr;
  cb, seb, ctxb: Table.Base;
  uBound: StackPos;

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

  stkHead: StackIndex ← StackNull;
  stkPtr: StackIndex;

  StackModelingError: PUBLIC SIGNAL = CODE;

  StkError: PRIVATE PROCEDURE =
    BEGIN SIGNAL StackModelingError END;

  Above: PUBLIC PROCEDURE
      [s: StackIndex, count: CARDINAL ← 1, nullOk: BOOLEAN ← 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 PROCEDURE [
      n: CARDINAL ← 1, inLink: BOOLEAN ← FALSE, tOffset: TempAddr,
      tLevel: Symbols.ContextLevel ← Symbols.lZ] =
    BEGIN
    s: StackIndex ← Top[n];
    THROUGH [0..n) DO
      IF cb[s].tag # onStack THEN StkError[];
      cb[s].data ←
	onStack[alsoLink: inLink, tOffset: tOffset, tLevel: tLevel];
      tOffset ← tOffset+1;
      s ← cb[s].uplink;
      ENDLOOP;
    END;

  Check: PUBLIC PROCEDURE [b: AltoDefs.BYTE] =
    BEGIN
    pusheffect: CARDINAL = P5.PushEffect[b];
    popeffect: CARDINAL = P5.PopEffect[b];
    extra: CARDINAL ← 0;
    s: StackIndex ← stkPtr;
    
    IF ~CPtr.stking THEN RETURN;
    THROUGH [0..popeffect) DO
      s ← cb[s].downlink;
      ENDLOOP;
    WHILE s # stkHead DO
      IF cb[s].tag = onStack THEN extra ← extra + 1;
      s ← cb[s].downlink;
      ENDLOOP;
    IF extra + pusheffect > uBound THEN Dump[];
    IF popeffect # 0 THEN LoadToDepth[popeffect];
    IF ~CPtr.dStar AND P5.MinimalStack[b] AND Depth[] # 0 THEN
      StkError[];
    Incr[pusheffect];
    END;

  Clear: PUBLIC PROCEDURE =
    BEGIN
    WHILE stkPtr # stkHead DO
      WITH cb[stkPtr] SELECT FROM
	inTemp, inLink => NULL;
	onStack => P5U.Out0[FOpCodes.qPOP];
	ENDCASE => StkError[]; -- shouldn't go over a mark
      DelStackItem[stkPtr];
      ENDLOOP;
    END;

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

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

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

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

  Dump: PUBLIC PROCEDURE =
    BEGIN
    extra: CARDINAL ← 0;
    s: StackIndex;
    wa: CARDINAL;
    savec: CodeDefs.CCIndex = CPtr.codeptr;
    next: CodeDefs.CCIndex;
    saveStking: BOOLEAN ← CPtr.stking;

    CPtr.stking ← FALSE; -- Off[];
    s ← stkPtr;
    WHILE s # stkHead DO
      WITH cb[s] SELECT FROM
	onStack => IF ~alsoLink AND tLevel = Symbols.lZ THEN
	  extra ← extra + 1;
	ENDCASE;
      s ← cb[s].downlink;
      ENDLOOP;
    IF extra # 0 THEN
      BEGIN
      tlex: se Lexeme ← P5.GenTempLex[extra];
      a: Symbols.BitAddress ← seb[tlex.lexsei].idValue;
      wa ← a.wd + extra-1;
      END;
    s ← stkPtr;
    WHILE s # stkHead DO
      WITH cb[s] SELECT FROM
	onStack => wa ← Store[s, wa];
	mark => CPtr.codeptr ← label;
	ENDCASE;
      s ← cb[s].downlink;
      ENDLOOP;
    CPtr.codeptr ← savec;
    UNTIL (next ← cb[CPtr.codeptr].flink) = CCNull DO
      CPtr.codeptr ← next;
      ENDLOOP;
    CPtr.stking ← saveStking;
    END;

  Dup: PUBLIC PROCEDURE [load: BOOLEAN ← FALSE] =
    BEGIN
    oldTop: StackIndex = stkPtr;
    saveStking: BOOLEAN ← CPtr.stking;
    CPtr.stking ← FALSE; -- Off[];
    IF Depth[]+1 > uBound THEN Dump[];
    Incr[1];
    WITH ss: cb[oldTop] SELECT FROM
      onStack =>
	BEGIN
	P5U.Out0[FOpCodes.qDUP];
	cb[stkPtr].data ← onStack[alsoLink: ss.alsoLink,
	  tOffset: ss.tOffset, tLevel: ss.tLevel];
	END;
      inTemp =>
	BEGIN
	cb[stkPtr].data ← inTemp[tOffset: ss.tOffset, tLevel: ss.tLevel];
	IF load THEN LoadItem[stkPtr];
	END;
      inLink =>
	BEGIN
	cb[stkPtr].data ← inLink[link: ss.link];
	IF load THEN LoadItem[stkPtr];
	END;
      ENDCASE => StkError[];
    CPtr.stking ← saveStking;
    END;

  Exchange: PUBLIC PROCEDURE =
    BEGIN
    st1: StackIndex = stkPtr;
    st2: StackIndex = cb[st1].downlink;
    t: StackIndex = cb[st2].downlink;
    
    IF st2 = stkHead OR cb[st2].tag = mark THEN StkError[];
    WITH cb[st1] SELECT FROM
      onStack => Load[st2, 1];
      inTemp, inLink =>
	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;
	END;
      ENDCASE => StkError[];
    END;

  Forget: PUBLIC PROCEDURE [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 PROCEDURE [count: CARDINAL ← 1] =
    BEGIN
    s: StackIndex;
    
    THROUGH [0..count) DO
      cb[stkPtr].uplink ← s ← P5U.GetChunk[SIZE[StackItem]];
      cb[s] ← [downlink: stkPtr, data: NULL];
      cb[s].data ← onStack[];
      stkPtr ← s;
      ENDLOOP;
    END;

  Init: PUBLIC PROCEDURE = 
    BEGIN
    uBound ← EvalStackSize - 2;
    stkHead ← P5U.GetChunk[SIZE[StackItem]];
    cb[stkHead] ← [downlink: stkHead, data: mark[LabelCCNull]];
    stkPtr ← stkHead;
    CPtr.stking ← FALSE;
    END;

  KeepOnly: PUBLIC PROCEDURE [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 PROCEDURE [s: StackIndex, count: CARDINAL ← 1] =
    BEGIN
    loc: StackLocRec ← Loc[s, count];
    first: StackIndex = s;
    last: StackIndex ← Above[first, count-1];
    ts: StackIndex;
    saveStking: BOOLEAN ← CPtr.stking;
    
    CPtr.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 cb[ts] SELECT FROM
	    onStack => ad ← ad+1;
	    ENDCASE => NULL;
	  ts ← cb[ts].downlink;
	  ENDLOOP;
	IF ad = 0 THEN GO TO linkToTop;
	IF ad = 1 AND count = 1 THEN
	  BEGIN P5U.Out0[FOpCodes.qEXCH]; GO TO linkToTop END;
	StoreItems[cb[last].uplink, ll.depth];
	GO TO linkToTop;
	END;
      inTemp =>
	BEGIN
	IF Depth[] + count > uBound THEN Dump[];
	ts ← first;
	THROUGH [0..count) DO
	  LoadItem[ts];
	  ts ← cb[ts].uplink;
	  ENDLOOP;
	GO TO linkToTop;
	END;
      inLink =>
	BEGIN -- count = 1
	IF Depth[] + 1 > uBound THEN Dump[];
	LoadItem[first];
	GO TO linkToTop;
	END;
      ENDCASE =>
	BEGIN -- usually some things in temps with some loaded above
	toLoad: CARDINAL ← count;
	extra: CARDINAL;
	ts ← first;
	THROUGH [0..count) DO
	  IF cb[ts].tag = onStack THEN toLoad ← toLoad-1;
	  ts ← cb[ts].uplink;
	  ENDLOOP;
	IF Depth[] + toLoad > uBound THEN Dump[];
	IF toLoad = count-1 AND count <= 4 AND cb[last].tag = onStack THEN
	  BEGIN
	  IF ts # StackNull THEN StoreItems[ts, VDepthOf[ts]+1]; -- unlikely
	  ts ← first;
	  THROUGH [0..toLoad) DO
	    LoadItem[ts];
	    P5U.Out0[FOpCodes.qEXCH];
	    ts ← cb[ts].uplink;
	    ENDLOOP;
	  GO TO linkToTop;
	  END;
	ts ← first; extra ← count;
	THROUGH [0..count) DO
	  IF cb[ts].tag # onStack THEN EXIT;
	  extra ← extra-1;
	  ts ← cb[ts].uplink;
	  ENDLOOP;
	StoreItems[ts, VDepthOf[ts]+1]; -- in the unlikely case stuff is above
	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;
    CPtr.stking ← saveStking;
    END;

  LoadItem: PRIVATE PROCEDURE [s: StackIndex] =
    BEGIN -- stking is off when called
    off: TempAddr;
    lvl: Symbols.ContextLevel;
    var: VarComponent;
    
    WITH cb[s] SELECT FROM
      inTemp =>
	BEGIN
	off ← tOffset;
	lvl ← tLevel;
	END;
      inLink =>
	BEGIN
	P5U.Out1[FOpCodes.qLLK, link];
	cb[s].data ← onStack [alsoLink: TRUE, tOffset: link];
	RETURN;
	END;
      onStack => RETURN;
      ENDCASE => StkError[];
    var ← [wSize: 1, space: frame[level: lvl, wd: off, immutable: TRUE]];
    P5L.LoadComponent[var];
    cb[s].data ← onStack[tOffset: off, tLevel: lvl];
    END;

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

  Loc: PUBLIC PROCEDURE [s: StackIndex, count: CARDINAL ← 1]
      RETURNS [StackLocRec] =
    BEGIN
    WITH cb[s] SELECT FROM
      onStack =>
	BEGIN
	d: StackPos ← 0;
	THROUGH (0..count) DO
	  s ← cb[s].uplink;
	  WITH cb[s] SELECT FROM
	    onStack => NULL;
	    mark => StkError[];
	    ENDCASE => RETURN[[mixed[]]];
	  ENDLOOP;
	WHILE s # stkPtr DO
	  d ← d+1;
	  s ← cb[s].uplink;
	  ENDLOOP;
	RETURN[[onStack[d]]];
	END;
      inTemp => 
	BEGIN
	lvl: Symbols.ContextLevel ← tLevel;
	off: TempAddr ← tOffset;
	i: CARDINAL;
	FOR i IN (0..count) DO
	  s ← cb[s].uplink;
	  WITH cb[s] SELECT FROM
	    inTemp => IF tLevel # lvl OR tOffset # off+i THEN
	      RETURN [[mixed[]]];
	    mark => StkError[];
	    ENDCASE => RETURN [[mixed[]]];
	  ENDLOOP;
	RETURN[[inTemp[tSize: count, tLevel: lvl, tOffset: off]]];
	END;
      inLink => IF count # 1 THEN RETURN [[mixed[]]]
	ELSE RETURN [[inLink[link]]];
      ENDCASE => StkError[]; -- shouldn't be a mark
    ERROR; -- Since compiler doesn't know StkError doesn't return
    END;

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

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

  Off: PUBLIC PROCEDURE =
    BEGIN
    CPtr.stking ← FALSE;
    END;

  On: PUBLIC PROCEDURE =
    BEGIN
    CPtr.stking ← TRUE;
    END;

  Pop: PUBLIC PROCEDURE [count: CARDINAL ← 1] =
    BEGIN
    saveStking: BOOLEAN ← CPtr.stking;
    CPtr.stking ← FALSE; -- Off[];
    THROUGH [0..count) DO
      WITH cb[stkPtr] SELECT FROM
	inTemp, inLink => NULL;
	onStack => P5U.Out0[FOpCodes.qPOP];
	ENDCASE => StkError[]; -- shouldn't go over a mark
      DelStackItem[stkPtr];
      ENDLOOP;
    CPtr.stking ← saveStking;
    END;

  Prefix: PUBLIC PROCEDURE [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 PROCEDURE [n: StackPos] =
    BEGIN
    extra: CARDINAL ← 0;
    s: StackIndex ← stkPtr;

    THROUGH [0..n) DO
      s ← cb[s].downlink;
      ENDLOOP;
    WHILE s # stkHead DO
      IF cb[s].tag = onStack THEN extra ← extra + 1;
      s ← cb[s].downlink;
      ENDLOOP;
    IF extra # 0 THEN Dump[];
    END;

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

  ResetToMark: PUBLIC PROCEDURE =
    BEGIN
    n: CARDINAL ← 0;
    s: StackIndex;
    FOR s ← 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
      BEGIN
      LoadToDepth[n];
      END;
    END;

  Restore: PUBLIC PROCEDURE [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;

  Store: PRIVATE PROCEDURE [
      s: StackIndex,
      addr: TempAddr,
      storeNew: BOOLEAN ← 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;
    off: TempAddr;
    link: BOOLEAN;
    BEGIN -- to set up label: store
    WITH cb[s] SELECT FROM
      onStack => IF storeNew OR ~(alsoLink OR tLevel # Symbols.lZ) THEN
	  GO TO store
        ELSE
	  BEGIN
	  P5U.Out0[FOpCodes.qPOP];
	  lvl ← tLevel; off ← tOffset; link ← alsoLink;
	  END;
      inTemp, inLink => RETURN;
      ENDCASE => StkError[];
    EXITS
      store =>
	BEGIN
	link ← FALSE;
	off ← addr;
	lvl ← ctxb[CPtr.tempcontext].level;
	StoreWord[addr, lvl];
	addr ← addr-1;
	END;
    END;
    IF link THEN cb[s].data ← inLink[off]
    ELSE cb[s].data ← inTemp[tOffset: off, tLevel: lvl];
    RETURN[addr];
    END;

  StoreItems: PRIVATE PROCEDURE [start: StackIndex, count: CARDINAL] =
    BEGIN -- not necessarily contiguously
    needed: CARDINAL ← 0;
    s, last: StackIndex;
    wa: CARDINAL;
    
    s ← start;
    THROUGH [0..count) DO
      IF s = StackNull THEN StkError[];
      WITH ss: cb[s] SELECT FROM
	inTemp, inLink => NULL;
	onStack => IF ~(ss.alsoLink OR ss.tLevel # Symbols.lZ) THEN
	  needed ← needed+1;
	ENDCASE => StkError[];
      last ← s;
      s ← cb[s].uplink;
      ENDLOOP;
    
    IF needed # 0 THEN
      BEGIN
      tlex: se Lexeme ← P5.GenTempLex[needed];
      a: Symbols.BitAddress ← seb[tlex.lexsei].idValue;
      wa ← a.wd + needed - 1;
      END;
    
    s ← last;
    THROUGH [0..count) DO
      WITH cb[s] SELECT FROM
	inTemp, inLink => NULL;
	onStack => wa ← Store[s, wa, FALSE];
	ENDCASE;
      s ← cb[s].downlink;
      ENDLOOP;
    END;

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

  TempStore: PUBLIC PROCEDURE [count: CARDINAL ← 1] RETURNS [VarComponent] =
    BEGIN -- store top of stack into contiguous temps
    firstIndex: StackIndex ← Top[count];
    s: StackIndex;
    tStart, tempPrev: TempAddr;
    ctlvl: Symbols.ContextLevel = ctxb[CPtr.tempcontext].level;
    lvlPrev: Symbols.ContextLevel;
    first: BOOLEAN ← TRUE;
    remaining: CARDINAL ← count;
    saveStking: BOOLEAN ← CPtr.stking;

    CPtr.stking ← FALSE; -- Stack.Off[];
    IF count = 1 THEN
      BEGIN -- trade space for clarity
      var: VarComponent;
      WITH cb[firstIndex] SELECT FROM
	onStack => StoreItems[firstIndex, 1];
        ENDCASE;
      WITH cb[firstIndex] SELECT FROM
	inTemp => var ← [wSize: 1, space:
	  frame[wd: tOffset, immutable: TRUE, level: tLevel]];
	inLink =>
	  var ← [wSize: 1, space: link[wd: link]];
	ENDCASE;
      DelStackItem[firstIndex];
      CPtr.stking ← saveStking;
      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
      WITH ss: cb[s] SELECT FROM
	inTemp => 
	  BEGIN
	  IF first THEN
	    BEGIN
	    tStart ← ss.tOffset;
	    lvlPrev ← ss.tLevel;
	    first ← FALSE;
	    END
	  ELSE
	    BEGIN
	    IF ss.tLevel # lvlPrev OR ss.tOffset # tempPrev+1 THEN
	      GO TO moveAll; -- not worth a check for hole after prev
	    END;
	  tempPrev ← ss.tOffset;
	  remaining ← remaining-1;
	  END;
	inLink => GO TO moveAll;
	onStack =>
	  BEGIN
	  IF ss.tLevel # Symbols.lZ THEN
	    BEGIN
	    IF first THEN
	      BEGIN
	      tStart ← tempPrev ← ss.tOffset;
	      lvlPrev ← ss.tLevel;
	      first ← FALSE;
	      END
	    ELSE
	      BEGIN
	      IF ss.tLevel # lvlPrev OR ss.tOffset # tempPrev+1 THEN
		GO TO moveAll; -- not worth a check for hole after prev
	      END;
	    tempPrev ← ss.tOffset;
	    remaining ← remaining-1;
	    LOOP;
	    END;
	  IF first OR lvlPrev # ctlvl OR tempPrev # CPtr.tempstart-1 THEN
	    GO TO moveAll;
	  GO TO moveRest;
	  END;
	ENDCASE => StkError[];
      ENDLOOP;
    EXITS
      moveAll =>
	BEGIN
	remaining ← count;
	tStart ← CPtr.tempstart;
	lvlPrev ← ctlvl;
	GO TO moveRest;
	END;
    END;
    EXITS
      moveRest =>
	BEGIN
	tlex: se Lexeme = P5.GenTempLex[remaining];
	a: Symbols.BitAddress = seb[tlex.lexsei].idValue;
	wa: CARDINAL ← a.wd + remaining - 1;
	THROUGH [0..remaining) DO -- fix someday to look for doubles
	  LoadItem[stkPtr];
	  wa ← Store[stkPtr, wa, TRUE];
	  DelStackItem[stkPtr]; -- this updates stkPtr
	  ENDLOOP;
	END;
    END;
    
    IF remaining < count THEN Pop[count-remaining];
      
    CPtr.stking ← saveStking;
    RETURN [[wSize: count,
      space: frame[wd: tStart, immutable: TRUE, level: lvlPrev]]];
    END;

  Top: PUBLIC PROCEDURE [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 PROCEDURE =
    BEGIN
    s: StackIndex;
    FOR s ← stkPtr, cb[s].downlink DO
      WITH cb[s] SELECT FROM
	mark =>
	  BEGIN
	  IF s = stkHead THEN StkError[]; -- fell off the end
	  DelStackItem[s];
	  RETURN
	  END;
	ENDCASE;
      ENDLOOP;
    END;

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

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

  END.