/* --- Copyright University of Sussex 1997. All rights reserved. ----------
 * File:	    C.alpha/src/amove.s
 * Purpose:
 * Author:          John Gibson, Sep 12 1994 (see revisions)
 */

;;; ---------------- ROUTINES TO MOVE MEMORY, ETC ------------------------

#_<

#_INCLUDE 'asm.ph'

>_#


ASM_START_FILE


ASM_CODE_PSECT


;;; --- COMPARE ROUTINES ------------------------------------------------

	;;; Compare two byte regions of equal nonzero length
	;;; Args: rt0 = len, rt1 = bptr1, rt2 = bptr2
	;;; Result: rt0 = 1 if same, 0 if not
	;;; Uses rt3 - rt5. Return in rchain.

ASM_ALIGN_QUAD
cmpbytes:
	;;; load unaligned quad from (rt1) into rt3
	ldq_u	rt3, 0(rt1)
	ldq_u	rt4, 7(rt1)
	extql	rt3, rt1, rt3
	extqh	rt4, rt1, rt4
	or	rt3, rt4, rt3	;;; quad1 in rt3

	;;; load unaligned quad from (rt2) into rt4
	ldq_u	rt4, 0(rt2)
	ldq_u	rt5, 7(rt2)
	extql	rt4, rt2, rt4
	extqh	rt5, rt2, rt5
	or	rt4, rt5, rt4	;;; quad2 in rt4

	;;; compare rt3 and rt4
	xor	rt3, rt4, rt3	;;; zero if equal, nonzero if not
	subq	rt0, 8, rt0	;;; decrement count by 1 quad
	bne	rt3, !$2f	;;; stop if bytes not all equal
	lda	rt1, 8(rt1)	;;; step ptrs
	lda	rt2, 8(rt2)
	bgt	rt0, cmpbytes	;;; loop if more to do

	;;; same
	mov	1, rt0	;;; return 1
	ret	rzero, (rchain)

	;;; bytes not all equal -- could be trailing bytes off end of strings
!$2:	bge	rt0, !$3f	;;; count nonneg -- all within strings
	mskql	rt3, rt0, rt3	;;; zero the bytes off end of strings
!$3:	cmpeq	rt3, 0, rt0	;;; result 1 if rt3 now zero
	ret	rzero, (rchain)


	;;; _bcmp(_______boffs, _______bptr1, _______bptr2) -> ____bool
	;;; _scmp(_______soffs, _______sptr1, _______sptr2) -> ____bool
	;;; compare two byte/short regions of the same length
ASM_ALIGN_QUAD
DEF_C_LAB (_bcmp)
DEF_C_LAB (_scmp)
	ldW	rt0, _WOFFS*2(rusp)	;;; ______offs
	ldW	rt1, _WOFFS(rusp)	;;; ______ptr1
	ldW	rt2, 0(rusp)		;;; ______ptr2
	lda	rusp, _WOFFS*2(rusp)
	beq	rt0, !$1f	;;; return true if zero length
	bsr	rchain, cmpbytes ;;; do compare
	blbc	rt0, !$2f	;;; not same
	;;; same
!$1:	lda	rt0, _TRUEOFFS(rfalse)
	stW	rt0, 0(rusp)
	ret	rzero, (rret)
!$2:	stW	rfalse, 0(rusp)	;;; else return false
	ret	rzero, (rret)


	;;; _icmp(_______ioffs, _______iptr1, _______iptr2) -> ____bool
	;;; compare two int regions of the same length
ASM_ALIGN_QUAD
DEF_C_LAB (_icmp)
#_IF WORD_BITS/==DOUBLE_BITS
DEF_C_LAB (_cmp)
#_ENDIF
	ldW	rt0, _WOFFS*2(rusp)	;;; ______offs
	ldW	rt1, _WOFFS(rusp)	;;; ______ptr1
	ldW	rt2, 0(rusp)		;;; ______ptr2
	lda	rusp, _WOFFS*2(rusp)
	beq	rt0, !$2f	;;; return if zero length

ASM_ALIGN_QUAD
!$1:	ldl	rt3, 0(rt1)
	ldl	rt4, 0(rt2)
	lda	rt1, 4(rt1)
	lda	rt2, 4(rt2)
	cmpeq	rt3, rt4, rt3
	subq	rt0, 4, rt0
	blbc	rt3, !$3f
	bgt	rt0, !$1b

	;;; same
