/* --- Copyright University of Sussex 1995. All rights reserved. ----------
 * File:		S.pcwnt/src/amove.s
 * Purpose:		Memory operations for 80x86 (Microsoft assembler)
 * Author:		Robert John Duncan, Apr 15 1994 (see revisions)
 * Related Files:	S.pcunix/src/amove.s
 */

/*************************************************************************
		THIS FILE WAS GENERATED AUTOMATICALLY FROM
		  /rsuna/pop/master/S.pcunix/src/amove.s
		     ON Fri Apr 15 10:37:39 BST 1994
	  AND SUBSEQUENTLY EDITED ON Fri Apr 15 13:08:26 BST 1994
*************************************************************************/

#_<

#_INCLUDE 'declare.ph'

lconstant macro	(

	USP = "ebx",

	_PAGESIZE = _:VPAGE_OFFS,
);

>_#

	.erre	@Version ge 611
	option	casemap:none
	.386
	.model	flat


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

	.code
	dword	L$text_size, C_LAB(Sys$-objmod_pad_key)
L$text_start:
	.data
	assume	cs:nothing
	dword	L$data_size, C_LAB(Sys$-objmod_pad_key)
L$data_start:

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


	.code

;;; === COMPARISONS ===================================================

;;; _BCMP:
;;;	Compare two byte regions of the same length

;;; Call:
;;;	_bcmp(_BYTE_LENGTH, _SRC1, _SRC2) -> BOOL

;;; Registers used:
;;;	ESI	pointer to region 1
;;;	EDI	pointer to region 2
;;;	ECX	length of the comparison in bytes

DEF_C_LAB(_bcmp)

	mov	edi, dword ptr [USP]
	mov	esi, dword ptr [USP+4]
	mov	ecx, dword ptr [USP+8]
	add	USP, 8

	;;; If size (ECX) is zero, then return <true> immediately

	cmp	ecx, 0
	je	L$1$1

	;;; Clear the direction flag to increment ESI/EDI

	cld

	;;; Compare the regions

	repz	cmpsb

	;;; If the last bytes compared equal return <true>,
	;;; otherwise return <false>

	je	L$1$1
	mov	dword ptr [USP], C_LAB(false)
	ret
L$1$1:	mov	dword ptr [USP], C_LAB(true)
	ret

	align	4

;;; _SCMP:
;;;	Compare two short (word) regions of the same length

;;; Call:
;;;	_scmp(_BYTE_LENGTH, _SRC1, _SRC2) -> BOOL

;;; Registers used:
;;;	ESI	pointer to region 1
;;;	EDI	pointer to region 2
;;;	ECX	length of the comparison

DEF_C_LAB(_scmp)

	mov	edi, dword ptr [USP]
	mov	esi, dword ptr [USP+4]
	mov	ecx, dword ptr [USP+8]
	add	USP, 8

	;;; Convert ECX to size in words; if zero, then return <true>
	;;; immediately

	sar	ecx, 1
	jz	L$1$2

	;;; Clear the direction flag to increment ESI/EDI

	cld

	;;; Compare the word regions

	repz	cmpsw

	;;; If the last words compared equal return <true>,
	;;; otherwise return <false>

	je	L$1$2
	mov	dword ptr [USP], C_LAB(false)
	ret
L$1$2:	mov	dword ptr [USP], C_LAB(true)
	ret

	align	4

;;; _ICMP:
;;; _CMP:
;;;	Compare two long word regions of the same length

;;; Call:
;;;	_cmp(_BYTE_LENGTH, _SRC1, _SRC2) -> BOOL

;;; Registers used:
;;;	ESI	pointer to region 1
;;;	EDI	pointer to region 2
;;;	ECX	length of the comparison

DEF_C_LAB(_icmp)
DEF_C_LAB(_cmp)

	mov	edi, dword ptr [USP]
	mov	esi, dword ptr [USP+4]
	mov	ecx, dword ptr [USP+8]
	add	USP, 8

	;;; Convert ECX to size in longs; if zero, then return <true>
	;;; immediately

	sar	ecx, 2
	jz	L$1$3

	;;; Clear the direction flag to increment ESI/EDI

	cld

	;;; Compare the longword regions

	repz	cmpsd

	;;; If the last longwords compared equal return <true>,
	;;; otherwise return <false>

	je	L$1$3
	mov	dword ptr [USP], C_LAB(false)
	ret
L$1$3:	mov	dword ptr [USP], C_LAB(true)
	ret

	align	4


