;;  precision.s -- context switching for the HP Precision architecture
;
;   With this architecture the stack grows upward.
;   A context array is laid out like this:
;
;	stack size word				(lowest address)
;	saved sp
;	saved general registers
;	saved floating point registers
;	argument save area for top-level callee (16 bytes)
;	frame marker  (32 bytes)
;	    ...
;	stack frames
;	    ...
;	unused space
;	    ...
;	magic word for catching overflow	(highest address)



MAGIC	.EQU	1019		; an arbitrary small integer for sanity checking

; the next three must be multiples of 8 for proper alignment
RSIZE	.EQU	120		; size of register save area (incl size word)
ASIZE	.EQU	16		; size of argument save area
PSIZE	.EQU	32		; size of procedure mark

	.GLOBAL
L$curr_ctx			; pointer to current context
	.WORD	L$dummy_ctx	; initially set to dummy context
	.ALIGN	8
L$dummy_ctx			; skeletal old context for first context switch
	.WORD	4+RSIZE+4	; length
	.BLOCK	RSIZE		; register save area
	.WORD	MAGIC		; magic word



;;  mpd_build_context(func,buf,bufsize,a1,a2,a3,a4)
;
;   args passed in:  r26  r25   r24  r23 stack...
;
;   func is the entry point of the code to be executed in the context.
;   stack is a buffer for holding the stack.
;   stacksize is the size of this buffer.
;   a1 - a4 are four int-size args to be passed to func.
;
;   We build a context that will execute the startup code, below,
;   when activated for the first time.

	.CODE
	.EXPORT	mpd_build_context
mpd_build_context
	.PROC
	.CALLINFO
	.ENTRY	
	STW	%arg2, (%arg1)		; init stacksize word
	ADD	%arg1, %arg2, %r21
	LDI	MAGIC, %r22
	STW	%r22, -4(%r21)		; init magic word
	ADDI	RSIZE+ASIZE+PSIZE, %arg1, %r22
	STW	%r22, 4(%arg1)		; init stack pointer
	LDIL	L'L$startup, %r1
	LDO	R'L$startup(%r1), %r22
	STW	%r22, 8(%arg1)		; init "return" address

	; save args in the slots of %r16 through %r13
	; save code address in slot of %r12
	STW	%arg3, 64(%arg1)	; %r16
	LDW	-52(%sp), %r22
	STW	%r22, 60(%arg1)		; %r15
	LDW	-56(%sp), %r22
	STW	%r22, 56(%arg1)		; %r14
	LDW	-60(%sp), %r22
	STW	%r22, 52(%arg1)		; %r13

	; under HP-UX 8.0, func address may be indirect
	BB,>=,N	%arg0,30,L$funcaddr	; if not indirect
	DEPI	0,31,2,%arg0		; clear indirect bit
	LDW	0(%arg0),%arg0		; load real address
L$funcaddr
	STW	%arg0, 48(%arg1)	; %r12  (func addr)

	; need to save valid floating point status register
	LDO	80(%arg1), %r20
	FSTDS,MA  %fr0, 8(%r20)		; base+80

	BV,N	(%r2)			; return

L$startup
	COPY	%r16, %arg0
	COPY	%r15, %arg1
	COPY	%r14, %arg2
	COPY	%r13, %arg3
	LDIL	L'L$under, %rp
	LDO	R'L$under(%rp), %rp	; direct return
	STW	%rp, -24(%sp)		; indirect return
	BV,N	(%r12)

	.EXIT
	.PROCEND




; mpd_chg_context(newstack) -- switch to the specified stack

	.EXPORT mpd_chg_context