!$2:	lda	rt0, _TRUEOFFS(rfalse)
	stW	rt0, 0(rusp)
	ret	rzero, (rret)

!$3:	;;; not same
	stW	rfalse, 0(rusp)	;;; else return false
	ret	rzero, (rret)


#_IF WORD_BITS==DOUBLE_BITS

	;;; _cmp(_______doffs, _______dptr1, _______dptr2) -> ____bool
	;;; compare two word (double) regions of the same length
ASM_ALIGN_QUAD
DEF_C_LAB (_cmp)
	ldW	rt0, _WOFFS*2(rusp)	;;; ______offs
	ldW	rt1, _WOFFS(rusp)	;;; ______ptr1
	ldW	rt2, 0(rusp)		;;; ______ptr2
	lda	rusp, _WOFFS*2(rusp)
	beq	rt0, !$2f	;;; return if zero length

ASM_ALIGN_QUAD
!$1:	ldq	rt3, 0(rt1)
	ldq	rt4, 0(rt2)
	lda	rt1, 8(rt1)
	lda	rt2, 8(rt2)
	cmpeq	rt3, rt4, rt3
	subq	rt0, 8, rt0
	blbc	rt3, !$3f
	bgt	rt0, !$1b

	;;; same
!$2:	lda	rt0, _TRUEOFFS(rfalse)
	stW	rt0, 0(rusp)
	ret	rzero, (rret)

!$3:	;;; not same
	stW	rfalse, 0(rusp)	;;; else return false
	ret	rzero, (rret)

#_ENDIF


;;; --- LOCATE CHARACTER ----------------------------------------------------

	;;; _skpc(______bptr, _______boffs, ______byte) -> _____________boffs_or_-1
	;;; skip string for character
ASM_ALIGN_QUAD
DEF_C_LAB (_skpc)
	mov	255, rt3	;;; all 1s mask for XOR
	br	locskp

	;;; _locc(______bptr, _______boffs, ______byte) -> _____________boffs_or_-1
	;;; search string for character
ASM_ALIGN_QUAD
DEF_C_LAB (_locc)
	clr	rt3		;;; all 0s mask for XOR

locskp:
	ldW	rt1, _WOFFS*2(rusp)	;;; ______bptr
	ldW	rt0, _WOFFS(rusp)	;;; _______boffs
	ldW	rt2, 0(rusp)		;;; ______byte
	lda	rusp, _WOFFS*2(rusp)
	beq	rt0, !$2f	;;; return not found if zero length

	;;; dup byte across quadword
	sll	rt2, 8, rt4
	or	rt2, rt4, rt2
	sll	rt2, 16, rt4
	or	rt2, rt4, rt2
	sll	rt2, 32, rt4
	or	rt2, rt4, rt2

!$1:	ldq_u	rt4, 0(rt1)	;;; load src quadword into rt4
	ldq_u	rt5, 7(rt1)
	extql	rt4, rt1, rt4
	extqh	rt5, rt1, rt5
	or	rt4, rt5, rt4

	cmpbge	rt2, rt4, rt5	;;; compare with byte quad in rt2
	cmpbge	rt4, rt2, rt4
	lda	rt1, 8(rt1)
	and	rt4, rt5, rt4	;;; AND results to get 1s for equal bytes
	xor	rt4, rt3, rt4	;;; then XOR with mask for equal/not equal
	subq	rt0, 8, rt0	;;; decrement count
	bne	rt4, !$3f	;;; finished if some equal/not equal
	bgt	rt0, !$1b	;;; loop if more to compare

	;;; not found/ nothing else found
!$2:	lda	rt0, -1(rzero)	;;; return -1
	stW	rt0, 0(rusp)
	ret	rzero, (rret)

	;;; find first bit 0 - 7 set in rt4
!$3:	lda	rt5, -8(rzero)	;;; -8 because rt1 was stepped
	and	rt4, 15, rt6
	bne	rt6, !$4f
	addq	rt5, 4, rt5
	srl	rt4, 4, rt4
!$4:	and	rt4, 3, rt6
	bne	rt6, !$5f
	addq	rt5, 2, rt5
	srl	rt4, 2, rt4
