-- VarBasics.mesa  Edited by Sweet, January 15, 1980  5:10 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [BYTE, wordlength],
  Code: FROM "code" USING [CodeNotImplemented, curctxlvl, dStar, stking],
  CodeDefs: FROM "codedefs",
  ControlDefs: FROM "controldefs" USING [FieldDescriptor,
    framelink, localbase],
  FOpCodes: FROM "fopcodes" USING [qADD, qAMUL, qAND, qBLT,
    qBLTC, qBLTL, qDADD, qLI, qLL, qLLK, qLP, qMUL, qNOOP,
    qPUSH, qR, qRFC, qSHIFT],
  InlineDefs: FROM "inlinedefs" USING [LongNumber],
  LiteralOps: FROM "literalops" USING [FindDescriptor],
  Literals: FROM "literals" USING [LTIndex, ltType],
  P5: FROM "p5" USING [MoveToCodeWord, WriteCodeWord],
  P5L: FROM "p5l" USING [
    AllLoaded, AddrComponent, AddrForVar, CopyToTemp, CopyVarItem,
    EasilyLoadable, FieldOfVar, GenAdd, GenVarItem, LoadAddress, LoadBoth,
    LoadComponent, LoadVar, LongVarAddress, ModComponent, 
    OVarItem, ReleaseVarItem, ReusableCopies, StoreVar, TOSComponent, TOSLex, 
    VarAddressEasy, VarAlignment, VarStackWords, Words],
  P5U: FROM "p5u" USING [Out0, Out1, Out2],
  Stack: FROM "stack" USING [Above, Also, Forget, Loc, Pop, Require, TempStore],
  Symbols: FROM "symbols" USING [ContextLevel, ctxType, lG, lZ, seType],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType];

