/*
 * This file is based on gcc/libgcc/config/m68k/fpgnulib.c
 * commit 0115ef57efa9 ("[PATCH] libgcc/m68k: More fixes for soft float").
 */

/* This is a stripped down version of floatlib.c.  It supplies only those
   functions which exist in libgcc, but for which there is not assembly
   language versions in m68k/lb1sf68.S.

   It also includes simplistic support for extended floats (by working in
   double precision).  You must compile this file again with -DEXTFLOAT
   to get this support.  */

/*
** gnulib support for software floating point.
** Copyright (C) 1991 by Pipeline Associates, Inc.  All rights reserved.
** Permission is granted to do *anything* you want with this file,
** commercial or otherwise, provided this message remains intact.  So there!
** I would appreciate receiving any updates/patches/changes that anyone
** makes, and am willing to be the repository for said changes (am I
** making a big mistake?).
**
** Pat Wood
** Pipeline Associates, Inc.
** pipeline!phw@motown.com or
** sun!pipeline!phw or
** uunet!motown!pipeline!phw
**
** 05/01/91 -- V1.0 -- first release to gcc mailing lists
** 05/04/91 -- V1.1 -- added float and double prototypes and return values
**                  -- fixed problems with adding and subtracting zero
**                  -- fixed rounding in truncdfsf2
**                  -- fixed SWAP define and tested on 386
*/

/*
** The following are routines that replace the gnulib soft floating point
** routines that are called automatically when -msoft-float is selected.
** The support single and double precision IEEE format, with provisions
** for byte-swapped machines (tested on 386).  Some of the double-precision
** routines work at full precision, but most of the hard ones simply punt
** and call the single precision routines, producing a loss of accuracy.
** long long support is not assumed or included.
** Overall accuracy is close to IEEE (actually 68882) for single-precision
** arithmetic.  I think there may still be a 1 in 1000 chance of a bit
** being rounded the wrong way during a multiply.  I'm not fussy enough to
** bother with it, but if anyone is, knock yourself out.
**
** Efficiency has only been addressed where it was obvious that something
** would make a big difference.  Anyone who wants to do this right for
** best speed should go in and rewrite in assembler.
**
** I have tested this only on a 68030 workstation and 386/ix integrated
** in with -msoft-float.
*/

/* the following deal with IEEE single-precision numbers */
#define EXCESS		126L
#define SIGNBIT		0x80000000L
#define HIDDEN		(1L << 23L)
#define SIGN(fp)	((fp) & SIGNBIT)
#define EXPMASK		0xFFL
#define EXP(fp)		(((fp) >> 23L) & 0xFF)
#define MANT(fp)	(((fp) & 0x7FFFFFL) | HIDDEN)
#define PACK(s,e,m)	((s) | ((e) << 23L) | (m))

/* the following deal with IEEE double-precision numbers */
#define EXCESSD		1022L
#define HIDDEND		(1L << 20L)
#define EXPDBITS	11
#define EXPDMASK	0x7FFL
#define EXPD(fp)	(((fp.l.upper) >> 20L) & 0x7FFL)
#define SIGND(fp)	((fp.l.upper) & SIGNBIT)
#define MANTD(fp)	(((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \
				(fp.l.lower >> 22))
#define MANTDMASK	0xFFFFFL /* mask of upper part */

/* the following deal with IEEE extended-precision numbers */
#define EXCESSX		16382L
#define HIDDENX		(1L << 31L)
#define EXPXBITS	15
#define EXPXMASK	0x7FFF
#define EXPX(fp)	(((fp.l.upper) >> 16) & EXPXMASK)
#define SIGNX(fp)	((fp.l.upper) & SIGNBIT)
#define MANTXMASK	0x7FFFFFFFL /* mask of upper part */

union double_long
{
  double d;
  struct {
      long upper;
      unsigned long lower;
    } l;
};

union float_long {
  float f;
  long l;
};

union long_double_long
{
  long double ld;
  struct
    {
      long upper;
      unsigned long middle;
      unsigned long lower;
    } l;
};

#ifndef EXTFLOAT

int
__unordsf2(float a, float b)
{
  union float_long fl;

  fl.f = a;
  if (EXP(fl.l) == EXP(~0lu) && (MANT(fl.l) & ~HIDDEN) != 0)
    return 1;
  fl.f = b;
  if (EXP(fl.l) == EXP(~0lu) && (MANT(fl.l) & ~HIDDEN) != 0)
    return 1;
  return 0;
}

