/* --- Copyright University of Sussex 1993. All rights reserved. ----------
 * File:	C.hppa/src/aprocess.s
 * Purpose:	Assembler support for processes on HP PA-RISC 1.1
 * Author:	Julian Clinton, February 1993
 */


#_<

#_INCLUDE 'declare.ph'
#_INCLUDE 'process.ph'

lconstant macro (

	_ID_VALOF		= @@ID_VALOF,
	_PD_EXECUTE		= @@PD_EXECUTE,
	_PD_EXIT		= @@PD_EXIT,
	_PD_FLAGS		= @@PD_FLAGS,
	_PD_FRAME_LEN		= @@PD_FRAME_LEN,
	_PD_NLOCALS		= @@PD_NLOCALS,
	_PD_NUM_STK_VARS	= @@PD_NUM_STK_VARS,
	_PD_REGMASK		= @@PD_REGMASK,
	_PD_TABLE		= @@PD_TABLE,
	_PS_CALLSTACK_LIM	= @@PS_CALLSTACK_LIM,
	_PS_CALLSTACK_PARTIAL	= @@PS_CALLSTACK_PARTIAL,
	_PS_FLAGS		= @@PS_FLAGS,
	_PS_PARTIAL_RETURN	= @@PS_PARTIAL_RETURN,
	_PS_STATE		= @@PS_STATE,
);

>_#

#_INCLUDE 'asm_macros.h'

/************************* wrapping structures ************************/

	.code
	.word	Lcode_end-Lcode_start, C_LAB(Sys$-objmod_pad_key)
Lcode_start
	.data
	.word	Ldata_end-Ldata_start, C_LAB(Sys$-objmod_pad_key)
Ldata_start


/**********************************************************************/

	.code


;;; === SWAPPING THE CALLSTACK ========================================

BRANCH_std .equ 8 	;;; Size of a standard branch instruction
			;;; (as placed by I_BRANCH_std)

;;; _SWAP_OUT_CALLSTACK:
;;;	Swap out the callstack for a process
;;;	(i.e. copy the callstack into the process record)

;;; Call:
;;;	_swap_out_callstack(PROCESS)

;;; Registers used:
;;;	%chain	process record
;;;	%arg0	limit of saved callstack
;;;	%arg1	start of saved callstack
;;;	%arg2	callstack pointer
;;;	%arg3	ident table pointer
;;;	%t1	(1) register mask
;;;		(2) number of dynamic locals (loop counter)
;;;		(3) number of on-stack lvars (loop counter)
;;;	%t2	(1) size of current stack frame
;;;		(2) pointer to next frame
;;;	%t3, %t4	temporaries

DEF_C_LAB (_swap_out_callstack)

	;;; Get pointer to process record and get the start and
	;;; limit of the saved callstack
	ldwm		4(%usp), %chain
	ldw		_PS_STATE(%chain), %arg1
	b		so_test
	ldw		_PS_CALLSTACK_LIM(%chain), %arg0	;;; delay slot

so_loop	;;; Copy out next stack frame: %r31 already contains relative
	;;; return address (done in branch delay slot).

	;;; Test if the procedure has dlocal expression code to run:
	;;; if so break out to run the code
	ldb		_PD_FLAGS(%pb), %t3
	ldi		_:M_PD_PROC_DLEXPR_CODE, %t4
	and,=		%t3, %t4, %t3
	b,n		so_brk

