;=====================================================
; VARIOUS PROCEDURES (3DNOW VERSIONS)
;=====================================================
; Procs actually implemented are referenced in the proc table
; found in 'Data\generaldata.inc'. Procs found here may
; not be used at all (either incomplete, inaccurate, or both).

;============================================================
ATXPROC VectorNormalize_3DNOW			; eax = vector (must not be null)
;============================================================
; eax unchanged

	push DWORD [eax+8]			; create two z component 32-bit floats
	push DWORD [eax+8]

	femms

	movq mm0,[eax]
	movq mm1,[esp]
	pfmul mm0,mm0
	pfmul mm1,mm1
	pfacc mm0,mm0
	pfadd mm0,mm1

	pfrsqrt mm3,mm0 			; 1/vectorlength (estimate)
	movq mm1,[eax]
	movq mm4,mm3
	pfmul mm3,mm3				; required for pfrsqit1
	pfrsqit1 mm0,mm3			; refine reciprocal square root value (part 1)
	pfrcpit2 mm0,mm4			; refine reciprocal square root value (part 2)
	movd mm2,[eax+8]

	pfmul mm1,mm0
	pfmul mm2,mm0
	movq [eax],mm1
	movd [eax+8],mm2

	femms

add esp,8
ret

;============================================================
ATXPROC NormalizeQuat_3DNOW			; eax = quat (w,x,y,z)
;============================================================
;eax unchanged

	femms

	movq mm0,[eax]
	movq mm1,[eax+8]
	pfmul mm0,mm0
	pfmul mm1,mm1
	pfadd mm0,mm1
	pfacc mm0,mm0				; mm0low = mm0high = mm0loworig+mm0highorig

	pfrsqrt mm3,mm0 			; 1/quatmagnitude (estimate)
	movq mm1,[eax]
	movq mm4,mm3
	pfmul mm3,mm3				; required for pfrsqit1
	pfrsqit1 mm0,mm3			; refine reciprocal square root value (part 1)
	pfrcpit2 mm0,mm4			; refine reciprocal square root value (part 2)
	movq mm2,[eax+8]

	pfmul mm1,mm0
	pfmul mm2,mm0
	movq [eax],mm1
	movq [eax+8],mm2

	femms
ret

;============================================================
ATXPROC CONVERT_TO_FACING_PROC_3DNOW		; [esp+4] = destvar, [esp+8]=param2, source quat=ecx
;============================================================

; [destvar+00h] = ((c4^2+(c0^2-q))*a0 + (c4*c8-c0*cC)*a4 + (c0*c8+c4*cC)*a8)*2
; [destvar+04h] = ((c8^2+(c0^2-q))*a4 + (c8*cC-c0*c4)*a8 + (c0*cC+c4*c8)*a0)*2
; [destvar+08h] = ((cC^2+(c0^2-q))*a8 + (c4*cC-c0*c8)*a0 + (c0*c4+c8*cC)*a4)*2
; note: in mm0 = var1, var2
;       var1 is low dword of register, var2 is high dword

	mov edx,[esp+8]
	cmp DWORD [edx+4],1.000000		; ybase = 1.000000?
	mov eax,[edx]
	    jnz .nonstandardbasevector
	test eax,07FFFFFFFh			; xbase = 0.000000 or -0.000000?
	mov eax,[edx+8]
	    jnz .nonstandardbasevector
	test eax,07FFFFFFFh			; zbase = 0.000000 or -0.000000?
	    jz CONVERT_TO_FACING_PROC_3DNOW_010
	.nonstandardbasevector: 		; (for (0.0, 1.0, 0.0) base vector)

	femms
	mov eax,[esp+4]
	sub esp,4
	movq mm0,qword [c0]
	movq mm1,qword [c8]
	pfmul mm0,mm0
	pfmul mm1,mm1
	pswapd mm2,mm0
	movd [esp],mm0
	push DWORD [esp]
	movq mm0,[esp]				; mm0 = c0^2,c0^2, mm1 = c8^2,cC^2, mm2 = c4^2,(c0^2)  (all ^2)

	pfadd mm1,mm0
	pfadd mm2,mm0
	pfsub mm1,qword [float_0_5]
	pfsub mm2,qword [float_0_5]
	pfmul mm1,qword [a4]			; mm1 = (c8^2+(c0^2-q))*a4,(cC^2+(c0^2-q))*a8
	pfmul mm2,qword [a0]			; mm2 = (c4^2+(c0^2-q))*a0

	movq mm5,qword [c0]
	pswapd mm3,mm5
	pfmul mm3,qword [c8]			; mm3 = c4*c8,c0*cC
	push [c0]
	push [cC]
	pswapd mm4,qword [c4]
	pfmul mm4,qword [esp]			; mm4 = c8*cC,c0*c4
	pfmul mm5,qword [c8]
	pswapd mm5,mm5				; mm5 = c4*cC,c0*c8