!$5:	blbs	rt4, !$6f
	addq	rt5, 1, rt5
	;;; (offset from quad start) - 8 now in rt5
!$6:	cmple	rt0, rt5, rt0	;;; if -ve overshoot on count <= rt5,
	blbs	rt0, !$2b	;;; equal byte was not in string

	;;; char/something else found
	ldW	rt0, 0(rusp)	;;; initial ptr again
	addq	rt1, rt5, rt1	;;; ptr to char
	subq	rt1, rt0, rt0	;;; offset to char
	stW	rt0, 0(rusp)	;;; return it
	ret	rzero, (rret)



;;; --- MOVE ROUTINES ------------------------------------------------------

	;;; _bmove(_______boffs, __________bptr_src, __________bptr_dst) -> __________next_dst
	;;; _smove(_______soffs, __________sptr_src, __________sptr_dst) -> __________next_dst
	;;; move bytes or shorts
ASM_ALIGN_QUAD
DEF_C_LAB (_bmove)
DEF_C_LAB (_smove)
	ldW	rt2, 0(rusp)		;;; _________ptr_dst
	ldW	rt0, _WOFFS*2(rusp)	;;; ______offs
	ldW	rt1, _WOFFS(rusp)	;;; _________ptr_src
	addq	rt2, rt0, rt4	;;; next destination
	lda	rusp, _WOFFS*2(rusp)
	subq	rt2, rt1, rt3	;;; dst - src
	stW	rt4, 0(rusp)	;;; next destination result
	beq	rt0, !$3f	;;; return if nothing to move
	blt	rt3, !$5f 	;;; br if moving down
	beq	rt3, !$3f	;;; return if source = destination

	;;; moving up
	addq	rt1, rt0, rt1	;;; src lim
	mov	rt4, rt2	;;; dest lim

!$1:	subq	rt0, 8, rt0	;;; decrement count by 1 quad
	lda	rt1, -8(rt1)	;;; step ptrs back 1 quad
	lda	rt2, -8(rt2)
	blt	rt0, !$7f	;;; br if count neg -- odd bytes at end

	;;; load unaligned quad from (rt1) into rt3
	ldq_u	rt3, 0(rt1)
	ldq_u	rt4, 7(rt1)
	extql	rt3, rt1, rt3
	extqh	rt4, rt1, rt4
	or	rt3, rt4, rt3	;;; the src quad in rt3

	;;; store src quad rt3 at unaligned dst (rt2)
!$2:	ldq_u	rt5, 7(rt2)
	ldq_u	rt4, 0(rt2)
	insqh	rt3, rt2, rt6
	insql	rt3, rt2, rt3
	mskqh	rt5, rt2, rt5
	mskql	rt4, rt2, rt4
	or	rt5, rt6, rt5
	or	rt3, rt4, rt3
	stq_u	rt5, 7(rt2)
	stq_u	rt3, 0(rt2)

	bgt	rt0, !$1b	;;; loop if more to go
!$3:	ret	rzero, (rret)


	;;; moving down
!$5:	subq	rt0, 8, rt0	;;; decrement count by 1 quad
	blt	rt0, !$8f	;;; odd bytes at end

	;;; load unaligned quad from (rt1) into rt3
	ldq_u	rt3, 0(rt1)
	ldq_u	rt4, 7(rt1)
	extql	rt3, rt1, rt3
	extqh	rt4, rt1, rt4
	or	rt3, rt4, rt3	;;; the src quad in rt3

!$6:	;;; store src quad rt3 at unaligned dst (rt2)
	ldq_u	rt5, 7(rt2)
	ldq_u	rt4, 0(rt2)
	insqh	rt3, rt2, rt6
	insql	rt3, rt2, rt3
	mskqh	rt5, rt2, rt5
	mskql	rt4, rt2, rt4
	or	rt5, rt6, rt5
	or	rt3, rt4, rt3
	stq_u	rt5, 7(rt2)
	stq_u	rt3, 0(rt2)

	lda	rt1, 8(rt1)	;;; step ptrs
	lda	rt2, 8(rt2)
	bgt	rt0, !$5b	;;; loop if more to go
	ret	rzero, (rret)


	;;; move odd bytes at end (moving up)
!$7:	subq	rt1, rt0, rt1	;;; get ptrs to start of bytes
	subq	rt2, rt0, rt2

	;;; move 1 - 7 bytes at end
