//---------------------------------------------------------
// TMSFLOAT.CPP
// Keith Larson
// TMS320 DSP Applications
// (c) Copyright 1995, 1996, 1997
// Texas Instruments Incorporated
//
// This is unsupported freeware code with no implied warranties or
// liabilities.  See the disclaimer document for details
//--------------------------------------------------------------------
// The functions found in this file are used to convert TMS320C3x/4x
// floating point numbers to and from IEEE floating point.  Functions
// are also provided to convert between various format types
//--------------------------------------------------------------------
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include "typedefs.h"
#include "tmsfloat.h"
char TMS_TEST=0;
//----------------------------------------------------------------------
// Converts TMS 32 bit float to 16 bit (4/1/11) float (short immediate)
//----------------------------------------------------------------------
ulong TMS_TMS_SHORT( ulong n )
{
  long e,m;
  if(n==0x80000000L) return 0x8000L;  // Special case for zero
  e = (n & 0xFF000000L);
  e = e>>24;
  m = n & 0x007FFFFFL;
  // Build Q24 representation for mantissa
  if(n&0x00800000L)        // neg 111111110.XXXXXXXXXXXXXXXXXXXXXXX
  { m |= 0xFF000000L;      //             -2.0 < mant < -1.00001
    m += 0x00000800L;      //NOTE: rounding the mag of a neg value
    m = m >> 12;           //      never carries into the exponent
    m |= 0x00000800L;      // Fixup sign bit
    m &= 0xfff;
    if(e>7) return 0x7800; // max negative
  }
  else
  {
    m |= 0x00800000L;      // pos 000000001.XXXXXXXXXXXXXXXXXXXXXXX
    m += 0x00000800L;      // add rounding bit
    e = e + (m>>24);       // add carry to the exponent
    m = m >> 12;
    m &= 0x7ff;
    if(e>7) return 0x77FF; // max positive
  }
  if(e<-8)             return 0x8000;  // return zero (both +/-)
  m = (e << 12) | m; // Shift to final position
  return( m & 0xffff);
}
//------------------------------------------------------------------------
// Converts TMS 32 bit float to 8 bit very short float or short log code
// format is 4 exp, 1 sign, 3 mantissa
//------------------------------------------------------------------------
ulong TMS_C3X_VSHORT( ulong n )
{
  long e,m;
  if(n==0x80000000L) return 0x80L;  // Special case for zero
  e = (n & 0xFF000000L);
  e = e>>24;
  m = n & 0x007FFFFFL;
  // Build Q24 representation for mantissa
  if(n&0x00800000L)        // neg 111111110.XXXXXXXXXXXXXXXXXXXXXXX
  { m |= 0xFF000000L;      //             -2.0 < mant < -1.00001
    m += 0x00080000L;      //NOTE: rounding the mag of a neg value
    m = m >> 20;           //      never carries into the exponent
    m |= 0x00000008L;      // Fixup sign bit
    m &= 0xf;
    if(e>7) return 0x78; // max negative
  }
  else
  {
    m |= 0x00800000L;      // pos 000000001.XXXXXXXXXXXXXXXXXXXXXXX
    m += 0x00080000L;      // add rounding bit
    e = e + (m>>24);       // add carry to the exponent
    m = m >> 20;
    m &= 0x7;
    if(e>7) return 0x77; // max positive
  }
  if(e<-8)             return 0x80;  // return zero (both +/-)
  m = (e << 4) | m; // Shift to final position
  return( m & 0xff);
}
//------------------------------------------------------------------------
// Converts TMS 32 bit float to 16 bit (8/1/7) float
//  - Converts TMS float to C32 external short float
//------------------------------------------------------------------------
ulong TMS_C32_16( ulong n )
{
  long e,m;
  if(n==0x80000000L) return 0x8000L;  // Special case for zero
  e = (n & 0xFF000000L);
  e = e>>24;
  m = n & 0x007FFFFFL;
  // Build Q24 representation for mantissa
  if(n&0x00800000L)        // neg 111111110.XXXXXXXXXXXXXXXXXXXXXXX
  { m |= 0xFF000000L;      //             -2.0 < mant < -1.00001
    m += 0x00008000L;      //NOTE: rounding the mag of a neg value
    m = m >> 16;           //      never carries into the exponent
    m |= 0x00000080L;      // Fixup sign bit
    m &= 0xff;
    if(e>127) return 0x7F80; // max negative
  }
  else
  {
    m |= 0x00800000L;      // pos 000000001.XXXXXXXXXXXXXXXXXXXXXXX
    m += 0x00008000L;      // add rounding bit
    e = e + (m>>24);       // add carry to the exponent
    m = m >> 16;
    m &= 0x7F;
    if(e>127) return 0x7F7F; // max positive
  }
  if(e<-128)  return 0x8000;  // return zero (both +/-)
  m = (e << 8) | m; // Shift to final position
  return( m & 0xffff);

}
//------------------------------------------------------------------------
// Converts a 32 bit IEEE float to TMS 8/1/23 format
//------------------------------------------------------------------------
ulong IEEE_TMS( float f )
{
#define P_HUGE_TMS  0x7F7FFFFFL
#define N_HUGE_TMS  0x7F800000L
  ulong e,s,m,n;

  if(f==0.0)
  { n = 0x80000000L;  // Special case for zero
    return n;
  }

  n = *( (long *) &f );

  s = n & 0x80000000L;
  m = n & 0x007FFFFFL;
  e = n & 0x7F800000L;
//e = n & 0x3F800000L;

  e = e << 1;
  if(e == 0xFF000000L)  // 2^-1 * F
  { if(m == 0) return P_HUGE_TMS;
    if(s == 0) return P_HUGE_TMS;
               return N_HUGE_TMS;
  }
  e = e - 0x7F000000L;

  if ( s )
  {
   m = m & 0x007FFFFFL;
   m = -m;
   m = m & 0x00FFFFFFL;
   if(m==0)
   {
     m = m | 0x00800000L;
     e = (long)e - 0x01000000L;
   }
  }
  n = e | m;
  return n;
}
//------------------------------------------------------------------------
// Converts a TMS 8/1/23 format to IEEE 32 bit
//------------------------------------------------------------------------
float TMS_IEEE( ulong n )
{
  ulong e,s,m;
  float f;
//if ( n == 0x80000000L ) return(0.0);
  e = n & 0xFF000000L;
  if ( e == 0x80000000L ) return(0.0);
  e = e + 0x7F000000L;
  s = (n & 0x00800000L) << 8;
  m = n & 0x007FFFFFL;

  if(e == 0xFF000000L)
  {
    if(m == 0) return    ERANGE;// math.h  #define'd
    if(s == 0) return +HUGE_VAL;// errno.h
               return -HUGE_VAL;
  }
//if(e==0) return 0.0; // Add this for remove denormal number support
                       // Denormal IEEE numbers are numbers whose
                       // exponents are -127.  In this range, an IEEE
                       // number is 2^-127 * Q23(frac)
  e >>= 1;
  if ( s )
  {
   m = (~m) & 0x007FFFFFL;
   m += 1L;
   e += m & 0x00800000L;
   e &= 0x7F800000L;
   m &= 0x007FFFFFL;
  }
  if ( n == 0x80000000L ) s = m = e = 0L;
  n = s | e | m;
  f = *( (float *) &n );
  return f;
}
//---------------------------------------------------------------------
// Converts a short TMS floatig point format (4/1/11) to IEEE format
//---------------------------------------------------------------------
float Short_Float( ulong n )
{
  /* ver 1.18 bug fix                                                  */
  /* If in any case Exp=-8, the result is zero.  This test is bypassed */
  /* when the assembler loopback mode is used for verifying that a     */
  /* disassembled, and then re-assembled binary opcode match exactly   */
  if(TMS_TEST)                     /* Use old version if loopback  */
  { if (n == 0x8000L) n = 0x80000000L;
    else
    { n = n << 16;                     // Push exponent sign to top
      n =  (long)n >> 4;               // extend back sign;
    }
  }
  else                                 /* Correct for processor        */
  { if ((n & 0xF000L) == 0x8000L) n = 0x80000000L;
    else
    { n = n << 16;        // Push exponent sign to top
      n =  (long)n >> 4;  // extend back sign;
    }
  }
  return TMS_IEEE(n);
}