; pfpnacc mm,mm
; -> mmlow = mmlow-mmhigh, mmhigh = mmlow+mmhigh

	pfpnacc mm3,mm3 			; mm3 = c4*c8-c0*cC,c4*c8+c0*cC
	pfpnacc mm4,mm4 			; mm4 = c8*cC-c0*c4,c8*cC+c0*c4
	pfpnacc mm5,mm5 			; mm5 = c4*cC-c0*c8,c4*cC+c0*c8

	pswapd mm0,qword [a0]
	pswapd mm6,qword [a4]
	pfmul mm3,mm0				; mm3 = (c4*c8-c0*cC)*a4,(c4*c8+c0*cC)*a0
	push [a8]
	push [a0]
	pfmul mm4,mm6				; mm4 = (c8*cC-c0*c4)*a8,(c8*cC+c0*c4)*a4
	pfmul mm5,qword [esp]			; mm5 = (c4*cC-c0*c8)*a0,(c4*cC+c0*c8)*a8

	pfadd mm1,mm4				; mm1 = ((c8^2+(c0^2-q))*a4 + (c8*cC-c0*c4)*a8),
						;      ((cC^2+(c0^2-q))*a8 + (c8*cC+c0*c4)*a4)
	pswapd mm6,mm1				; mm6 = ((cC^2+(c0^2-q))*a8 + (c8*cC+c0*c4)*a4)
	pfadd mm6,mm5				; mm6 = ((cC^2+(c0^2-q))*a8 + (c8*cC+c0*c4)*a4 + (c4*cC-c0*c8)*a0)

	pswapd mm5,mm5
	pfadd mm2,mm3				; mm2 = (c4^2+(c0^2-q))*a0 + (c4*c8-c0*cC)*a4

	pswapd mm3,mm3
	pfadd mm2,mm5				; mm2 = ((c4^2+(c0^2-q))*a0 + (c4*c8-c0*cC)*a4 + (c4*cC+c0*c8)*a8)
	pfadd mm1,mm3				; mm1 = ((c8^2+(c0^2-q))*a4 + (c8*cC-c0*c4)*a8 + (c4*c8+c0*cC)*a0)

	pfadd mm2,mm2				; each *2
	pfadd mm1,mm1
	pfadd mm6,mm6
	movd [eax],mm2
	movd [eax+4],mm1
	movd [eax+8],mm6

	femms

	add esp,24
ret 8

; [destvar+00h] = ((c4^2+(c0^2-q))*a0 + (c4*c8+(-c0)*cC)*a4 + (c0*c8+c4*cC)*a8)*2
; [destvar+04h] = ((c8^2+(c0^2-q))*a4 + (c8*cC+(-c0)*c4)*a8 + (c0*cC+c4*c8)*a0)*2
; [destvar+08h] = ((cC^2+(c0^2-q))*a8 + (c4*cC+(-c0)*c8)*a0 + (c0*c4+c8*cC)*a4)*2

;============================================================
ATXPROC CONVERT_TO_FACING_PROC_3DNOW_010	; [esp+4] = destvar, [esp+8]=param2 (ignored), source quat=ecx
;============================================================
; [destvar+00h] = (c4*c8-c0*cC)*2     (X facing value)
; [destvar+04h] = (c8^2+(c0^2-q))*2   (Y facing value)
; [destvar+08h] = (c0*c4+c8*cC)*2     (Z facing value)

; executes in parallel

	femms

	mov eax,[esp+4]

	push [cC]
	push [c4]
	push [c8]
	push [c0]

	pswapd mm0,qword [c0]			; c4,c0
	movq mm2,[esp]				; c0,c8
	movq mm3,mm2				; c0,c8

	pfmul mm2,[esp+8]			; c0*c4,c8*cC
	pfmul mm3,mm3				; c0^2,c8^2
	pfmul mm0,qword [c8]			; c4*c8,c0*cC

	pfacc mm3,mm3				; c0^2+c8^2
	pfpnacc mm0,mm2 			; c4*c8-c0*cC,c8*cC+c0*c4
	pfsub mm3,qword [float_0_5]		; (c0^2+c8^2)-0.5

	pfmul mm0,qword [float_two]		; (c4*c8-c0*cC)*2,(c8*cC+c0*c4)*2
	pfmul mm3,qword [float_two]		; (c0^2+c8^2-0.5)*2

	pswapd mm1,mm0
	movd [eax],mm0
	movd [eax+4],mm3
	movd [eax+8],mm1

	femms
	add esp,16