!$8:	addq	rt0, 8, rt0	;;; _N = 1 - 7

	addq	rt1, rt0, rt3
	ldq_u	rt4, 0(rt1)
	ldq_u	rt5, -1(rt3)
	extql	rt4, rt1, rt4
	extqh	rt5, rt1, rt5
	or	rt4, rt5, rt4
	mskql	rt4, rt0, rt3	;;; the _N src bytes aligned at the bottom

	addq	rt2, rt0, rt1
	lda	rt4, -1(rzero)
	mskql	rt4, rt0, rt0	;;; mask for _N bytes

	ldq_u	rt4, -1(rt1)
	ldq_u	rt5, 0(rt2)

	insqh	rt0, rt2, rchain
	insqh	rt3, rt2, rt6
	bic	rt4, rchain, rt4
	or	rt4, rt6, rt4
	stq_u	rt4, -1(rt1)

	insql	rt0, rt2, rchain
	insql	rt3, rt2, rt6
	bic	rt5, rchain, rt5
	or	rt5, rt6, rt5
	stq_u	rt5, 0(rt2)

	ret	rzero, (rret)



	;;; _imove(_______ioffs, ___________iptr_src, __________iptr_dst) -> __________next_dst
	;;; _dmove(_______doffs, __________dptr_src, __________dptr_dst) -> __________next_dst
	;;; _move(_______woffs, __________wptr_src, __________wptr_dst) -> __________next_dst
	;;; move ints or doubles (i.e. Alpha longwords/quads).
	;;; _moveq is the same.

ASM_ALIGN_QUAD
DEF_C_LAB (_imove)
DEF_C_LAB (_dmove)
DEF_C_LAB (_move)
DEF_C_LAB (_moveq)
	ldW	rt2, 0(rusp)		;;; __________wptr_dst
	ldW	rt0, _WOFFS*2(rusp)	;;; _______woffs
	ldW	rt1, _WOFFS(rusp)	;;; __________wptr_src
	addq	rt2, rt0, rt4		;;; next destination
	lda	rusp, _WOFFS*2(rusp)
	stW	rt4, 0(rusp)		;;; next destination result
	;;; drop thru to movwords

	;;; Do quadword/longword move.
	;;; word offset to move in rt0, source in rt1, dest in rt2
	;;; Uses rt3, rt4, rt5
movwords:
	cmple	rt0, 4, rt3	;;; less than 2 longwords to move?
	subq	rt2, rt1, rt4	;;; dst - src
	blbs	rt3, !$8f	;;; br if less than 2 longwords

	;;; can use forward move if moving down or dest beyond end of src
	;;; (mem system anticipates moving forward thru mem rather than back)
	cmpule	rt0, rt4, rt3 	;;; woffs unsigned <= dst - src ?
	blbc	rt3, movback	;;; no -- do backward move

	;;; NO OVERLAP -- DO THE MOVE FORWARDS
	and	rt2, 4, rt3	;;; test alignment on dst
	beq	rt3, !$1f	;;; br if dst is quad aligned
	;;; dst not quad aligned -- move 1 longword first
	ldl	rt3, 0(rt1)
	lda	rt2, 4(rt2)
	lda	rt1, 4(rt1)
	stl	rt3, -4(rt2)	;;; rt2 now aligned
	subq	rt0, 4, rt0
!$1:	and	rt1, 4, rt3	;;; test alignment on src
	beq	rt3, !$6f	;;; br if src also now quad aligned

	;;; dst aligned, src not
	ldl	rt3, 0(rt1)	;;; initial lo part
	subq	rt0, 8, rt0
	extll	rt3, 0, rt3	;;; zero hi part
	br	!$3f

ASM_ALIGN_QUAD
!$2:	ldq	rt4, 4(rt1)
	subq	rt0, 8, rt0
	lda	rt2, 8(rt2)
	sll	rt4, 32, rt5	;;; lo -> hi
	lda	rt1, 8(rt1)
	or	rt5, rt3, rt5
	srl	rt4, 32, rt3	;;; previous hi -> next lo
	stq	rt5, -8(rt2)
!$3:	bgt	rt0, !$2b

	blt	rt0, !$4f
	ldl	rt4, 4(rt1)
	stl	rt4, 4(rt2)