so_cont
	;;; Continue here after running the dlocal expression code:
	;;; set the callstack pointer
	ldb		_PD_FRAME_LEN(%pb), %t2	;;; proc. frame size

	;;; Convert frame size to bytes and subtract from %sp to get
	;;; pointer to next frame in %t2 and pointer to low part of current
	;;; stackframe in %arg2
	sh2add		%t2, 0, %t2
	sub		%sp, %t2, %t2
	addi		4, %t2, %arg2

	;;; Save registers in use (as determined by the register mask) in the
	;;; process record, and restore those registers from the stack frame
	ldh		_PD_REGMASK(%pb), %t1

	;;; Save non-pop registers. Use a jump table indexed on
	;;; the right most bits to jump to the store for the first
	;;; register that needs to be saved
	extru		%t1, 31, 8, %arg3
	blr		%arg3, 0
	nop

	stws,ma		%npop4, 4(%arg1)
	ldws,ma		4(%arg2), %npop4
	stws,ma		%npop3, 4(%arg1)
	ldws,ma		4(%arg2), %npop3
	stws,ma		%npop2, 4(%arg1)
	ldws,ma		4(%arg2), %npop2
	stws,ma		%npop1, 4(%arg1)
	ldws,ma		4(%arg2), %npop1
	stws,ma		%npop0, 4(%arg1)
	ldws,ma		4(%arg2), %npop0

	;;; Save pop registers. Again use a jump table. This time
	;;; the MSB of the half-word is used.
	extru		%t1, 23, 8, %arg3
	blr		%arg3, 0
	nop

	stws,ma		%pop5, 4(%arg1)
	ldws,ma		4(%arg2), %pop5
	stws,ma		%pop4, 4(%arg1)
	ldws,ma		4(%arg2), %pop4
	stws,ma		%pop3, 4(%arg1)
	ldws,ma		4(%arg2), %pop3
	stws,ma		%pop2, 4(%arg1)
	ldws,ma		4(%arg2), %pop2
	stws,ma		%pop1, 4(%arg1)
	ldws,ma		4(%arg2), %pop1
	stws,ma		%pop0, 4(%arg1)
	ldws,ma		4(%arg2), %pop0

	;;; Test if there are any dynamic locals
	ldb		_PD_NLOCALS(%pb), %t1
	comib,=,n	0, %t1, L$8
	addi		_PD_TABLE, %pb, %arg3		;;; ptr to ident table

	;;; If so, copy the current values of the locals into the process
	;;; record, and restore their previous values from the stack
L$7	ldws,ma		4(%arg3), %t3		;;; load ident to %t3
	ldw		_ID_VALOF(%t3), %t4	;;; load idval to %t4
	stws,ma		%t4, 4(%arg1)		;;; store idval in process record
	ldws,ma		4(%arg2), %t4		;;; load previous idval from stack
	stw		%t4, _ID_VALOF(%t3)	;;; restore idval to ident table
	comib,<<	1, %t1, L$7		;;; unless last lvar, branch
	addi		-1, %t1, %t1		;;; branch delay slot

	;;; Copy any on-stack lvars into the process record
L$8	ldb		_PD_NUM_STK_VARS(%pb), %t1
	comib,=,n	0, %t1, L$10

L$9	ldws,ma		4(%arg2), %t3		;;; get stack lvar value
	stws,ma		%t3, 4(%arg1)		;;; save in process record
	comib,<<	1, %t1, L$9		;;; unless last stack lvar, branch
	addi		-1, %t1, %t1		;;; branch delay slot

L$10	;;; Save relative return address and owner address in process record
	stws,ma		%pb, 4(%arg1)
	stws,ma		%r31, 4(%arg1)

	;;; Set return address and procedure base from next stack frame
	ldw		(%t2), %r31
	ldw		-4(%t2), %pb

	;;; Clear the current stack frame
	copy		%t2, %sp

so_test
	;;; Test if there are any more stack frames to swap out. If so
	;;; make the return address relative to the procedure base
	comb,>>,n	%arg0, %arg1, so_loop
	sub		%r31, %pb, %r31			;;; delay slot

	;;; Finished swapping out -- chain procedure on stack.
	stw		0, _PS_CALLSTACK_PARTIAL(%chain)
	sth		0, _PS_FLAGS(%chain)		;;; 0 flags = suspended
	ldwm		4(%usp), %arg0			;;; pop pdr in %arg0
	CHAIN		%arg0
	nop

so_brk
	;;; Come here to run dlocal expression code, by jumping into the
	;;; appropriate part of the exit code of the procedure.

	;;; Save relative return address and callstack save pointer in the
	;;; process record, and go to the procedure's suspend code.
	stw		%r31, _PS_PARTIAL_RETURN(%chain)
	add		%pb, %r31, %r31			;;; make it absolute again
	stw		%arg1, _PS_CALLSTACK_PARTIAL(%chain)
	ldw		_PD_EXIT(%pb), %t4
	addi		-BRANCH_std*2, %t4, %t4

	;;; Chain the suspend code
	ldsid		(%t4), %r2
	mtsp		%r2, %sr0
	be,n		(%sr0, %t4)


	;;; The suspend code returns here to resume the swap-out loop

