/* powmod-i386.S -- (C) Geoffrey Reynolds, October 2006.

   uint64_t powmod64(uint64_t a, uint64_t n, uint64_t p);
     %edx:%eax <-- a^n (mod p). Assumes %st(0) contains 1.0/p.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
*/

#ifdef __MINGW32__
#define powmod64 _powmod64
#endif

	.text
	.p2align 4
	.globl powmod64

#if HAVE_CMOV
powmod64:
	push	%ebp
	push	%edi
	push	%esi
	push	%ebx
	sub	$20, %esp
	mov	56(%esp), %ebx
	mov	60(%esp), %esi  /* p --> %esi:%ebx */
	mov	$1, %eax
	xor	%edx, %edx	/* $1 --> %edx:%eax */
	xor	%ecx, %ecx
	testb	$1, 48(%esp)	/* n odd? */
	cmovnz	40(%esp), %eax
	cmovnz	44(%esp), %edx  /* a --> %edx:%eax */
	jmp	1f

	.p2align 4

0:	/* b <-- b^2 (mod p) */
	fildll	40(%esp)	/* b */
	mov	40(%esp), %eax
	mul	%eax
	fmul	%st(0), %st(0)
	mov	%eax, %edi
	mov	%edx, %ebp
	mov	44(%esp), %eax
	fmul	%st(1), %st(0)
	mull	40(%esp)
	fistpll	8(%esp)
	lea	(%ebp,%eax,2), %ebp /* %ebp <-- 2 * %eax + %ebp */
	mov	%ebx, %eax
	mull	8(%esp)
	sub	%eax, %edi
	sbb	%edx, %ebp
	mov	%esi, %eax
	mull	8(%esp)
	sub	%eax, %ebp
	mov	%ebx, %eax
	mull	12(%esp)
	sub	%eax, %ebp
	mov	%ebx, %eax
	mov	%esi, %edx	/* p --> %edx:%eax */
	cmovge	%ecx, %eax
	cmovge	%ecx, %edx	/* $0 --> %edx:%eax */
	add	%edi, %eax
	adc	%ebp, %edx
	
	testb	$1, 48(%esp)	/* n odd? */
	mov	%eax, 40(%esp)
	mov	%edx, 44(%esp)
	jz	2f

	/* a <-- a*b (mod p) */
	fildll	(%esp)		/* a */
	fildll	40(%esp)	/* b */
	mull	(%esp)
	fmulp	%st(0), %st(1)
	mov	%eax, %edi
	mov	%edx, %ebp
	mov	4(%esp), %eax
	fmul	%st(1), %st(0)
	mull	40(%esp)
	fistpll	8(%esp)
	add	%eax, %ebp
	mov	(%esp), %eax
	mull	44(%esp)
	add	%eax, %ebp
	mov	%ebx, %eax
	mull	8(%esp)
	sub	%eax, %edi
	sbb	%edx, %ebp
	mov	%esi, %eax
	mull	8(%esp)
	sub	%eax, %ebp
	mov	%ebx, %eax
	mull	12(%esp)
	sub	%eax, %ebp
	mov	%ebx, %eax
	mov	%esi, %edx	/* p --> %edx:%eax */
	cmovge	%ecx, %eax
	cmovge	%ecx, %edx	/* $0 --> %edx:%eax */
	add	%edi, %eax
	adc	%ebp, %edx
1:
	mov	%eax, (%esp)
	mov	%edx, 4(%esp)

2:	/* n <-- n >> 1 */
	mov	48(%esp), %eax
	mov	52(%esp), %edx
	shrd	$1, %edx, %eax
	shr	%edx
	mov	%eax, 48(%esp)
	mov	%edx, 52(%esp)
	or	%edx, %eax      /* n zero? */
	jnz	0b

	mov	(%esp), %eax
	mov	4(%esp), %edx
	add	$20, %esp
	pop	%ebx
	pop	%esi
	pop	%edi
	pop	%ebp
	ret
#else
powmod64:
	push	%edi
	push	%esi
	push	%ebx
	sub	$16, %esp
	mov	48(%esp), %ebx
	mov	52(%esp), %esi  /* p --> %esi:%ebx */
	testb	$1, 40(%esp)	/* n odd? */
	jz	0f

	mov	32(%esp), %edi
	mov	36(%esp), %ecx  /* a --> %ecx:%edi */
	jmp	3f
0:
	mov	$1, %edi
	xor	%ecx, %ecx	/* $1 --> %ecx:%edi */
	jmp	3f

	.p2align 4

1:	/* b <-- b^2 (mod p) */
	fildll	32(%esp)	/* b */
	mov	32(%esp), %eax
	mul	%eax
	fmul	%st(0), %st(0)
	mov	%eax, %edi
	mov	%edx, %ecx
	mov	36(%esp), %eax
	fmul	%st(1), %st(0)
	mull	32(%esp)
	fistpll	8(%esp)
	lea	(%ecx,%eax,2), %ecx /* %ecx <-- 2 * %eax + %ecx */
	mov	%ebx, %eax
	mull	8(%esp)
	sub	%eax, %edi
	sbb	%edx, %ecx
	mov	%esi, %eax
	mull	8(%esp)
	sub	%eax, %ecx
	mov	%ebx, %eax
	mull	12(%esp)
	sub	%eax, %ecx
	jge	2f

	add	%ebx, %edi
	adc	%esi, %ecx
2:
	testb	$1, 40(%esp)	/* n odd? */
	mov	%edi, 32(%esp)
	mov	%ecx, 36(%esp)
	jz	4f

	/* a <-- a*b (mod p) */
	fildll	(%esp)		/* a */
	fildll	32(%esp)	/* b */
	mov	(%esp), %eax
	mull	%edi
	fmulp	%st(0), %st(1)
	mov	%eax, %edi
	mov	%edx, %ecx
	mov	4(%esp), %eax
	fmul	%st(1), %st(0)
	mull	32(%esp)
	fistpll	8(%esp)
	add	%eax, %ecx
	mov	(%esp), %eax
	mull	36(%esp)
	add	%eax, %ecx
	mov	%ebx, %eax
	mull	8(%esp)
	sub	%eax, %edi
	sbb	%edx, %ecx
	mov	%esi, %eax
	mull	8(%esp)
	sub	%eax, %ecx
	mov	%ebx, %eax
	mull	12(%esp)
	sub	%eax, %ecx
	jge	3f

	add	%ebx, %edi
	adc	%esi, %ecx
3:
	mov	%edi, (%esp)
	mov	%ecx, 4(%esp)

4:	/* n <-- n >> 1 */
	mov	40(%esp), %eax
	mov	44(%esp), %edx
	shrd	$1, %edx, %eax
	shr	%edx
	mov	%eax, 40(%esp)
	mov	%edx, 44(%esp)
	or	%edx, %eax      /* n zero? */
	jnz	1b

	mov	(%esp), %eax
	mov	4(%esp), %edx
	add	$16, %esp
	pop	%ebx
	pop	%esi
	pop	%edi
	ret
#endif
