-- VarMove.mesa
-- last edited by Sweet, March 13, 1981  2:56 PM
-- last edited by Satterthwaite, May 25, 1982 12:40 pm

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [CodeNotImplemented, curctxlvl, stking],
  CodeDefs: TYPE USING [
    Base, BdoVarIndex, Byte, codeType, EvalStackSize, MaxParmsInStack,
    MoveDirection, StackIndex, StackLocRec, VarComponent, VarIndex],
  Environment: TYPE USING [bitsPerWord],
  FOpCodes: TYPE USING [
    qADD, qAMUL, qDADD, qDESCB, qGADRB, qLADRB,
    qLCO, qLG, qLGD, qLI, qLL, qLLD, qLLK, qMUL, qPUSH,
    qR, qRD, qRDL, qRF, qRFL, qRFS, qRFSL, qRIG, qRIGL, qRIL,
    qRILL, qRL, qRSTR, qRSTRL, qRXGL, qRXL, qRXLL,
    qSG, qSGD, qSL, qSLD, qW, qWD, qWDL, qWF, qWFL, qWFS, qWFSL,
    qWIG, qWIGL, qWIL, qWILL, qWL, qWSTR, qWSTRL, qWXGL, qWXL, qWXLL],
  Inline: TYPE USING [BITAND, BITSHIFT, LongNumber],
  Literals: TYPE USING [Base, LTIndex, LTNull, ltType],
  OpCodeParams: TYPE USING [GlobalHB, HB, LocalHB],
  P5L: TYPE USING [
    AddrComponent, BaseComponent, EasilyLoadable, GenAdd, GenAnd, GenRFC, GenShift,
    GenVarItem, LoadBoth, LoadSum, ModComponent, ReleaseVarItem, TOSComponent, Words],
  P5U: TYPE USING [Out0, Out1, Out2],
  PrincOps: TYPE USING [FieldDescriptor, localbase],
  Stack: TYPE USING [
    Above, Also, Depth, Dump, Exchange, Forget, Load, Loc, Pop, TempStore,
    Top, VDepthOf],
  Symbols: TYPE USING [Base, ContextLevel, lG, lZ, seType];

