/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss or reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

/*
*	These are the speed-critical routines in the NANU package.
*	Their C equivalents can be found in the nanu_utils.c file.
*	In that file you can also find more detailed explanations.
*	Definitions in the nanu-nanu.h control what's get compiled
*	in C or used as assembly routines.
*/

#define	ASSEMBLY_SOURCE
#include <arm_func.h>
#include <nanu-nanu.h>

#if NANU_ASM_SUB

/*
*	R = A - B where all three has length 'l'
*
*	Returns a flag is there was a borrow or not
*
*	At entry:
*	r0	R[]
*	r1	A[]
*	r2	B[]
*	r3	l
*/

	afunc	_bc_NanuSub2
	str		r4,[sp,#-4]!
	subs	r12,r12,r12
	b		1f
0:
	ldr		r12,[r1],#4
	ldr		r4,[r2],#4
	sbcs	r12,r12,r4
	str		r12,[r0],#4
1:
	sub		r3,r3,#1
	tst		r3,r3
	bpl		0b
	mov		r0,#0
	adc		r0,r0,r0
	eor		r0,r0,#1
	ldr		r4,[sp],#4
	bx		lr
	.endfunc

/*
*	R = A - ( s ? 1 : 0 ) where A and R are l long.
*
*	Returns if there was a borrow at the end
*
*	At entry:
*	r0	R[]
*	r1	A[]
*	r2	s
*	r3	l
*/

	afunc	_bc_NanuSub1
	rsbs	r2,r2,#0
	b		1f
0:
	ldr		r2,[r1],#4
	sbcs	r2,r2,#0
	str		r2,[r0],#4
1:
	sub		r3,r3,#1
	tst		r3,r3
	bpl		0b
	mov		r0,#0
	adc		r0,r0,r0
	eor		r0,r0,#1
	bx		lr
	.endfunc

#endif

#if NANU_ASM_ADD

/*
*	R = A + B where all three has length 'l'
*
*	Returns a flag is there was a carry or not
*
*	At entry:
*	r0	R[]
*	r1	A[]
*	r2	B[]
*	r3	l
*/

	afunc	_bc_NanuAdd2
	str		r4,[sp,#-4]!
	adds	r4,r4,#0
	b		1f
0:
	ldr		r12,[r1],#4
	ldr		r4,[r2],#4
	adcs	r12,r12,r4
	str		r12,[r0],#4
1:
	sub		r3,r3,#1
	tst		r3,r3
	bpl		0b
	mov		r0,#0
	adc		r0,r0,r0
	ldr		r4,[sp],#4
	bx		lr
	.endfunc

/*
*	R = A + ( s ? 1 : 0 ) where A and R are l long.
*
*	Returns if there was a carry at the end
*
*	At entry:
*	r0	R[]
*	r1	A[]
*	r2	s
*	r3	l
*/

	afunc	_bc_NanuAdd1
	adds	r2,r2,#-1
	b		1f
0:
	ldr		r2,[r1],#4
	adcs	r2,r2,#0
	str		r2,[r0],#4
1:
	sub		r3,r3,#1
	tst		r3,r3
	bpl		0b
	mov		r0,#0
	adc		r0,r0,r0
	bx		lr
	.endfunc

#endif

#if NANU_ASM_MULR

/*
*	Calculates R = ( R << 32 ) + Y * x where R is 'l' long and Y is 's' long.
*	Only the lower 'l' words of the result are stored, the up to 33 most
*	significant bits are returned.
*
*	At entry:
*	r0		R[]
*	r1		Y[]
*	r2		x
*	r3		s
*	sp[0]	l
*/

	afunc	_bc_NanuMulr
	ldr		r12,[sp]
	stmdb	sp!,{r4-r7}
	mov		r4,#0
	mov		r5,#0
	mov		r6,#0
	b		1f
0:
	cmp		r4,r3
	ldrlt	r7,[r1,r4,lsl #2]
	umlallt	r6,r5,r7,r2
	add		r7,r0,r4,lsl #2
	swp		r6,r6,[r7]
	adds	r6,r6,r5
	mov		r5,#0
	adc		r5,r5,r5
	add		r4,r4,#1
1:
	cmp		r4,r12
	blt		0b
	mov		r0,r6
	mov		r1,r5
	ldmia	sp!,{r4-r7}
	bx		lr
	.endfunc

#endif

#if NANU_ASM_MULU

/*
*	Calculates the R += A * b function, R and A are 'l' long multiwords,
*	b is single word. The retun value is the most significant 32 bits of
*	the result. That 32 bits do not get stored, only returned.
*
*	At entry:
*	r0		R[]
*	r1		A[]
*	r2		b
*	r3		l
*/

	afunc	_bc_NanuMulu
	stmdb	sp!,{r4-r11,lr}
	mov		r14,#0
	mov		r12,#0
0:
	subs	r3,r3,#4
	bmi		1f
	ldmia	r0,{r4-r7}
	ldmia	r1!,{r8-r11}
	adds	r4,r4,r12
	adc		r12,r14,r14
	umlal	r4,r12,r8,r2
	adds	r5,r5,r12
	adc		r12,r14,r14
	umlal	r5,r12,r9,r2
	adds	r6,r6,r12
	adc		r12,r14,r14
	umlal	r6,r12,r10,r2
	adds	r7,r7,r12
	adc		r12,r14,r14
	umlal	r7,r12,r11,r2
	stmia	r0!,{r4-r7}
	b		0b
1:
	adds	r3,r3,#3
	b		3f
2:
	ldr		r4,[r0]
	ldr		r8,[r1],#4
	adds	r4,r4,r12
	adc		r12,r14,r14
	umlal	r4,r12,r8,r2
	str		r4,[r0],#4
	subs	r3,r3,#1
3:
	bpl		2b
	mov		r0,r12
	ldmia	sp!,{r4-r11,lr}
	bx		lr
	.endfunc

#endif

#if NANU_ASM_MULM

/*
*	Calculates R = ( R + A * k + M * t + (f << 32*l) ) >> 32
*	The return value is the overflow, if any.
*
*	At entry:
*	r0		R[]
*	r1		A[]
*	r2		M[]
*	r3		t
*	sp[0]	k	-> r4
*	sp[1]	f	-> r5
*	sp[2]	a	-> r6			length of A
*	sp[3]	l	-> r7			length of R and M
*/

#define	R	r0
#define	A	r1
#define	M	r2
#define	t	r3
#define	k	r4
#define	f	r5
#define	a	r6
#define	l	r7
#define	qh	r8
#define	ql	r9
#define	ph	r10
#define	pl	r11
#define	c	r12
#define	w	r14

	afunc	_bc_NanuMulm
	mov		r12,sp
	stmdb	sp!,{r4-r11,lr}
	ldmia	r12,{r4-r7}

	//	a = a - 1;
	//	p = (unsigned long long) *A++ * k;
	//	q = (unsigned long long) *M++ * t;

	sub		a,a,#1
	ldr		pl,[A],#4
	umull	pl,ph,k,pl
	ldr		ql,[M],#4
	umull	ql,qh,t,ql

	//	q = q + *R;

	ldr		w,[R]
	adds	ql,ql,w
	adc		qh,qh,#0

	//	p = p + (unsigned) q;

	adds	pl,pl,ql
	adc		ph,ph,#0

	//	p = ( p >> 32 ) + ( q >> 32 );
	//	c = p >> 32;
	//	p = (unsigned) p;

	adds	pl,ph,qh
	mov		ph,#0
	adc		c,ph,ph

	//	while ( --l ) {

	b		1f
0:
	//		if ( (int) --a >= 0 ) p += (unsigned long long) *A++ * k;

	subs	a,a,#1
	ldrpl	w,[A],#4
	umlalpl	pl,ph,w,k

	//		q  = R[ 1 ];

	ldr		ql,[R,#4]
	mov		qh,#0

	//		q += (unsigned long long) *M++ * t;

	ldr		w,[M],#4
	umlal	ql,qh,w,t

	//		q += (unsigned) p;

	adds	ql,ql,pl

	//		p  = ( q >> 32 ) + ( p >> 32 ) + c;
	//		c  = p >> 32;
	//		p  = (unsigned) p;

	mov		w,ph
	mov		ph,#0
	adcs	pl,w,qh
	adc		w,ph,ph
	adds	pl,pl,c
	adc		c,w,ph

	//		*R++ = q;

	str		ql,[R],#4

	//	}

1:
	subs	l,l,#1
	bhi		0b

	//	p = p + f;
	//	*R = p;
	//	return( c + ( p >> 32 ) );

	adds	pl,pl,f
	str		pl,[R]
	adc		r0,c,ph
	ldmia	sp!,{r4-r11,lr}
	bx		lr
	.endfunc

#undef	R
#undef	A
#undef	M
#undef	t
#undef	k
#undef	f
#undef	a
#undef	l
#undef	qh
#undef	ql
#undef	ph
#undef	pl
#undef	c
#undef	w

#endif

#if NANU_ASM_MULS

// FIXME	Do it faster, it's in the Montgomery, so very important

/*
*	Calculates R = R - M * q
*	~~~~~~~~~~~~~~~~~~~~~~~~~
*	R and M are l-long multiwords, q is a single word. The return value
*	is the borrow, a signed 64-bit quantity.
*
*	At entry:
*	r0		R[]
*	r1		M[]
*	r2		q
*	r3		l
*/

	afunc	_bc_NanuMuls
	stmdb	sp!,{r4-r7}
	mov		r4,#0
	mov		r5,#0
	b		1f
0:
	ldr		r12,[r1],#4
	umull	r6,r7,r12,r2		// u
	ldr		r12,[r0]
	adds	r4,r4,r12			// s
	adc		r5,r5,#0
	subs	r4,r4,r6
	sbc		r5,r5,#0
	str		r4,[r0],#4
	mov		r4,r5
	mov		r5,r5,asr #32
	subs	r4,r4,r7
	sbc		r5,r5,#0
1:
	subs	r3,r3,#1
	bpl		0b
	mov		r0,r4
	mov		r1,r5
	ldmia	sp!,{r4-r7}
	bx		lr
	.endfunc

#endif