!$4:	stl	rt3, 0(rt2)
	ret	rzero, (rret)

	;;; both quad aligned
ASM_ALIGN_QUAD
!$5:	stq	rt3, -8(rt2)
	lda	rt1, 8(rt1)
!$6:	subq	rt0, 8, rt0
	ldq	rt3, 0(rt1)
	lda	rt2, 8(rt2)
	bgt	rt0, !$5b

	blt	rt0, !$7f
	stq	rt3, -8(rt2)
	ret	rzero, (rret)
!$7:	stl	rt3, -8(rt2)
	ret	rzero, (rret)

	;;; LESS THAN 2 LONGWORDS TO MOVE
!$8:	beq	rt0, !$9f	;;; return if nothing to move
	ldl	rt3, 0(rt1)	;;; else move 1 longword and return
	stl	rt3, 0(rt2)
!$9:	ret	rzero, (rret)


	;;; SRC AND DST OVERLAP -- DO THE MOVE BACKWARDS
ASM_ALIGN_QUAD
movback:
	beq	rt4, !$10f	;;; return if source = destination
	addq	rt1, rt0, rt1	;;; src lim
	addq	rt2, rt0, rt2	;;; dst lim
	and	rt2, 4, rt3	;;; test alignment on dst lim
	beq	rt3, !$3f	;;; br if dst lim is quad aligned
	;;; dst lim not quad aligned -- move 1 longword first
	ldl	rt3, -4(rt1)
	lda	rt2, -4(rt2)
	lda	rt1, -4(rt1)
	stl	rt3, 0(rt2)	;;; rt2 now aligned
	subq	rt0, 4, rt0
!$3:	and	rt1, 4, rt3	;;; test alignment on src lim
	beq	rt3, !$8f	;;; br if src lim also now quad aligned

	;;; dst lim aligned, src not
	ldl	rt3, -4(rt1)	;;; initial lo part
	subq	rt0, 8, rt0
	sll	rt3, 32, rt3	;;; previous lo -> next hi
	br	!$5f

ASM_ALIGN_QUAD
!$4:	ldq	rt4, -12(rt1)
	subq	rt0, 8, rt0
	lda	rt2, -8(rt2)
	srl	rt4, 32, rt5	;;; hi -> lo
	lda	rt1, -8(rt1)
	or	rt5, rt3, rt5	;;; or with hi
	sll	rt4, 32, rt3	;;; previous lo -> next hi
	stq	rt5, 0(rt2)
!$5:	bgt	rt0, !$4b

	srl	rt3, 32, rt4
	blt	rt0, !$6f
	ldl	rt3, -8(rt1)
	stl	rt3, -8(rt2)
!$6:	stl	rt4, -4(rt2)
	ret	rzero, (rret)

	;;; both quad aligned
ASM_ALIGN_QUAD
!$7:	stq	rt3, 0(rt2)
	lda	rt1, -8(rt1)
!$8:	subq	rt0, 8, rt0
	ldq	rt3, -8(rt1)
	lda	rt2, -8(rt2)
	bgt	rt0, !$7b

	blt	rt0, !$9f
	stq	rt3, 0(rt2)
	ret	rzero, (rret)
!$9:	srl	rt3, 32, rt4
	stl	rt4, 4(rt2)
!$10:	ret	rzero, (rret)


;;; --- FILL ROUTINES ----------------------------------------------------

	;;; _bfill(______byte, _______boffs, ______bptr)
	;;; fill a region of bytes with a given byte

ASM_ALIGN_QUAD
DEF_C_LAB (_bfill)
	ldW	rt1, _WOFFS*2(rusp)	;;; ______byte
	ldW	rt0, _WOFFS(rusp)	;;; _______boffs
	ldW	rt2, 0(rusp)		;;; ______bptr
	lda	rusp, _WOFFS*3(rusp)
	beq	rt0, !$3f	;;; return if nothing to do
	;;; dup byte across quadword
	sll	rt1, 8, rt3
	or	rt1, rt3, rt1
	sll	rt1, 16, rt3
	or	rt1, rt3, rt1
	sll	rt1, 32, rt3
	or	rt1, rt3, rt1
	insql	rt1, rt2, rt3	;;; split to rt3, rt4
	insqh	rt1, rt2, rt4

!$1:	subq	rt0, 8, rt0	;;; decrement count by 1 quad
	blt	rt0, !$4f	;;; not end or no odd bytes at end

