:TITLE[Return.0mc, August 12, 1982  3:10 PM, van Melle];

* Things to do when returning from one frame to its caller:
* 	Free my FX
* 	Free my BF, if I am its sole user
*
* Ordinarily, the caller, my BF and my FX are contiguous and all usecounts
* are zero.  In this case, all I have to do is turn my BF/FX into a big
* free block and set up to resume execution in the caller.
* It is this case that we optimize for.
* If things are at all out of the ordinary, my slow return flag is on.

@Return:
	T ← (lspEp) - (11c), opcode[020];
	PFetch1[lspStkBr, lspEp];		* Get my alink, slow flg
	lspL4 ← T, loadpage[pgReturn];		* Save FX+1 for hard return
				* (No bypass effect because lspStkBr=0)
	T ← Stack&-1;				* Save return value in L0,1
onpage[pgReturn];
	lspL1 ← T;	* memory is busy fetching lspEp, so this is free...
	T ← Stack&-1;
	lspL0 ← T;

	lspEp ← (lspEp) - (14c), goto[SlowReturn, R Odd];
					* Ep -> ALink-2 (quad aligned)
	lspDefx0 ← 0c, goto[RetnTail];	* no flags for fast return


RV2[Blink, Clink, IP[lspDefx0]];

SlowReturn:	* Come here with slow bit on
	* lspL4 = returner's FX+1
	* lspEp = my Alink field - 14 (= returnee's FX-2 + 1 for slow bit)
	* L0, 1 have return value to save!
*
* Things to worry about: my alink and clink must be the same
* (if not, punt to software to patch things up).
* Returnee's usecount must be zero (we can never run in a frame
* with nonzero usecount; punt will copy frame).
* returnee must be followed by a free block (possibly created by the return,
* when my BF immediately follows returnee).

	T ← (lspL4) + (7c);
	PFetch2[lspStkBr, Blink], call[retLBL];	* Get returner's b/clinks
	T ← (lspEp) + 1, loadpage[pgReturnA];	* returnee's FX (my adjusted alink)
	lspLN ← T;				* save it for lspRtn2