DEF_C_LAB (_swap_out_continue)

	;;; Restore relative return address, callstack save limit,
	;;; callstack save pointer from the process record and
	;;; continue the swapout loop
	ldw		_PS_PARTIAL_RETURN(%chain), %r31
	ldw		_PS_CALLSTACK_PARTIAL(%chain), %arg1
	b		so_cont
	ldw		_PS_CALLSTACK_LIM(%chain), %arg0


;;; _SWAP_IN_CALLSTACK:
;;;	Swap in the callstack for a process
;;;	(i.e. copy stack frames from the process record onto the stack)

;;; Call:
;;;	_swap_in_callstack(PROCESS)

;;; Registers used:
;;;	%chain	process record
;;;	%arg0	start of saved callstack
;;;	%arg1	limit of saved callstack
;;;	%arg2	callstack pointer
;;;	%arg3	ident table pointer
;;;	%t1	(1) register mask
;;;		(2) number of dynamic locals (loop counter)
;;;		(3) number of on-stack lvars (loop counter)
;;;	%t2	(1) size of current stack frame
;;;		(2) pointer to old frame
;;;	%t3, %t4	temporaries

DEF_C_LAB (_swap_in_callstack)

	ldwm		4(%usp), %chain			;;; Process record

	;;; Load limit of saved callstack and start of saved callstack,
	;;; then jump to test. Note: %arg1 points to the word above the
	;;; first relative return address so need to subtract 4 so it
	;;; is consistent with a stack pointer
	ldw		_PS_CALLSTACK_LIM(%chain), %arg1
	ldw		_PS_STATE(%chain), %arg0
	b		si_test
	ldo		-4(%arg1), %arg1		;;; branch delay slot

si_loop
	;;; Set callstack pointer
	ldb		_PD_FRAME_LEN(%pb), %t2	;;; frame size of this procedure
	sh2add		%t2, 0, %t2		;;; byte size

	;;; Save current return address and restore the old (relative)
	;;; one from process leaving the process record pointer pointing
	;;; at the owner procedure address
	stw		%r31, (%sp)
	ldws,ma		-4(%arg1), %r31

	;;; Allocate new stack frame
	add		%sp, %t2, %sp		;;; new frame
	addi		-4, %sp, %arg2		;;; old frame pointer + 4

	;;; Restore owner address to stack
	stw		%pb, -4(%sp)

	;;; Restore on-stack lvars from the process record to the stack
	ldb		_PD_NUM_STK_VARS(%pb), %t1
	comib,=,n	0, %t1, L$22

L$21	ldws,mb		-4(%arg1), %t3		;;; get saved stack lvar value
	stws,mb		%t3, -4(%arg2)		;;; save on stack
	comib,<<	1, %t1, L$21		;;; unless last, branch
	addi		-1, %t1, %t1

L$22	;;; Test if there are any dynamic locals
	ldb		_PD_NLOCALS(%pb), %t1
	comib,=,n	0, %t1, L$24
	addi		_PD_TABLE, %pb, %arg3

	;;; If so, copy the current values of the locals into the process
	;;; record, and restore their previous values from the stack.
	;;; Convert to bytes and add to _PD_TABLE to point to the end
	;;; of ident table
	sh2add		%t1, %arg3, %arg3

L$23
	ldws,mb		-4(%arg3), %t3		;;; load ident to %t3
	ldw		_ID_VALOF(%t3), %t4	;;; load idval to %t4
	stws,mb		%t4, -4(%arg2)		;;; push current idval on stack
	ldws,mb		-4(%arg1), %t4		;;; new idval from process to %t4
	stw		%t4, _ID_VALOF(%t3)	;;; store new idval in ident table
	comib,<<	1, %t1, L$23		;;; unless last, branch
	addi		-1, %t1, %t1		;;; decrement counter

