; 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 !1,2,FsLs6,FsGe6; !1,2,NStkOv,StkOv; !1,2,Ge3Arg,Ls3Arg; !1,1,Ls3Ar1; !1,2,Gr3Arg,Eq3Arg; !1,2,ArgLp,ExitGF; GetFrame: L← MAR← AC2 -1; Set stack bottom NEWF5← L, IR← 0, TASK; (store into NEWF5 to zero IR for Start1) MD← LREG; 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← NEWF; 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; MD← LREG; 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) T← NEWF; MAR← -3 +T; Start store of XJMP,AC2 T← 4 +T+1; new frame + 5 MD← LREG, L← T, TASK; 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 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, 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 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 in ROM