/*---------------------------------------------------------------*/
/*   Copyright (c) 1992 Regents of the University of California  */
/*                 All Rights Reserved                           */
/*---------------------------------------------------------------*/
/*-----------------------------------------------------------------
  Binary format conversion routines for the HP 1000.
  An HP 1000 stores all numeric values in MSB first format.

  To do:
      Nothing is done for exponents which are greater than 2**7
      when converting from IEEE to HP doubles.
      This should cause the HP number to be "undefined".
  
  Development History:
    Begun: 03/20/92 - Todd King

  Version:
      %W%     (UCLA/IGPP)    %G%
-----------------------------------------------------------------*/
#include "BC.h"
#include <stdio.h>	/* Remove when all functions are operational */

typedef union {
   struct {
/* #ifdef sparc */
      unsigned_int4 sign : 1;
      unsigned_int4 mantissa1 : 7;
      unsigned_int4 mantissa0b : 8;
      unsigned_int4 mantissa0a : 8;
      unsigned_int4 exponent : 7;
      unsigned_int4 esign : 1;
/* #endif sparc */
   } field;
   float val;
} HP1000Float;

typedef union {
   struct {
/* #ifdef sparc */
      unsigned_int4 sign : 1;
      unsigned_int4 mantissa3 : 4;
      unsigned_int4 mantissa2b : 11;
      unsigned_int4 mantissa2a : 5;
      unsigned_int4 mantissa1b : 11;
      unsigned_int4 mantissa1a : 5;
      unsigned_int4 mantissa0b : 11;
      unsigned_int4 mantissa0a : 5;
      unsigned_int4 lost : 3;
      unsigned_int4 exponent : 7;
      unsigned_int4 esign : 1;
/* #endif sparc */
   } field;
   double val;
} HP1000Double;

/*---------------------------------------------------------
  The single precision floating point fields are as follows:

    31      30                                 8          1     0
   +-------+----------------------------------+----------+-------+
   | sign  |          mantissa                | exponent | esign |
   +-------+----------------------------------+----------+-------+
   |<- 1 ->|<------------ 23 -----------------|<-- 7 --->|<- 1 ->|
  
  Idiosyncrasies:

     The exponent is only 7 bits, IEEE is 8. The mantissa is 23 bits as
     is IEEE.

  Range of Values:


  Precision:

     one part in 2**24.

-----------------------------------------------------------*/
void 
HP1000FloatToIEEE (float *val)
{
   HP1000Float hp;
   BCIEEEFloat ieee;
   int4 exp;

   if (*val == 0.0) return;  /* zero is same in both formats */

   hp.val = *val;

   exp = hp.field.exponent;
   if(hp.field.esign) exp |= 0xffffff80;	/* Sign extend */

   ieee.field.exponent = exp;
   ieee.field.mantissa0 = (hp.field.mantissa0b << 8) + hp.field.mantissa0a;
   ieee.field.mantissa1 = hp.field.mantissa1;
   ieee.field.sign = hp.field.sign;

   *val = ieee.val;
   return;
}

void 
HP1000IEEEToFloat (float *val)
{
   HP1000Float hp;
   BCIEEEFloat ieee;

   if (*val == 0.0) return;  /* zero is same in both formats */

   ieee.val = *val;

   if(ieee.field.exponent & 0x80) hp.field.esign = 1;
   else hp.field.esign = 0;

   hp.field.exponent = ieee.field.exponent & 0x7f;
   hp.field.mantissa0a = (ieee.field.mantissa0 & 0x00ff);
   hp.field.mantissa0b = ((ieee.field.mantissa0 & 0xff00) >> 8);
   hp.field.mantissa1 = ieee.field.mantissa1;
   hp.field.sign = ieee.field.sign;

   *val = hp.val;
   return;
}

/*---------------------------------------------------------
  The double precision floating point fields are as follows:

    64       63                                8          1     0
   +-------+----------------------------------+----------+-------+
   | sign  |          mantissa                | exponent | sign  |
   +-------+----------------------------------+----------+-------+
   |<- 1 ->|<------------ 55 -----------------|<-- 7 --->|<- 1 ->|
  
  Idiosyncrasies:

     The exponent is only 7 bits, IEEE is 11. The mantissa is 56 bits,
     IEEE is 52. So an Hp 1000 floating point has less range than
     an IEEE, but more precision. We drop least significant bits.

     The mantissa is stored as a 56 bit sign integer with the implied
     decimal point between bits 55 and 54 numbered from 0.

     There is no hidden bit.

  Range of Values:


  Precision:

     one part in 2**24.

-----------------------------------------------------------*/
void 
HP1000DoubleToIEEE (double *val)
{
   HP1000Double hp;
   BCIEEEDouble ieee;
   int4 exp;

   if(*val == 0.0) return;	/* Same on both computers */
   hp.val = *val;

   exp = hp.field.exponent;
   if (hp.field.esign) exp |= 0xffffff80;  /* sign extend */

   ieee.field.sign = hp.field.sign;
   ieee.field.mantissa0 = (hp.field.mantissa0b << 5) + hp.field.mantissa0a;
   ieee.field.mantissa1 = (hp.field.mantissa1b << 5) + hp.field.mantissa1a;
   ieee.field.mantissa2 = (hp.field.mantissa2b << 5) + hp.field.mantissa2a;
   ieee.field.mantissa3 = hp.field.mantissa3;

   *val = ieee.val;
   return;
}

/* NOTE: Nothing is done for exponents which are greater than 2**7
	 this should cause the HP number to be "undefined".
*/
void 
HP1000IEEEToDouble (double *val)
{
   HP1000Double hp;
   BCIEEEDouble ieee;

   if(*val == 0.0) return;	/* Same on both computers */
   ieee.val = *val;

   hp.field.exponent = ieee.field.exponent & 0x7f;	/* bits 1-7 */
   hp.field.esign = ieee.field.exponent & 0x400;	/* bit 11 */
   hp.field.sign = ieee.field.sign;
   hp.field.mantissa0a = (ieee.field.mantissa0 & 0x001f);
   hp.field.mantissa0b = (ieee.field.mantissa0 & 0xffe0) >> 5;
   hp.field.mantissa1a = (ieee.field.mantissa1 & 0x001f);
   hp.field.mantissa1b = (ieee.field.mantissa1 & 0xffe0) >> 5;
   hp.field.mantissa2a = (ieee.field.mantissa2 & 0x001f);
   hp.field.mantissa2b = (ieee.field.mantissa2 & 0xffe0) >> 5;
   hp.field.mantissa3 = ieee.field.mantissa3;
   hp.field.lost = 0;

   *val = ieee.val;
   return;
}

void 
BCHP1000Format (void)
{
   BCFloatToIEEE = HP1000FloatToIEEE;
   BCIEEEToFloat = HP1000IEEEToFloat;

   BCDoubleToIEEE = HP1000DoubleToIEEE;
   BCIEEEToDouble = HP1000IEEEToDouble;

   BCGFloatToIEEE = BCDoNothing;
   BCIEEEToGFloat = BCDoNothing;

   BCShortToIEEE = BCDoNothing;
   BCIEEEToShort = BCDoNothing;

   BCIntegerToIEEE = BCDoNothing;
   BCIEEEToInteger = BCDoNothing;

   BCLongIntegerToIEEE = BCDoNothing;
   BCIEEEToLongInteger = BCDoNothing;

   BCCharToASCII = BCDoNothing;
   BCASCIIToChar = BCDoNothing;

   BCReverseByteOrder = BCGenericReverseByteOrder;
}
