; 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