int
__unorddf2(double a, double b)
{
  union double_long dl;

  dl.d = a;
  if (EXPD(dl) == EXPDMASK
      && ((dl.l.upper & MANTDMASK) != 0 || dl.l.lower != 0))
    return 1;
  dl.d = b;
  if (EXPD(dl) == EXPDMASK
      && ((dl.l.upper & MANTDMASK) != 0 || dl.l.lower != 0))
    return 1;
  return 0;
}

/* convert unsigned int to double */
double
__floatunsidf (unsigned long a1)
{
  long exp = 32 + EXCESSD;
  union double_long dl;

  if (!a1)
    {
      dl.l.upper = dl.l.lower = 0;
      return dl.d;
    }

  while (a1 < 0x2000000L)
    {
      a1 <<= 4;
      exp -= 4;
    }

  while (a1 < 0x80000000L)
    {
      a1 <<= 1;
      exp--;
    }

  /* pack up and go home */
  dl.l.upper = exp << 20L;
  dl.l.upper |= (a1 >> 11L) & ~HIDDEND;
  dl.l.lower = a1 << 21L;

  return dl.d;
}

/* convert int to double */
double
__floatsidf (long a1)
{
  long sign = 0, exp = 31 + EXCESSD;
  union double_long dl;

  if (!a1)
    {
      dl.l.upper = dl.l.lower = 0;
      return dl.d;
    }

  if (a1 < 0)
    {
      sign = SIGNBIT;
      a1 = (long)-(unsigned long)a1;
      if (a1 < 0)
	{
	  dl.l.upper = SIGNBIT | ((32 + EXCESSD) << 20L);
	  dl.l.lower = 0;
	  return dl.d;
        }
    }

  while (a1 < 0x1000000L)
    {
      a1 <<= 4;
      exp -= 4;
    }

  while (a1 < 0x40000000L)
    {
      a1 <<= 1;
      exp--;
    }

  /* pack up and go home */
  dl.l.upper = sign;
  dl.l.upper |= exp << 20L;
  dl.l.upper |= (a1 >> 10L) & ~HIDDEND;
  dl.l.lower = a1 << 22L;

  return dl.d;
}

/* convert unsigned int to float */
float
__floatunsisf (unsigned long l)
{
  double foo = __floatunsidf (l);
  return foo;
}

/* convert int to float */
float
__floatsisf (long l)
{
  double foo = __floatsidf (l);
  return foo;
}

/* convert float to double */
double
__extendsfdf2 (float a1)
{
  register union float_long fl1;
  register union double_long dl;
  register long exp;
  register long mant;

  fl1.f = a1;

  dl.l.upper = SIGN (fl1.l);
  if ((fl1.l & ~SIGNBIT) == 0)
    {
      dl.l.lower = 0;
      return dl.d;
    }

  exp = EXP(fl1.l);
  mant = MANT (fl1.l) & ~HIDDEN;
  if (exp == 0)
    {
      /* Denormal.  */
      exp = 1;
      while (!(mant & HIDDEN))
	{
	  mant <<= 1;
	  exp--;
	}
      mant &= ~HIDDEN;
    }
  exp = exp - EXCESS + EXCESSD;
  /* Handle inf and NaN */
  if (exp == EXPMASK - EXCESS + EXCESSD)
    exp = EXPDMASK;
  dl.l.upper |= exp << 20;
  dl.l.upper |= mant >> 3;
  dl.l.lower = mant << 29;

  return dl.d;
}

/* convert double to float */
float
__truncdfsf2 (double a1)
{
  register long exp;
  register long mant;
  register union float_long fl;
  register union double_long dl1;
  int sticky;
  int shift;

  dl1.d = a1;

  if ((dl1.l.upper & ~SIGNBIT) == 0 && !dl1.l.lower)
    {
      fl.l = SIGND(dl1);
      return fl.f;
    }

  exp = EXPD (dl1) - EXCESSD + EXCESS;

  sticky = dl1.l.lower & ((1l << 22) - 1);
  mant = MANTD (dl1);
  /* shift double mantissa 6 bits so we can round */
  sticky |= mant & ((1 << 6) - 1);
  mant >>= 6;
  if (exp == EXPDMASK - EXCESSD + EXCESS)
    {
      exp = EXPMASK;
      mant = (mant >> 1) | (mant & 1) | (!!sticky);
    }
  else
    {
      /* Check for underflow and denormals.  */
      if (exp <= 0)
	{
	  if (exp < -24)
	    {
	      sticky |= mant;
	      mant = 0;
	    }
	  else
	    {
	      sticky |= mant & ((1 << (1 - exp)) - 1);
	      mant >>= 1 - exp;
	    }
	  exp = 0;
	}

      /* now round */
      shift = 1;
      if ((mant & 1) && (sticky || (mant & 2)))
	{
	  int rounding = exp ? 2 : 1;

	  mant += 1;

	  /* did the round overflow? */
	  if (mant >= (HIDDEN << rounding))
	    {
	      exp++;
	      shift = rounding;
	    }
	}
      /* shift down */
      mant >>= shift;
      if (exp >= EXPMASK)
	{
	  exp = EXPMASK;
	  mant = 0;
	}
    }

  mant &= ~HIDDEN;

  /* pack up and go home */
  fl.l = PACK (SIGND (dl1), exp, mant);
  return (fl.f);
}