;;; === MOVES =========================================================

;;; _MOVEQ:
;;;	Quick longword move. The move is done from the lowest word first,
;;;	so if the source and destination regions overlap, the direction of
;;;	the move must be downwards to preserve correctness.
;;;	Returns a pointer to the next destination longword.

;;; Call:
;;;	_moveq(_BYTE_LENGTH, _SRCADDR, _DSTADDR) -> _NEXT_DSTADDR;

;;; Registers used:
;;;	ESI	source pointer
;;;	EDI	destination pointer
;;;	ECX	length of the move

DEF_C_LAB(_moveq)

	mov	edi, dword ptr [USP]
	mov	esi, dword ptr [USP+4]
	mov	ecx, dword ptr [USP+8]
	add	USP, 8

	;;; Compare source and destination pointers and quit if the same

	cmp	edi, esi
	je	L$2$1

	;;; Convert the byte-length in ECX to longwords and quit if zero

	sar	ecx, 2
	jz	L$1$4

	;;; Do the move

	cld
	rep	movsd

L$1$4:	;;; Return the next destination address

	mov	dword ptr [USP], edi
	ret

L$2$1:	;;; Source and destination the same:
	;;; compute and return the next destination address

	add	edi, ecx
	mov	dword ptr [USP], edi
	ret

	align	4

;;; _BMOVE:
;;; _SMOVE:
;;; _IMOVE:
;;; _MOVE:
;;;	General purpose moves for bytes, words and longs. Cope with any
;;;	alignment and with moves in both directions. Return address of the
;;;	next destination.

;;; Call:
;;;	_move(_BYTE_LENGTH, _SRCADDR, _DSTADDR) -> _NEXT_DSTADDR;

;;; Uses:
;;;	movchars, movchars_l (defined below)

DEF_C_LAB(_bmove)
DEF_C_LAB(_smove)

	;;; Set register arguments for -movchars-

	mov	edi, dword ptr [USP]
	mov	esi, dword ptr [USP+4]
	mov	ecx, dword ptr [USP+8]
	add	USP, 8

	;;; Push the next destination address as the return value

	lea	eax, dword ptr [edi+ecx]
	mov	dword ptr [USP], eax

	;;; Do the move

	jmp	movchars

	align	4

DEF_C_LAB(_imove)
DEF_C_LAB(_dmove)
DEF_C_LAB(_move)

	;;; Set register arguments for movchars

	mov	edi, dword ptr [USP]
	mov	esi, dword ptr [USP+4]
	mov	ecx, dword ptr [USP+8]
	add	USP, 8

	;;; Push the next destination address as the return value

	lea	eax, dword ptr [edi+ecx]
	mov	dword ptr [USP], eax

	;;; Do the move in longword steps

	jmp	movchars_l

	align	4

;;; MOVCHARS:
;;; MOVCHARS_L:
;;; 	General purpose memory move. Not callable from Pop, as it takes its
;;;	arguments in registers rather than on the stack (but cf. _bmove,
;;;	_smove, _move above). MOVCHARS_L is for use when the move is
;;;	longword-aligned.

;;; Arguments:
;;;	ESI	source address
;;;	EDI	destination address
;;;	ECX	length of move in bytes

;;; Results:
;;;	none

;;; Other registers used:
;;;	EAX	work

movchars:

	;;; OR together the bits of the source address, destination address
	;;; and byte count to test for word alignment

	mov	eax, esi
	or	eax, edi
	or	eax, ecx

	;;; If neither of the bottom two bits is set, the move is longword
	;;; aligned so move a longword at a time

	test	eax, 3
	jz	movchars_l

	;;; Otherwise, move a byte at a time

movchars_b:

	;;; If ECX is zero there's nothing to move, so exit

	cmp	ecx, 0
	je	L$2$2

	;;; Clear the direction flag for a default downwards move

	cld

	;;; Test the direction of the move:
	;;; if ESI > EDI, then the move is down, so jump straight to it;
	;;; if ESI = EDI, then there's no move at all, so return

	cmp	esi, edi
	ja	L$1$5
	je	L$2$2

	;;; Otherwise, ESI < EDI, so it's an upward move: change the
	;;; direction flag, and adjust the pointers to point to the last
	;;; byte in each region rather than the first

	std
	lea	esi, dword ptr [esi+ecx-1]
	lea	edi, dword ptr [edi+ecx-1]

L$1$5:	;;; Do the move

	rep	movsb