!$2:	;;; store src quad rt1 split in rt3, rt4 at unaligned dst (rt2)
	ldq_u	rt6, 7(rt2)
	ldq_u	rt5, 0(rt2)
	mskqh	rt6, rt2, rt6
	mskql	rt5, rt2, rt5
	or	rt6, rt4, rt6
	or	rt5, rt3, rt5
	stq_u	rt6, 7(rt2)
	stq_u	rt5, 0(rt2)

	lda	rt2, 8(rt2)	;;; step ptr
	bgt	rt0, !$1b	;;; loop if more to go
!$3:	ret	rzero, (rret)

	;;; insert odd trailing dst bytes in src quad
!$4:	ldq_u	rt5, 0(rt2)
	ldq_u	rt6, 7(rt2)
	extql	rt5, rt2, rt5
	extqh	rt6, rt2, rt6
	or	rt5, rt6, rt5	;;; last dst quad in rt5
	mskql	rt1, rt0, rt1	;;; src bytes wanted
	mskqh	rt5, rt0, rt5	;;; dst bytes wanted
	or	rt1, rt5, rt1	;;; correct quad to store
	insql	rt1, rt2, rt3	;;; split it to rt3, rt4
	insqh	rt1, rt2, rt4
	br	!$2b		;;; store it


	;;; _ifill(_____int, _______ioffs, ______iptr)
	;;; fill a region of longwords with a given longword

ASM_ALIGN_QUAD
DEF_C_LAB (_ifill)
#_IF WORD_BITS/==DOUBLE_BITS
DEF_C_LAB (_fill)
#_ENDIF
	ldW	rt2, 0(rusp)		;;; ______wptr
	ldW	rt1, _WOFFS(rusp)	;;; _______woffs
	ldW	rt0, _WOFFS*2(rusp)	;;; ______word
	br	!$2f

ASM_ALIGN_QUAD
!$1:	stl	rt0, 0(rt2)	;;; store longword at dst
	subq	rt1, 4, rt1
	lda	rt2, 4(rt2)
!$2:	bne	rt1, !$1b

	lda	rusp, _WOFFS*3(rusp)
	ret	rzero, (rret)


#_IF WORD_BITS==DOUBLE_BITS

	;;; _fill(______word, ________woffs, ______wptr)
	;;; fill a region of words (quads) with a given quadword

ASM_ALIGN_QUAD
DEF_C_LAB (_fill)
	ldW	rt2, 0(rusp)		;;; ______wptr
	ldW	rt1, _WOFFS(rusp)	;;; _______woffs
	ldW	rt0, _WOFFS*2(rusp)	;;; ______word
	br	!$2f

ASM_ALIGN_QUAD
!$1:	stq	rt0, 0(rt2)		;;; store word at dst
	subq	rt1, 8, rt1
	lda	rt2, 8(rt2)
!$2:	bne	rt1, !$1b

	lda	rusp, _WOFFS*3(rusp)
	ret	rzero, (rret)

#_ENDIF


;;;--------------------------------------------------------------------------


	;;; _move_userstack(_______woffs)
	;;; move the user stack up or down by word offset _______woffs

ASM_ALIGN_QUAD
DEF_C_LAB (_move_userstack)
	ldW	rt2, 0(rusp)		;;; _______woffs
	ldW	rt3, _SVB_OFFS(_userhi)(rsvb)	;;; _userhi value
	lda	rusp, _WOFFS(rusp)
	subq	rt3, rusp, rt0		;;; length of u/s to move in rt0
	mov	rusp, rt1		;;; u/s top -- src for move
	addq	rt3, rt2, rt3		;;; _userhi + _______woffs = new _userhi
	addq	rusp, rt2, rt2		;;; top + _______woffs = destination for move
	stW	rt3, _SVB_OFFS(_userhi)(rsvb)	;;; update _userhi
	mov	rt2, rusp		;;; and new stack top
	br	movwords		;;; do the move (return to caller)


;;; --- ROUTINES TO HANDLE USERSTACK SWAPPING -------------------------------

	;;; _ussave(_______woffs, _________saveptr)
	;;; save and erase a given offset at the end of the userstack