mpd_chg_context
	.PROC
	.CALLINFO
	.ENTRY
	; validate old stack
	ADDIL	L'L$curr_ctx-$global$, %dp
	LDO	R'L$curr_ctx-$global$(%r1), %r19	; &curr_ctx
	LDW	(%r19), %r20		; curr stack pointer
	LDW	(%r20), %r22		; stack length
	ADD	%r20, %r22, %r21	; end of stack
	LDW	-4(%r21), %r21		; magic word
	SUBI,=	MAGIC, %r21, %r21
	  B,N	L$bad			; if magic word clobbered
	; validate new stack
	LDW	(%arg0), %r22		; stack length
	ADD	%arg0, %r22, %r21	; end of stack
	LDW	-4(%r21), %r21		; magic word
	SUBI,=	MAGIC, %r21, %r21
	  B,N	L$bad			; if magic word clobbered
	; save general registers
	STW	%sp, 4(%r20)
	STW	%r2, 8(%r20)
	STW	%r3, 12(%r20)
	STW	%r4, 16(%r20)
	STW	%r5, 20(%r20)
	STW	%r6, 24(%r20)
	STW	%r7, 28(%r20)
	STW	%r8, 32(%r20)
	STW	%r9, 36(%r20)
	STW	%r10, 40(%r20)
	STW	%r11, 44(%r20)
	STW	%r12, 48(%r20)
	STW	%r13, 52(%r20)
	STW	%r14, 56(%r20)
	STW	%r15, 60(%r20)
	STW	%r16, 64(%r20)
	STW	%r17, 68(%r20)
	STW	%r18, 72(%r20)
	; save floating registers  (note: must be double-word aligned)
	LDO	80(%r20), %r22
	FSTDS,MA  %fr0, 8(%r22)		; base+80
	FSTDS,MA  %fr12, 8(%r22)	; base+88
	FSTDS,MA  %fr13, 8(%r22)	; base+96
	FSTDS,MA  %fr14, 8(%r22)	; base+104
	FSTDS,MA  %fr15, 8(%r22)	; base+112

	; load general registers
	LDW	4(%arg0), %sp
	LDW	8(%arg0), %r2
	LDW	12(%arg0), %r3
	LDW	16(%arg0), %r4
	LDW	20(%arg0), %r5
	LDW	24(%arg0), %r6
	LDW	28(%arg0), %r7
	LDW	32(%arg0), %r8
	LDW	36(%arg0), %r9
	LDW	40(%arg0), %r10
	LDW	44(%arg0), %r11
	LDW	48(%arg0), %r12
	LDW	52(%arg0), %r13
	LDW	56(%arg0), %r14
	LDW	60(%arg0), %r15
	LDW	64(%arg0), %r16
	LDW	68(%arg0), %r17
	LDW	72(%arg0), %r18
	; load floating registers
	LDO	80(%arg0), %r22
	FLDDS,MA  8(%r22), %fr0		; base+80
	FLDDS,MA  8(%r22), %fr12	; base+88
	FLDDS,MA  8(%r22), %fr13	; base+96
	FLDDS,MA  8(%r22), %fr14	; base+104
	FLDDS,MA  8(%r22), %fr15	; base+112

	STW	%arg0, (%r19)		; update current-context pointer
	BV,N	(%r2)			; return into new context
	.EXIT
	.PROCEND

	
	.EXPORT mpd_check_stk
mpd_check_stk
	.PROC
	.CALLINFO
	.ENTRY
	ADDIL	L'L$curr_ctx-$global$, %dp
	LDW	R'L$curr_ctx-$global$(%r1), %r20    ; current context pointer
	LDW	(%r20), %r22		; stack length
	ADD	%r20, %r22, %r21	; end of stack
	SUB,<	%sp, %r21, %r0
	  B,N	L$over			; if stack grew too far
	LDW	-4(%r21), %r21		; magic word
	SUBI,=	MAGIC, %r21, %r21
	  B,N	L$bad			; if magic word clobbered
	BV,N	(%r2)			; return
	.EXIT
	.PROCEND



; error handlers

	.IMPORT mpd_stk_overflow
	.IMPORT mpd_stk_underflow
	.IMPORT mpd_stk_corrupted

L$over	LDIL	L%mpd_stk_overflow, %r1
	BE,N	R%mpd_stk_overflow(%sr4,%r1)

L$under	LDIL	L%mpd_stk_underflow, %r1
	BE,N	R%mpd_stk_underflow(%sr4,%r1)

L$bad	LDIL	L%mpd_stk_corrupted, %r1
	BE,N	R%mpd_stk_corrupted(%sr4,%r1)

	.END