VarMove: PROGRAM
    IMPORTS CPtr: Code, Inline, P5U, P5L, Stack
    EXPORTS P5L, CodeDefs =
  BEGIN OPEN FOpCodes, CodeDefs, Symbols;

  wordlength: CARDINAL = Environment.bitsPerWord;

  cb: CodeDefs.Base;
  seb: Symbols.Base;
  ltb: Literals.Base;

  VarMoveNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];
    cb ← base[codeType];
    ltb ← base[Literals.ltType];
    END;
    
  LoadComponent: PUBLIC PROC [var: VarComponent] =
    BEGIN
    Mask: ARRAY [0..15] OF CARDINAL = [
      0b, 1b, 3b, 7b, 17b, 37b, 77b, 177b, 377b, 777b,
      1777b, 3777b, 7777b, 17777b, 37777b, 77777b];
    wS: CARDINAL = P5L.Words[var.wSize, var.bSize];
    WITH vv: var SELECT FROM
      faddr =>
	BEGIN
	w: CARDINAL ← vv.wd;
	lvl: ContextLevel = vv.level;
	SELECT lvl FROM
	  lZ => ERROR;
	  lG => P5U.Out1[qGADRB, w];
	  CPtr.curctxlvl => P5U.Out1[qLADRB, w];
	  ENDCASE =>
	    BEGIN
	    LoadComponent[P5L.BaseComponent[lvl]];
	    w ← w - PrincOps.localbase;
	    IF w # 0 THEN P5L.GenAdd[w];
	    END;
	END;
      frame =>
	BEGIN
	w: CARDINAL ← vv.wd; 
	b: CARDINAL = vv.bd;
	lvl: ContextLevel = vv.level;
	delta: CARDINAL ← 0;
	IF b # 0 THEN
	  BEGIN
	  IF Stack.Depth[] < EvalStackSize-2 AND b + var.bSize = wordlength THEN
	    BEGIN
	    LoadComponent[[wSize: 1, space: frame[wd: w, level: lvl]]];
	    P5L.GenAnd[Mask[var.bSize]];
	    var.bSize ← 0;
	    END
	  ELSE
	    BEGIN
	    r: VarIndex = P5L.GenVarItem[bo];
	    IF var.wSize # 0 THEN ERROR;
	    cb[r] ← [body: bo[
	      base: P5L.AddrComponent[var],
	      offset: [bSize: var.bSize, space: frame[bd: b]]]];
	    MoveBo[r, load];
	    RETURN
	    END;
	  delta ← 1;
	  END;
	BEGIN -- to declare LFOp
	LFOp: ARRAY BOOLEAN OF ARRAY [1..2] OF Byte = [[qLL, qLLD], [qLG, qLGD]];
	IF var.wSize # 0 THEN SELECT lvl FROM
	  lZ => ERROR;
	  CPtr.curctxlvl, lG => 
	    BEGIN
	    IF var.wSize IN [1..2] THEN {
	      P5U.Out1[LFOp[lvl=lG][var.wSize], w+delta];
	      IF vv.immutable AND CPtr.stking THEN
	        Stack.Also[n: var.wSize, tOffset: w+delta, tLevel: lvl]}
	    ELSE
	      FOR i: CARDINAL IN [0..var.wSize) DO
		P5U.Out1[LFOp[lvl=lG][1], w+delta+i];
	        IF vv.immutable AND CPtr.stking THEN
	          Stack.Also[tOffset: w+delta+i, tLevel: lvl];
		ENDLOOP;
	    END;
	  ENDCASE =>
	    BEGIN
	    r: VarIndex = P5L.GenVarItem[bo];
	    cb[r] ← [body: bo[
	      base: P5L.BaseComponent[lvl],
	      offset: [wSize: var.wSize, space:
		frame[wd: w+delta - PrincOps.localbase]]]];
	    MoveBo[r, load];
	    END;
	END; -- scope of LFOp
	IF var.bSize # 0 THEN
	  BEGIN -- delta = 0 in this case
	  IF Stack.Depth[] < EvalStackSize-2 THEN
	    BEGIN
	    LoadComponent[[wSize: 1, space: frame[wd: w+var.wSize, level: lvl]]];
	    P5L.GenShift[var.bSize - wordlength];
	    END
	  ELSE
	    BEGIN
	    r: VarIndex = P5L.GenVarItem[bo];
	    cb[r] ← [body: bo[
	      base: P5L.AddrComponent[var],
	      offset: [bSize: var.bSize, space: frame[wd: var.wSize]]]];
	    MoveBo[r, load];
	    END;
	  END;
	END;
      frameup =>
	BEGIN
	r: VarIndex = P5L.GenVarItem[bo];
	cb[r] ← [body: bo[
	  base: [wSize: vv.pwSize, space: frame[wd: vv.wd, level: vv.level]],
	  offset: [wSize: var.wSize, space: frame[wd: vv.delta]]]];
	MoveBo[r, load];
	END;
      linkup =>
	BEGIN
	P5U.Out1[qLLK, vv.wd];
	P5U.Out1[IF var.wSize = 2 THEN qRD ELSE qR, vv.delta];
	END;
      caddr => P5U.Out1[qLCO, vv.wd];
      code =>
	BEGIN
	w: CARDINAL ← vv.wd; 
	b: CARDINAL = vv.bd;
	lti: Literals.LTIndex = vv.lti;
	delta: CARDINAL ← 0;
	IF lti # Literals.LTNull THEN
	  BEGIN

	  LoadFirst: PROC [val: CARDINAL] =
	    BEGIN
	    IF b # 0 THEN
	      BEGIN
	      IF var.wSize # 0 AND var.bSize + b # wordlength THEN ERROR;
	      val ← Inline.BITAND[ 
		Inline.BITSHIFT[val, b + var.bSize - wordlength], Mask[var.bSize]];
	      var.bSize ← 0;
	      END;
	    P5U.Out1[qLI, val];
	    END;

	  LoadLast: PROC [val: CARDINAL] =
	    BEGIN
	    IF var.bSize # 0 THEN
	      val ← Inline.BITSHIFT[val, var.bSize - wordlength];
	    P5U.Out1[qLI, val];
	    END;

	  WITH ll: ltb[lti] SELECT FROM
	    short => LoadFirst[ll.value];
	    long =>
	      BEGIN
	      LoadFirst[ll.value[0]];
	      FOR i: CARDINAL IN [1..wS-1) DO
		P5U.Out1[qLI, ll.value[i]];
		ENDLOOP;
	      IF wS > 1 THEN LoadLast[ll.value[wS-1]];
	      END;
	    ENDCASE => ERROR;
	  RETURN
	  END;
	IF b # 0 THEN
	  BEGIN
	  IF var.wSize # 0 AND var.bSize + b # wordlength THEN ERROR;
	  P5U.Out1[qLCO, w];
	  P5L.GenRFC[0, b, var.bSize];
	  var.bSize ← 0; delta ← 1;
	  END;
	FOR i: CARDINAL IN [0..var.wSize) DO
	  P5U.Out1[qLCO, w];
	  P5L.GenRFC[i+delta, 0, wordlength];
	  ENDLOOP;
	IF var.bSize # 0 THEN
	  BEGIN
	  P5U.Out1[qLCO, w];
	  P5L.GenRFC[var.wSize, 0, var.bSize];
	  END;
	END;
      link => P5U.Out1[qLLK, vv.wd];
      stack =>
	BEGIN
	w: CARDINAL = vv.wd;
	b: [0..wordlength) = vv.bd;
	IF w # 0 THEN
	  BEGIN
	  IF Stack.VDepthOf[vv.sti] # wS+w-1 THEN
	    BEGIN
	    nsti: StackIndex;
	    Stack.Dump[];
	    nsti ← Stack.Above[vv.sti, w];
	    Stack.Forget[vv.sti, w];
	    vv.sti ← nsti;
	    GO TO notOnTop;
	    END;
	  IF wS > 1 THEN
	    BEGIN
	    fvar: VarComponent ← Stack.TempStore[wS];
	    THROUGH [0..w) DO Stack.Pop[]; ENDLOOP;
	    WITH vv: fvar SELECT FROM
	      frame =>
		BEGIN
		vv.bd ← b;
		IF var.bSize # 0 THEN
		  BEGIN
		  fvar.wSize ← fvar.wSize-1;  fvar.bSize ← var.bSize;
		  END;
		END;
	      ENDCASE;
	    LoadComponent[fvar];
	    RETURN;
	    END
	  ELSE THROUGH [0..w) DO Stack.Exchange[]; Stack.Pop[] ENDLOOP;
	  vv.sti ← Stack.Top[1];
	  EXITS
	    notOnTop => NULL;
	  END;
	IF b # 0 THEN
	  BEGIN
	  rest: StackIndex;
	  IF var.wSize # 0 THEN
	    BEGIN
	    rest ← Stack.Above[vv.sti, 1];
	    IF b+var.bSize # wordlength THEN ERROR;
	    END;
	  Stack.Load[vv.sti, 1];
	  SELECT b + var.bSize FROM
	    > wordlength => ERROR;
	    = wordlength => NULL;
	    ENDCASE => P5L.GenShift[b + var.bSize - wordlength];
	  P5L.GenAnd[Mask[var.bSize]];
	  var.bSize ← 0;
	  IF var.wSize # 0 THEN Stack.Load[rest, var.wSize];
	  END
	ELSE Stack.Load[vv.sti, wS];
	IF var.bSize # 0 THEN P5L.GenShift[var.bSize - wordlength];
	END;
      const => 
	BEGIN
	v1: CARDINAL ← vv.d1;
	b: [0..wordlength) = vv.bd;
	nw: CARDINAL = P5L.Words[var.wSize, var.bSize];
	IF b # 0 THEN
	  BEGIN -- Field taking machinery takes care of less than word case
	  IF b + var.bSize # wordlength THEN ERROR;
	  v1 ← Inline.BITAND[v1, Mask[var.bSize]];
	  var.bSize ← 0;
	  END;
	P5U.Out1[qLI, v1];
	IF nw = 2 THEN 
	  IF var.bSize # 0 THEN
	    P5U.Out1[qLI, Inline.BITSHIFT[vv.d2, var.bSize - wordlength]]
	  ELSE P5U.Out1[qLI, vv.d2];
	END;
      pdesc => P5U.Out1[qDESCB, vv.ep];
      ENDCASE => ERROR;
    END;
    
  LoadVar: PUBLIC PROC [r: VarIndex] =
    BEGIN
    WITH cb[r] SELECT FROM
      o => {LoadComponent[var]; P5L.ReleaseVarItem[r]};
      bo => MoveBo[r, load];
      bdo => MoveBdo[r, load];
      ind => MoveInd[r, load];
      ENDCASE => ERROR;
    END;
    
  MoveBo: PROC [r: VarIndex, dir: MoveDirection] =
    BEGIN
    base, offset: VarComponent;
    foffset: POINTER TO frame VarComponent;
    psize: CARDINAL;
    RWFOp: ARRAY MoveDirection OF ARRAY [1..2] OF Byte =
      [[qRF, qRFL], [qWF, qWFL]];
    
    MoveInitialFragment: PROC =
      BEGIN
      SELECT foffset.bd+offset.bSize FROM
	< wordlength => IF offset.wSize # 0 THEN ERROR;
	> wordlength => ERROR;
	ENDCASE;
      LoadComponent[base];
      P5U.Out2[
	RWFOp[dir][psize],
	foffset.wd,
	LOOPHOLE[PrincOps.FieldDescriptor[
	  offset: 0, posn: foffset.bd, size: offset.bSize]]];
      offset.bSize ← 0;
      END;
    
    MoveFinalFragment: PROC =
      BEGIN
      LoadComponent[base];
      P5U.Out2[
	RWFOp[dir][psize],
	foffset.wd,
	LOOPHOLE[PrincOps.FieldDescriptor[
	  offset: 0, posn: foffset.bd, size: offset.bSize]]];
      END;
    
    WITH cc: cb[r] SELECT FROM
      bo => {base ← cc.base; offset ← cc.offset};
      ENDCASE => ERROR;
    P5L.ReleaseVarItem[r];
    WITH oo: offset SELECT FROM
      frame => foffset ← @oo;
      code =>
	BEGIN
	IF dir = store THEN ERROR;
	IF oo.bd # 0 THEN
	  BEGIN
	  IF offset.wSize # 0 THEN 
	    IF oo.bd + offset.bSize # wordlength THEN ERROR
	    ELSE IF offset.wSize # 0 THEN 
	      base ← P5L.EasilyLoadable[base, dir];
	  LoadComponent[base];
	  P5L.GenRFC[oo.wd, oo.bd, offset.bSize];
	  offset.bSize ← 0; oo.wd ← oo.wd + 1;
	  END;
	IF offset.wSize > 1 OR offset.wSize # 0 AND offset.bSize # 0 THEN
	  base ← P5L.EasilyLoadable[base, dir];
	FOR i: CARDINAL IN [0..offset.wSize) DO
	  LoadComponent[base];
	  P5L.GenRFC[oo.wd+i, 0, wordlength];
	  ENDLOOP;
	IF offset.bSize # 0 THEN
	  BEGIN
	  LoadComponent[base];
	  P5L.GenRFC[oo.wd+offset.wSize, 0, offset.bSize];
	  END;
	RETURN;
	END;
      ENDCASE => ERROR;
    psize ← P5L.Words[base.wSize, base.bSize];
    IF psize NOT IN [1..2] THEN ERROR;
    IF foffset.level # lZ THEN
      BEGIN -- a field descriptor
      RWFSOp: ARRAY MoveDirection OF ARRAY [1..2] OF Byte =
	[[qRFS, qRFSL], [qWFS, qWFSL]];
      SIGNAL CPtr.CodeNotImplemented;
      -- the following is only half baked, when field variables
      --  happen, need to work through in detail
      LoadComponent[base];
      LoadComponent[offset];
      P5U.Out0[ RWFSOp[dir][psize]];
      RETURN
      END;
    WITH bb: base SELECT FROM
      frame =>
	BEGIN
	RWilOp: ARRAY MoveDirection OF ARRAY [1..2] OF ARRAY BOOLEAN OF Byte =
	  [[[qRIL, qRIG], [qRILL, qRIGL]], [[qWIL, qWIG], [qWILL, qWIGL]]];
	IF offset.wSize # 1 OR foffset.wd NOT IN OpCodeParams.HB
	  OR foffset.bd # 0 OR offset.bSize # 0
	  OR base.bSize # 0 THEN GO TO notMagic;
	SELECT bb.level FROM
	  lG => IF bb.wd NOT IN OpCodeParams.GlobalHB THEN GO TO notMagic;
	  CPtr.curctxlvl => IF bb.wd NOT IN OpCodeParams.LocalHB THEN
	    GO TO notMagic;
	  ENDCASE => GO TO notMagic;
	P5U.Out2[
	  RWilOp[dir][psize][bb.level = lG],
	  bb.wd,
	  foffset.wd];
	RETURN;
	EXITS
	  notMagic => NULL;
	END;
      faddr =>
	BEGIN
	SELECT bb.level FROM
	  lG, CPtr.curctxlvl =>
	    IF offset.wSize IN [1..2] AND offset.bSize = 0 AND foffset.bd = 0 THEN
	      BEGIN
	      LSFOp: ARRAY MoveDirection OF ARRAY [1..2] OF ARRAY BOOLEAN OF Byte =
		[[[qLL, qLG], [qLLD, qLGD]], [[qSL, qSG], [qSLD, qSGD]]];
	      P5U.Out1[LSFOp[dir][offset.wSize][bb.level = lG], bb.wd + foffset.wd];
	      RETURN
	      END;
	  ENDCASE => 
	    BEGIN
	    tr: VarIndex = P5L.GenVarItem[bo];
	    cb[tr] ← [body: bo[base: P5L.BaseComponent[bb.level],
	      offset: [wSize: offset.wSize, bSize: offset.bSize,
		space: frame[
		  wd: bb.wd - PrincOps.localbase + foffset.wd, bd: foffset.bd]]]];
	    MoveBo[tr, dir];
	    RETURN
	    END;
	END;
      stack => IF base.bSize = 0 AND base.wSize IN [1..2] THEN
	BEGIN
	loc: StackLocRec = Stack.Loc[bb.sti, base.wSize];
	WITH loc SELECT FROM
	  inTemp =>
	    BEGIN -- let recursive call check other criteria
	    tr: VarIndex = P5L.GenVarItem[bo];
	    cb[tr] ← [body: bo[base: P5L.EasilyLoadable[base, dir], offset: offset]];
	    MoveBo[tr, dir];
	    RETURN;
	    END;
	  ENDCASE;
	END;
      ENDCASE;
    IF foffset.bd # 0 OR offset.bSize # 0 THEN
      BEGIN
      IF offset.wSize # 0 THEN base ← P5L.EasilyLoadable[base, dir];
      IF foffset.bd # 0 THEN
	BEGIN
	IF dir = load THEN MoveInitialFragment[];
	foffset.wd ← foffset.wd+1;
	END
      ELSE IF dir = store THEN MoveFinalFragment[];
      END;
    SELECT offset.wSize FROM
      0 => NULL;
      > MaxParmsInStack => ERROR;
      1, 2 => 
	BEGIN
	RWOp: ARRAY MoveDirection OF ARRAY [1..2] OF ARRAY [1..2] OF Byte =
	  [[[qR, qRL], [qRD, qRDL]], [[qW, qWL], [qWD, qWDL]]];
	LoadComponent[base];
	P5U.Out1[RWOp[dir][offset.wSize][psize], foffset.wd];
	END;
      ENDCASE =>
	BEGIN
	n, wo, s: CARDINAL;
	tr: VarIndex;
	base ← P5L.EasilyLoadable[base, dir];
	n ← offset.wSize;
        s ← MIN[n, 2];
	wo ← IF dir = load THEN foffset.wd ELSE foffset.wd + n-s;
	WHILE n # 0 DO
	  tr ← P5L.GenVarItem[bo];
	  cb[tr] ← [body: bo[base: base, offset: [wSize: s, space: frame[wd: wo]]]];
	  MoveBo[tr, dir];
	  n ← n - s;
	  IF dir = load THEN {wo ← wo + s; s ← MIN[n, 2]}
	  ELSE {s ← MIN[n, 2]; wo ← wo - s};
	  ENDLOOP;
	END;
    IF offset.bSize # 0 THEN
      BEGIN
      IF foffset.bd # 0 THEN foffset.wd ← foffset.wd - 1;
      IF dir = load THEN MoveFinalFragment[]
      ELSE IF foffset.bd # 0 THEN MoveInitialFragment[];
      END;
    END;
    
  MoveBdo: PROC [r: VarIndex, dir: MoveDirection] =
    BEGIN
    base, disp, offset: VarComponent;
    dpSize, bpSize: [1..2];
    magicOffset: BOOLEAN;
    foffset: POINTER TO frame VarComponent;
    
    BDCommute: PROC =
      BEGIN
      t: VarComponent = base;
      i: CARDINAL = bpSize;
      base ← disp; disp ← t;
      bpSize ← dpSize; dpSize ← i;
      END;
    
    MagicBase: PROC [b: VarComponent] RETURNS [BOOLEAN] =
      BEGIN
      WITH bb: b SELECT FROM
	frame =>
	  BEGIN
	  IF bb.bd # 0 OR b.bSize # 0 THEN RETURN [FALSE];
	  IF b.wSize = 1 AND bb.level = CPtr.curctxlvl THEN
	    RETURN [bb.wd IN OpCodeParams.LocalHB];
	  IF b.wSize = 2 THEN SELECT bb.level FROM
	    lG => RETURN [bb.wd IN OpCodeParams.GlobalHB];
	    CPtr.curctxlvl => RETURN [bb.wd IN OpCodeParams.LocalHB];
	    ENDCASE;
	  END;
	ENDCASE;
      RETURN [FALSE];
      END;
    
    WITH cc: cb[r] SELECT FROM
      bdo => {base ← cc.base; disp ← cc.disp; offset ← cc.offset};
      ENDCASE => ERROR;
    P5L.ReleaseVarItem[r];
    
    -- N.B. some of the following code is copied in MakeBo.  Any bugs should
    --   be fixed in both places.

    WITH vv: disp SELECT FROM
      const =>
	BEGIN
	owd: CARDINAL;
	ld: Inline.LongNumber;
        bor: VarIndex;
	IF disp.wSize > 1 OR disp.bSize # 0 THEN GO TO tooBig;
	WITH offset SELECT FROM
	  frame => owd ← wd;
	  code => owd ← wd;
	  ENDCASE => ERROR;
	ld.lc ← LONG[CARDINAL[vv.d1]] + LONG[owd];
	IF ld.highbits # 0 THEN GO TO tooBig;
	P5L.ModComponent[var: @offset, wd: vv.d1];
	bor ← P5L.GenVarItem[bo];
	cb[bor] ← [body: bo[base: base, offset: offset]];
	MoveBo[bor, dir];
	RETURN;
	EXITS
	 tooBig => NULL;
	END;
      ENDCASE;
    
    WITH oo: offset SELECT FROM
      frame => foffset ← @oo;
      code =>
	BEGIN
	tr: VarIndex = P5L.GenVarItem[bo];
	LoadComponent[disp]; --more likely to be already loaded
	LoadComponent[base];
	P5U.Out0[qADD];
	cb[tr] ← [body: bo[base: P5L.TOSComponent[1], offset: offset]];
	MoveBo[tr, dir];
	RETURN;
	END;
      ENDCASE => ERROR;
    
    magicOffset ← offset.bSize = 0 AND offset.wSize = 1 AND
	foffset.level = lZ AND foffset.wd IN OpCodeParams.HB;
    bpSize ← P5L.Words[base.wSize, base.bSize];
    dpSize ← P5L.Words[disp.wSize, disp.bSize];
    
    BEGIN -- to set up some exit labels
    SELECT bpSize FROM
      >dpSize => GO TO different;
      <dpSize => BEGIN BDCommute[]; GO TO different END;
      =1 =>
	BEGIN
	IF ~magicOffset THEN GO TO notMagic;
	IF MagicBase[base] THEN GO TO magic;
	IF MagicBase[disp] THEN BEGIN BDCommute[]; GO TO magic END;
	GO TO notMagic;
	EXITS
	  magic =>
	    BEGIN
	    LoadComponent[disp];
	    WITH bb: base SELECT FROM
	      frame =>
		P5U.Out2[IF dir = load THEN qRXL ELSE qWXL, bb.wd, foffset.wd];
	      ENDCASE;
	    RETURN;
	    END;
	END;
      =2 => GO TO notMagic;
      ENDCASE => ERROR;
    EXITS
      different =>
	BEGIN
	IF magicOffset AND MagicBase[base] THEN
	  BEGIN
	  RWXFLOp: ARRAY MoveDirection OF ARRAY BOOLEAN OF Byte =
	    [[qRXLL, qRXGL], [qWXLL, qWXGL]];
	  LoadComponent[disp];
	  WITH bb: base SELECT FROM
	    frame =>
	      P5U.Out2[RWXFLOp[dir][bb.level = lG], bb.wd, foffset.wd];
	    ENDCASE => ERROR;
	  RETURN;
	  END;
	END;
      notMagic => NULL;
    END;
    bpSize ← P5L.LoadSum[@disp, @base];
    BEGIN
    tr: VarIndex = P5L.GenVarItem[bo];
    cb[tr] ← [body: bo[base: P5L.TOSComponent[bpSize], offset: offset]];
    MoveBo[tr, dir];
    END;
    END;
    
  MoveInd: PROC [r: VarIndex, dir: MoveDirection] =
    BEGIN
    base, index, offset: VarComponent;
    packed, simple: BOOLEAN;
    eWords, grain: CARDINAL;
    
    WITH cc: cb[r] SELECT FROM
      ind =>
	BEGIN
	base ← cc.base;  index ← cc.index;  offset ← cc.offset;
	simple ← cc.simple;
	WITH pp: cc SELECT FROM
	  packed => {packed ← TRUE; grain ← pp.grain};
	  notPacked => {packed ← FALSE; eWords ← pp.eWords};
	  ENDCASE;
	END;
      ENDCASE => ERROR;
    P5L.ReleaseVarItem[r];
    
    IF ~packed THEN
      BEGIN
      tr: BdoVarIndex = LOOPHOLE[P5L.GenVarItem[bdo]];
      cb[tr] ← [body: bdo[base: base, disp: NULL, offset: offset]];
      IF eWords = 1 THEN cb[tr].disp ← index
      ELSE
	BEGIN
	WITH vv: index SELECT FROM
	  const =>
	    BEGIN
	    ld: Inline.LongNumber;
	    ld.lc ← LONG[CARDINAL[vv.d1]] * LONG[eWords];
	    vv.d1 ← ld.lowbits;
	    IF ld.highbits # 0 THEN {vv.wSize ← 2; vv.d2 ← ld.highbits};
	    cb[tr].disp ← index;
	    GO TO const;
	    END;
	  ENDCASE;
	LoadComponent[index];
	P5U.Out1[qLI, eWords];
	IF simple THEN
	  BEGIN
	  P5U.Out0[qMUL];
	  cb[tr].disp ← P5L.TOSComponent[1];
	  END
	ELSE
	  BEGIN
	  P5U.Out0[qAMUL];
	  P5U.Out0[qPUSH];
	  cb[tr].disp ← P5L.TOSComponent[2];
	  END;
	EXITS
	  const => NULL;
	END;
      MoveBdo[tr, dir];
      END
    ELSE
      BEGIN
      shift: [4..7] ← (SELECT grain FROM
	1 => 4, 2 => 5, 4 => 6, 8 => 7, ENDCASE => ERROR);
      obd: [0..wordlength);
      owd: CARDINAL;
      fd: PrincOps.FieldDescriptor;
      bpSize: CARDINAL;
      RWFSOp: ARRAY MoveDirection OF ARRAY [1..2] OF Byte =
	[[qRFS, qRFSL], [qWFS, qWFSL]];
      WITH oo: offset SELECT FROM
	code => {obd ← oo.bd; owd ← oo.wd};
	frame =>
	  BEGIN
	  IF oo.level # lZ THEN ERROR;
	  obd ← oo.bd; owd ← oo.wd;
	  END;
	ENDCASE => ERROR;
      fd ← [offset: 0, posn: obd, size: offset.bSize];
      bpSize ← P5L.Words[base.wSize, base.bSize];
      IF bpSize NOT IN [1..2] OR offset.wSize # 0 OR offset.bSize = 0
	THEN ERROR;
      WITH oo: offset SELECT FROM
	code =>
	  BEGIN
	  Mask: ARRAY [4..7] OF CARDINAL = [17b, 7b, 3b, 1b];
	  tr: VarIndex = P5L.GenVarItem[bdo];
	  fr: VarIndex = P5L.GenVarItem[ind];
	  iscomp, bscomp: VarComponent;
	  IF dir = store THEN ERROR;
	  IF owd # 0 THEN
	    BEGIN
	    LoadComponent[index];
	    P5L.GenAdd[owd];
	    index ← Stack.TempStore[1];
	    END
	  ELSE index ← P5L.EasilyLoadable[index, load];
	  LoadComponent[index];
	  P5L.GenShift[shift-8];
	  cb[tr] ← [body: bdo[
	    base: base, 
	    disp: P5L.TOSComponent[1], 
	    offset: [wSize: 1, space: code[wd: 0]]]];
	  MoveBdo[tr, load]; -- get word containing field
	  LoadComponent[P5L.AddrComponent[Stack.TempStore[1]]];
	  bscomp ← P5L.TOSComponent[1];
	  LoadComponent[index];
	  P5L.GenAnd[Mask[shift]];
	  iscomp ← P5L.TOSComponent[1];
	  cb[fr] ← [body: ind[
	    base: bscomp, index: iscomp,
	    offset: [bSize: offset.bSize, space: frame[bd: obd]],
	    simple: TRUE,
	    packinfo: packed[grain: grain]]];
	  MoveInd[fr, load];
	  RETURN
	  END;
	ENDCASE;
      IF fd = [offset: 0, posn: 0, size: 8] THEN
	BEGIN
	RWSTROp: ARRAY MoveDirection OF ARRAY [1..2] OF Byte =
	  [[qRSTR, qRSTRL], [qWSTR, qWSTRL]];
	alpha: CARDINAL ← owd;
	P5L.LoadBoth[@base, @index, FALSE];
	IF alpha > LAST[Byte] THEN
	  BEGIN
	  P5L.GenAdd[alpha-LAST[Byte]];
	  alpha ← LAST[Byte];
	  END;
	P5U.Out1[RWSTROp[dir][bpSize], alpha];
	RETURN
	END;
      IF simple THEN
	BEGIN
	IF bpSize = 2 AND index.tag = stack THEN
	  index ← P5L.EasilyLoadable[index, dir]; -- move to temp
	P5L.LoadBoth[@base, @index, FALSE];
	IF owd # 0 THEN P5L.GenAdd[owd];
	END
      ELSE
	BEGIN
	Mask: ARRAY [4..7] OF CARDINAL = [17b, 7b, 3b, 1b];
	IF owd # 0 THEN
	  BEGIN
	  LoadComponent[index];
	  P5L.GenAdd[owd];
	  index ← Stack.TempStore[1];
	  END
	ELSE index ← P5L.EasilyLoadable[index, load];
	LoadComponent[base];
	LoadComponent[index];
	P5L.GenShift[shift-8];
	IF bpSize = 1 THEN P5U.Out0[FOpCodes.qADD]
	ELSE {P5U.Out1[FOpCodes.qLI, 0]; P5U.Out0[FOpCodes.qDADD]};
	LoadComponent[index];
	P5L.GenAnd[Mask[shift]];
	END;
      P5L.GenShift[shift];
      IF fd # LOOPHOLE[0] THEN P5L.GenAdd[fd];
      P5U.Out0[RWFSOp[dir][bpSize]];
      END;
    
    END;
    
  StoreComponent: PUBLIC PROC [var: VarComponent] =
    BEGIN
    w, b: CARDINAL;
    lvl: ContextLevel;
    WITH vv: var SELECT FROM
      frame => {w ← vv.wd; b ← vv.bd; lvl ← vv.level};
      frameup =>
	BEGIN
	r: VarIndex = P5L.GenVarItem[bo];
	cb[r] ← [body: bo[
	  base: [wSize: vv.pwSize, space: frame[wd: vv.wd, level: vv.level]],
	  offset: [wSize: vv.wSize, space: frame[wd: vv.delta]]]];
	MoveBo[r, store];
	END;
      linkup =>
	BEGIN
	r: VarIndex = P5L.GenVarItem[bo];
	cb[r] ← [body: bo[
	  base: [wSize: 1, space: link[wd: vv.wd]],
	  offset: [wSize: vv.wSize, space: frame[wd: vv.delta]]]];
	MoveBo[r, store];
	END;
      ENDCASE => ERROR;
    IF b # 0 OR var.bSize # 0 THEN
      BEGIN
      r: VarIndex = P5L.GenVarItem[bo];
      cb[r] ← [body: bo[
	base: P5L.AddrComponent[var],
	offset: [wSize: var.wSize, bSize: var.bSize, space: frame[bd: b]]]];
      MoveBo[r, store];
      END
    ELSE
      BEGIN
      SFOp: ARRAY BOOLEAN OF ARRAY [1..2] OF Byte = [[qSL, qSLD], [qSG, qSGD]];
      SELECT lvl FROM
	lZ => ERROR;
	CPtr.curctxlvl, lG => IF var.wSize IN [1..2] THEN
	  P5U.Out1[SFOp[lvl=lG][var.wSize], w]
	ELSE
	  FOR i: CARDINAL DECREASING IN [0..var.wSize) DO
	    P5U.Out1[SFOp[lvl=lG][1], w+i];
	    ENDLOOP;
	ENDCASE =>
	  BEGIN
	  r: VarIndex = P5L.GenVarItem[bo];
	  cb[r] ← [body: bo[base: P5L.BaseComponent[lvl],
	    offset: [wSize: var.wSize,
	      space: frame[wd: w - PrincOps.localbase]]]];
	  MoveBo[r, store];
	  END;
      END;
    END;
    
  StoreVar: PUBLIC PROC [r: VarIndex] =
    BEGIN
    WITH cb[r] SELECT FROM
      o => {StoreComponent[var]; P5L.ReleaseVarItem[r]};
      bo => MoveBo[r, store];
      bdo => MoveBdo[r, store];
      ind => MoveInd[r, store];
      ENDCASE => ERROR;
    END;
    
  END.