; 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 !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