/*
** Copyright 2001, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/

.globl mymemcpy_sse

.align 4
mymemcpy_sse:
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%edi	/* dest */
	movl	16(%esp),%esi	/* source */
	movl	20(%esp),%ecx	/* count */
	cld 

	/* see if we have enough to copy to bother with sse */
	cmpl	$256, %ecx
	jl		fallback_copy

	/* move the total count to edx */
	movl	%ecx, %edx

	/* are we unevenly 8 byte aligned (ie, source 1 byte, dest 3 byte)? */
	movl	%edi,%eax
	xorl	%esi,%eax
	andl	$0x7,%eax
	jnz		fallback_copy

	/* are we dest 16 byte aligned? */
	movl	%edi,%eax
	andl	$0xf,%eax
	jz		aligned

	/* dest is unaligned to a 16 byte boundary */
unaligned:
	/* align the destination to 16 bytes */
	movl	%edi, %ecx
	neg		%ecx
	andl	$0xf, %ecx

	/* subtract the amount we're about to move from the total remaining size (edx) */
	subl	%ecx, %edx

	rep
	movsb

	/* dest aligned to a 16 byte boundary */
aligned:

#if 1
	/* prefetch half a page in front of the source */
	movl	$4096, %ecx
prefetch_loop:
	movb	(%esi),%al
	prefetchnta	32(%esi)
	addl	$32, %esi
	subl	$32, %ecx
	jnbe	prefetch_loop

	subl	$4096, %esi
#endif

	movl	%edx, %ecx
	shrl	$4, %ecx

	/* copy 16 bytes at a time using a sse register */
aligned16_loop:
	jecxz	aligned_done
	movups	(%esi), %xmm0
	addl	$16, %esi
	movntps %xmm0, (%edi)
	addl	$16, %edi

	nop; nop; nop;
	nop; nop; nop;

	decl	%ecx
	jmp		aligned16_loop

aligned_done:
	/* we should have less than 16 bytes left to copy */
	movl	%edx, %ecx
	andl	$0xf, %ecx

bytewise:
	/* move any remaining data by bytes */
	rep
	movsb

done:	
	movl	12(%esp),%eax	/* return dest */
	popl	%edi
	popl	%esi
	ret

fallback_copy:
	movl	%ecx, %edx
	shrl	$2, %ecx
	rep
	movsl

	movl	%edx, %ecx
	andl	$3, %ecx

	rep
	movsb

	jmp done
