/*
 * $Id: gen_num.c,v 1.4 1994/02/06 23:44:16 mintha Exp $
 *
 * Set of general routines for use in C programs.
 * These routines provide numeric conversion, etc. routines.
 *
 * $Log: gen_num.c,v $
 * Revision 1.4  1994/02/06  23:44:16  mintha
 * Got rid of unused variable
 *
 * Revision 1.3  1993/10/20  09:53:30  mintha
 * Include gen_utils_x.h now
 *
 * Revision 1.2  1993/10/05  09:29:37  mintha
 * New vms float conversion, and swap int and float routines
 *
 * Revision 1.1  1993/09/23  09:18:42  mintha
 * Initial revision
 *
 */

#include <stdio.h>
#include "gen_utils_x.h"

/*
 * swap_float - Swap the bytes around for a 4 byte float.  The two types of 
 *              swaps are BYTE & WORD, or both.
 *              BYTE:   1234 becomes 4321
 *              WORD:   1234 becomes 3412
 *              BYTE | WORD:  1234 becomes 2143
 */

float
swap_float(float value, int type)
{
  float rtn_val;
  char *in_ptr = (char *) &value;
  char *out_ptr = (char *) &rtn_val;
  
  switch(type)
    {
    case WORD:
      out_ptr[0]=in_ptr[2];
      out_ptr[1]=in_ptr[3];
      out_ptr[2]=in_ptr[0];
      out_ptr[3]=in_ptr[1];
      break;
    case BYTE:
      out_ptr[0]=in_ptr[3];
      out_ptr[1]=in_ptr[2];
      out_ptr[2]=in_ptr[1];
      out_ptr[3]=in_ptr[0];
      break;
    case WORD | BYTE:
      out_ptr[0]=in_ptr[1];
      out_ptr[1]=in_ptr[0];
      out_ptr[2]=in_ptr[3];
      out_ptr[3]=in_ptr[2];
      break;
    }
  
  return rtn_val;
}

/*
 * swap_int - Swap the bytes around for a 4 byte integer.  The two types of 
 *            swaps are BYTE & WORD, or both.
 *            BYTE:   1234 becomes 4321
 *            WORD:   1234 becomes 3412
 *            BYTE | WORD:  1234 becomes 2143
 */

unsigned int
swap_int(unsigned int value, int type)
{
  unsigned int rtn_val;
  char *in_ptr = (char *) &value;
  char *out_ptr = (char *) &rtn_val;
  
  switch(type)
    {
    case WORD:
      out_ptr[0]=in_ptr[2];
      out_ptr[1]=in_ptr[3];
      out_ptr[2]=in_ptr[0];
      out_ptr[3]=in_ptr[1];
      break;
    case BYTE:
      out_ptr[0]=in_ptr[3];
      out_ptr[1]=in_ptr[2];
      out_ptr[2]=in_ptr[1];
      out_ptr[3]=in_ptr[0];
      break;
    case WORD | BYTE:
      out_ptr[0]=in_ptr[1];
      out_ptr[1]=in_ptr[0];
      out_ptr[2]=in_ptr[3];
      out_ptr[3]=in_ptr[2];
      break;
    }
  
  return rtn_val;
}

/*
 * vms_float - Convert a vms format floating point number to IEEE
 */

float
vms_float(float value)
{
  struct vax_style
    {
      unsigned int mantissa2: 16;
      unsigned int sign     : 1;
      unsigned int exp      : 8;
      unsigned int mantissa1: 7;
    };
  struct ieee_style
    {
      unsigned int sign     : 1;
      unsigned int exp      : 8;
      unsigned int mantissa : 23;
    };
  struct limits 
    {
      struct vax_style vax;
      struct ieee_style ieee;
    };
  struct limits max = 
    {
      {	0x7f, 0xff, 0x0, 0xffff },   /* Max Vax single number */
      { 0x0, 0xff, 0x0 }             /* Max IEEE single number */
    };
  struct limits min = 
    {
      { 0x0, 0x0, 0x0, 0x0 },        /* Min Vax single number */
      { 0x0, 0x0, 0x0}               /* Min IEEE single number */
    };
  unsigned int vax_bias = 129, ieee_bias = 127;
  struct vax_style vs;
  struct ieee_style is;
  float rtn_val;
  
  value = swap_float(value, BYTE);
  vs = *((struct vax_style *) &value);
  switch(vs.exp)
    {
    case 0:   /* vax float with zero exponent maps to zero */
      is = min.ieee;
      break;
    case 2:   /* these map to IEEE subnormals */
    case 1:
      is.exp = 0;
      is.mantissa = (vs.mantissa1 << 16) | vs.mantissa2;
      /* lose some precision */
      is.mantissa >>= 3 - vs.exp;
      is.mantissa += (1 << (20 + vs.exp));
      break;
    case 0xff: /* max vax number */
      if(vs.mantissa2 == max.vax.mantissa2 && vs.mantissa1 == max.vax.mantissa1)
	{
	  /* ieee infinity */
	  is = max.ieee;
	  break;
	}
      /* otherwise just a normal number */
    default:
      is.exp = vs.exp - vax_bias + ieee_bias;
      is.mantissa = (vs.mantissa1 << 16) | vs.mantissa2;
    }
  
  is.sign = vs.sign;

  rtn_val = *((float *) &is);
  return rtn_val;
}
