/****************************************************************************/
/*	Copyright 2008 (c) Bendor Research Pty. Ltd. All rights reserved.		*/
/*  This software is licenced under a BSD licence. That is, you can use,	*/
/*	modify and distribute it for any purpose whatsoever as long as you		*/
/*	credit Bendor Research for creating it. This software is provided		*/
/*	"as is", without any expressed or implied warranty and you assume any	*/
/*	and all risk associated with the performance of the software.			*/
/*	By using this program you acknowledge that neither Bendor Research nor 	*/
/*	its personnel can be held liable for damages of any kind including but	*/
/*	not restricted to direct, indirect or consequential damage arising out	*/
/*	of the use or inability to use of this program or its documentation, 	*/
/*	or the failure or inadequate performance of this program even if Bendor */
/*	Research was advised of the possibility of such damages.				*/
/****************************************************************************/

#ifndef _MISC_MULTIPLY_H
#define	_MISC_MULTIPLY_H

/** @addtogroup misc_mult */
/**	@{ */

#ifndef __thumb__

/**
*	Unsigned 32x32 => 64 integer multiplication.
*
*	@param	a, b	The 32 bit multiplicands
*	@return			The 64 bit result
*/

static inline unsigned long long umul32x32_64( unsigned int a,
											   unsigned int b )
{
	return( (unsigned long long) a * b );
}

/**
*	Unsigned 32x32 => 64 integer multiply-accumulate.
*
*	@param	s		The 64 bit to add the result to
*	@param	a, b	The 32 bit multiplicands
*	@return			The 64 bit result, s += a * b
*/

static inline unsigned long long umac32x32_64( unsigned long long s,
											   unsigned int a,
											   unsigned int b )
{
	asm volatile
	(
		"umlal	%Q[s],%R[s],%[a],%[b]\n\t"
		:
		[s] "=r"  (s)
		:
		    "[s]" (s),
		[a] "r"   (a),
		[b] "r"   (b)
	);

	return( s );
}

/**
*	Signed 32x32 => 64 integer multiplication.
*
*	@param	a, b	The 32 bit multiplicands
*	@return			The 64 bit result
*/

static inline signed long long smul32x32_64( signed int a, signed int b )
{
	return( (signed long long) a * b );
}

/**
*	Signed 32x32 => 64 integer multiply-accumulate.
*
*	@param	s		The 64 bit to add the result to
*	@param	a, b	The 32 bit multiplicands
*	@return			The 64 bit result, s += a * b
*/

static inline signed long long smac32x32_64( signed long long s,
											 signed int a,
											 signed int b )
{
	asm volatile
	(
		"smlal	%Q[s],%R[s],%[a],%[b]\n\t"
		:
		[s] "=r"  (s)
		:
		    "[s]" (s),
		[a] "r"   (a),
		[b] "r"   (b)
	);

	return( s );
}

/**
*	Unsigned 64x32 => 64 integer multiplication.
*
*	@param	a		The 64 bit multiplicand
*	@param	b		The 32 bit multiplicand
*	@return			The lower 64 bits of the 96 bit result
*/

static inline unsigned long long umul64x32_64( unsigned long long a,
											   unsigned int b )
{
	return( a * b );
}

/**
*	Signed 64x32 => 64 integer multiplication.
*
*	@param	a		The 64 bit multiplicand
*	@param	b		The 32 bit multiplicand
*	@return			The lower 64 bits of the 96 bit result
*/

static inline signed long long smul64x32_64( signed long long a,
											 signed int b )
{
	return( a * b );
}

/**
*	64x64 => 64 integer multiplication (signed or unsigned).
*
*	@param	a, b	The 64 bit multiplicands.
*	@return			The lower 64 bits of the 128 bit result.
*/

static inline long long mul64x64_64( long long a, long long b )
{
	return( a * b );
}

#else

unsigned long long	umul32x32_64( unsigned int a, unsigned int b )
	__attribute__((const));

signed long long	smul32x32_64( signed int a, signed int b )
	__attribute__((const));

long long			mul64x64_64( long long a, long long b )
	__attribute__((const));

unsigned long long	umul64x32_64( unsigned long long a, unsigned int b )
	__attribute__((const));

signed long long	smul64x32_64( signed long long a, signed int b )
	__attribute__((const));

unsigned long long umac32x32_64( unsigned long long s, unsigned a, unsigned b )
	__attribute__((const));

long long smac32x32_64( long long s, signed int a, signed int b )
	__attribute__((const));

#endif

long long			udiv32prep( unsigned div )
	__attribute__((const));

unsigned			udiv32( long long divisor, unsigned dividend )
	__attribute__((const));

long long 			sdiv32prep( int div )
	__attribute__((const));

int					sdiv32( long long divisor, int dividend )
	__attribute__((const));

/** @} */

#endif