ASM_ALIGN_QUAD
DEF_C_LAB (_ussave)
	;;; copy the area to be saved
	ldW	rt2, 0(rusp)		;;; _________saveptr = dst for move
	ldW	rt0, _WOFFS(rusp)	;;; _______woffs = offset to move
	ldW	rt6, _SVB_OFFS(_userhi)(rsvb)	;;; stack base
	lda	rusp, _WOFFS*2(rusp)
	mov	rret, rchain		;;; save return
	subq	rt6, rt0, rt1		;;; _userhi - _______woffs = src for move ...
	mov	rt1, rt6		;;; ... remembered in rt6
	bsr	rret, movwords		;;; save the area

	;;; now shift up the remaining stack contents
	ldW	rt2, _SVB_OFFS(_userhi)(rsvb)	;;; _userhi again
	mov	rusp, rt1		;;; stack top = src for move
	subq	rt6, rt1, rt0		;;; save src - stack top = offs to move
	subq	rt2, rt0, rt2		;;; _userhi - offs = dst for move ...
	mov	rt2, rusp		;;; ... and new stack top afterwards
	mov	rchain, rret		;;; set return to caller
	br	movwords		;;; do the move


	;;; _usrestore(_______woffs, _________restptr)
	;;; restore a given offset at the end of the userstack

ASM_ALIGN_QUAD
DEF_C_LAB (_usrestore)
	;;; shift down the current stack contents to make room
	ldW	rt0, _SVB_OFFS(_userhi)(rsvb)	;;; stack base
	ldW	rt6, _WOFFS(rusp)	;;; _______woffs (keep for next part)
	mov	rret, rchain		;;; save return
	subq	rt0, rusp, rt0		;;; _userhi - top = offset to move
	mov	rusp, rt1		;;; stack top = src for move
	subq	rusp, rt6, rt2		;;; top - _______woffs = dst for move ...
	mov	rt2, rusp		;;; ... and new stack top afterwards
	bsr	rret, movwords		;;; save the area

	;;; now move in the area to be restored
	ldW	rt2, _SVB_OFFS(_userhi)(rsvb)	;;; stack base again
	ldW	rt1, 0(rusp)		;;; _________restptr = src for move
	lda	rusp, _WOFFS*2(rusp)	;;; remove args
	mov	rt6, rt0		;;; _______woffs = offset for move
	subq	rt2, rt0, rt2		;;; _userhi - _______woffs = dst for move
	mov	rchain, rret		;;; set return to caller
	br	movwords		;;; do the move


	;;; _userasund(_______woffs)
	;;; erase a given offset at the end of the userstack

ASM_ALIGN_QUAD
DEF_C_LAB (_userasund)
	ldW	rt2, 0(rusp)		;;; _______woffs
	ldW	rt0, _SVB_OFFS(_userhi)(rsvb)	;;; stack base
	lda	rt1, _WOFFS(rusp)	;;; stack top + _WOFFS = src for move
	addq	rt1, rt2, rt2		;;; plus _______woffs = dst for move ...
	mov	rt2, rusp		;;; ... and new stack top afterwards
	subq	rt0, rt2, rt0		;;; _userhi - new top = size to move
	br	movwords		;;; do the move


;;; ------------------------------------------------------------------------

	;;; _move_callstack(_______woffs, _________limaddr)
	;;; move the callstack from sp to _________limaddr by a signed offset _______woffs

ASM_ALIGN_QUAD
DEF_C_LAB(_move_callstack)
	ldW	rt0, 0(rusp)		;;; _________limaddr
	ldW	rt2, _WOFFS(rusp)	;;; _______woffs
	lda	rusp, _WOFFS*2(rusp)
	subq	rt0, rsp, rt0		;;; _________limaddr - sp = offset to move in rt0
	mov	rsp, rt1		;;; sp = src for move in rt1
	addq	rsp, rt2, rt2		;;; sp + _______woffs = destination in rt2 ...
	mov	rt2, rsp		;;; ... and new sp afterwards
	br	movwords		;;; do the move


;;; --- BITFIELD ROUTINES ------------------------------------------------


	;;; access an unsigned bit field
	;;; rt0 = struct address, rt1 = bitoffset, rt2 = field width W,
	;;; returns field value in rt0
