;///////////////////////////////////////////////////////////////////////////////
;/// @file memcpy.asm
;///
;/// @brief Destiny3D Memory Manager memcpy replacement
;///
;/// @author Lightning
;///
;/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
;/// contents of this file is subject to the Destiny3D Member License which
;/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
;///
;/// This file is distributed "AS IS" without warranty of any kind.  Novus
;/// Delta, LLC. does not guarantee the fitness of the contents of this file
;/// for any particular purpose.
;///
;/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
;///
;/// <hr>
;///                                 Change History
;/// <hr>
;///
;/// @date May 2001
;/// @author Lightning
;/// @remarks Creation
;///
;///////////////////////////////////////////////////////////////////////////////

.586p					;pentium commands allowed
.mmx					;MMX allowed
.k3d					;3DNow allowed
.model flat, C

.CODE

;i take care of pushing and popping edi, esi, ebp, and ebx if I need to use them
;as it makes it easier to cut down on the code that the function goes thru

;this array is what is used for copying memory that has a length of 7 bytes or
;smaller. It is global and the labels in the Internal_ASM_memcpy function are public
;labels for this area
ByteArray dd	memcpy_Bytes_0, memcpy_Bytes_1, memcpy_Bytes_2, memcpy_Bytes_3,
				memcpy_Bytes_4, memcpy_Bytes_5, memcpy_Bytes_6, memcpy_Bytes_7,
				06867694Ch,		06E696E74h,		065440067h,		090904433h

memcpy_Bytes_0:
	pop eax
	pop ebx
	ret

memcpy_Bytes_1:
	mov al, [ecx]
	mov [ebx], al
	pop eax
	pop ebx
	ret

memcpy_Bytes_2:
	mov ax, [ecx]
	mov [ebx], ax
	pop eax
	pop ebx
	ret

memcpy_Bytes_3:
	mov ax, [ecx]
	mov [ebx], ax
	mov cl, [ecx+2]
	mov [ebx+2], cl
	pop eax
	pop ebx
	ret

memcpy_Bytes_4:
	mov eax, [ecx]
	mov [ebx], eax
	pop eax
	pop ebx
	ret

memcpy_Bytes_5:
	mov eax, [ecx]
	mov [ebx], eax
	mov dl, [ecx+4]
	mov [ebx+4], dl
	pop eax
	pop ebx
	ret

memcpy_Bytes_6:
	mov eax, [ecx]
	mov [ebx], eax
	mov dx, [ecx+4]
	mov [ebx+4], dx
	pop eax
	pop ebx
	ret

memcpy_Bytes_7:
	mov eax, [ecx]
	mov [ebx], eax
	mov dx, [ecx+4]
	mov [ebx+4], dx
	mov cl, [ecx+6]
	mov [ebx+6], cl
	pop eax
	pop ebx
	ret


Internal_ASM_memcpy proc
	;the Paramaters are void *Ptr1, void *Ptr2, long Len

	push ebx
	mov ebx, esp
	align 4

	;divide by 16, edx = remainder
	mov ecx, [ebx + 0Ch]	;Ptr2
	mov eax, [ebx + 10h]	;Len
	mov ebx, [ebx + 08h]	;Ptr1

	mov edx, eax	;set incase we are doing 7 bytes or less

	push ebx	;pop'd to set eax on return to the pointer we wrote to

	test eax, 0FFFFFFF8h
	jnz short LargerThan7Bytes

	jmp dword ptr ByteArray[eax*4]

	LargerThan7Bytes:
		;save ecx so we can divide
		push ecx

		xor edx, edx
		mov ecx, 8
		div ecx
		
		;restore it now
		pop ecx

		;eax now is set to how many times we gotta go thru the loop below
		;push edx so we can find out later (after the loop) where to jump to
		push edx

	MemCpyLoop:
		mov edx, [ecx]
		mov [ebx], edx

		mov edx, [ecx+4]
		mov [ebx+4], edx

		add ecx, 8
		add ebx, 8
		dec eax
		jnz short MemCpyLoop

	;recall the value from the div
	pop edx
	jmp dword ptr ByteArray[edx*4]

