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

/*
*	This file contains a classical radix-2 implementation of the fast Fourier
*	transformation, for 30 bit signed integer data. The actual implementation
*	details are described by (compilable) C source as comments. To extract the
*	C code do this:
*
*	cat fft32c.S | grep "^//" | sed -e 's/^\/\/\t\?//' > fft32c_c_code.c
*
*	Now the C source wouldn't help if you want to understand how radix-2
*	FFT works. For that, you should consult textbooks or the Internet.
*	On the other hand, if you know how FFT works, the C code might help
*	to understand what the assembly does as sometimes it may not be obvious,
*	and, possibly could help you to port the code to a different processor.
*/

#include <arm_func.h>

/*
*	Register definitions, do *not* change them.
*/

#define	DP		r0				// Data pointer
#define	CP		r1				// Constant table pointer
#define	P2		r2				// Number of samples = 2^P2
#define	RV		r3				// 0: forward transform, !0: inverse transform
#define	Ar		r4				// Real part of dp[ n6 ]
#define	Ai		r5				// Imaginary part of dp[ n6 ]
#define	Br		r6				// Real part of the other dp[ n7 ]
#define	Bi		r7				// Imaginary part of dp[ n7 ]
#define	Ci		r8				// sin( phi )
#define	Cr		r9				// cos( phi )
#define	N0		r10
#define	N2		P2				// Alias, saved on stack
#define	N1		r11
#define	N3		Ar				// Alias, saved on stack
#define	N4		r12
#define	N5		r14
#define	N6		RV				// Alias but with trickey!

//	/* Reverses all bits in a 32 bit word, i.e. b31<=>b0, b30<=>b1 etc. */
//
//	static inline unsigned bitrev( unsigned x )
//	{
//		x = ( ( x & 0x55555555 ) << 1 ) | ( ( x >> 1 ) & 0x55555555 );
//		x = ( ( x & 0x33333333 ) << 2 ) | ( ( x >> 2 ) & 0x33333333 );
//		x = ( ( x & 0x0f0f0f0f ) << 4 ) | ( ( x >> 4 ) & 0x0f0f0f0f );
//		x = ( ( x & 0x00ff00ff ) << 8 ) | ( ( x >> 8 ) & 0x00ff00ff );
//		x = ( x >> 16 ) | ( x << 16 );
//		return( x );
//	}
//
//	/* Halve a 32-bit signed number and round the result */
//
//	static inline int halfround( int x )
//	{
//		if ( x >= 0 && ( x & 1 ) )
//			return( ( x >> 1 ) + 1 );
//		else
//			return( x >> 1 );
//	}
//
//	/* Shift a 64-bit signed number to the right by the given number of bits,
//	   round the upper 32 bits and return the upper 32 bits as an integer */
//
//	static inline int shiftround( long long x, unsigned shift )
//	{
//		x >>= shift - 1;
//		if ( x >= 0 ) x++;
//		return( (int) ( x >> 1 ) );
//	}
//
//	#define	RE( x ) (__real__ (x))
//	#define	IM( x ) (__imag__ (x))
//
//	void	fft32c( _Complex int *dp, int *tp, int p2, int rv )
//	{
//	int n0, n1, n2, n3;
//	int n4, n5, n6, n7;
//	int ar, br, cr;
//	int ai, bi, ci;
//	long long tl;
//

	.globl	_blm_fft32c
	afunc	fft32c
_blm_fft32c:
	stmdb	sp!,{r4-r11,lr}
	tst		RV,RV
	movne	RV,#1

//		n3 = p2;
//		n0 = 1 << p2;
//		n1 = n0;
//		n5 = 1;

	mov		N3,P2
	mov		N0,#1
	mov		N0,N0,lsl P2
	mov		N1,N0;
	mov		N5,#1