L$24	;;; Save registers in use (as determined by the register mask) in the
	;;; stack frame, and restore those registers from the process record
	ldh		_PD_REGMASK(%pb), %t1

	;;; Load pop registers using a jump table.
	;;; The MSB of the half-word is used.
	extru		%t1, 23, 8, %arg3
	blr		%arg3, 0
	nop

	stw		%pop5, -24(%arg2)
	ldw		-24(%arg1), %pop5
	stw		%pop4, -20(%arg2)
	ldw		-20(%arg1), %pop4
	stw		%pop3, -16(%arg2)
	ldw		-16(%arg1), %pop3
	stw		%pop2, -12(%arg2)
	ldw		-12(%arg1), %pop2
	stw		%pop1, -8(%arg2)
	ldw		-8(%arg1), %pop1
	stw		%pop0, -4(%arg2)
	ldw		-4(%arg1), %pop0

	;;; Now adjust %arg1 and %arg2
	subi		6, %arg3, %arg3		;;; no. pop registers restored
	sh2add		%arg3, 0, %arg3		;;; get word adjustment
	sub		%arg1, %arg3, %arg1
	sub		%arg2, %arg3, %arg2

	;;; Load non-pop registers. Use a jump table indexed on
	;;; the right most bits to jump to the store for the first
	;;; register that needs to be saved
	extru		%t1, 31, 8, %arg3
	blr		%arg3, 0
	nop

	stw		%npop4, -20(%arg2)
	ldw		-20(%arg1), %npop4
	stw		%npop3, -16(%arg2)
	ldw		-16(%arg1), %npop3
	stw		%npop2, -12(%arg2)
	ldw		-12(%arg1), %npop2
	stw		%npop1, -8(%arg2)
	ldw		-8(%arg1), %npop1
	stw		%npop0, -4(%arg2)
	ldw		-4(%arg1), %npop0

	;;; Now adjust %arg1 and %arg2
	subi		5, %arg3, %arg3		;;; no. npop registers restored
	sh2add		%arg3, 0, %arg3		;;; get word adjustment
	sub		%arg1, %arg3, %arg1
	sub		%arg2, %arg3, %arg2

	;;; Test if the procedure has dlocal expression code to run:
	;;; if so break out to run the code
	addi		-4, %arg1, %arg1	;;; points to rel. ret.addr.
	ldb		_PD_FLAGS(%pb), %t3
	ldi		_:M_PD_PROC_DLEXPR_CODE, %t4
	and,=		%t3, %t4, %t3
	b,n		si_brk

si_cont
	;;; Continue here after running the dlocal expression code:
	;;; make return address absolute
	add		%pb, %r31, %r31

si_test
	;;; Test if there are any more stack frames to swap in
	;;; and restore owner address from process if there are.
	comb,<<,n	%arg0, %arg1, si_loop
	ldw		-4(%arg1), %pb			;;; branch delay slot

	;;; Finished swapping in -- chain procedure from stack
	ldwm		4(%usp), %arg0			;;; pop procedure to %t3
	stw		0, _PS_CALLSTACK_PARTIAL(%chain)
	CHAIN		%arg0
	nop

si_brk
	;;; Come here to run dlocal expression code, by jumping into the
	;;; appropriate part of the exit code of the procedure.

	;;; Save relative return address and callstack save pointer in the
	;;; process record, and go to the procedure's resume code
	stw		%r31, _PS_PARTIAL_RETURN(%chain)
	add		%pb, %r31, %r31		;;; make it absolute again
	stw		%arg1, _PS_CALLSTACK_PARTIAL(%chain)
	ldw		_PD_EXIT(%pb), %t4
	addi		-BRANCH_std, %t4, %t4

	;;; Chain the resume code
	ldsid		(%t4), %r2
	mtsp		%r2, %sr0
	be,n		(%sr0, %t4)

	;;; The resume code returns here to continue the swap-in loop

DEF_C_LAB (_swap_in_continue)

	;;; Restore relative return address, callstack save limit and
	;;; callstack save pointer from the process record and
	;;; continue the swapout loop
	ldw		_PS_PARTIAL_RETURN(%chain), %r31
	ldw		_PS_STATE(%chain), %arg0
	b		si_cont
	ldw		_PS_CALLSTACK_PARTIAL(%chain), %arg1


;;; === SWAPPING THE USERSTACK ========================================

;;; _USSAVE:
;;;	Save and erase a number of bytes from the end of the userstack

;;; Call:
;;;	_ussave(_BYTE_LENGTH, _DST_ADDR);

