; IfsCallProc.asm
; Copyright Xerox Corporation 1979, 1980

; Last modified February 27, 1980 11:07 AM by Taft
; Last modified December 20, 1979 4:55 PM by Wobber
; (fixed numargs bug on direct call)

; outgoing
.ent CallProcProcess, CallProc

; incoming
.bext Block, CtxRunning, Enqueue

.srel

CallProcProcess: .CallProcProcess
CallProc: .CallProc
callProcQ: .callProcQ

.nrel

; Procedure Call Descriptor (PCD) layout
link = 0
nargs = 1
; number of args passed down, -1 means done
args = 2
; pointer to args passed down, result returned here

; BCPL runtime statics
getframe = 370
storeargs = 367
return = 366

; BCPL frame layout
flink = 0
fret = 1
ftemp = 2
fargx = 3

; extended frame offsets for direct call, (see below)
farg0 = 4
farg1 = 5
fswat = 6
;swat...nobody should re-execute from here
fnargs = 7
;numargs for call
fcoret = 10
;jsr @return ... return to original frame
fextra = 11
; size of frame

; CallProc(proc,arg,...,arg; numargs na) = valof
; Calls Proc(arg,...,arg) in a big stack, and returns the result.
; This code is self-modifying, so it can’t be called from interrupt level.


.CallProc:
sta 3 fret 2; free an index register
sta 0 ftemp 2; and one other register
lda 0 cppCtx
snz 0 0; are contexts running yet?
jmp cp1; no. call directly
lda 3 @pCtxRunning
se 0 3; is CtxRunning = CallProcProcess?
jmp cp5; no. queue the call

; Come here if the call should be made directly rather then queueing it
; on callProcQ. This will happen for 2 reasons: 1) contexts aren’t
; running yet or 2) we are already running in the CallProcProcess context.

; An extra frame is allocated here with no argument space. It will include
; three words other than the overhead: a CallSwat, a numargs word to be
; filled in, and a jsr @return. The caller’s arguments and numargs will be
; fixed and then the call will be continued with 3 pointing to xnargs 2.
; The word above xnargs on the stack is a CallSwat because one should not
; be re-executing, (as in overlay,) from here.

cp1:
lda 0 fret 2; get old return pointer
jsr .+2 ; fake out getframe with a "numargs" of 2
2
sta 3 fret 2; just for the following call
jsr @getframe; make a new frame, (save AC0, AC1)
fextra; size of extra frame
jsr @storeargs
lda 3 flink 2; get previous frame
lda 1 farg0 2; get back return address
sta 1 fret 3; replace old return
lda 1 ftemp 3; get proc to be called
sta 1 ftemp 2; put it in our frame
lda 1 cSwat
sta 1 fswat 2
lda 1 cReturn
sta 1 fcoret 2
lda 1 @fret 3; numargs
neg 1 1
com 1 1
sta 1 fnargs 2; numargs -1 for the call we pass on
lda 0 c3
sge 1 0; 3 or more args?
jmp cp2 ; no
sne 1 0; exactly 3?
jmp cp3 ; yes

; There were more than 4 args to the original call, so there will be more
; than 3 args to the call we pass on, so the arg vector is still needed,
; though it is shortened by one.

lda 1 fargx 3
add 1 3
inc 3 3; bump vector
lda 1 2 3 ; old 3rd arg, new 2nd arg
sub 2 3
sta 3 fargx 2; store new arg vec offset
cp4:
lda 3 cfnargs ; 3 should point to fnargs
add 2 3
lda 0 farg1 2; old 2nd arg is new first arg
jmp @ftemp 2; continue the call, returns to fcoret 2

; There were less than 3 args to the original call.

cp2:
lda 1 fargx 3; old 3rd arg, new 2nd arg
jmp cp4; go call the procedure

; There were 4 args to the original call, so we will pass on 3.
; Move the 3rd arg into where the arg vec offset was.

cp3:
lda 1 fargx 3
add 1 3
lda 1 3 3 ; old 3rd arg, new 2nd arg
lda 3 4 3 ; old 4th arg, new 3rd arg
sta 3 fargx 2; put it in place of the arg vec offset
jmp cp4; go call the procedure

; Come here if we must queue the call and let the CallProcProcess do it.

cp5:
lda 0 @fret 2; numargs
lda 3 c7; frame overhead + space for pcd
add 0 3
sta 3 cpf; frame size
lda 0 ftemp 2; pick up first arg again (proc)
jsr @getframe
cpf:
0
jsr @storeargs
lda 3 c4
add 2 3
mov 3 1; -> arg list
add 0 3; -> pcd
neg 0 0
com 0 0
sta 0 nargs 3; numargs -1
sta 1 args 3; arg list
mov 3 1; pcd
lda 0 @pCallProcQ
jsrii pEnqueue; Queue the call
2
cp6:
jsrii pBlock; wait
0
lda 3 flink 2; callers frame
lda 3 @fret 3; numargs
add 2 3
lda 0 c4; frame overhead
add 0 3; -> pcd
lda 0 nargs 3
com 0 0 szr; done flag set?
jmp cp6; no
lda 0 args 3; get result
jsr @return

pCtxRunning:
CtxRunning
pCallProcQ:
callProcQ
pEnqueue:
Enqueue
cSwat:
77400
cReturn:
jsr @return
cfnargs:
fnargs
c4:
4
c7:
7

pBlock:
Block
c3:
3
cppCtx:
0; -> CallProcProcess’ ctx
.callProcQ:
0; head
0; tail

; CallProcProcess(ctx)
; A context which waits for a PCD to appear on CallProcQ.
; It executes the procedure, and leaves the result in PCD.result.

.CallProcProcess:
sta 3 fret 2
sta 0 cppCtx; our ctx
jsr @getframe
6
jsr @storeargs

cpp1:
jsrii pBlock; Block()
0
lda 3 .callProcQ; queue head
snz 3 3; anything to do?
jmp cpp1; no

lda 0 nargs 3
sta 0 cppargs; setup numargs for call
lda 3 args 3; pointer to proc and arg vector
lda 1 c3
sge 0 1; 3 or more args?
jmp cpp3; no
se 0 1; exactly 3?
jmp cpp2; more than 3
lda 0 3 3; exactly 3, put 3rd arg in fargx word
jmp cpp4

cpp2:
mov 3 0
sub 2 0; frame offset of arg vector
cpp4:
sta 0 fargx 2

cpp3:
lda 0 1 3; load up first 2 args
lda 1 2 3
jsr @0 3; call the procedure
cppargs: 0
lda 3 .callProcQ
sta 0 args 3; result
mkminusone 0 0
sta 0 nargs 3; set done flag
lda 0 link 3
sta 0 .callProcQ; dequeue call descriptor
jmp cpp1; wait for more work


.end