; IfsBcplTricks.asm ; Copyright Xerox Corporation 1979, 1980 ; Last modified February 29, 1980 7:22 PM by Taft ; - use new XMLoad instruction, make it work on non-XM Altos ; Last modified by Butterfield, October 10, 1979 8:15 PM ; - FramesCaller, make it work for extended calls - 10/10 ; - swat extended calls to FrameSize, ReturnTo, and GotoLabel - 10/10 ; - RetryCall, make extended call retryable - 10/9/79 ; Derived from BcplTricks.asm: ; Last modified July 13, 1979 10:23 PM by Taft callersFrame = 0 savedPC = 1 temp = 2 extraArguments = 3 XARGS = -4 XJMP = -3 XPC = -2 .dusr XJMP0 = 64034 .dusr XMLoad = 63420 ; AC0 ← (@AC1 in bank AC0) .dusr XMStore = 63421 ; (@AC1 in bank AC0) ← AC3 .dusr swat = 77400 getframe = 370 return = 366 .ent FrameSize, MyFrame .ent CallersFrame, FramesCaller .ent CallFrame, GotoFrame .ent CoCall, CoReturn .ent ReturnTo, ReturnFrom .ent GotoLabel, RetryCall .srel FrameSize: .FrameSize MyFrame: .MyFrame CallersFrame: .CallersFrame FramesCaller: .FramesCaller CallFrame: .CallFrame GotoFrame: .GotoFrame CoCall: .CoCall CoReturn: .CoReturn ReturnTo: .ReturnTo ReturnFrom: .ReturnFrom GotoLabel: .GotoLabel RetryCall: .RetryCall .nrel ; FrameSize(proc) returns the frame size required by proc. .FrameSize: sta 3 savedPC,2 mov 0 3 lda 0 0,3 lda 1 cMinus4 and 1 0 lda 1 cXJMP0 sne 0 1 swat lda 0 2,3 jmp Done cMinus4: -4 cXJMP0: XJMP0 ; MyFrame() returns pointer to current frame. .MyFrame: mov 2 0 jmp 1,3 ; CallersFrame([f]) returns pointer to frame that calls f. ; Default f=current frame .CallersFrame: sta 3 savedPC,2 lda 1 0,3 ;get number of arguments snz 1 1 mov 2 0 ;use present frame mov 0 3 lda 0 callersFrame,3 jmp Done ; FramesCaller(foo) returns the address to which the caller of ; the frame foo sent control, provided that he made the call ; with a jsris or jsrii, and that there has been no monkey-business ; with the frame subsequently. ; If the call was not made with a jsris or jsrii it returns 0. ; If foo is omitted it defaults to the caller's frame. .FramesCaller: sta 3 savedPC,2 lda 1 0,3 snz 1 1 ; default the argument? mov 2 0 mov 0 3 ; 3 has frame in question lda 3 callersFrame,3 ; get frame of caller sta 3 temp,2 ; save the frame in temp in case jsris ; figure out whether normal or extended caller sub 0 0 ; assume normal (bank 0) lda 1 cXARGS add 3 1 lda 3 savedPC,3 se 1 3 jmp FcGetCallInst lda 0 XJMP-XARGS,3 ; extended caller, set 0 to XJMP & 3 lda 1 c3 and 1 0 lda 3 XPC-XARGS,3 ; set 3 to XPC - 1 adc 1 1 add 1 3 ; compute the address of the jsrxx instruction using ac3 for the pc FcGetCallInst: sta 0 extraArguments 2 ; save bank number adc 1 1 ; backup the pc add 3 1 XMLoad ; get the call instruction ; find out what the opcode is lda 3 opJsris sub 3 0 lda 3 cLmask and 0 3 szr jmp NotJsris ; calling instruction was jsris sta 3 extraArguments 2 ; jsris gets indirect word from bank zero lda 1 temp,2 ; jsris uses stack pointer for base jmp FcFinishUp NotJsris: lda 3 opJsriiMinusJsris sub 3 0 ; lda 3 cLMask ; opJsriiMinusJsris = cLMask and 0 3 szr jmp NotJsrii ; calling instruction was jsrii, uses instruction address for base FcFinishUp: ; ac0 now has just the address field -- the opcode is zero. ; the next three instructions extend the sign of the address field, now in 0 lda 3 c200 andzl 0 3 sub 3 0 ; now add in the base (PC or frame) specified by the opcode add 0 1 ; address of indirect word lda 0 extraArguments 2 ; bank number XMLoad ; get the indirect word mov 0 3 ; ac0 now has address of static lda 0 0,3 ; get contents of static jmp Done NotJsrii: mkzero 0 0 ; return zero if can't recognize opcode jmp Done c3: 3 c200: 200 opJsriiMinusJsris: ; (jsrii .)-(jsris 0) -- assembler chokes on this cLmask: 177400 cRmask: 377 opJsris: jsris 0 ; CallFrame(destframe[, arg 1[, arg 2]]) ; Sends control to the specified frame and links it back to this one. .CallFrame: sta 3 savedPC,2 mov 0 3 sta 2 callersFrame,3 Callf1: mov 1 0 lda 1 extraArguments,2 Callf2: mov 3 2 Done: lda 3 savedPC,2 jmp 1,3 ; GotoFrame(destframe[, arg 1[, arg 2]]) ; Just like CallFrame, but doesn't plant the return link. ; This is like Mesa's transfer. .GotoFrame: sta 3 savedPC,2 mov 0 3 jmp Callf1 ; CoCall(a, b) = CallFrame(MyFrame()>>F.callersFrame, a, b) .CoCall: sta 3 savedPC,2 lda 3 callersFrame,2 sta 2 callersFrame,3 jmp Callf2 ; CoReturn(a, b) ; Just like CoCall, but doesn't plant the return link .CoReturn: sta 3 savedPC,2 lda 2 callersFrame,2 jmp Done ; ReturnTo(label) ; Does a return to the specified label .ReturnTo: lda 2 callersFrame,2 mov 0 3 jmp 0,3 ; GotoLabel(frame, label, v) ; Sends control to the specified label in the specified frame, ; and passes v in AC0 .GotoLabel: mov 0 3 lda 0 extraArguments,2 mov 3 2 mov 1 3 jsr 0,3 ;in case proc head, must find #args in @ac3 1 ; RetryCall(p1, p2) ; Repeats the call which appears to have given control to the caller ; of RetryCall with p1 and p2 as the first two arguments, and the other ; arguments unchanged .RetryCall: nop lda 2 callersFrame,2 lda 3 savedPC,2 sta 0 temp,2 lda 0 cXARGS add 2 0 sne 3 0 jmp RetryXCall lda 0 temp,2 jmp -1,3 RetryXCall: dsz XPC,2 dsz XPC,2 lda 0 temp,2 jmp XJMP,2 cXARGS: XARGS ; ReturnFrom(fnOrFrame, v) ; Looks for a frame f which is either equal to fnOrFrame, ; or has FramesCaller(f) equal to fnOrFrame. It then cuts back ; the stack to f and simulates a return from f with v as the value. ; If it runs into trouble it returns 0 ; local variables for this routine fnOrFrame = 4 v = 5 nextFrame = 6 count = 7 .ReturnFrom: sta 3 savedPC,2 jsr @getframe count+1 nop lda 0 cRmask sta 0 count,2 mov 2 3 ; the frame we just looked at is in 3 RfLoop: lda 0 callersFrame,3 ; if we've looked at too many frames, give up dsz count 2 ; if the next frame is 0, give up snz 0 0 jmp RfFailed ; if the next frame is equal to the last one, give up sne 0 3 jmp RfFailed sta 0 nextFrame,2 ; is the next frame equal to fnOrFrame? lda 1 fnOrFrame,2 sne 0 1 jmp FoundFrame ; is its caller equal to fnOrFrame? jsr .FramesCaller 1 lda 1 fnOrFrame,2 sne 0 1 jmp FoundFrame ; it's not the one we want; charge on lda 3 nextFrame,2 jmp RfLoop FoundFrame: lda 0 v,2 lda 2 nextFrame,2 lda 2 callersFrame,2 ;get his caller (return FROM him) jmp Done RfFailed: mkzero 0 0 jsr @return .end