L$2$2:	;;; Return

	ret

	align	4

movchars_l:

	;;; Convert ECX to the length of the move in longwords; if zero,
	;;; jump to the end.

	sar	ecx, 2
	jz	L$2$3

	;;; Clear the direction flag for a default downwards move

	cld

	;;; Test the direction of the move:
	;;; if ESI > EDI, then the move is down, so jump straight to it;
	;;; if ESI = EDI, then there's no move at all, so return

	cmp	esi, edi
	ja	L$1$6
	je	L$2$3

	;;; Otherwise, ESI < EDI, so it's an upward move: change the
	;;; direction flag, and adjust the pointers to point to the last
	;;; word in each region rather than the first

	std
	lea	esi, dword ptr [esi+ecx*4-4]
	lea	edi, dword ptr [edi+ecx*4-4]

L$1$6:	;;; Do the move

	rep	movsd

L$2$3:	;;; Return

	ret

	align	4


;;; === FILLING =======================================================

;;; _BFILL:
;;;	Fill a region of bytes with a given byte.

;;; Call:
;;;	_bfill(_BYTE, _BYTE_LENGTH, _DSTADDR);

;;; Registers used:
;;;	EAX	the byte
;;;	ECX	length of the region in bytes
;;;	EDI	destination pointer

DEF_C_LAB(_bfill)

	mov	edi, dword ptr [USP]
	mov	ecx, dword ptr [USP+4]
	mov	eax, dword ptr [USP+8]
	add	USP, 12

	;;; Clear the direction flag to increment EDI and do the fill

	cld
	rep	stosb
	ret

	align	4

;;; _IFILL:
;;; _FILL:
;;;	Fill a region of longwords with a given longword.

;;; Call:
;;;	_fill(_LONG, _BYTE_LENGTH, _DSTADDR);

;;; Registers used:
;;;	EAX	the longword
;;;	ECX	length of the region in bytes
;;;	EDI	destination pointer

DEF_C_LAB(_ifill)
DEF_C_LAB(_fill)

	mov	edi, dword ptr [USP]
	mov	ecx, dword ptr [USP+4]
	mov	eax, dword ptr [USP+8]
	add	USP, 12

	;;; Clear the direction flag to increment EDI, convert ECX to
	;;; length in longwords and do the filling

	cld
	sar	ecx, 2
	rep	stosd
	ret

	align	4


;;; === RELOCATING POPLOG MEMORY REGIONS ==============================

;;; _MOVE_USERSTACK:
;;;	Move the user stack up or down.

;;; Call:
;;;	_move_userstack(_BYTE_OFFS)

;;; Register usage:
;;;	ESI	source pointer
;;;	EDI	destination pointer
;;;	ECX	_USERHI (start of user stack), then stack length in longwords
;;;		(the amount to move)
;;;	EAX	the amount of the shift in bytes (_BYTE_OFFS)

DEF_C_LAB(_move_userstack)

	;;; Load the amount to shift (in bytes) into EAX

	mov	eax, dword ptr [USP]
	add	USP, 4

	;;; Test the direction of the move

	test	eax, eax
	jl	move_userstack_down
	jg	move_userstack_up

	;;; Zero move -- return

	ret

	align	4

move_userstack_down:

	;;; Move from the bottom (USP) upwards

	;;; Adjust the value of USERHI

	mov	ecx, dword ptr I_LAB(_userhi)
	add	ecx, eax
	mov	dword ptr I_LAB(_userhi), ecx

	;;; Adjust the stack pointer and make the old value the source
	;;; pointer and the new value the destination pointer

	mov	esi, USP
	add	USP, eax
	mov	edi, USP

	;;; Compute the length of the move (USERHI - USP) in ECX and do it

	sub	ecx, USP
	sar	ecx, 2
	cld
	rep	movsd
	ret

	align	4

move_userstack_up:

	;;; Move from the top (USERHI) downwards

	;;; Adjust USERHI. Make the old value the source pointer and the new
	;;; the destination pointer. Pointers are offset by 4 bytes because
	;;; USERHI points one word beyond the top of stack.

	mov	ecx, dword ptr I_LAB(_userhi)
	lea	esi, dword ptr [ecx-4]
	add	ecx, eax
	lea	edi, dword ptr [ecx-4]
	mov	dword ptr I_LAB(_userhi), ecx

	;;; Adjust the stack pointer

	add	USP, eax

	;;; Compute the length of the move (USERHI - USP) in ECX and do it

	sub	ecx, USP
	sar	ecx, 2
	std
	rep	movsd
	ret

	align	4

