
/*
 * fpencode.c
 *
 * (C) Copyright 1995 Archie L. Cobbs
 */

#include <stdio.h>
#include <assert.h>
#include "util.h"
#include "names.h"
#include "error.h"
#include "asm.h"

/*
 * DEFINITIONS
 */

/* This is assumed below */

	#define	DOUBLE_SIZE	8

/* Define this if you want to see the details */

/*
	#define	DEBUG_FP
*/

/*
 * INTERNAL FUNCTIONS
 */

	static	void	ReverseBytes(void *p, int size);

/*
  Applesoft floating point representation (5 bytes):

  0000000011111111222222224444444455555555
  7654321076543210765432107654321076543210 

  |-bexp-|S|----------mantissa-----------|

  bexp:       8 bit unsigned, biased exponent (bexp = exp + 129)
  S:          1 bit sign of value
  mantissa:   31 bit fractional part (to the right of the "decimal" point)

  Thus, our value can be written in base 2 as:

    (-1)^S x 1.mantissa x 2^(bexp - 129)

  Zero is indicated by a zero exponent. So the allowable exponent
  range is -128 <= e <= 126.

*/

/*
   EncodeFloat() -- convert host double type to Apple 2 floating point

   Host machine must have IEEE 8 byte double encoded as shown below.
   It may be big or little endian. On the Apple 2, AppleSoft uses a five
   byte encoding.

   IEEE big endian floating point representation (4 byte float, 8 byte double):

    Byte 0   Byte 1   Byte 2   Byte 3   Byte 4   Byte 5   Byte 6   Byte 7

    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
    S|-exponent-||----mantissa--------| |----double extended mantissa-----|
    1  11 bits         20 bits						32 more bits

    S = sign bit
    0 <= exponent <= 2047
    Exponent bias:  1023 (subtract from exponent to get actual value)

   SPARC and HP are both BIG endians. The Intel 80x86 is a LITTLE endian,
   although the FP encoding style is same -- that is, just reverse the order
   of the above bytes (either all four or all eight).

   This should work on any host platform supporting IEEE format.
 */

void
EncodeFloat(double f, Byte *value)
{
	unsigned char	*bytes;
	unsigned		f0, f1, mant;
	int				exp;

/* Sanity */

	assert(sizeof(double) == DOUBLE_SIZE);
	assert(FLOAT_SIZE_6502 == 5);

/* Check trivial case	*/

	if (f == 0.0)
	{
		value[0] =
		value[1] =
		value[2] =
		value[3] =
		value[4] = 0;
		return;
	}

/* Make the double big endian so we can think straight */

	#ifdef	DEBUG_FP
	fprintf(stderr, "FP--> Value is [ %.10g ]\n", f);
	#endif

	if (f0 = 1, *((Byte *) &f0))
		ReverseBytes(&f, sizeof(f));
	bytes = (Byte *) &f;

/* Split f into two 32 bit integers so we can play with bits */

	f0 = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
	f1 = (bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7];

	#ifdef	DEBUG_FP
	fprintf(stderr, "FP--> That's IEEE encoded as 0x%08X 0x%08X\n", f0, f1);
	#endif

/* Compute exponent */

	exp = ((f0 >> 20) & 0x000007FF) - 1023;

	#ifdef	DEBUG_FP
	fprintf(stderr, "FP--> Exponent = %d\n", exp);
	#endif

/* Clip it if it's out of bounds */

	if (exp < -128 || exp > 126)
	{
		uerror("floating point exponent out of range");
		exp = (exp < -128) ? -128 : 126;
	}

/* Get mantissa bits as a 32 bit integer */

	mant  = (f0 << 11) & 0x7FFFF800;	/* 20 matissa bits from first word */
	mant |= (f1 >> 21) & 0x000007FF;	/* 11 matissa more from second word */

/* Round up using next bit */

	if (f1 & 0x00100000)
		mant++;

/* Put in sign bit */

	mant |= f0 & 0x80000000;

/* Construct 5 byte value */

	value[0] = exp + 129;
	value[1] = (mant >> 24) & 0xFF;
	value[2] = (mant >> 16) & 0xFF;
	value[3] = (mant >>  8) & 0xFF;
	value[4] = (mant) & 0xFF;

	#ifdef	DEBUG_FP
	fprintf(stderr, "FP--> Apple value is 0x%02X 0x%08X\n", value[0], mant);
	#endif
}

static	void
ReverseBytes(void *p, int size)
{
	unsigned char	swap, *bytes;
	int				bytenum;

	for (bytes = (unsigned char *) p, bytenum = 0;
			bytenum < size / 2;
			bytenum++)
	{
		swap = bytes[size - bytenum - 1];
		bytes[size - bytenum - 1] = bytes[bytenum];
		bytes[bytenum] = swap;
	}
}