onpage[pgReturnA];
	PFetch1[lspStkBr, lspL2], call[retLBL];	* L2 ← returnee's FX flags
	CLink ← (CLink) - (12c);		* My clink, adjusted to FX
	T ← (CLink) xor T;			* is it same as my alink?
	lu ← (rhmask[lspL2]) or T;		* & returnee's usecount zero?
	T ← BLink, skip[alu=0];
	   goto[RetUfn];   * Alink # Clink, or returnee's FX usecount not zero
	PFetch2[lspStkBr, lspL2];	* Fetch returner's BF base words:
					* L2 = flags,,usecount, L3 = ivar
	T ← (lspLN) + (4c), call[retLBL];
	PFetch1[lspStkBr, RTemp], call[retLBL];	* Get returnee's Next
	T ← RTemp;
	lu ← (lspL3) xor T;		* is my BF start = Returnee's Next?
	lu ← rhmask[lspL2], skip[alu#0];
	  dblgoto[RetUfn, DoReturn, alu#0];
			* if usecount zero, is fine, since it will then
			* become a free block; if nonzero, is hard
	PFetch1[lspStkBr, RTemp];	* fetch first word beyond returnee
	lu ← (RTemp) xor (FreeStackBlock);	* is it a free block?
	dblgoto[RetUfn, DoReturn, alu#0];	* if so, fine, else hard

RetUfn:	* Punt
	T ← (lspL4) + (11c);		* Restore my Ep
	lspEp ← T, loadpage[pgUfnx];
	lspUFN ← 20c, gotop[lspUfnxP4];

DoReturn:
	* At this point, Blink points at my BF, LspL2 are its flags,,count,
	* L3 is my Ivar.  L4 is still my FX+1

	T ← (lspL4) - (3c);
	PFetch2[lspStkBr, xBuf];	* Fetch the two words before my FX
					* Either my BF or a residual BF
	lu ← rhmask[lspL2];		* Test BF usecount
	T ← (lspL3) - (2c), goto[BFinUse, alu#0];
	T ← (BLink) - T;		* Usecount is zero, here is BF length
	RTemp1 ← T;
	RTemp ← (freeStackBlock);	* Construct free block of right length
	T ← lspL3;
	PStore2[lspStkBr, RTemp], goto[freeFX];	* Store it in front of BF

BFinUse:			* If BF usecount > 0, just decrement it
	lspL2 ← (lspL2) - 1;
	T ← BLink;
	PStore1[lspStkBr, lspL2], goto[freeFX];

freeFX:	* Time to turn my FX, and possibly my dummy BF, into a free block
	T ← lspEsp;			* end of stack
	xBuf3 ← T;
	T ← xBuf ← ldf[xBuf, 6, 1];	* Fetch residual bit
	T ← xBuf ← (xBuf) + T;		* Times 2 => 0 or 2
	T ← (lspL4) - T - 1;		* Point at my FX or FX-2
	xBuf2 ← (freeStackBlock);
	xBuf3 ← (xBuf3) - T, loadpage[pgReturn];	* length of FX or FX+dummyBF
	PStore2[lspStkBr, xBuf2], gotop[lspRtn2];
					* make a free block of out of someone


**** General Return to frame.  LspLN = FX, L0,1 = value

	onpage[pgReturn];

lspRtn2:
	T ← (lspLN) - (2c);		* Get FX-2 (= BF)
	lspEp ← T, call[retLBL];	* Wait for Ep writeback
	PFetch1[lspEp, lspEsp, 6], call[retLBL];   * fetch Next of returnee
	PFetch1[lspEp, lspDefx0, 2];	* Get FX flags,,usecount for later

	T ← lspEsp, call[.+1];
ExtendStk:
	PFetch2[lspStkBr, lspL2];	* get next stack block
	lu ← (lspL2) xor (freeStackBlock);
	T ← lspL3, skip[alu#0];		* if it's free, gobble it
	  T ← lspEsp ← (lspEsp) + T, return; * to ExtendStk

*	goto[RetnTail];

**** Common Return tail.
**** Come here with lspEp pointing at returnee's FX-2 = BF
**** lspDefx0 has FX flags, if it is possible that InCall or NoPush is on

onpage[pgReturn];

RetnTail:
	PFetch4[lspEp, lspIfuBr, 4];	* Get IfuBr, Next, PC of returnee
					* MC1 busy for 4 cycles, MC2 4 more
	StkState ← 377c;
	StkState ← (StkState) or (140000c);	* StkState ← rcy[1777,2]

	PFetch1[lspEp, lspIbasex, 1];	* Get IVAR pointer
	lspEp ← (lspEp) + (14c);	* Returnee's EP

	T ← (lspNext) - (2c);		* TOS
	lspTsp ← T;
	T ← rhmask[lspIfuBrHi], task;
	T ← lspIfuBrHi ← (lsh[lspIfuBrHi, 10]) or T;	* maybe redundant
	PCBhi ← T, loadpage[pgJump];
	T ← rsh[lspPC, 1];
onpage[pgJump];
	PFetch4[lspIfuBr, IBUF];	* Must be on page pgJump

	PCB ← T;	* Bypass kludge: PCB← addr of what we just fetched
	PCB ← (PCB) and not (3c);	* Remove low-order bits of PCB
	PCF ← lspPC;
	* since IfuBr is quad-aligned, PC's low bits are correct for PCF

:IF[StatsMode];
	lspStatsPtr, goto[ReturnStat, R>=0];
:ENDIF;
ReturnStatDone:

		* Tsp now points at top element of stack.  We want
		* it to be quadodd; if it is, we will start out with
		* 2 elts on stack (StkState = 3776), else one (1777)

	lu ← (lspTsp) and (2c);
	T ← IP[HStack0]c, goto[rsodd2, alu#0];
	lspTsp ← (lspTsp) + (2c);	* Do a push
	T ← (ldf[lspTsp, 14, 4]) + T + 1;
	lspL4 ← T, loadpage[pgHstack];
	Stkp ← lspL4, callp[HStkUndflw];  * Get 2 elements, 1 of them garbage
	Stack&-2, goto[rsodd3];		* compensate for the LspTsp+2
					* StkState = 1777 = 1 elt

rsodd2:					* tsp is odd, as we like it
	T ← (ldf[lspTsp, 14, 4]) + T + 1;
	lspL4 ← T, loadpage[pgHstack];
	Stkp ← lspL4, callp[HStkUndflw];	* Get 2 elements
	StkState ← lsh[StkState, 1], goto[rsodd3];
					* StkState = 3776 = 2 elts

rsodd3:					* stack fine, now check cases

	lu ← (lspDefx0) and (OR[FxInCall!, FxNoPushReturn!]c);
	T ← lspL0, goto[RetFunny, alu#0];
	Stack&+1 ← T;			* Push result (L0,1) and return
	T ← lspL1;
	Stack&+1 ← T;
	StkState ← lcy[StkState, 1], goto[IFE[StatsMode, 1, CheckStatOvfl, nxiLBL]];

RetFunny:
	lu ← (lspDefx0) and (FxInCall);
	T ← (lspEp) - (12c), goto[RetCall, alu#0];

					* no push case
	lspDefx0 ← (lspDefx0) and not (FxNoPushReturn);
					* turn off bit, store back

	PStore1[lspStkBr, lspDefx0], goto[IFE[StatsMode, 1, CheckStatOvfl, nxiLBL]];
					* Point back at Pvars

RetCall:				* Call in progress
	lspDefx0 ← (lspDefx0) and not (FxInCall);
	loadpage[pgCallOps];
	PStore1[lspStkBr, lspDefx0], gotop[ApplyFn];	* store updated flags


	:END[Return];