ret 8

;============================================================
ATXPROC QuaternionMul_3DNOW		; eax=q1 , ecx=q2, edx=new quat
;============================================================
; Quaternion multiplication (eax=q1, ecx=q2, edx=result)
; This 3DNOW version uses 2x fewer multiplication instructions and
; 17 less instructions than the STD version
;
; (Q1 * Q2).w = (w1w2 - x1x2 - y1y2 - z1z2)
; (Q1 * Q2).x = (w1x2 + x1w2 + y1z2 - z1y2)
; (Q1 * Q2).y = (w1y2 - x1z2 + y1w2 + z1x2)
; (Q1 * Q2).z = (w1z2 + x1y2 - y1x2 + z1w2) = (w1z2 + x1y2 + z1w2 - y1x2)

	femms

	movq mm5,qword [w1]			; w1,x1
	movq mm6,qword [y1]			; y1,z1

;------------- w
	movq mm0,qword [w2]			; w2,x2
	movq mm1,qword [y2]			; y2,z2
	pfmul mm0,mm5				; w1*w2,x1*x2
	pfmul mm1,mm6				; y1*y2,z1*z2
	pfpnacc mm0,mm1 			; w1*w2-x1*x2,y1*y2+z1*z2
	pfnacc mm0,mm0				; w1*w2-x1*x2-(y1*y2+z1*z2) (low 32-bit)
;------------- x
; do not use: mm0
	pswapd mm1,qword [w2]			; x2,w2
	pswapd mm2,qword [y2]			; z2,y2
	pfmul mm1,mm5				; w1*x2,x1*w2
	pfmul mm2,mm6				; y1*z2,z1*y2
	pfpnacc mm2,mm1 			; y1*z2-z1*y2,w1*x2+x1*w2
	pfacc mm2,mm2				; y1*z2-z1*y2+w1*x2+x1*w2 (low 32-bit)
;------------- y
; do not use: mm0,mm2
	movq mm1,qword [y2]			; y2,z2
	movq mm3,qword [w2]			; w2,x2
	pfmul mm1,mm5				; w1*y2,x1*z2
	pfmul mm3,mm6				; y1*w2,z1*x2
	pfpnacc mm1,mm3 			; w1*y2-x1*z2,y1*w2+z1*x2
	pfacc mm1,mm1				; w1*y2-x1*z2+y1*w2+z1*x2 (low 32-bit)
;------------- z
; do not use: mm0,mm2,mm1
	pswapd mm3,qword [y2]			; z2,y2
	pswapd mm4,mm6				; z1,y1
	pfmul mm3,mm5				; w1*z2,x1*y2
	pfmul mm4,qword [w2]			; z1*w2,y1*x2
	pfpnacc mm4,mm3 			; z1*w2-y1*x2,w1*z2+x1*y2
	pfacc mm4,mm4				; z1*w2-y1*x2+w1*z2+x1*y2 (low 32-bit)
;------------- store
	movd [edx],mm0
	movd [edx+4],mm2
	movd [edx+8],mm1
	movd [edx+12],mm4

	femms
ret


ATXPROC QuaternionMul_3DNOW_GAMEVERSION 	; eax=q1 , ecx=q2, edx=new quat
	mov eax,ecx
	mov edx,[esp+4]
	mov ecx,[esp+8]

	femms

	movq mm5,qword [w1]			; w1,x1
	movq mm6,qword [y1]			; y1,z1

;------------- w
	movq mm0,qword [w2]			; w2,x2
	movq mm1,qword [y2]			; y2,z2
	pfmul mm0,mm5				; w1*w2,x1*x2
	pfmul mm1,mm6				; y1*y2,z1*z2
	pfpnacc mm0,mm1 			; w1*w2-x1*x2,y1*y2+z1*z2
	pfnacc mm0,mm0				; w1*w2-x1*x2-(y1*y2+z1*z2) (low 32-bit)
;------------- x
; do not use: mm0
	pswapd mm1,qword [w2]			; x2,w2
	pswapd mm2,qword [y2]			; z2,y2
	pfmul mm1,mm5				; w1*x2,x1*w2
	pfmul mm2,mm6				; y1*z2,z1*y2
	pfpnacc mm2,mm1 			; y1*z2-z1*y2,w1*x2+x1*w2
	pfacc mm2,mm2				; y1*z2-z1*y2+w1*x2+x1*w2 (low 32-bit)
