; IfsGetFrame.mu
; Copyright Xerox Corporation 1979, 1980

; Last modified by Taft, March 18, 1980  10:01 PM
; Last modified by Butterfield, February 20, 1980  11:16 AM
; - rewrote to get and setup stackBottom and unconditionally setup XJMP, and
;   added an entry GetF1 for extra frame below stackBottom ~10/30 - 2/20/80
; - return through BcplUtility - 8/21/79

; Derived from GetFrame.mu:
; Last modified October 16, 1977  5:41 PM

; [ ** These comments have not been updated to reflect the IFS XM changes ** ]
; Alto BCPL stack frame allocator.
; This is an exact emulation of the one in the Alto operating
; system, even to the extent of always storing the first two
; arguments whether or not they were supplied by the caller
; (at least one existing program, VMEM, is known to depend
; on this "feature").  The only exception is that the Alto
; OS frame allocator always allocates two extra (wasted) words,
; while this one simply enforces a minimum frame size of 6.

; This microcode expects to be called in response to some
; emulator instruction that traps into the RAM (however, the
; code for doing the trap dispatch is not included in this module
; but must be placed elsewhere).  One should then replace the
; standard software GetFrame procedure (pointed to by #370)
; by a new procedure with the trap opcode as its first instruction.

; Consider the standard BCPL procedure entry sequence:
;	STA 3 1 2	; (E0)
;	JSR @370	; (E1)
;	 frame size	; (E2)
;	 JSR @367	; (E3) extra args routine
;	first instruction of procedure  ; (E4)

; @370:	GetFrameTrap	; (G0) the opcode that traps into the RAM
;	stack overflow handler  ; (G1)

; That is, the microcode expects the instructions STA 3 1 2
; and JSR @370 already to have been executed and AC3 to be
; pointing to the frame size (E2).  In the normal case
; (no stack overflow), the microcode allocates the frame,
; stores all the arguments (including extra arguments, if any),
; loads the number of arguments into AC0, and returns control
; to the emulator with the PC set to the first instruction of
; the procedure proper (E4).

; If a stack overflow occurs, control returns to (G1), the
; instruction after the GetFrameTrap instruction, having not
; modified the emulator state in any way.  Hence the code
; starting at (G1) is expected to deal with the stack overflow
; in whatever way is appropriate (possibly simply by jumping
; to the standard system frame allocator).
; R-registers shared with the emulator
$NEWF	$R5;	New frame base
$NEWF5	$R7;	New frame base +5
$OLDF3	$R10;	Old frame base +3


	L← MAR← AC2 -1;		Set stack bottom
	NEWF5← L, IR← 0, TASK;	(store into NEWF5 to zero IR for Start1)

	MAR← AC3;		Start fetch of frame size
	L← 0;			0 extra words (beyond stackBottom)
GetF1:	NEWF5← L;		Use NEWF5 to save number of extra words needed
	T← 6;			Minimum frame size is 6
	L← MD -T, T← MD;	Test for minimum frame size
	MAR← AC2 -1, ALUCY;	Start fetch of stack bottom
	:FsLs6;			[FsLs6, FsGe6]
FsLs6:	T← 6;			Assign minimum frame size of 6
FsGe6:	L← MD -T;
	MAR← 335;		Start fetch of StackMin
	NEWF← L;		New frame base
	T← -4 +T+1;		Must have room for stack bottom plus 3 extra
	L← MD -T-1;		Stack overflow if StackMin ge frame-4
	MAR← NEWF, ALUCY;	Start store of frame return link
	TASK, :NStkOv;		[NStkOv, StkOv]
NStkOv:	MD← AC2;		Complete store of return link

	MAR← T← NEWF-1;		Start store of stack bottom
	L← NEWF5 +T;
	MAR← 177740;		Start fetch of bank register
	T← sr10;		sr10 = 64024
	T← 27 +T+1;		64054
	L← MD +T;		64034 + bank bits (top 12 bank bits are ones)
	MAR← -3 +T;		Start store of XJMP,AC2
	T← 4 +T+1;		new frame + 5
	NEWF5← L;		Save new frame + 5 for later use

	MAR←NEWF5-1;		Store arg 1 in word 4 of new frame
	T←AC3;			Set new pc beyond entry sequence
	MAR←T←AC2+1;		Fetch saved pc from caller's frame
	L←2+T;			Compute and save old frame base +3
	T←MD;			PC of caller
	MAR←T, T←3;		Fetch number of args
	L←MD-T, T←MD;		Test number of args
	MAR←NEWF5, SH<0;	Start store of second arg in word 5
	AC3←L, L←T, SH=0, :Ge3Arg; [Ge3Arg, Ls3Arg] AC3 ← # args -3

; Less than 3 args.  Branch and memory reference pending
Ls3Arg:	AC0←L, TASK, :Ls3Ar1;	[Ls3Ar1, Ls3Ar1] Store # of args
Ls3Ar1:	MD←AC1, :ExitGF;	Finish store of second arg
; GetFrame (cont'd)

; 3 args or more.  Branch and memory reference pending
Ge3Arg:	AC0←L, TASK, :Gr3Arg;	[Gr3Arg, Eq3Arg] Store # of args

; Exactly 3 args.  Task and memory reference pending.
Eq3Arg:	MD←AC1;			Finish store of second arg

	MAR←OLDF3;		Fetch word 3 of caller's frame
	MAR←NEWF5+1;		Store in word 6 of new frame
	MD←LREG, :ExitGF;

; Greater than 3 args. Task and memory reference pending
Gr3Arg:	MD←AC1;			Finish store of second arg

	MAR←OLDF3;		Start fetch of extra args offset
	T←AC0-1;		Number of args -1
	T←AC2+T+1;		Compute address of last arg
	MAR←LREG, :ArgLp1;	Start fetch of last arg

; for i = nArgs-3 to 0 by -1 do (newF+6)!i = (oldF+offset+3)!i
; (i is kept in AC3)
ArgLp:	MAR←L←AC2-1;		Start fetch from extra args vector
ArgLp1:	AC2←L;			Update extra arg pointer
	T←AC3;			Get arg index
	T←NEWF5+T+1;		Where to store arg in new frame
	L←MD;			Finish fetching the arg
	MAR←T, T←LREG;		Start store in new frame
	L←AC3-1;		Decrement and test arg index
	AC3←L, L←T, SH<0, TASK;
	MD←LREG, :ArgLp;	[ArgLp, ExitGF] Store the arg

; Exit GetFrame
ExitGF:	L←NEWF, TASK;		AC2 ← new frame base
	AC2←L, :Start1;;	Go to START or StartX

; Stack overflow -- exit to @376 without altering any state
; A memory reference is pending, but we just let it die
StkOv:	NOP;			Here after TASK
	L← ONE, :XEmulatorTrap;	Trap to @376

; Return
; Performs:
;	AC2 ← AC2!0
;	PC ← AC2!1

Return:	MAR←AC2;		Fetch return frame link
	IR← 0;			Zero IR to force Start1 to switch to ROM
	MAR←LREG+1;		Fetch return pc from old frame
	NEWF←L;			Pointer to caller's frame
	L←MD+1, TASK;
	PC←L, :ExitGF;		AC2←NEWF and go to START in ROM