/* convert double to int */
long
__fixdfsi (double a1)
{
  register union double_long dl1;
  register long exp;
  register long l;

  dl1.d = a1;

  if (!dl1.l.upper && !dl1.l.lower)
    return 0;

  exp = EXPD (dl1) - EXCESSD - 31;
  l = MANTD (dl1);

  if (exp > 0)
    {
      /* Return largest integer.  */
      return SIGND (dl1) ? 0x80000000L : 0x7fffffffL;
    }

  if (exp <= -32)
    return 0;

  /* shift down until exp = 0 */
  if (exp < 0)
    l >>= -exp;

  return (SIGND (dl1) ? -l : l);
}

/* convert float to int */
long
__fixsfsi (float a1)
{
  double foo = a1;
  return __fixdfsi (foo);
}

#else /* EXTFLOAT */

/* We do not need these routines for coldfire, as it has no extended
   float format. */
#if !defined (__mcoldfire__)

/* Primitive extended precision floating point support.

   We assume all numbers are normalized, don't do any rounding, etc.  */

/* Prototypes for the above in case we use them.  */
double __floatunsidf (unsigned long);
double __floatsidf (long);
float __floatsisf (long);
double __extendsfdf2 (float);
float __truncdfsf2 (double);
long __fixdfsi (double);
long __fixsfsi (float);
int __cmpdf2 (double, double);

int
__unordxf2(long double a, long double b)
{
  union long_double_long ldl;

  ldl.ld = a;
  if (EXPX(ldl) == EXPXMASK
      && ((ldl.l.middle & MANTXMASK) != 0 || ldl.l.lower != 0))
    return 1;
  ldl.ld = b;
  if (EXPX(ldl) == EXPXMASK
      && ((ldl.l.middle & MANTXMASK) != 0 || ldl.l.lower != 0))
    return 1;
  return 0;
}

/* convert double to long double */
long double
__extenddfxf2 (double d)
{
  register union double_long dl;
  register union long_double_long ldl;
  register long exp;

  dl.d = d;
  /*printf ("dfxf in: %g\n", d);*/

  ldl.l.upper = SIGND (dl);
  if ((dl.l.upper & ~SIGNBIT) == 0 && !dl.l.lower)
    {
      ldl.l.middle = 0;
      ldl.l.lower = 0;
      return ldl.ld;
    }

  exp = EXPD (dl) - EXCESSD + EXCESSX;

  dl.l.upper &= MANTDMASK;

  /* Recover from a denorm. */
  if (exp == -EXCESSD + EXCESSX)
    {
      exp++;
      while ((dl.l.upper & HIDDEND) == 0)
	{
	  exp--;
	  dl.l.upper = (dl.l.upper << 1) | (dl.l.lower >> 31);
	  dl.l.lower = dl.l.lower << 1;
	}
    }

  /* Handle inf and NaN */
  else if (exp == EXPDMASK - EXCESSD + EXCESSX)
    {
      exp = EXPXMASK;
      /* Add hidden one bit for NaN */
      if (dl.l.upper != 0 || dl.l.lower != 0)
        dl.l.upper |= HIDDEND;
    }
  else
    {
      dl.l.upper |= HIDDEND;
    }

  ldl.l.upper |= exp << 16;
  /* 31-20: # mantissa bits in ldl.l.middle - # mantissa bits in dl.l.upper */
  ldl.l.middle = dl.l.upper << (31 - 20);
  /* 1+20: explicit-integer-bit + # mantissa bits in dl.l.upper */
  ldl.l.middle |= dl.l.lower >> (1 + 20);
  /* 32 - 21: # bits of dl.l.lower in ldl.l.middle */
  ldl.l.lower = dl.l.lower << (32 - 21);

  /*printf ("dfxf out: %s\n", dumpxf (ldl.ld));*/
  return ldl.ld;
}

