; GetFrame.mu ; 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 !1,2,FsLs6,FsGe6; !1,2,NStkOv,StkOv; !1,2,Ge3Arg,Ls3Arg; !1,1,Ls3Ar1; !1,2,Gr3Arg,Eq3Arg; !1,2,ArgLp,ExitGF; GetFrame: MAR←AC3; Fetch frame size L←335; Temp StackMin constant (see below) NEWF←L; T←6; 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 L←2+T; MD←AC0; MAR←T←AC2+1; Fetch saved pc from caller's frame PC←L; L←2+T; Compute and save old frame base +3 T←MD; PC of caller MAR←T, T←3; Fetch number of args OLDF3←L; 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 NOP; L←MD; MAR←NEWF5+1; Store in word 6 of new frame TASK; 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 L←MD+T; 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 AC2←L, :START; ; 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 NOP; L←MD; 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