;;; Registers used:
;;;	%arg0	byte length (1st argument to -movwords-)
;;;	%arg1	source address of move (2nd argument to -movwords-)
;;;	%arg2	destination address of move (3rd argument to -movwords-)
;;;	%t2	saved source address of first move (not used by movwords)
;;;	%t3	value of _userhi (not used by movwords)

;;; Uses:
;;;	-movwords- (defined in amove.s)

DEF_C_LAB (_ussave)

	;;; Save return address
	stwm		%r31, 4(%sp)

	;;; Save the bytes from the end of the userstack
	ldwm		4(%usp), %arg2		;;; destination of saved part
	ldwm		4(%usp), %arg0		;;; number of bytes to save
	ldw		_SVB_OFFS(_userhi)(%svb), %t3	;;; stack base _userhi
	sub		%t3, %arg0, %arg1	;;; source of move (_userhi - length)
	bl		movwords, %r31
	copy		%arg1, %t2		;;; save source address for later

	;;; Reposition the rest above
	copy		%usp, %arg1		;;; source of move
	sub		%t2, %arg1, %arg0	;;; size of rest to move
	sub		%t3, %arg0, %usp	;;; new stack top after move ...
	copy		%usp, %arg2		;;; ... also destination of move
	b		movwords		;;; do the move, returning to caller
	ldwm		-4(%sp), %r31		;;; restore return address


;;; _USRESTORE:
;;;	Restore a number of bytes at the end of the user stack

;;; Call:
;;;	_usrestore(_BYTE_LENGTH, _SRC_ADDR)

;;; Registers used:
;;;	%arg0	byte length (1st argument to -movwords-)
;;;	%arg1	source address of move (2nd argument to -movwords-)
;;;	%arg2	destination address of move (3rd argument to -movwords-)
;;;	%t2	number of bytes to restore (not used by movwords)
;;;	%t3	value of _userhi (not used by movwords)

;;; Uses:
;;;	-movwords- (defined in amove.s)

DEF_C_LAB (_usrestore)

	;;; Save return address
	stwm		%r31, 4(%sp)

	;;; Move existing stack down, leaving room for restored bytes
	;;; Save the bytes from the end of the userstack
	ldw		4(%usp), %t2		;;; no. of bytes to restore
	ldw		_SVB_OFFS(_userhi)(%svb), %t3	;;; stack base _userhi
	sub		%t3, %usp, %arg0	;;; size of curr stack to move
	copy		%usp, %arg1		;;; stack top is source of move
	sub		%usp, %t2, %usp		;;; new stack top after move
	bl		movwords, %r31
	copy		%usp, %arg2		;;; also destination of move

	;;; Move in the new part: address of part to restore
	;;; and remove 2 arguments
	ldwm		8(%usp), %arg1
	copy		%t2, %arg0		;;; number of bytes to restore
	sub		%t3, %arg0, %arg2	;;; destination of move
	b		movwords		;;; do the move, returning to caller
	ldwm		-4(%sp), %r31		;;; setup return link


;;; _USERASUND:
;;;	Erase a number of bytes from the end of the user stack

;;; Call:
;;;	_userasund(_BYTE_LENGTH)

;;; Registers used:
;;;	%arg0	byte length (1st argument to -movwords-)
;;;	%arg1	source address of move (2nd argument to -movwords-)
;;;	%arg2	destination address of move (3rd argument to -movwords-)
;;;	%t1	value of _userhi
;;;	%t2	number of bytes to erase

;;; Uses:
;;;	-movwords- (defined in amove.s)

DEF_C_LAB (_userasund)

	ldw		_SVB_OFFS(_userhi)(%svb), %t1	;;; stack base _userhi
	ldwm		4(%usp), %t2		;;; Number of bytes to erase
	copy		%usp, %arg1		;;; source of move
	add		%arg1, %t2, %usp	;;; new stack top after move
	copy		%usp, %arg2		;;; also destination of move
	b		movwords		;;; do the move, returning to caller
	sub		%t1, %usp, %arg0	;;; length of move


	.code
	.import		C_LAB(Sys$-objmod_pad_key), data
	.import		movwords, code


/***************** end labels for wrapping structures *****************/

	.code
	.align  8
Lcode_end
	.data
	.align  8
Ldata_end

/**********************************************************************/
