/****************************************************************************/
/*! \file		intrinsic.h
	\brief		C55XX intrinsics functions
	\author		PowerDSP, Iliya D. Voronov
				http://powerdsp.narod.ru/, Email:powerdsp@narod.ru
	\version	1.2
     
	C55XX intrinsics functions for other compiler.
*/
/*****************************************************************************/

#ifndef _INTRINSIC_H_
#define _INTRINSIC_H_


typedef long			int32;
typedef unsigned long	uint32;
typedef short			int16;
typedef unsigned short	uint16;
typedef long long int	int40;

#define MAX_INT16   ((int16)0x7fff)
#define MIN_INT16   ((int16)0x8000)
#define MAX_UINT16  ((uint16)0xffffU)
#define MAX_INT32   (0x7fffffffL)
#define MIN_INT32   (0x80000000L)
#define MAX_UINT32  (0xffffffffUL)

#ifdef COMPILER_MSVC
  #define _inline_ __inline
#else
  #define _inline_ static inline
#endif


//! Saturate long value to int
_inline_ int16 sat( int32 a )
{
  if ( a > (int32)MAX_INT16 )
    a = (int32)MAX_INT16;
  if ( a < ((int32)MIN_INT16) )
    a = ((int32)MIN_INT16);
  return (int16)a;
}

#ifdef COMPILER_MSVC


/******************************************************************************
Saturate 40-bit value to 32-bit
******************************************************************************/

_inline_ int32 _lsat( int40 a )
{
	if( a > (int40)MAX_INT32 )
		a = MAX_INT32;
	if( a < -((int40)MIN_INT32) )
		a = -((int40)MIN_INT32);
	return (int32)a;
}

/******************************************************************************
Saturated addition and subtraction
******************************************************************************/

_inline_ int16 _sadd(  int16 a,  int16 b )
{
  int32 r  = (int32)a + (int32)b;
  return sat( r );
}


_inline_ int32 _lsadd(  int32 a,  int32 b )
{
  int32 r  = a + b;
  if ( ( (( a ^ b ) & 0x80000000L ) == 0 ) && 
       ( (( a ^ r ) & 0x80000000L ) != 0 )    ) // overflow
    r = ( a < 0 ) ? 0x80000000L : 0x7fffffffL;  // restore satureted value
  return r;
}


_inline_ int40 _llsadd(  int40 a,  int40 b )
{
  // overflow is improbable in PC 64 bit arithmetics
  return a + b;
}


_inline_ int16 _ssub(  int16 a,  int16 b )
{
  int32 r  = (int32)a - (int32)b;
  return sat( r );
}


_inline_ int32 _lssub(  int32 a,  int32 b )
{
  int32 r  = a - b;
  if ( ( (( a ^ b ) & 0x80000000L ) != 0 ) && 
       ( (( a ^ r ) & 0x80000000L ) != 0 )    ) // overflow
    r = ( a < 0 ) ? 0x80000000L : 0x7fffffffL;  // restore satureted value
  return r;
}


_inline_ int40 _llssub(  int40 a,  int40 b )
{
  // overflow is improbable in PC 64 bit arithmetics
  return a - b;
}

_inline_ int16 _sneg( int16 a )
{
	if( a == -0x8000 )
		a = 0x8001;
	return 0 - a;
}


/******************************************************************************
Minimum and maximum
******************************************************************************/

_inline_ int16 _min( int16 a, int16 b )
{
  return ( a < b ) ? a : b;
}

_inline_ int32 _lmin( int32 a, int32 b )
{
  return ( a < b ) ? a : b;
}

_inline_ int16 _max( int16 a, int16 b )
{
  return ( a > b ) ? a : b;
}

_inline_ int32 _lmax( int32 a, int32 b )
{
  return ( a > b ) ? a : b;
}


/******************************************************************************
Saturated left shift
******************************************************************************/


//! Left shift, correctly process negative shift value
_inline_ int16 _shl( int16 a, int16 s )
{
  return ( s >= 0 ) ? a<<s : a>>(-s);
}


//! Saturated left shift, correctly process negative shift value
_inline_ int16 _sshl(  int16 a, int16 s )
{
  if ( s >= 0 )
  {
    int32 r = ((int32)a)<<s;
    return sat( r );
  } else {
    return a>>(-s);
  }
}


//! Saturated left shift, correctly process negative shift value
_inline_ int32 _lsshl( int32 a, int16 s )
{
  if( s >= 0 )
  {
    long long int r = ((long long int) a)<<s;
    if( r > 0x7FFFFFFFLL )
      r = 0x7FFFFFFFLL;
    if( r < -0x80000000LL )
      r = -0x80000000LL;
    return (int32)r;
  } else {
    return a>>(-s);
  }
}


//! Right shift, correctly process negative shift value
_inline_ int16 _shrs( int16 a, int16 s )
{
  return ( s >= 0 ) ? a>>s : a<<(-s);
}



/******************************************************************************
Saturated absolute value
******************************************************************************/


_inline_ int16 _abss ( int16 x )
{
  if( x == MIN_INT16 )
    return MAX_INT16;
  if( x < 0 )
    x = -x;
  return x;
}

_inline_ int32 _labss ( int32 x )
{
  if( x == MIN_INT32 )
    return MAX_INT32;
  if( x < 0 )
    x = -x;
  return x;
}


/******************************************************************************
Multiply (nonsaturated), MAC (accumulation is saturated), Square
******************************************************************************/


_inline_ int32 _lsmpy(int16 a, int16 b)
{
  int32 r = (int32)a * (int32)b;
  if ((a == (int16)0x8000) && (b == (int16)0x8000))
    return (0x7fffffffL);
  return (r << 1);
}


_inline_ int16 _smpy(int16 a, int16 b)
{
  return (int16)( _lsmpy( a, b ) >> 16 );
}


_inline_ int32 _smac ( int32 s, int16 a, int16 b )
{
  return _lsadd( s, _lsmpy( a, b ) );
}


_inline_ int32 _smas ( int32 s, int16 a, int16 b )
{
  return _lssub( s, _lsmpy( a, b ) );
}

_inline_ int40 _llsmac( int40 s, int16 a, int16 b )
{
	return _llsadd( s, _lsmpy( a, b ) );
}

_inline_ int40 _llsmas( int40 s, int16 a, int16 b )
{
	return _llssub( s, _lsmpy( a, b ) );
}


/******************************************************************************
Exponent
******************************************************************************/

/*! Exponent, number of left shift to normalize value 
*/
_inline_ uint16 _norm (int16 val)
{
  uint16 res=0;
  if ( val == 0 )
    return 16; // maximal shift
  while ( (val^(int16)(val<<1)) > 0 ) //MSB != MSB-1
  {
    val<<=1;
    res++;
  }
  return res;
}


#endif //#ifndef COMPILER_C55


#endif //#ifndef _INTRINSIC_H_

