-- VarBasics.mesa
-- last edited by Sweet,  5-Aug-82 14:10:10
-- last edited by Satterthwaite, January 11, 1983 5:14 pm

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [CodeNotImplemented, curctxlvl],
  CodeDefs: TYPE USING [
    Base, BoVarIndex, BYTE, codeType, DataStackIndex, Lexeme, NullLex,
    StackBackup, StackLocRec, VarComponent, VarIndex, VarNull, wordlength],
  FOpCodes: TYPE USING [
    qACD, qADC, qADD, qAMUL, qAND, qBLT, qBLTC, qBLTCL, qBLTL,
    qDADD, qDIS, qLI, qLL, qLLK, qLP, qMUL, qR, qRC, qRCFS, qREC, qSHIFTSB],
  Inline: TYPE USING [LongNumber],
  LiteralOps: TYPE USING [FindDescriptor],
  Literals: TYPE USING [Base, LTIndex, ltType],
  P5: TYPE USING [MoveToCodeWord, WriteCodeWord],
  P5L: TYPE USING [
    AllLoaded, CopyToTemp, CopyVarItem, EasilyLoadable,
    FieldOfVar, GenVarItem, LoadComponent, LoadVar, ModComponent,
    LongVarAddress, OVarItem, ReleaseVarItem, ReusableCopies,
    StoreVar, TOSComponent, TOSLex, VarAddressEasy, VarAlignment],
  P5U: TYPE USING [Out0, Out1, RecordConstant],
  PrincOps: TYPE USING [FieldDescriptor, framelink, localbase],
  Stack: TYPE USING [
    Above, Also, DataIndex, Forget, Load, Loc, Pop, TempStore, Top],
  Symbols: TYPE USING [Base, ContextLevel, ctxType, lG, lZ, seType];

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

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

  VarBasicsNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];
    ctxb ← base[Symbols.ctxType];
    cb ← base[codeType];
    ltb ← base[Literals.ltType];
    END;
    
  AddrComponent: PUBLIC PROC [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];
	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];
	GenAdd[vv.delta];
	RETURN [P5L.TOSComponent[1]]
	END;
      stack =>
	IF vv.wd = 0 AND vv.bd = 0 THEN
	  BEGIN
	  loc: StackLocRec = Stack.Loc[vv.sti, var.wSize];
	  WITH ll: loc SELECT FROM
	    contig => WITH bb: ll.place SELECT FROM
	      frame => 
	        BEGIN
	        Stack.Forget[vv.sti, var.wSize];
	        RETURN [[wSize: 1, space: faddr[level: bb.tLevel, wd: bb.tOffset]]]
	        END;
	      ENDCASE;
	    ENDCASE;
	  END;
      caddr => ERROR; -- nobody should be taking the address of a code address
      ENDCASE; -- faddr, link, const, pdesc
    -- put it in a temp, generate addr of temp
    RETURN [AddrComponent[P5L.CopyToTemp[P5L.OVarItem[var]].var]]
    END;
    
  AddrForVar: PUBLIC PROC [r: VarIndex, codeOk: BOOL←FALSE]
      RETURNS [avar: VarComponent] =
    BEGIN
    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 WITH vv: cc.base SELECT FROM
	      faddr => {vv.wd ← vv.wd + oo.wd; avar ← vv};
	      ENDCASE => 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: BOOL = LoadAddress[r, codeOk];
	avar ← P5L.TOSComponent[IF long THEN 2 ELSE 1];
	END;
    END;
    
  BaseComponent: PUBLIC PROC [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: PrincOps.framelink,
	level: CPtr.curctxlvl]]];
    P5U.Out1[qLL, PrincOps.framelink];
    THROUGH (lvl..CPtr.curctxlvl) DO
      P5U.Out1[qR, PrincOps.framelink - PrincOps.localbase];
      ENDLOOP;
    RETURN [P5L.TOSComponent[1]]
    END;
    
  GenAdd: PUBLIC PROC [delta: UNSPECIFIED, long: BOOL←FALSE] =
    BEGIN
    n: [1..2] = IF long THEN 2 ELSE 1;
    Stack.Load[Stack.Top[n],n];
    P5U.Out1[qLI, LOOPHOLE[delta, CARDINAL]];
    P5U.Out0[IF long THEN qADC ELSE qADD];
    END;
    
  GenAnd: PUBLIC PROC [delta: UNSPECIFIED] =
    BEGIN
    P5U.Out1[qLI, LOOPHOLE[delta, CARDINAL]];
    P5U.Out0[qAND];
    END;
    
  GenRFC: PUBLIC PROC [wd: CARDINAL, bd: [0..16), len: [1..16]] =
    BEGIN
    IF bd = 0 AND len = 16 THEN {
      IF wd > BYTE.LAST THEN {GenAdd[wd-BYTE.LAST]; wd ← BYTE.LAST};
      P5U.Out1[qRC, wd]}
    ELSE {
      IF wd > BYTE.LAST THEN {GenAdd[wd-BYTE.LAST]; wd ← BYTE.LAST};
      P5U.Out1[qLI, 
        LOOPHOLE[PrincOps.FieldDescriptor[offset: wd, posn: bd, size: len]]];
      P5U.Out0[qRCFS]};
    END;
    
  GenShift: PUBLIC PROC [delta: UNSPECIFIED] =
    BEGIN
    IF INTEGER[delta] IN [-15..15] THEN 
      P5U.Out1[qSHIFTSB, delta]
    ELSE {
      P5U.Out0[qDIS];
      P5U.Out1[qLI, 0]};
    END;
    
  LoadAddress: PUBLIC PROC [r: VarIndex, codeOk: BOOL←FALSE] RETURNS [long: BOOL] =
    BEGIN
    bor: BoVarIndex;
    base, offset: VarComponent;
    owd: CARDINAL;
    WITH cc: cb[r] SELECT FROM
      o =>
	BEGIN
	avar: VarComponent ← AddrComponent[cc.var];
	P5L.ReleaseVarItem[r];
	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 ← Words[base.wSize, base.bSize] > 1;
    WITH oo: offset SELECT FROM
      frame =>
	BEGIN
	IF oo.level # lZ THEN ERROR CPtr.CodeNotImplemented;
	owd ← oo.wd;
	END;
      code =>
	BEGIN
	IF ~codeOk OR long THEN ERROR;
	owd ← oo.wd;
	END;
      ENDCASE => ERROR;
    WITH vv: base SELECT FROM
      faddr => IF vv.wd + owd IN BYTE THEN
	BEGIN
	vv.wd ← vv.wd + owd;
	owd ← 0;
	END;
      ENDCASE;
    P5L.LoadComponent[base];
    IF owd # 0 THEN GenAdd[owd, long];
    END;
    
  LoadBoth: PUBLIC PROC [atC1, atC2: POINTER TO VarComponent, abelian: BOOL] =
    BEGIN
    [] ← LoadBothInternal[atC1, atC2, abelian];
    END;
    
  LoadBothInternal: PRIVATE PROC [atC1, atC2: POINTER TO VarComponent, abelian: BOOL] 
     RETURNS [s1Size, s2Size: CARDINAL] =
    BEGIN
    c1: VarComponent ← atC1↑;
    c2: VarComponent ← atC2↑;
    c1Loaded, c2Loaded: BOOL ← 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 => {c1Depth ← depth; c1Loaded ← TRUE};
	  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 => {c2Depth ← depth; c2Loaded ← TRUE};
	  ENDCASE;
	END;
      ENDCASE;
    BEGIN -- to set up loadBoth label
    IF ~(c1Loaded OR c2Loaded) THEN GO TO loadBoth;
    IF c1Loaded AND c2Loaded THEN 
      SELECT TRUE FROM
        (c1Depth = c2Size AND c2Depth = 0) => RETURN [c1Size, c2Size];
        (abelian AND c2Depth = c1Size AND c1Depth = 0) => RETURN [c2Size, c1Size];
        ENDCASE => GO TO loadBoth; -- considered unlikely
    IF c1Loaded THEN
      BEGIN
      IF c1Depth # 0 THEN P5L.LoadComponent[c1];
      P5L.LoadComponent[c2];
      RETURN [c1Size, c2Size];
      END
    ELSE -- c2Loaded
      BEGIN
      IF c2Depth # 0 THEN {P5L.LoadComponent[c2]; c2 ← P5L.TOSComponent[c2Size]};
      IF ~abelian AND (c1Size>1 OR c2Size>1) THEN c2 ← Stack.TempStore[c2Size];
      P5L.LoadComponent[c1];
      IF ~abelian THEN P5L.LoadComponent[c2];
      IF abelian THEN RETURN[c2Size, c1Size] ELSE RETURN [c1Size, c2Size];
      END;
    EXITS
      loadBoth => {P5L.LoadComponent[c1]; P5L.LoadComponent[c2]; RETURN [c1Size, c2Size]};
    END;
    END;
    
  LoadSum: PUBLIC PROC [atB, atD: POINTER TO VarComponent] RETURNS [bpSize: [1..2]] =
    BEGIN -- atB and atD are of size IN [1..2]
    s1Size, s2Size: [1..2];
    AddOp: ARRAY [1..2] OF ARRAY [1..2] OF BYTE = [
      [FOpCodes.qADD, FOpCodes.qACD], [FOpCodes.qADC, FOpCodes.qDADD]];
      
    [s1Size, s2Size] ← LoadBothInternal[atB, atD, TRUE];
    P5U.Out0[AddOp[s1Size][s2Size]];
    RETURN [MAX[s1Size, s2Size]];
    END;
    
  MakeBo: PUBLIC PROC [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;
	vbSize: [0..16) = var.bSize;
	vwSize: CARDINAL = var.wSize;
	wS: CARDINAL = Words[vwSize, vbSize];
	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: vwSize, space: frame[wd: vv.delta]];
	    RETURN
	    END;
	  linkup =>
	    BEGIN
	    cb[bor].base ← [wSize: 1, space: link[wd: vv.wd]];
	    cb[bor].offset ← [wSize: vwSize, 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]];
	    var.wSize ← wS; var.bSize ← 0; vv.bd ← 0};
	  const =>
	    BEGIN
	    IF wS = 1 THEN { 
	      cb[bor].offset ← -- can't index packed in code anyway
	        [wSize: NULL, bSize: NULL, space: frame[bd: vv.bd]];
	      var.wSize ← wS; var.bSize ← 0; vv.bd ← 0}
	    ELSE
	      BEGIN -- wS = 2
	      const: ARRAY [0..1] OF CARDINAL ← [vv.d1, vv.d2];
	      lti: Literals.LTIndex = LiteralOps.FindDescriptor[DESCRIPTOR[const]].lti;
	      cb[bor].offset ← [wSize: NULL, bSize: NULL, space: code[bd: vv.bd]];
	      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]];
		    P5U.RecordConstant[ll.codeIndex, 2];
		    END;
		  var ← [wSize: 2, space: code[wd: ll.codeIndex]];
		  END;
		ENDCASE => ERROR;
	      END;
	    END;
	  ENDCASE => ERROR;
	cb[bor].base ← AddrComponent[var];
	cb[bor].offset.wSize ← vwSize;
	cb[bor].offset.bSize ← vbSize;
	RETURN
	END;
      bdo => {disp ← cc.disp; base ← cc.base; offset ← cc.offset};
      ind =>
	BEGIN
	eWords: CARDINAL;
	base ← cc.base;
	disp ← cc.index;
	offset ← cc.offset;
	WITH pp: cc SELECT FROM
	  packed => {P5L.ReleaseVarItem[bor]; RETURN [LOOPHOLE[VarNull]]};
	  notPacked => eWords ← pp.eWords;
	  ENDCASE;
	IF eWords # 1 THEN
	  BEGIN
	  WITH vv: disp 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};
	      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[qREC];
	    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: Inline.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 PROC [r: VarIndex, allowFields: BOOL ← FALSE]
      RETURNS [var: VarComponent] =
    BEGIN
    wS: CARDINAL;
    WITH cc: cb[r] SELECT FROM
      o => {var ← cc.var; GO TO freer};
      bo =>
	BEGIN
	WITH oo: cc.offset SELECT FROM
	  code =>
	    WITH bb: cc.base SELECT FROM
	      caddr => 
		BEGIN
		var ← [wSize: cc.offset.wSize, bSize: cc.offset.bSize,
		  space: code[wd: bb.wd + oo.wd, bd: oo.bd]];
		GO TO freer;
		END;
	      ENDCASE;
	  frame => 
	    WITH bb: cc.base SELECT FROM
	      faddr => SELECT bb.level FROM
		lG, CPtr.curctxlvl => 
		  BEGIN
		  var ← [
		    wSize: cc.offset.wSize, bSize: cc.offset.bSize,
		    space: frame[level: bb.level, wd: bb.wd + oo.wd, bd: oo.bd]];
		  GO TO freer;
		  END;
		ENDCASE;
	      frame => IF ~allowFields AND cc.base.bSize = 0
		AND cc.base.wSize IN [1..2] AND cc.offset.bSize = 0
		AND cc.offset.wSize = 1 AND oo.level = lZ THEN
		  BEGIN
		  var ← [
		    wSize: cc.offset.wSize, space: frameup[
		      level: bb.level, wd: bb.wd, pwSize: cc.base.wSize,
		      delta: oo.wd, immutable: bb.immutable]];
		  GO TO freer;
		  END;
	      link => IF ~allowFields AND cc.offset.bSize = 0
		AND cc.offset.wSize IN [1..2] AND oo.level = lZ THEN
		  BEGIN
		  var ← [
		    wSize: cc.offset.wSize, space: linkup[wd: bb.wd, delta: oo.wd]];
		  GO TO freer;
		  END;
	      ENDCASE;
	  ENDCASE => ERROR;
	wS ← Words[cc.offset.wSize, cc.offset.bSize];
	END;
      bdo => wS ← Words[cc.offset.wSize, cc.offset.bSize];
      ind => wS ← Words[cc.offset.wSize, cc.offset.bSize];
      ENDCASE;
    IF wS > 2 THEN var ← P5L.CopyToTemp[r].var
    ELSE {P5L.LoadVar[r]; var ← P5L.TOSComponent[wS]};
    EXITS
      freer => P5L.ReleaseVarItem[r];
    END;
    
  VarVarAssign: PUBLIC PROC [to, from: VarIndex, isexp: BOOL] 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: BOOL ← FALSE;
      tLevel: Symbols.ContextLevel ← Symbols.lZ;
      backup1, backup2: StackBackup ← [none[]];
      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: DataStackIndex = Stack.DataIndex[vv.sti];
	      backup1 ← cb[vsti].backup;
	      IF wS = 2 THEN
	        BEGIN
		nsti: DataStackIndex = Stack.DataIndex[Stack.Above[vsti]];
		backup2 ← cb[nsti].backup;
		END;
	      END;
	    ENDCASE;
	  ENDCASE;
	IF backup1.where = none OR (wS = 2 AND backup2.where = none) THEN
	  WITH cb[to] SELECT FROM
	    o => WITH vv: var SELECT FROM
	      frame => IF vv.bSize = 0 AND vv.bd = 0 THEN
	        BEGIN
	        level: Symbols.ContextLevel = vv.level;
	        SELECT level FROM
		  lG, CPtr.curctxlvl => {
		    backup1 ← [frame[tLevel: level, tOffset: vv.wd]];
		    backup2 ← [frame[tLevel: level, tOffset: vv.wd+1]]};
		  ENDCASE;
	        END;
	      ENDCASE;
	    ENDCASE;
	END;
      P5L.ReleaseVarItem[from];
      P5L.StoreVar[to];
      IF isexp THEN
	BEGIN
	P5U.Out0[qREC]; IF backup1.where # none THEN Stack.Also[backup1];
	IF wS = 2 THEN {
	  P5U.Out0[qREC]; IF backup2.where # none THEN Stack.Also[backup2]};
	l ← P5L.TOSLex[wS];
	END;
      END
    ELSE IF P5L.AllLoaded[from] THEN
      BEGIN -- large thing, all on stack
      IF isexp THEN 
	BEGIN
	tr: VarIndex;
	[first: to, next: tr] ← P5L.ReusableCopies[to, store, FALSE];
	l ← [bdo[tr]];
	END;
      P5L.StoreVar[to];
      END
    ELSE IF bSize = 0 THEN
      BEGIN
      fromCode: BOOL;
      longDest: BOOL ← P5L.LongVarAddress[to];
      longSource: BOOL ← P5L.LongVarAddress[from];
      sourceAddr, destAddr: VarComponent;
      destInTemp: BOOL ← FALSE;
      expBase: {unknown, source, dest} ← unknown;
      BltOp: ARRAY BOOL OF ARRAY BOOL OF BYTE =
	[[qBLT, qBLTL], [qBLTC, qBLTCL]];
      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  isexp AND longDest = longSource AND P5L.VarAddressEasy[from]
	AND ~P5L.VarAddressEasy[to] THEN expBase ← source;
      sourceAddr ← 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];
	IF ~fromCode THEN P5U.Out0[qLP];
	END
      ELSE P5L.LoadComponent[sourceAddr];
      P5U.Out1[qLI, wSize];
      destAddr ← 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;
      [first: fMain, next: fNub] ← P5L.ReusableCopies[from, store, TRUE];
      [first: tMain, next: tNub] ← P5L.ReusableCopies[to, store, FALSE];
      IF tbd = 0 THEN
	BEGIN
	tr: VarIndex ← tMain;
	tMain ← tNub; tNub ← tr;
	tr ← fMain; fMain ← fNub; fNub ← fMain;
	END;
      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 PROC [w, b: CARDINAL] RETURNS [CARDINAL] =
    BEGIN
    RETURN [w + ((b+15)/16)];
    END;

  END.