ASM_ALIGN_QUAD
DEF_C_LAB (_bfield)
	mov	64, rt4
	subq	rt4, rt2, rt4		;;; 64-W = field shift-down
	and	rt1, 7, rt3		;;; bitoffs within start byte
	subq	rt4, rt3, rt3		;;; 64-W-bitoffs = field shift-up
	sra	rt1, 3, rt1		;;; trunc bit offset to byte offset
	addq	rt0, rt1, rt0		;;; add to struct addr in rt0
	;;; load quadword from unaligned address in rt0
	ldq_u	rt1, 0(rt0)
	ldq_u	rt2, 7(rt0)
	extql	rt1, rt0, rt1
	extqh	rt2, rt0, rt2
	or	rt1, rt2, rt0		;;; the quadword containing the field
	;;; align the bitfield
	sll	rt0, rt3, rt0		;;; shift up to reg top
	srl	rt0, rt4, rt0		;;; logical shift down
	ret	rzero, (rret)


	;;; access a signed bit field (same except for last shift)
	;;; rt0 = struct address, rt1 = bitoffset, rt2 = field width W,
	;;; returns field value in rt0
ASM_ALIGN_QUAD
DEF_C_LAB (_sbfield)
	mov	64, rt4
	subq	rt4, rt2, rt4		;;; 64-W = field shift-down
	and	rt1, 7, rt3		;;; bitoffs within start byte
	subq	rt4, rt3, rt3		;;; 64-W-bitoffs = field shift-up
	sra	rt1, 3, rt1		;;; trunc bit offset to byte offset
	addq	rt0, rt1, rt0		;;; add to struct addr in rt0
	;;; load quadword from unaligned address in rt0
	ldq_u	rt1, 0(rt0)
	ldq_u	rt2, 7(rt0)
	extql	rt1, rt0, rt1
	extqh	rt2, rt0, rt2
	or	rt1, rt2, rt0		;;; the quadword
	;;; align the bitfield
	sll	rt0, rt3, rt0		;;; shift up to reg top
	sra	rt0, rt4, rt0		;;; arithmetic shift down
	ret	rzero, (rret)


	;;; update a bit field
	;;; rt0 = struct address, rt1 = bitoffset, rt2 = field width W,
	;;; field updated from value off stack
ASM_ALIGN_QUAD
DEF_C_LAB (_ubfield)
	mov	64, rt4
	subq	rt4, rt2, rt4		;;; 64-W = field shift-down
	and	rt1, 7, rt3		;;; bitoffs within start byte
	subq	rt4, rt3, rt3		;;; 64-W-bitoffs = field shift-up
	sra	rt1, 3, rt1		;;; trunc bit offset to byte offset
	addq	rt0, rt1, rt0		;;; add to struct addr in rt0
	;;; load quadword from unaligned address in rt0
	ldq_u	rt1, 0(rt0)
	ldq_u	rt2, 7(rt0)
	extql	rt1, rt0, rt5
	extqh	rt2, rt0, rt6
	or	rt5, rt6, rt5		;;; the quadword containing the field
	;;; clear old field and insert new
	lda	rt6, -1(rzero)		;;; all 1s for mask
	sll	rt6, rt4, rt6		;;; shift up by 64-W
	srl	rt6, rt3, rt6		;;; shift down by 64-W-bitoffs = mask
	subq	rt4, rt3, rt4		;;; (64-W)-(64-W-bitoffs) = bitoffs
	ldW	rt3, 0(rusp)		;;; new value for field
	bic	rt5, rt6, rt5		;;; clear old field in quadword
	lda	rusp, _WOFFS(rusp)
	sll	rt3, rt4, rt3		;;; shift up new field by bitoffs
	and	rt3, rt6, rt3		;;; mask new field
	or	rt5, rt3, rt5		;;; insert new field in quadword
	;;; store rt5 quadword at unaligned address (loads still in rt1, rt2)
	insqh	rt5, rt0, rt4
	insql	rt5, rt0, rt3
	mskqh	rt2, rt0, rt2
	mskql	rt1, rt0, rt1
	or	rt2, rt4, rt2
	or	rt1, rt3, rt1
	stq_u	rt2, 7(rt0)		;;; must store high first
	stq_u	rt1, 0(rt0)
	ret	rzero, (rret)



ASM_END_FILE



/* --- Revision History ---------------------------------------------------
--- John Gibson, Feb 10 1997
	Removed _mt*chc subroutine
 */