//		do
//		{

	str		P2,[sp,#-4]!
0:
	str		N3,[sp,#-4]!

//			n4 = 0;
//			n2 = 0;

	mov		N4,#0
	mov		N2,#0

//			do
//			{

1:

//				if ( n4 <= n0 >> 2 )
//				{
//					cr = tp[ ( n0 >> 2 ) - n4 ];
//					ci = tp[ n4 ];
//				}
//				else
//				{
//					cr = -tp[ n4 - ( n0 >> 2 ) ];
//					ci = tp[ ( n0 >> 1 ) - n4 ];
//				}
//				if ( ! rv ) ci = -ci;

	cmp		N4,N0,lsr #2
	ldrle	Ci,[CP,N4,lsl #2]
	suble	Cr,N0,N4,lsl #2
	ldrle	Cr,[CP,Cr]
	subgt	Ci,N0,N4,lsl #1
	ldrgt	Ci,[CP,Ci,lsl #1]
	rsbgt	Cr,N0,N4,lsl #2
	ldrgt	Cr,[CP,Cr]
	rsbgt	Cr,Cr,#0
	tst		RV,RV
	rsbeq	Ci,Ci,#0

//				n4 += n5;

	add		N4,N4,N5

//				n6 = n2;

	movs	N6,N6,lsr #1			// CY = PV
	mov		N6,N2
	adc		N2,N2,N2				// N2 = 2*N2 + PV

//				do
//				{

2:

//					n7 = n6 + ( n1 >> 1 );
//					ar = RE( dp[ n6 ] ) - RE( dp[ n7 ] );
//					ai = IM( dp[ n6 ] ) - IM( dp[ n7 ] );
//					br = RE( dp[ n6 ] ) + RE( dp[ n7 ] );
//					bi = IM( dp[ n6 ] ) + IM( dp[ n7 ] );
//					if ( ! rv )
//					{
//							ar = halfround( ar );
//							ai = halfround( ai );
//							br = halfround( br );
//							bi = halfround( bi );
//					}

	add		DP,DP,N6,lsl #3			// DP = dp + n6
	ldmia	DP,{Ar,Ai}
	add		DP,DP,N1,lsl #2			// DP = dp + n7
	ldmia	DP,{Br,Bi}
	sub		Ar,Ar,Br				// Ar: dp[n6].re - dp[n7].re
	add		Br,Ar,Br,lsl #1			// Br: dp[n6].re + dp[n7].re
	sub		Ai,Ai,Bi				// Ai: dp[n6].im - dp[n7].im
	add		Bi,Ai,Bi,lsl #1			// Bi: dp[n6].im + dp[n7].im
	tst		N2,#1					// It actually tests RV
	bne		3f
	movs	Ar,Ar,asr #1
	adcpl	Ar,Ar,#0
	movs	Ai,Ai,asr #1
	adcpl	Ai,Ai,#0
	movs	Br,Br,asr #1
	adcpl	Br,Br,#0
	movs	Bi,Bi,asr #1
	adcpl	Bi,Bi,#0
3:

//					RE( dp[ n6 ] ) = br;
//					IM( dp[ n6 ] ) = bi;

	sub		DP,DP,N1,lsl #2			// DP = dp + n6
	stmia	DP,{Br,Bi}

//					tl  = (long long) cr * ar;
//					tl += (long long) ci * ai;
//					RE( dp[ n7 ] ) = shiftround( tl, 30 );

	add		DP,DP,N1,lsl #2			// DP = dp + n7
	smull	Br,Bi,Cr,Ar
	smlal	Br,Bi,Ci,Ai
	mov		Bi,Bi,lsl #2
	orrs	Bi,Bi,Br,lsr #30
	adcpl	Bi,Bi,#0
	str		Bi,[DP,#0]				// Bi is free too

//					ar  = -ar;
//					tl  = (long long) ci * ar;
//					tl += (long long) cr * ai;
//					IM( dp[ n7 ] ) = shiftround( tl, 30 );

	rsb		Ar,Ar,#0
	smull	Br,Bi,Cr,Ai
	smlal	Br,Bi,Ci,Ar
	mov		Bi,Bi,lsl #2
	orrs	Bi,Bi,Br,lsr #30
	adcpl	Bi,Bi,#0
	str		Bi,[DP,#4]
	sub		DP,DP,N1,lsl #2			// DP = dp + n6
	sub		DP,DP,N6,lsl #3			// DP = dp

//					n6 += n1;

	add		N6,N6,N1

//				} while ( n6 < n0 );

	cmp		N6,N0
	blt		2b
	movs	N2,N2,lsr #1			// Restore N2 and RV
	movcs	RV,#1
	movcc	RV,#0

//			} while ( ++n2 < ( n1 >> 1 ) );

	add		N2,N2,#1
	cmp		N2,N1,lsr #1
	blt		1b

//			n5 <<= 1;
//			n1 >>= 1;
//		} while ( --n3 );

	ldr		N3,[sp],#4
	mov		N5,N5,lsl #1
	mov		N1,N1,lsr #1
	subs	N3,N3,#1
	bne		0b
	ldr		P2,[sp],#4

//		n1 = 0;
//		do
//		{

	mov		N1,#0
	mov		N5,#1
	orr		N5,N5,N5,lsl #8
	orr		N5,N5,N5,lsl #16		// n5 = 0x01010101
	rsb		P2,P2,#32				// p2 = 32 - p2
3:

//			ar = RE( dp[ n1 ] );
//			ai = IM( dp[ n1 ] );

	add		Ci,DP,N1,lsl #3
	ldmia	Ci,{Ar,Ai}

//			n4 = bitrev( n1 ) >> ( 32 - p2 );

	mov		N4,N1
	add		N6,N5,N5,lsl #2			// n6 = 0x05050505
	orr		N6,N6,N6,lsl #4			// n6 = 0x55555555
	and		CP,N6,N4,lsr #1
	and		N4,N4,N6
	orr		N4,CP,N4,lsl #1
	add		N6,N5,N5,lsl #1			// n6 = 0x03030303
	orr		N6,N6,N6,lsl #4			// n6 = 0x33333333
	and		CP,N6,N4,lsr #2
	and		N4,N4,N6
	orr		N4,CP,N4,lsl #2
	add		N6,N5,N5,lsl #1			// n6 = 0x03030303
	orr		N6,N6,N6,lsl #2			// n6 = 0x0f0f0f0f
	and		CP,N6,N4,lsr #4
	and		N4,N4,N6
	orr		N4,CP,N4,lsl #4
	mov		N6,#255					// n6 = 0x000000ff
	orr		N6,N6,N6,lsl #16		// n6 = 0x00ff00ff
	and		CP,N6,N4,lsr #8
	and		N4,N4,N6
	orr		N4,CP,N4,lsl #8
	mov		N4,N4,ror #16
	mov		N4,N4,lsr P2

//			if ( n1 < n4 )
//			{
//				br = RE( dp[ n4 ] );
//				bi = IM( dp[ n4 ] );
//				RE( dp[ n4 ] ) = ar;
//				IM( dp[ n4 ] ) = ai;
//				ar = br;
//				ai = bi;
//			}

	cmp		N1,N4
	addlt	Cr,DP,N4,lsl #3
	swplt	Ar,Ar,[Cr]
	addlt	Cr,Cr,#4
	swplt	Ai,Ai,[Cr]

//			if ( ar >= +0x4000000 ) ar = +0x3fffffff;
//			if ( ar <= -0x4000000 ) ar = -0x3fffffff;
//			if ( ai >= +0x4000000 ) ai = +0x3fffffff;
//			if ( ai <= -0x4000000 ) ai = -0x3fffffff;

	mov		Br,#0x40000000
	rsb		Bi,Br,#0
	cmp		Ar,Br
	subge	Ar,Br,#1
	cmp		Ar,Bi
	addle	Ar,Bi,#1
	cmp		Ai,Br
	subge	Ai,Br,#1
	cmp		Ai,Bi
	addle	Ai,Bi,#1

//			RE( dp[ n1 ] ) = ar;
//			IM( dp[ n1 ] ) = ai;

	stmia	Ci,{Ar,Ai}

//		} while ( ++n1 < n0 );

	add		N1,N1,#1
	cmp		N1,N0
	blt		3b

//	}

	ldmia	sp!,{r4-r11,lr}
	bx		lr
	.endfunc

