; GetFrame.mu
; Copyright Xerox Corporation 1979

;	Last modified October 16, 1977  5:41 PM

; 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


	MAR←AC3;		Fetch frame size
	L←335;			Temp StackMin constant (see below)
	L←MD-T, T←MD;		Test for minimum frame size
	MAR←NEWF, ALUCY;	Start fetch of StackMin
	L←AC2-T, T←AC2, :FsLs6;	[FsLs6, FsGe6] New frame base
FsLs6:	L←T←-7+T+1, :GetF1;	Assign minimum frame size of 6
FsGe6:	T←LREG;			Assign requested frame size (ge 6)
GetF1:	NEWF←L;
	L←MD-T;			Stack overflow if StackMin ge frame
	MAR←T, ALUCY;		Start store of frame return link
	L←5+T, :NStkOv;		[NStkOv, StkOv]
NStkOv:	NEWF5←L, TASK;		Save new frame +5 for later use
	MD←AC2;			Complete store of return link

	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, SWMODE;		AC2 ← new frame base

; Stack overflow -- exit without altering any state
; A memory reference is pending, but we just let it die
StkOv:	L←AC2, TASK;		Haven't TASKed for a long time
	NEWF←L, :ExitGF;

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

Return:	MAR←AC2;		Fetch return frame link
	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