/****************************************************************************/
/*	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.																	*/
/****************************************************************************/

#include <arm_func.h>

/*
*	These two functions are the most crucial functions in the entire
*	library because modular multiplication and thus modular
*	Assembly equivalent of the following C code:
*
------------------------------------------------------------------------------
unsigned _bc_NanuMulu( unsigned *r, unsigned *a, unsigned b, unsigned l )
{
unsigned long long temp;

	for ( temp = 0 ; l-- ; temp >>= 32 ) {

		temp += (unsigned long long) b * *a++ + *r;
		*r++ = temp;
	}

	return( temp );
}
------------------------------------------------------------------------------
*	The most crucial piece of code in the entire library, as the speed of
*	the slowest operation, the exponentiation depends on this. Naturally
*	it is written in assembly :-)
*	It is basically a multiply-accumulate, where an N-word long number is
*	multiplied with a single word, the result is added to the N-word
*	accumulator (i.e. the result) and the N+1 -th word of the result,
*	which is *not* stored in the accumulator, is returned.
*
*	Also note that there can not be overflows in this. Assume that
*	temp was 0x00000000ffffffff, b is 0xffffffff, *a is 0xffffffff and *r
*	is also 0xffffffff. Then:
*
*	0x00000000ffffffff	temp
*	0xfffffffe00000001	a * b
*	0x00000000ffffffff	r
*	------------------
*	0xffffffffffffffff	and there's no overflow.
*/

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

/*
*	unsigned _bc_NanuMulm( R, A, M, t, b, f, l )
*	{
*		R  += A * b + M * t;
*		R >>= 32;
*	}
*
*	where variables in uppercase indicate multiword numbers, the
*	argument 'l' is the length of these numbers. In normal C it looks
*	like this (not efficient but equivalent):
---------------------------------------------------------------------------
unsigned	_bc_NanuMulm( unsigned *R, unsigned *A, unsigned *M,
						  unsigned t, unsigned b, unsigned f, unsigned l )
{
unsigned long long	p, q, z;
unsigned int		c;

	p = (unsigned long long) *A++ * b;
	q = (unsugned long long) *M++ * t;

	p = p + (unsigned) q;
	q = q + *R;
	p = ( p >> 32 ) + ( q >> 32 );
	c = p >> 32;
	p = (unsigned) p;

	while ( --l ) {

		q  = R[ 1 ];
		p += (unsigned long long) *A++ * b;
		q += (unsigned long long) *M++ * t;
		q += (unsigned) p;
		p  = ( q >> 32 ) + ( p >> 32 ) + c;
		c  = p >> 32;
		p  = (unsigned) p;
		*R++ = q;
	}

	p = p + f;
	*r = p;

	return( c + ( p >> 32 ) );
}
---------------------------------------------------------------------------
*	In the code below 'c' is directly mapped to r14, the lower half of p
*	is equivalent to r12. The arguments are in R:r0, A:r1, M:r2, t:r3,
*	b:r4 and l:r5. The rest of the registers have no direct equivalent
*	in the C code, in fact, the assembly processes the data in 3-word
*	blocks rather than one word at the time.
*/

	afunc	_bc_NanuMulm
	mov		r12,sp					// Save SP, we need it for parameters
	stmdb	sp!,{r4-r11,lr}			// We need all the regs we can have...
	ldr		r4,[r12]				// Fetch 'b'
	ldr		r5,[r12,#8]				// fetch 'l'
	mov		r12,#0

	// We have r0-r5 in use, r6-r12 and r14 is available.
	// Do the first word, because it is special and we will discard
	// the result at the end anyway.

	ldr		r6,[r0]					// r6 = *R
	ldr		r7,[r1],#4				// r7 = *A++
	umull	r8,r9,r7,r4				// r9:r8 = *A * b
	ldr		r7,[r2],#4				// r7 = *M++
	mov		r10,#0
	umlal	r8,r10,r7,r3
	adds	r9,r9,r10				// r9:r8 = *A * b + *M * t, carry is set
	str		r9,[r0],#4				// *R++ = (r9:r8) >> 32
	mov		r12,#0
	adc		r12,r12,#0				// Carry moved to R12
 	mov		r14,r9					// Upper word moved to R14

	// Now we can start to loop, if we have at least 3 words
	// remaining. Note that we've already processed the first word.

	subs	r5,r5,#4
	bmi		1f
0:
	ldmia	r0,{r6-r8}				// 3 words from R
	ldmia	r1!,{r9-r11}			// 3 words from A

	umlal	r6,r12,r9,r4			// A * b first part + the carry
	adds	r6,r6,r14				// Add the carried over word
	adcs	r7,r12,r7				// Add the carried over carry
	mov		r12,#0
	adc		r12,r12,#0				// Let R12 be the carry

	umlal	r7,r12,r10,r4			// A * b second part
	adds	r8,r12,r8
	mov		r12,#0
	adc		r12,r12,#0

	umlal	r8,r12,r11,r4			// A * b third part

	ldmia	r2!,{r9-r11}			// 3 words from M
	mov		r14,#0

	umlal	r6,r14,r9,r3			// M * t first part
	adds	r7,r14,r7
	mov		r14,#0
	adc		r14,r14,#0

	umlal	r7,r14,r10,r3			// M * t second part
	adds	r8,r14,r8
	mov		r14,#0
	adc		r14,r14,#0
	umlal	r8,r14,r11,r3			// M * t third part

	adds	r14,r14,r12
	mov		r12,#0
	adc		r12,r12,#0

	sub		r0,r0,#4
	stmia	r0!,{r6-r8}
	add		r0,r0,#4

	subs	r5,r5,#3
	bpl		0b
1:
	adds	r5,r5,#3
	beq		1f
	mov		r10,#0
0:
	// We go one word at a time

	ldr		r6,[r0,#4]
	ldr		r7,[r1],#4
	ldr		r8,[r2],#4
	umlal	r6,r12,r7,r4
	umlal	r14,r9,r8,r3
	adds	r6,r6,r14
	adcs	r14,r9,r12
	adc		r12,r10,r10
	str		r6,[r0],#4
	subs	r5,r5,#1
	bne		0b
1:
	mov		r1,r14
	mov		r2,r12
	ldmia	sp!,{r4-r11,r14}
	ldr		r3,[sp,#4]				// fetch f
	adds	r1,r1,r3
	adc		r2,r2,#0
	str		r1,[r0]
	mov		r0,r2
	bx		lr