/* convert long double to double */
double
__truncxfdf2 (long double ld)
{
  register long exp;
  register union double_long dl;
  register union long_double_long ldl;

  ldl.ld = ld;
  /*printf ("xfdf in: %s\n", dumpxf (ld));*/

  dl.l.upper = SIGNX (ldl);
  if ((ldl.l.upper & ~SIGNBIT) == 0 && !ldl.l.middle && !ldl.l.lower)
    {
      dl.l.lower = 0;
      return dl.d;
    }

  exp = EXPX (ldl) - EXCESSX + EXCESSD;
  /* Check for underflow and denormals. */
  if (exp <= 0)
    {
      long shift = 1 - exp;
      if (shift > 52)
	{
	  ldl.l.middle = 0;
	  ldl.l.lower = 0;
	}
      else if (shift >= 32)
	{
	  ldl.l.lower = (ldl.l.middle) >> (shift - 32);
          ldl.l.middle = 0;
	}
      else
	{
	  ldl.l.lower = (ldl.l.middle << (32 - shift)) | (ldl.l.lower >> shift);
          ldl.l.middle = ldl.l.middle >> shift;
	}
      exp = 0;
    }
  else if (exp == EXPXMASK - EXCESSX + EXCESSD)
    {
      exp = EXPDMASK;
      ldl.l.middle |= ldl.l.lower;
    }
  else if (exp >= EXPDMASK)
    {
      exp = EXPDMASK;
      ldl.l.middle = 0;
      ldl.l.lower = 0;
    }
  dl.l.upper |= exp << (32 - (EXPDBITS + 1));
  /* +1-1: add one for sign bit, but take one off for explicit-integer-bit */
  dl.l.upper |= (ldl.l.middle & MANTXMASK) >> (EXPDBITS + 1 - 1);
  dl.l.lower = (ldl.l.middle & MANTXMASK) << (32 - (EXPDBITS + 1 - 1));
  dl.l.lower |= ldl.l.lower >> (EXPDBITS + 1 - 1);

  /*printf ("xfdf out: %g\n", dl.d);*/
  return dl.d;
}

/* convert a float to a long double */
long double
__extendsfxf2 (float f)
{
  long double foo = __extenddfxf2 (__extendsfdf2 (f));
  return foo;
}

/* convert a long double to a float */
float
__truncxfsf2 (long double ld)
{
  float foo = __truncdfsf2 (__truncxfdf2 (ld));
  return foo;
}

/* convert an int to a long double */
long double
__floatsixf (long l)
{
  double foo = __floatsidf (l);
  return foo;
}

/* convert an unsigned int to a long double */
long double
__floatunsixf (unsigned long l)
{
  double foo = __floatunsidf (l);
  return foo;
}

/* convert a long double to an int */
long
__fixxfsi (long double a)
{
  union long_double_long ldl;
  long exp;

  ldl.ld = a;

  exp = EXPX (ldl);
  if (exp == 0 && ldl.l.middle == 0 && ldl.l.lower == 0)
    return 0;

  exp = exp - EXCESSX - 32;

  if (exp >= 0)
    {
      /* Return largest integer.  */
      return SIGNX (ldl) ? 0x80000000L : 0x7fffffffL;
    }

  if (exp <= -32)
    return 0;

  ldl.l.middle >>= -exp;

  return SIGNX (ldl) ? -ldl.l.middle : ldl.l.middle;
}

/* The remaining provide crude math support by working in double precision.  */

long double
__addxf3 (long double x1, long double x2)
{
  return (double) x1 + (double) x2;
}

long double
__subxf3 (long double x1, long double x2)
{
  return (double) x1 - (double) x2;
}

long double
__mulxf3 (long double x1, long double x2)
{
  return (double) x1 * (double) x2;
}

long double
__divxf3 (long double x1, long double x2)
{
  return (double) x1 / (double) x2;
}

long double
__negxf2 (long double x1)
{
  return - (double) x1;
}

long
__cmpxf2 (long double x1, long double x2)
{
  return __cmpdf2 ((double) x1, (double) x2);
}

long
__eqxf2 (long double x1, long double x2)
{
  return __cmpdf2 ((double) x1, (double) x2);
}

long
__nexf2 (long double x1, long double x2)
{
  return __cmpdf2 ((double) x1, (double) x2);
}

long
__ltxf2 (long double x1, long double x2)
{
  return __cmpdf2 ((double) x1, (double) x2);
}

long
__lexf2 (long double x1, long double x2)
{
  return __cmpdf2 ((double) x1, (double) x2);
}

long
__gtxf2 (long double x1, long double x2)
{
  return __cmpdf2 ((double) x1, (double) x2);
}

long
__gexf2 (long double x1, long double x2)
{
  return __cmpdf2 ((double) x1, (double) x2);
}

#endif /* !__mcoldfire__ */
#endif /* EXTFLOAT */