;;; _MOVE_CALLSTACK:
;;;	Relocate the top part of the callstack between the stack pointer
;;;	ESP and the _LIMIT address. The size of the relocation is _BYTE_OFFS
;;;	and may be up or down.

;;; Call:
;;;	_move_callstack(_BYTE_OFFS, _LIMIT);

;;; Registers used:
;;;	ESI	source pointer
;;;	EDI	destination pointer
;;;	ECX	LIMIT address, then the amount to move (LIMIT - ESP)
;;;	EAX	the size of the shift (_BYTE_OFFS)

DEF_C_LAB(_move_callstack)

	;;; Load limit address to ECX, byte offset to EAX

	mov	ecx, dword ptr [USP]
	mov	eax, dword ptr [USP+4]
	add	USP, 8

	;;; Test the direction of the move

	test	eax, eax
	jl	move_callstack_down
	jg	move_callstack_up

	;;; Zero move -- return

	ret

	align	4

move_callstack_down:

	;;; Move from the bottom (ESP) upwards:

	;;; The length of the move is (_LIMIT - ESP)

	sub	ecx, esp

	;;; Adjust the stack pointer and make the old value the source
	;;; pointer and the new value the destination pointer.
	;;; For moves bigger than one page, we have to probe the stack
	;;; to ensure we don't step over the guard page

	mov	esi, esp
@@:	add	eax, _PAGESIZE
	jge	@F
	lea	esp, dword ptr [esp-_PAGESIZE]
	or	dword ptr [esp], 0
	jmp	@B
@@:	lea	esp, dword ptr [esp+eax-_PAGESIZE]
	mov	edi, esp

	;;; Do the move

	sar	ecx, 2
	cld
	rep	movsd
	ret

	align	4

move_callstack_up:

	;;; Move from the _LIMIT address downwards

	;;; Make _LIMIT the source address (offset by 4 to point to the
	;;; first word to be moved) and (SOURCE + _BYTE_OFFS) the destination

	lea	esi, dword ptr [ecx-4]
	lea	edi, dword ptr [esi+eax]

	;;; The length of the move is (_LIMIT - ESP)

	sub	ecx, esp

	;;; Do the move

	sar	ecx, 2
	std
	rep	movsd

	;;; Set the new stack pointer

	lea	esp, dword ptr [edi+4]

	ret

	align	4


;;; === BITFIELD OPERATIONS ===========================================

;;; _BFIELD:
;;; _SBFIELD:
;;;	Extract an (un)signed bitfield. Not called directly from POP, but
;;;	used to implement M_MOVE(s)bit and I_PUSH_FIELD instructions.

;;; Arguments:
;;;	EDI	structure address
;;;	EAX	bit offset of field within structure
;;;	EDX	length of field in bits

;;; Results:
;;;	EAX	the extracted bitfield, right-justified and zero- or
;;;		sign-extended as appropriate

DEF_C_LAB(_bfield)

	;;; Copy bit offset to ECX

	mov	ecx, eax

	;;; Convert bit offset to word index

	sar	eax, 5

	;;; Compute address of the first word of the bitfield in EDI.
	;;; Load first word to EAX and second to EDI in case of overspill.

	lea	edi, dword ptr [edi+eax*4]
	mov	eax, dword ptr [edi]
	mov	edi, dword ptr [edi+4]

	;;; Mask the copied offset in ECX to get a bit-within-word offset
	;;; and shift EAX down by that amount to right-align the field
	;;; (bits are pulled in to the top end from EDI)

	and	ecx, 31
	shrd	eax, edi, cl

	;;; Zero the top (32 - field-width) bits

	mov	ecx, 32
	sub	ecx, edx
	sal	eax, cl
	shr	eax, cl

	ret

	align	4

DEF_C_LAB(_sbfield)

	;;; Copy bit offset to ECX

	mov	ecx, eax

	;;; Convert bit offset to word index

	sar	eax, 5

	;;; Compute address of the first word of the bitfield in EDI.
	;;; Load first word to EAX and second to EDI in case of overspill.

	lea	edi, dword ptr [edi+eax*4]
	mov	eax, dword ptr [edi]
	mov	edi, dword ptr [edi+4]

	;;; Mask the copied offset in ECX to get a bit-within-word offset
	;;; and shift EAX down by that amount to right-align the field
	;;; (bits are pulled in to the top end from EDI)

	and	ecx, 31
	shrd	eax, edi, cl

	;;; Sign-extend the field into the top (32 - field-width) bits

	mov	ecx, 32
	sub	ecx, edx
	sal	eax, cl
	sar	eax, cl

	ret

	align	4

