-- VarBasics.mesa
-- last edited by Sweet,  3-Sep-81 12:24:46
-- last edited by Satterthwaite, August 13, 1982 1:17 pm

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [CodeNotImplemented, curctxlvl, stking],
  CodeDefs: TYPE USING [
    Base, BoVarIndex, Byte, codeType, Lexeme, NullLex, StackIndex,
    StackLocRec, TempAddr, VarComponent, VarIndex, VarNull],
  PrincOps: TYPE USING [FieldDescriptor, framelink, localbase],
  Environment: TYPE USING [bitsPerWord],
  FOpCodes: TYPE USING [
    qADD, qAMUL, qAND, qBLT, qBLTC, qBLTCL, qBLTL, qDADD,
    qLI, qLL, qLLK, qLP, qMUL, qPUSH, qR, qRFC, qSHIFT],
  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, LongVarAddress, ModComponent,
    OVarItem, ReleaseVarItem, ReusableCopies, StackSpareAddr, StoreVar,
    TOSComponent, TOSLex, VarAddressEasy, VarAlignment],
  P5U: TYPE USING [Out0, Out1, Out2, RecordConstant],
  Stack: TYPE USING [Above, Also, 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;

  wordlength: CARDINAL = Environment.bitsPerWord;

  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 THEN
	  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
    -- put it in a temp, generate addr of temp
    RETURN [AddrComponent[P5L.CopyToTemp[P5L.OVarItem[var]].var]]
    END;
    
  AddrForVar: PUBLIC PROC [r: VarIndex, codeOk: BOOLEAN ← 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: BOOLEAN = 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: BOOLEAN ← FALSE] =
    BEGIN
    n: [1..2] = IF long THEN 2 ELSE 1;
    Stack.Load[Stack.Top[n],n];
    P5U.Out1[qLI, LOOPHOLE[delta, CARDINAL]];
    IF long THEN P5U.Out1[qLI, 0];
    P5U.Out0[IF long THEN qDADD 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 wd > LAST[Byte] THEN {GenAdd[wd-LAST[Byte]]; wd ← LAST[Byte]};
    P5U.Out2[qRFC, wd,
      LOOPHOLE[PrincOps.FieldDescriptor[offset: 0, posn: bd, size: len]]];
    END;
    
  GenShift: PUBLIC PROC [delta: UNSPECIFIED] =
    BEGIN
    P5U.Out1[qLI, delta];
    P5U.Out0[qSHIFT];
    END;
    
  LoadAddress: PUBLIC PROC [r: VarIndex, codeOk: BOOLEAN ← FALSE]
      RETURNS [long: BOOLEAN] =
    BEGIN
    bor: BoVarIndex;
    base, offset: VarComponent;
    owd: CARDINAL;
    WITH cc: cb[r] SELECT FROM
      o =>
	BEGIN
	avar: VarComponent ← AddrComponent[cc.var];
	IF avar.tag = caddr AND ~codeOk THEN ERROR;
	P5L.LoadComponent[avar];
	P5L.ReleaseVarItem[r];
	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: 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 => {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
      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]; 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];
      END;
    EXITS
      loadBoth => {P5L.LoadComponent[c1]; P5L.LoadComponent[c2]};
    END;
    END;
    
  LoadSum: PUBLIC PROC [atB, atD: POINTER TO VarComponent] RETURNS [bpSize: [1..2]] =
    BEGIN
    base, disp: VarComponent;
    dpSize: [1..2];
    
    BDCommute: PROC =
      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];
    BEGIN -- to set up "different" exit label
    SELECT bpSize FROM
      > dpSize => GO TO different;
      < dpSize => {BDCommute[]; GO TO different};
      ENDCASE => LoadBoth[@base, @disp, TRUE];
    EXITS
      different =>
	BEGIN
	P5L.LoadComponent[disp];
	P5U.Out1[qLI, 0]; --lengthen disp
	disp ← P5L.TOSComponent[2];
	LoadBoth[@base, @disp, TRUE];
	END;
    END;
    P5U.Out0[IF bpSize = 1 THEN qADD ELSE qDADD];
    RETURN
    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;
	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 = 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]];
		    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 ← var.wSize;
	cb[bor].offset.bSize ← var.bSize;
	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[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: 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: BOOLEAN ← 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: 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 => {alsoLink ← TRUE; tLevel ← vv.wd};
	    frame => IF vv.bSize = 0 AND vv.bd = 0 THEN
	      BEGIN
	      level: Symbols.ContextLevel = vv.level;
	      SELECT level FROM
		lG, CPtr.curctxlvl => {tLevel ← level; tOffset ← vv.wd};
		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;
	[first: to, next: 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;
      destInTemp: BOOLEAN ← FALSE;
      expBase: {unknown, source, dest} ← unknown;
      BltOp: ARRAY BOOLEAN OF ARRAY BOOLEAN OF Byte =
	[[qBLT, qBLTL], [qBLTC, qBLTCL]]; -- 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  isexp AND longDest = longSource AND P5L.VarAddressEasy[from]
	AND ~P5L.VarAddressEasy[to] THEN expBase ← source;
      IF longDest AND ~P5L.StackSpareAddr[to] THEN
	BEGIN
	[] ← LoadAddress[to];
	destAddr ← Stack.TempStore[2];
        destInTemp ← TRUE;
	END;
      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];
	P5U.Out0[qLP];
	END
      ELSE P5L.LoadComponent[sourceAddr];
      P5U.Out1[qLI, wSize];
      IF ~destInTemp THEN 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.