;------------- y
; do not use: mm0,mm2
	movq mm1,qword [y2]			; y2,z2
	movq mm3,qword [w2]			; w2,x2
	pfmul mm1,mm5				; w1*y2,x1*z2
	pfmul mm3,mm6				; y1*w2,z1*x2
	pfpnacc mm1,mm3 			; w1*y2-x1*z2,y1*w2+z1*x2
	pfacc mm1,mm1				; w1*y2-x1*z2+y1*w2+z1*x2 (low 32-bit)
;------------- z
; do not use: mm0,mm2,mm1
	pswapd mm3,qword [y2]			; z2,y2
	pswapd mm4,mm6				; z1,y1
	pfmul mm3,mm5				; w1*z2,x1*y2
	pfmul mm4,qword [w2]			; z1*w2,y1*x2
	pfpnacc mm4,mm3 			; z1*w2-y1*x2,w1*z2+x1*y2
	pfacc mm4,mm4				; z1*w2-y1*x2+w1*z2+x1*y2 (low 32-bit)
;------------- store
	movd [edx],mm0
	movd [edx+4],mm2
	movd [edx+8],mm1
	movd [edx+12],mm4

	mov eax,edx
	mov ecx,edx

; normalizes quaternion
	movq mm0,[eax]
	movq mm1,[eax+8]
	pfmul mm0,mm0
	pfmul mm1,mm1
	pfadd mm0,mm1
	pfacc mm0,mm0				; mm0low = mm0high = mm0loworig+mm0highorig

    ; mm0 = magnitude of quaternion (in both mm0low and mm0 high)

	movq mm5,mm0
	pfsubr mm5,QWORD [float_one]				; substract one
	pand mm5,[floatabsvaluemask]		; get absolute value
	pfcmpgt mm5,QWORD [quatnormrange]	; if below or equal, bits will be set to 0
	movd [esp+8],mm5					; overwrites parameter, which the game does anyway
	test BYTE [esp+8],1					; (all bits will be set to same value, can test only one)
	     jz .nonormalize

	pfrsqrt mm3,mm0 			; 1/quatmagnitude (estimate)
	movq mm1,[eax]
	movq mm4,mm3
	pfmul mm3,mm3				; required for pfrsqit1
	pfrsqit1 mm0,mm3			; refine reciprocal square root value (part 1)
	pfrcpit2 mm0,mm4			; refine reciprocal square root value (part 2)
	movq mm2,[eax+8]

	pfmul mm1,mm0
	pfmul mm2,mm0
	movq [eax],mm1
	movq [eax+8],mm2
;-------------

	.nonormalize:

	femms

ret 8
QuaternionMul_3DNOW_GAMEVERSION_END:


;============================================================
ATXPROC QuaternionMulLocalQ2AxisX_3DNOW 	; eax=q1 , ecx=q2, edx=new quat
;============================================================
; Limited Quaternion multiplication (eax=q1, ecx=q2, edx=result)
; supposes second quaternion is rotation around x axis only (y2 and z2 null)
; eax = source quat (all valid)
; ecx = around-x quat
; w2 = non-zero
; x2 = non-zero
; y2 = null
; z2 = null
; (Q1 * Q2).w = (w1w2 - x1x2)
; (Q1 * Q2).x = (x1w2 + w1x2)
; (Q1 * Q2).y = (y1w2 + z1x2)
; (Q1 * Q2).z = (z1w2 - y1x2)

	 femms
	 movq mm4,qword [w2]			; w2,x2

	 movq mm0,qword [w1]			; w1,x1
	 movq mm2,qword [y1]			; y1,z1
	 pswapd mm1,qword [w1]			; x1,w1
	 pswapd mm3,qword [y1]			; z1,y1

	 pfmul mm0,mm4				; w1*w2,x1*x2
	 pfmul mm1,mm4				; x1*w2,w1*x2
	 pfmul mm2,mm4				; y1*w2,z1*x2
	 pfmul mm3,mm4				; z1*w2,y1*x2

	 pfpnacc mm0,mm1			; w1*w2-x1*x2,x1*w2+w1*x2
	 pfpnacc mm3,mm2			; z1*w2-y1*x2,y1*w2+z1*x2
	 pswapd mm3,mm3 			; y1*w2+z1*x2,z1*w2-y1*x2

	 movq [edx],mm0
	 movq [edx+8],mm3

	 femms

ret