;;; _UBFIELD:
;;;	Update a bitfield within a structure. Not called directly from POP
;;;	but used to implement M_UPDbit and I_POP_FIELD instructions.

;;; Arguments:
;;;	EDI	structure address
;;;	EAX	bit offset of field within structure
;;;	EDX	length of the field in bits
;;;	(%USP)	(top of user stack) the new value as a system integer

;;; Results:
;;;	none

;;; Other registers used:
;;;	EAX	overwritten with the new value of the bitfield
;;;	EDX	the bit offset within the first word of the field
;;;	ESI	the current value of the bitfield

DEF_C_LAB(_ubfield)

	;;; Compute (32 - field-width) in ECX

	mov	ecx, 32
	sub	ecx, edx

	;;; Copy the bit offset into EDX

	mov	edx, eax

	;;; Convert bit offset to word index and add to the structure
	;;; address to get a pointer to the first word of the bitfield

	sar	eax, 5
	lea	edi, dword ptr [edi+eax*4]

	;;; Get the new value of the field from the stack top to EAX
	;;; EAX = [ ___ | MMM' ]

	mov	eax, dword ptr [USP]
	add	USP, 4

	;;; Shift left by (32 - field-width) to left-align it in EAX
	;;; EAX = [ MMM' | ___ ]

	shl	eax, cl

	;;; Mask the copied bit offset in EDX to get a bit-within-word
	;;; offset

	and	edx, 31

	;;; Subtract from the value in ECX: this gives the number of unwanted
	;;; bits in the high end of the first word

	sub	ecx, edx

	;;; If negative, the field must overspill into a second word,
	;;; so jump to handle that

	jl	L$1$7

	;;; Otherwise, load the current value of the target word into ESI
	;;; ESI = [ HHH | MMM | LLL ]

	mov	esi, dword ptr [edi]

	;;; Rotate to move the current field value to the top
	;;; ESI = [ MMM | LLL | HHH ]

	rol	esi, cl

	;;; Reinstate the shift count and pull the extra bits from ESI
	;;; into the top of EAX
	;;; EAX = [ LLL | HHH | MMM' ]

	add	ecx, edx
	shrd	eax, esi, cl

	;;; Rotate the low order bits to their correct place
	;;; EAX = [ HHH | MMM' | LLL ]

	mov	ecx, edx
	rol	eax, cl

	;;; Store the updated word and return

	mov	dword ptr [edi], eax
	ret

L$1$7:	;;; Field overspills into a second word -- load that first
	;;; ESI = [ HHH | MMH ]

	mov	esi, dword ptr [edi+4]

	;;; The size of the overspill is the absolute value of ECX;
	;;; shift ESI by that amount, then pull in the top bits of the
	;;; new value. Drop the corresponding bits from EAX.

	neg	ecx
	shr	esi, cl	;;; ESI = [ 000 | HHH ]
	shld	esi, eax, cl	;;; ESI = [ HHH | MMH' ]
	shl	eax, cl	;;; EAX = [ MML' | 000 ]

	;;; Store the updated high word

	mov	dword ptr [edi+4], esi

	;;; Now get the low word
	;;; ESI = [ MML | LLL ]

	mov	esi, dword ptr [edi]

	;;; Only the bottom bits of this are wanted, the count being in
	;;; EDX. Shift them into the high part of EAX, then rotate to
	;;; place them in the low part

	mov	ecx, edx
	shrd	eax, esi, cl	;;; EAX = [ LLL | MML' ]
	rol	eax, cl	;;; EAX = [ MML' | LLL ]

	;;; Store the low word and return

	mov	dword ptr [edi], eax
	ret

	align	4



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

	.code
L$text_end:
	L$text_size	equ	L$text_end-L$text_start
	.data
	assume	cs:nothing
L$data_end:
	L$data_size	equ	L$data_end-L$data_start

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

@CurSeg	ends
	extern	C_LAB(Sys$-objmod_pad_key):near
	extern	C_LAB(false):near
	extern	C_LAB(true):near
	extern	I_LAB(_userhi):near
	end


/* --- Revision History ---------------------------------------------------
--- John Gibson, Apr  6 1995
	Added _icmp, _imove, _dmove, _ifill as equivalent to word versions
 */
