; Contextml.asm
; L. Stewart, December 8, 1982  4:13 PM
; modified to maintain ←stackLimit, L. Stewart, October 5, 1982  12:51 PM
; directly manipulated wakeup disable counter
; November 8, 1982  3:06 PM, use osstatics.c
; November 10, 1982  3:49 PM, add max runtime stuff
; December 8, 1982  4:13 PM, remove blken
; February 23, 1983  3:10 PM, remove osstatics
; March 3, 1983  2:42 PM, access to statics

$INCLUDE(Lark.d)

C←CODE SEGMENT

CTXNext	EQU	0		; link field
CTXsp	EQU	2		; saved stack pointer
CTXBase	EQU	4		; stack limit
CTXpc	EQU	6		; initial PC
CTXnm	EQU	8		; string name of context
CTXmr	EQU	10		; maxrun
CTXcl	EQU	12		; caller who ran maxrun

lenCTX	EQU	30		; bytes, leave space for RPC and system

C←DATA	SEGMENT

EXTRN	←stackLimit:WORD
EXTRN	←wdc:WORD

; following statics must stay in order for GetCtxData to work
←CtxRunning	DW	0
←ctxCal	DW	0
←startT	DW	0
dummyC	DW	0
	DW	?
dummySL	DW	?
	DW	?
dummynm	DW	?
	DW	?
	DW	?

C←DATA	ENDS

ASSUME CS:C←CODE, DS:C←DATA, ES:C←DATA

EXTRN	←Zero:NEAR
EXTRN	←CallDebugger:NEAR

←CurrentContext	PROC	NEAR
	MOV	BX,←CtxRunning
	RET
←CurrentContext	ENDP

←InitCtxPkg	PROC	NEAR
	MOV	←ctxCal,0
	MOV	DI,OFFSET dummyC
	CLD
	MOV	←CtxRunning, DI
	XOR	AX,AX
	STOSW
	STOSW
	MOV	AX,←stackLimit
	STOSW
	XOR	AX,AX
	STOSW
	MOV	AX,"noContext"
	STOSW
	RET
←InitCtxPkg	ENDP

; struct ctx *InitNContext(name, region, length, proc)
;   char *name;		/* string */
;   char *region;	/* base of available space */
;   int length;		/* length in words of available space */
;   int proc();		/* proc to call on first invocation */
; returns
;  struct RPCCtx *  see context.h
←InitNContext	PROC	NEAR
	PUSH	BP
	MOV	BP,SP
	MOV	AX,[4+BP]	; region
	PUSH	AX
	CALL	←InitContext
	POP	AX		; dump arguement
	MOV	AX,[6+BP]	; fetch name
	MOV	[CTXnm+BX],AX	; put into context
	MOV	SP,BP
	POP	BP
	RET
←InitNContext	ENDP

; struct ctx *InitContext(region, length, proc)
;   char *region;	/* base of available space */
;   int length;		/* length in words of available space */
;   int proc();		/* proc to call on first invocation */
; returns
;  struct RPCCtx *  see context.h

; InitContext puts the context structure at the top of the region
; because the stack grows downwards.  The initial value of the
; frame pointer will be 0 when proc is called.  This is a clue to
; stack tracers that it is the first frame.

←InitContext	PROC	NEAR
	PUSH	BP
	MOV	BP,SP
; zero it
	PUSH	CX
	PUSH	BX
	MOV	BX,CX		; length
	MOV	CX,[4+BP]	; region
	CALL	←Zero
	POP	BX
	POP	CX
; continue
	MOV	SI,[4+BP]	; region
	MOV	DI,SI		; copy it
	ADD	DI,CX		; add length *2
	ADD	DI,CX
	SUB	DI,lenCTX	; construct pointer to base of ctx
	MOV	WORD PTR [CTXNext+DI], 0
	MOV	[CTXBase+DI],SI	; set stackbase
	MOV	WORD PTR [CTXnm+DI], 0	; name
	MOV	[CTXpc+DI],BX	; proc
	LEA	BX,[DI-2]	; addr of first frame word on stack
	MOV	[CTXsp+DI],BX	; set initial sp to just below ctx
	MOV	WORD PTR [BX],0	; initial frame pointer
	MOV	BX,DI		; return context
	MOV	SP,BP
	POP	BP
	RET
←InitContext	ENDP

; CallContext(c)
;   struct ctx *c;

←CallContext	LABEL	NEAR
	PUSH	BP
	PUSH	←stackLimit	; save stackLimit
	PUSH	←ctxCal		; save previous ctxCal
	PUSH	←CtxRunning	; save running context
	MOV	←ctxCal,SP	; save this ctxCal
	JMP	nxtCtx		; jump into Block code

←Block	LABEL	NEAR
	MOV	BX,←CtxRunning	; pick up current context
	PUSH	BP		; save frame pointer
	MOV	[CTXsp+BX],SP	; save stack pointer
; measure runtime
	MOV	SI,clklo
	MOV	AX,[SI]		; get new time
	SUB	AX,←startT	; subtract start time
	CMP	AX,[CTXmr+BX]	; new record?
	JNA	blkb
	MOV	[CTXmr+BX],AX
	MOV	SI,SP
	MOV	AX,[2+SI]
	MOV	[CTXcl+BX],AX
blkb:
	MOV	BX,[CTXNext+BX]	; next context to try

; here to resume next context on list

nxtCtx:
	OR	BX,BX		; check for end of list
				; or 0 arg to CallContext
	JZ	donCtx		; none left
	MOV	←CtxRunning,BX	; save new context
runCtx:
	MOV	BX,clklo
	MOV	AX,[BX]
	MOV	←startT,AX
	CMP	←wdc,0
	JNZ	←BADBLK
	CLI			; fast IWDC
	MOV	BX,←CtxRunning
	MOV	SP,[CTXsp+BX]	; load saved stack pointer
	MOV	AX,[CTXBase+BX]	; load saved stack limit
	ADD	AX,018H		; 24 byte cushion
	MOV	←stackLimit,AX	; checked by StkChk
	CMP	←wdc,0		; fast DWDC
	JNZ	iwasoff
	STI
iwasoff:
	POP	BP		; load saved frame pointer
	OR	BP,BP		; if frame==0, call proc
	JZ	firstRun
	RET			; return from Block in new process
firstRun:
	MOV	AX,[CTXpc+BX]
	CALL	AX		; call procedure with ctx as arg
	JMP	←Block		; if it returns, call again

←BADBLK:			; musn't block with ints disabled
	MOV	BX,08765H
	CALL	←CallDebugger
donCtx:
	MOV	AX,←ctxCal	; restore stack of ctxCal
	OR	AX,AX
	JZ	notCtx		; Block called from outside
	MOV	SP,AX
	POP	←CtxRunning
	POP	←ctxCal
	POP	←stackLimit
	POP	BP
	RET			; return from CallContext

; If Block is called from outside any CallContext, it just returns
notCtx:	MOV	BX,←CtxRunning
	JMP	runCtx

←GetCtxData	PROC	NEAR
	MOV	BX,OFFSET ←CtxRunning
	RET
←GetCtxData	ENDP

PUBLIC  ←CtxRunning
PUBLIC  ←ctxCal
PUBLIC  ←InitCtxPkg
PUBLIC  ←Block
PUBLIC  ←CurrentContext
PUBLIC  ←InitNContext
; PUBLIC  ←InitContext
PUBLIC  ←CallContext
PUBLIC  ←GetCtxData

C←CODE ENDS
	END