Internal_ASM_memcpy endp


Internal_ASM_MMX_memcpy proc
	;the Paramaters are void *Ptr1, void *Ptr2, long Len

	push ebx
	mov ebx, esp
	align 4

	;divide by 16, edx = remainder
	mov ecx, [ebx + 0Ch]	;Ptr2
	mov eax, [ebx + 10h]	;Len
	mov ebx, [ebx + 08h]	;Ptr1

	mov edx, eax	;set incase we are doing 15 bytes or less

	push ebx	;pop'd to set eax on return to the pointer we wrote to

	test eax, 0FFFFFFF0h
	jnz short LargerThan15Bytes

	;i have the following instead of changing the above jump
	;so that if there is only 1 thru 7 bytes i don't call emms

	;see if the number of bytes left is 8 or more, if so then take a jmp
	mov edx, eax
	test eax, 8
	jnz short BytesLeft8

	;smaller than 8, jump to the place the array points to
	jmp dword ptr ByteArray[eax*4]

	LargerThan15Bytes:
		;save ecx so we can divide
		push ecx
		
		xor edx, edx
		mov ecx, 16
		div ecx
		
		;restore ecx
		pop ecx

		;eax now is set to how many times we gotta go thru the loop below
		;push edx so we can find out later (after the loop) where to jump to
		push edx

	MemCpyLoop_MMX:
		movq mm0, [ecx]
		movq mm1, [ecx+8]
		movq [ebx], mm0
		movq [ebx+8], mm1


		add ecx, 16
		add ebx, 16
		dec eax
		jnz short MemCpyLoop_MMX

	;recall the value from the div
	pop edx

	;do a test for 8 bytes or more...
	test edx, 8
	jz BytesLeft7

	BytesLeft8:
		movq mm0, [ecx]
		movq [ebx], mm0
		add ecx, 8
		add ebx, 8
		and edx, 7

	BytesLeft7:
		emms	;finish the MMX state
		jmp dword ptr ByteArray[edx*4]

Internal_ASM_MMX_memcpy endp

;you may ask, what is diff between this and Internal_ASM_MMX_memcpy?
;the diff is that this uses femms. I tried incorporating prefetch but
;it actually caused the code to run slower. The femms slices off a tad
;more time. More time for the engine :)
Internal_ASM_AMD_MMX_memcpy proc
	;the Paramaters are void *Ptr1, void *Ptr2, long Len

	push ebx
	mov ebx, esp
	align 4

	;divide by 16, edx = remainder
	mov ecx, [ebx + 0Ch]	;Ptr2
	mov eax, [ebx + 10h]	;Len
	mov ebx, [ebx + 08h]	;Ptr1

	mov edx, eax	;set incase we are doing 15 bytes or less

	push ebx	;pop'd to set eax on return to the pointer we wrote to

	test eax, 0FFFFFFF0h
	jnz short LargerThan15Bytes

	;see if the number of bytes left is 8 or more, if so then take a jmp
	test eax, 8
	jnz BytesLeft8

	;smaller than 8, jump to the place the array points to
	jmp dword ptr ByteArray[eax*4]

	LargerThan15Bytes:
		;save ecx so we can divide
		push ecx
		
		xor edx, edx
		mov ecx, 16
		div ecx
		
		;restore ecx
		pop ecx

		;eax now is set to how many times we gotta go thru the loop below
		;push edx so we can find out later (after the loop) where to jump to
		push edx

	MemCpyLoop_MMX:
		movq mm0, [ecx]
		movq mm1, [ecx+8]
		movq [ebx], mm0
		movq [ebx+8], mm1


		add ecx, 16
		add ebx, 16
		dec eax
		jnz short MemCpyLoop_MMX

	;recall the value from the div
	pop edx

	;do a test for 8 bytes or more...
	test edx, 8
	jz BytesLeft7

	BytesLeft8:
		movq mm0, [ecx]
		movq [ebx], mm0
		add ecx, 8
		add ebx, 8
		and edx, 7

	BytesLeft7:
		femms	;finish the MMX state
		jmp dword ptr ByteArray[edx*4]

Internal_ASM_AMD_MMX_memcpy endp

END