VarBasics: PROGRAM
  IMPORTS LCPtr: Code, LiteralOps, P5, P5U, P5L, Stack
  EXPORTS P5L, CodeDefs =
  BEGIN OPEN FOpCodes, CodeDefs, Symbols;

  CPtr: POINTER TO FRAME [Code] = LCPtr;

  wordlength: CARDINAL = AltoDefs.wordlength;
  BYTE: TYPE = AltoDefs.BYTE;

  cb, seb, ctxb, ltb: Table.Base;

  VarBasicsNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];
    ctxb ← base[Symbols.ctxType];
    cb ← base[Tree.treeType];
    ltb ← base[Literals.ltType];
    RETURN
    END;
    
  AddrComponent: PUBLIC PROCEDURE [var: VarComponent] RETURNS [avar: VarComponent] =
    BEGIN
    WITH vv: var SELECT FROM
      code => RETURN [[wSize: 1, space: caddr[wd: vv.wd]]];
      frame => RETURN [[wSize: 1, space: faddr[level: vv.level, wd: vv.wd]]];
      frameup =>
	BEGIN
	base: VarComponent =
	  [wSize: vv.pwSize, space:
	    frame[wd: vv.wd, level: vv.level, immutable: vv.immutable]];
	IF vv.delta = 0 THEN RETURN[base];
	P5L.LoadComponent[base];
	P5L.GenAdd[vv.delta, base.wSize > 1];
	RETURN[P5L.TOSComponent[base.wSize]];
	END;
      linkup =>
	BEGIN
	IF vv.delta = 0 THEN RETURN [[wSize: 1, space: link[wd: vv.wd]]];
	P5U.Out1[qLLK, vv.wd];
	P5L.GenAdd[vv.delta];
	RETURN [P5L.TOSComponent[1]];
	END;
      stack =>
	BEGIN
	loc: StackLocRec = Stack.Loc[vv.sti, var.wSize];
	WITH loc SELECT FROM
	  inTemp =>
	    BEGIN
	    Stack.Forget[vv.sti, var.wSize];
	    RETURN [[wSize: 1, space: faddr[level: tLevel, wd: tOffset]]];
	    END;
	  ENDCASE;
	END;
      caddr => ERROR; -- nobody should be taking the address of a code address
      ENDCASE; -- faddr, link, const, pdesc
    BEGIN -- put it in a temp, generate addr of temp
    tvar: VarComponent ← P5L.CopyToTemp[P5L.OVarItem[var]].var;
    RETURN [P5L.AddrComponent[tvar]];
    END;
    END;
    
  AddrForVar: PUBLIC PROCEDURE [r: VarIndex, codeOk: BOOLEAN ← FALSE] RETURNS [avar: VarComponent] =
    BEGIN
    BEGIN -- to set up "loadIt" label
    WITH cc: cb[r] SELECT FROM
      o => 
	BEGIN
	IF ~codeOk AND cc.var.tag = code THEN ERROR;
	avar ← AddrComponent[cc.var];
	END;
      bo =>
	BEGIN
	WITH oo: cc.offset SELECT FROM
	  frame => IF oo.level = lZ AND oo.wd = 0 THEN avar ← cc.base
	    ELSE GO TO loadIt;
	  code => IF codeOk AND oo.wd = 0 THEN avar ← cc.base
	    ELSE GO TO loadIt;
	  ENDCASE => ERROR;
	END;
      ENDCASE => GO TO loadIt;
    P5L.ReleaseVarItem[r];
    EXITS
      loadIt =>
	BEGIN
	long: BOOLEAN ← P5L.LoadAddress[r, codeOk];
	avar ← P5L.TOSComponent[IF long THEN 2 ELSE 1];
	END;
    END;
    END;
    
  BaseComponent: PUBLIC PROCEDURE [lvl: ContextLevel] RETURNS [VarComponent] =
    BEGIN
    IF lvl >= CPtr.curctxlvl THEN ERROR;
    IF lvl + 1 = CPtr.curctxlvl THEN
      RETURN [[wSize: 1, space: frame[
	immutable: TRUE, 
	wd: ControlDefs.framelink,
	level: CPtr.curctxlvl]]];
    P5U.Out1[qLL, ControlDefs.framelink];
    THROUGH (lvl..CPtr.curctxlvl) DO
      P5U.Out1[qR, ControlDefs.framelink - ControlDefs.localbase];
      ENDLOOP;
    RETURN[P5L.TOSComponent[1]];
    END;
    
  GenAdd: PUBLIC PROCEDURE [delta: UNSPECIFIED, long: BOOLEAN ← FALSE] =
    BEGIN
    P5U.Out1[qLI, LOOPHOLE[delta, CARDINAL]];
    IF long THEN P5U.Out1[qLI, 0];
    P5U.Out0[IF long THEN qDADD ELSE qADD];
    END;
    
  GenAnd: PUBLIC PROCEDURE [delta: UNSPECIFIED] =
    BEGIN
    P5U.Out1[qLI, LOOPHOLE[delta, CARDINAL]];
    P5U.Out0[qAND];
    END;
    
  GenRFC: PUBLIC PROCEDURE [wd: CARDINAL, bd: [0..16), len: [1..16]] =
    BEGIN
    IF wd > LAST[BYTE] THEN
      BEGIN
      GenAdd[wd-LAST[BYTE]];
      wd ← LAST[BYTE];
      END;
    P5U.Out2[qRFC, wd,
      LOOPHOLE[ControlDefs.FieldDescriptor[offset: 0, posn: bd, size: len]]];
    END;
    
  GenShift: PUBLIC PROCEDURE [delta: UNSPECIFIED] =
    BEGIN
    P5U.Out1[qLI, delta];
    P5U.Out0[qSHIFT];
    END;
    
  LoadAddress: PUBLIC PROCEDURE [r: VarIndex, codeOk: BOOLEAN ← FALSE]
      RETURNS [long: BOOLEAN] =
    BEGIN
    bor: BoVarIndex;
    base, offset: VarComponent;
    WITH cc: cb[r] SELECT FROM
      o =>
	BEGIN
	avar: VarComponent ← AddrComponent[cc.var];
	IF avar.tag = caddr AND ~codeOk THEN ERROR;
	P5L.LoadComponent[avar];
	RETURN [FALSE];
	END;
      ENDCASE;
    bor ← MakeBo[r]; 
    IF bor = VarNull THEN SIGNAL CPtr.CodeNotImplemented;
    base ← cb[bor].base; offset ← cb[bor].offset;
    P5L.ReleaseVarItem[bor];
    long ← P5L.Words[base.wSize, base.bSize] > 1;
    P5L.LoadComponent[base];
    WITH oo: offset SELECT FROM
      frame =>
	BEGIN
	IF oo.level # lZ THEN ERROR CPtr.CodeNotImplemented;
	IF oo.wd # 0 THEN
	  BEGIN
	  P5U.Out1[qLI, oo.wd];
	  IF long THEN P5U.Out1[qLI, 0]; -- lengthen
	  P5U.Out0[IF long THEN qDADD ELSE qADD];
	  END;
	END;
      code =>
	BEGIN
	IF ~codeOk OR long THEN ERROR;
	IF oo.wd # 0 THEN GenAdd[oo.wd];
	END;
      ENDCASE => ERROR;
    END;
    
  LoadBoth: PUBLIC PROCEDURE [atC1, atC2: POINTER TO VarComponent, abelian: BOOLEAN] =
    BEGIN
    c1: VarComponent ← atC1↑;
    c2: VarComponent ← atC2↑;
    c1Loaded, c2Loaded: BOOLEAN ← FALSE;
    c1Depth, c2Depth: CARDINAL;
    c1Size: CARDINAL = Words[c1.wSize, c1.bSize];
    c2Size: CARDINAL = Words[c2.wSize, c2.bSize];
    
    WITH cc: c1 SELECT FROM
      stack => IF cc.bd = 0 AND c1.bSize = 0 THEN
	BEGIN
	loc: StackLocRec = Stack.Loc[cc.sti, c1Size];
	WITH loc SELECT FROM
	  onStack =>
	    BEGIN c1Depth ← depth; c1Loaded ← TRUE; END;
	  ENDCASE;
	END;
      ENDCASE;
    
    WITH cc: c2 SELECT FROM
      stack => IF cc.bd = 0 AND c2.bSize = 0 THEN
	BEGIN
	loc: StackLocRec = Stack.Loc[cc.sti, c2Size];
	WITH loc SELECT FROM
	  onStack =>
	    BEGIN c2Depth ← depth; c2Loaded ← TRUE; END;
	  ENDCASE;
	END;
      ENDCASE;
    
    BEGIN -- to set up loadBoth label
    IF ~(c1Loaded OR c2Loaded) THEN GO TO loadBoth;
    IF c1Loaded AND c2Loaded THEN
      IF (c1Depth = c2Size AND c2Depth = 0) OR
	(abelian AND c2Depth = c1Size AND c1Depth = 0) THEN RETURN
      ELSE GO TO loadBoth; -- considered unlikely
    IF c1Loaded THEN
      BEGIN
      IF c1Depth # 0 THEN P5L.LoadComponent[c1];
      P5L.LoadComponent[c2];
      END
    ELSE -- c2Loaded
      BEGIN
      IF c2Depth # 0 THEN P5L.LoadComponent[c2];
      IF ~abelian AND (c1Size>1 OR c2Size>1) THEN
	c2 ← Stack.TempStore[c2Size];
      P5L.LoadComponent[c1];
      IF ~abelian THEN P5L.LoadComponent[c2];
      END;
    EXITS
      loadBoth =>
	BEGIN
	P5L.LoadComponent[c1];
	P5L.LoadComponent[c2];
	END;
    END;
    END;
    
  LoadSum: PUBLIC PROCEDURE [atB, atD: POINTER TO VarComponent]
      RETURNS [bpSize: [1..2]] =
    BEGIN
    base, disp: VarComponent;
    dpSize: [1..2];
    alreadyOn: CARDINAL ← 0;
    
    BDCommute: PROCEDURE =
      BEGIN
      t: VarComponent = base;
      i: CARDINAL ← bpSize;
      base ← disp; disp ← t;
      bpSize ← dpSize; dpSize ← i;
      END;
    
    base ← atB↑; disp ← atD↑;
    bpSize ← Words[base.wSize, base.bSize];
    dpSize ← Words[disp.wSize, disp.bSize];
    WITH base SELECT FROM
      stack => alreadyOn ← alreadyOn + bpSize;
      ENDCASE;
    WITH disp SELECT FROM
      stack => alreadyOn ← alreadyOn + dpSize;
      ENDCASE;
    IF (bpSize = 2 OR dpSize = 2) AND ~CPtr.dStar THEN
      Stack.Require[alreadyOn]; -- could dump what was on
    
    BEGIN -- to set up "different" exit label
    SELECT bpSize FROM
      >dpSize => GO TO different;
      <dpSize => BEGIN BDCommute[]; GO TO different END;
      ENDCASE => P5L.LoadBoth[@base, @disp, TRUE];
    EXITS
      different =>
	BEGIN
	P5L.LoadComponent[disp];
	P5U.Out1[qLI, 0]; --lengthen disp
	disp ← P5L.TOSComponent[2];
	P5L.LoadBoth[@base, @disp, TRUE];
	END;
    END;
    P5U.Out0[IF bpSize = 1 THEN qADD ELSE qDADD];
    RETURN
    END;
    
  MakeBo: PUBLIC PROCEDURE [r: VarIndex] RETURNS [bor: BoVarIndex] =
    BEGIN
    base, disp, offset: VarComponent;
    bpSize: [1..2];
    
    WITH cb[r] SELECT FROM
      bo => RETURN [LOOPHOLE[r]];
      ENDCASE;
    bor ← LOOPHOLE[P5L.GenVarItem[bo]];
    cb[bor] ← [body: bo[base: NULL, offset: NULL]]; -- set tag
    WITH cc: cb[r] SELECT FROM
      o => 
	BEGIN
	var: VarComponent ← cc.var;
	
	P5L.ReleaseVarItem[r];
	WITH vv: var SELECT FROM
	  frameup =>
	    BEGIN
	    cb[bor].base ←
	      [wSize: vv.pwSize, space:
		frame[wd: vv.wd, level: vv.level, immutable: vv.immutable]];
	    cb[bor].offset ← [wSize: var.wSize, space: frame[wd: vv.delta]];
	    RETURN
	    END;
	  linkup =>
	    BEGIN
	    cb[bor].base ← [wSize: 1, space: link[wd: vv.wd]];
	    cb[bor].offset ← [wSize: var.wSize, space: frame[wd: vv.delta]];
	    RETURN
	    END;
	  code => cb[bor].offset ←
	    [wSize: NULL, bSize: NULL, space: code[bd: vv.bd]];
	  frame => cb[bor].offset ←
	    [wSize: NULL, bSize: NULL, space: frame[bd: vv.bd]];
	  stack => cb[bor].offset ←
	    [wSize: NULL, bSize: NULL, space: frame[bd: vv.bd]];
	  const =>
	    BEGIN
	    wS: CARDINAL = P5L.Words[var.wSize, var.bSize];
	    IF wS = 1 THEN 
	      cb[bor].offset ← -- can't index packed in code anyway
	        [wSize: NULL, bSize: NULL, space: frame[bd: vv.bd]]
	    ELSE
	      BEGIN -- wS = 2
	      const: ARRAY [0..1] OF CARDINAL ← [vv.d1, vv.d2];
	      lti: Literals.LTIndex;
	      cb[bor].offset ←
	        [wSize: NULL, bSize: NULL, space: code[bd: vv.bd]];
	      lti ← LiteralOps.FindDescriptor[DESCRIPTOR[const]];
	      WITH ll: ltb[lti] SELECT FROM
		long =>
		  BEGIN
		  IF ll.codeIndex = 0 THEN
		    BEGIN
		    ll.codeIndex ← P5.MoveToCodeWord[];
		    P5.WriteCodeWord[const[0]];
		    P5.WriteCodeWord[const[1]];
		    END;
		  var ← [wSize: 2, space: code[wd: ll.codeIndex]];
		  END;
		ENDCASE => ERROR;
	      END;
	    END;
	  ENDCASE => ERROR;
	cb[bor].base ← P5L.AddrComponent[var];
	cb[bor].offset.wSize ← var.wSize;
	cb[bor].offset.bSize ← var.bSize;
	RETURN
	END;
      bdo =>
	BEGIN
	disp ← cc.disp;
	base ← cc.base;
	offset ← cc.offset;
	END;
      ind =>
	BEGIN
	eWords: CARDINAL;
	base ← cc.base;
	disp ← cc.index;
	offset ← cc.offset;
	WITH pp: cc SELECT FROM
	  packed => 
	    BEGIN P5L.ReleaseVarItem[bor]; RETURN [LOOPHOLE[VarNull]] END;
	  notPacked =>  eWords ← pp.eWords;
	  ENDCASE;
	IF eWords # 1 THEN
	  BEGIN
	  WITH vv: disp SELECT FROM
	    const =>
	      BEGIN
	      ld: InlineDefs.LongNumber;
	      ld.lc ← LONG[CARDINAL[vv.d1]] * LONG[eWords];
	      vv.d1 ← ld.lowbits;
	      IF ld.highbits # 0 THEN
		BEGIN vv.wSize ← 2; vv.d2 ← ld.highbits; END;
	      GO TO const;
	      END;
	    ENDCASE;
	  P5L.LoadComponent[disp];
	  P5U.Out1[qLI, eWords];
	  IF cc.simple THEN
	    BEGIN
	    P5U.Out0[qMUL];
	    disp ← P5L.TOSComponent[1];
	    END
	  ELSE
	    BEGIN
	    P5U.Out0[qAMUL];
	    P5U.Out0[qPUSH];
	    disp ← P5L.TOSComponent[2];
	    END;
	  EXITS
	    const => NULL;
	  END;
	END;
      ENDCASE;
    P5L.ReleaseVarItem[r];
    
    WITH vv: disp SELECT FROM
      const => IF vv.wSize = 1 AND vv.bSize = 0 THEN
	BEGIN
	ld: InlineDefs.LongNumber;
	owd: CARDINAL;
	WITH oo: offset SELECT FROM
	  frame => owd ← oo.wd;
	  code => owd ← oo.wd;
	  ENDCASE => ERROR;
        ld.lc ← LONG[owd] + LONG[CARDINAL[vv.d1]];
	IF ld.highbits = 0 THEN
	  BEGIN
	  P5L.ModComponent[var: @offset, wd: vv.d1];
	  cb[bor].base ← base;
	  cb[bor].offset ← offset;
	  RETURN
	  END;
	END;
      ENDCASE;
    bpSize ← LoadSum[@base, @disp];
    cb[bor].base ← P5L.TOSComponent[bpSize];
    cb[bor].offset ← offset;
    END;
    
  MakeComponent: PUBLIC PROCEDURE [r: VarIndex, allowFields: BOOLEAN ← FALSE]
      RETURNS [var: VarComponent] =
    BEGIN
    wS: CARDINAL;
    WITH cb[r] SELECT FROM
      o => RETURN[var];
      bo =>
	BEGIN
	WITH oo: offset SELECT FROM
	  code =>
	    WITH bb: base SELECT FROM
	      caddr => 
		BEGIN
		var ← [wSize: offset.wSize, bSize: offset.bSize,
		  space: code[wd: bb.wd + oo.wd, bd: oo.bd]];
		GO TO freer;
		END;
	      ENDCASE;
	  frame => 
	    WITH bb: base SELECT FROM
	      faddr => SELECT bb.level FROM
		lG, CPtr.curctxlvl => 
		  BEGIN
		  var ← [
		    wSize: offset.wSize, bSize: offset.bSize,
		    space: frame[
		      level: bb.level, wd: bb.wd + oo.wd, bd: oo.bd]];
		  GO TO freer;
		  END;
		ENDCASE;
	      frame => IF ~allowFields AND base.bSize = 0
		AND base.wSize IN [1..2] AND offset.bSize = 0
		AND offset.wSize = 1 AND oo.level = lZ THEN
		  BEGIN
		  var ← [
		    wSize: offset.wSize, space: frameup[
		      level: bb.level, wd: bb.wd, pwSize: base.wSize,
		      delta: oo.wd, immutable: bb.immutable]];
		  GO TO freer;
		  END;
	      link => IF ~allowFields AND offset.bSize = 0
		AND offset.wSize IN [1..2] AND oo.level = lZ THEN
		  BEGIN
		  var ← [
		    wSize: offset.wSize, space: linkup[
		      wd: bb.wd, delta: oo.wd]];
		  GO TO freer;
		  END;
	      ENDCASE;
	  ENDCASE => ERROR;
	wS ← Words[offset.wSize, offset.bSize];
	END;
      bdo => wS ← Words[offset.wSize, offset.bSize];
      ind => wS ← Words[offset.wSize, offset.bSize];
      ENDCASE;
    IF wS > 2 THEN var ← P5L.CopyToTemp[r].var
    ELSE BEGIN P5L.LoadVar[r]; var ← P5L.TOSComponent[wS]; END;
    EXITS
      freer => P5L.ReleaseVarItem[r];
    END;
    
  VarVarAssign: PUBLIC PROCEDURE [to, from: VarIndex, isexp: BOOLEAN]
      RETURNS [l: Lexeme] =
    BEGIN
    bSize, tbd: [0..wordlength);
    wSize, wS: CARDINAL;
    trashOnStack: CARDINAL ← 0;
    
    l ← NullLex;
    [bd: tbd, bSize: bSize, wSize: wSize] ←
      P5L.VarAlignment[to, store];
    wS ← Words[wSize, bSize];
    WITH cc: cb[from] SELECT FROM
      o => WITH vv: cc.var SELECT FROM
	stack => IF vv.wd # 0 THEN
	  IF isexp THEN
	    BEGIN
	    P5L.LoadVar[from]; -- causing from to be freed
	    from ← P5L.OVarItem[P5L.TOSComponent[wS]];
	    END
	  ELSE
	    BEGIN
	    tvar: VarComponent;
	    trashOnStack ← vv.wd;
	    vv.wd ← 0;
	    tvar ← cc.var; -- to avoid passing address of chunk
	    P5L.ModComponent[var: @tvar, wd: trashOnStack];
	    cc.var ← tvar;
	    END;
	ENDCASE;
      ENDCASE;
    IF wS <= 2 THEN
      BEGIN -- it's 2  words at most
      alsoLink: BOOLEAN ← FALSE;
      tOffset: TempAddr;
      tLevel: Symbols.ContextLevel ← Symbols.lZ;
      IF ~P5L.AllLoaded[from] THEN  -- anything above it is part of "to"
	BEGIN
	P5L.LoadVar[from];
	from ← P5L.OVarItem[P5L.TOSComponent[wS]];
	END;
      IF isexp THEN
	BEGIN -- locate a backup site for stack model after PUSH
	-- "from" is a stack o varitem, see if it is alsotemp
	-- otherwise, see if "to" is a friendly frame loc (doesn't
	-- have to be immutable in this case
	WITH cb[from] SELECT FROM
	  o => WITH vv: var SELECT FROM
	    stack => 
	      BEGIN
	      vsti: StackIndex = vv.sti;
	      WITH sv: cb[vsti] SELECT FROM
		onStack =>
		  BEGIN
		  IF sv.alsoLink OR sv.tLevel # lZ THEN
		    BEGIN
		    nsti: StackIndex;
		    alsoLink ← sv.alsoLink;
		    tOffset ← sv.tOffset;
		    tLevel ← sv.tLevel;
		    IF wS = 1 THEN GO TO foundOne;
		    IF tLevel # lZ THEN
		      BEGIN
		      nsti ← Stack.Above[vsti];
		      WITH sv2: cb[nsti] SELECT FROM
		        onStack =>
		          IF sv2.tLevel = tLevel AND 
			  sv2.tOffset = tOffset+1 THEN
			    GO TO foundOne;
		        ENDCASE;
		      END;
		    alsoLink ← FALSE; tLevel ← lZ;
		    END;
		  END;
		ENDCASE;
	      END;
	    ENDCASE;
	  ENDCASE;
	WITH cb[to] SELECT FROM
	  o => WITH vv: var SELECT FROM
	    link =>
	      BEGIN alsoLink ← TRUE; tLevel ← vv.wd; END;
	    frame => IF vv.bSize = 0 AND vv.bd = 0 THEN
	      BEGIN
	      level: Symbols.ContextLevel = vv.level;
	      SELECT level FROM
		lG, CPtr.curctxlvl =>
		  BEGIN tLevel ← level; tOffset ← vv.wd; END;
		ENDCASE;
	      END;
	    ENDCASE;
	  ENDCASE;
	EXITS
	  foundOne => NULL;
	END;
      P5L.ReleaseVarItem[from];
      P5L.StoreVar[to];
      IF isexp THEN
	BEGIN
	THROUGH [0..wS) DO P5U.Out0[qPUSH]; ENDLOOP;
	IF (alsoLink OR tLevel # lZ) AND CPtr.stking THEN 
	  Stack.Also[n: wS, inLink: alsoLink, tOffset: tOffset, tLevel: tLevel];
	l ← P5L.TOSLex[wS];
	END;
      END
    ELSE IF P5L.AllLoaded[from] THEN
      BEGIN -- large thing, all on stack
      IF isexp THEN 
	BEGIN
	tr: VarIndex;
	[to, tr] ← P5L.ReusableCopies[to, store, FALSE];
	l ← [bdo[tr]];
	END;
      P5L.StoreVar[to];
      END
    ELSE IF bSize = 0 THEN
      BEGIN
      fromCode: BOOLEAN;
      longDest: BOOLEAN ← P5L.LongVarAddress[to];
      longSource: BOOLEAN ← P5L.LongVarAddress[from];
      sourceAddr, destAddr: VarComponent;
      expBase: {unknown, source, dest} ← unknown;
      BltOp: ARRAY BOOLEAN OF ARRAY BOOLEAN OF BYTE =
	[[qBLT, qBLTL], [qBLTC, qNOOP]]; -- peephole will choke on BLTCL
				-- but it shouldn't be generated, anyway
    
      WITH ff: cb[from] SELECT FROM
	o => fromCode ← ff.var.tag = code;
	bo => fromCode ← ff.offset.tag = code;
	bdo => fromCode ← ff.offset.tag = code;
	ind => fromCode ← ff.offset.tag = code;
	ENDCASE => ERROR;
      IF fromCode AND longDest THEN
	BEGIN
	tvar: VarComponent = P5L.CopyToTemp[from].var;
	[] ← VarVarAssign[to: to, from: P5L.OVarItem[tvar], isexp: FALSE];
	IF isexp THEN l ← [bdo[P5L.OVarItem[tvar]]];
	RETURN -- trashOnStack = 0
	END;
      IF ~CPtr.dStar OR longSource OR longDest THEN
	Stack.Require[P5L.VarStackWords[from]+ P5L.VarStackWords[to]];
      IF  isexp AND longDest = longSource AND P5L.VarAddressEasy[from]
	AND ~P5L.VarAddressEasy[to] THEN expBase ← source;
      sourceAddr ← P5L.AddrForVar[r: from, codeOk: TRUE];
      IF longDest AND ~longSource THEN 
	BEGIN
	IF isexp THEN
	  BEGIN
	  sourceAddr ← P5L.EasilyLoadable[sourceAddr, store];
	  expBase ← source;
	  END;
	P5L.LoadComponent[sourceAddr];
	P5U.Out0[qLP];
	END
      ELSE P5L.LoadComponent[sourceAddr];
      P5U.Out1[qLI, wSize];
      destAddr ← P5L.AddrForVar[to];
      IF longSource AND ~longDest THEN
	BEGIN
	IF isexp THEN
	  BEGIN
	  destAddr ← P5L.EasilyLoadable[destAddr, store];
	  expBase ← dest;
	  END;
	P5L.LoadComponent[destAddr];
	P5U.Out0[qLP];
	longDest ← TRUE;
	END
      ELSE
	BEGIN
	IF isexp AND expBase = unknown THEN
	  BEGIN
	  destAddr ← P5L.EasilyLoadable[destAddr, store];
	  expBase ← dest;
	  END;
	P5L.LoadComponent[destAddr];
	END;
      P5U.Out0[BltOp[fromCode][longDest]];
      IF isexp THEN
	BEGIN
	tr: BoVarIndex = LOOPHOLE[P5L.GenVarItem[bo]];
	SELECT expBase FROM
	  source => NULL;
	  dest => sourceAddr ← destAddr;
	  ENDCASE => ERROR;
        cb[tr] ← [body: bo[base: sourceAddr, offset: NULL]];
	IF expBase = source AND fromCode THEN
	  cb[tr].offset ← [wSize: wSize, space: code[wd: 0]]
	ELSE
	  cb[tr].offset ← [wSize: wSize, space: frame[wd: 0]];
	l ← [bdo[tr]];
	END;
      END
    ELSE
      BEGIN
      -- vars are ragged on at most one end; i.e., if wSize # 0 and bSize # 0
      --   then bd = 0 or bd+bSize = wordlength, also bd # 0 implies bSize # 0
      fMain, fNub: VarIndex;
      tMain, tNub: VarIndex;
      
      [fMain, fNub] ← P5L.ReusableCopies[from, store];
      [tMain, tNub] ← P5L.ReusableCopies[to, store];
      IF isexp THEN
	l ← [bdo[P5L.CopyVarItem[tMain]]]; -- has been rendered reusable
      IF tbd = 0 THEN
	BEGIN
	P5L.FieldOfVar[r: fMain, wSize: wSize];
	P5L.FieldOfVar[r: fNub, wd: wSize, bSize: bSize];
	P5L.FieldOfVar[r: tMain, wSize: wSize];
	P5L.FieldOfVar[r: tNub, wd: wSize, bSize: bSize];
	[] ← VarVarAssign[to: tNub, from: fNub, isexp: FALSE];
	[] ← VarVarAssign[to: tMain, from: fMain, isexp: FALSE];
	END
      ELSE
	BEGIN
	IF tbd + bSize # wordlength THEN ERROR;
	P5L.FieldOfVar[r: fMain, wSize: wSize, bd: bSize];
	P5L.FieldOfVar[r: fNub, bSize: bSize];
	P5L.FieldOfVar[r: tMain, wSize: wSize, bd: bSize];
	P5L.FieldOfVar[r: tNub, bSize: bSize];
	[] ← VarVarAssign[to: tMain, from: fMain, isexp: FALSE];
	[] ← VarVarAssign[to: tNub, from: fNub, isexp: FALSE];
	END;
      END;
    THROUGH [0..trashOnStack) DO Stack.Pop[]; ENDLOOP;
    END;
    
  Words: PUBLIC PROCEDURE [w: CARDINAL, b: [0..wordlength)]
      RETURNS [CARDINAL] =
    BEGIN
    RETURN [w + CARDINAL[b+15]/16];
    END;
  END.