* dcache.c: Add prototypes. Make many functions static.

* (dcache_peek dcache_fetch dcache_poke):  Make dcache_fetch and
	dcache_poke call dcache_xfer_memory directly in order to fix
	problems with turning off dcache.  dcache_peek is now unnecessary,
	so it goes away.

	* defs.h:  Define new macros HOST_{FLOAT DOUBLE	LONG_DOUBLE}_FORMAT
	and TARGET_{FLOAT DOUBLE LONG_DOUBLE}_FORMAT to specify a pointer
	to a struct floatformat.  This allows for better handling of
	targets whose floating point formats differ from the host by more
	than just byte order.
	* (floatformat_to_long_double floatformat_from_long_double):
	Prototypes for new functions in utils.c.
	* (floatformat_to_doublest floatformat_from_doublest):  Prototypes
	for pointers to floating point conversion functions.  The actual
	function uses either double or long double if the host supports it.
	* findvar.c (floatformat_to_doublest floatformat_from_doublest):
	Initialize to point at correct function depending on HAVE_LONG_DOUBLE.
	* (extract_floating store_floating):  Rewrite.  Now, if host fp
	format is the same as the target, we just do a copy.  Otherwise,
	we call floatformat_{to from}_doublest.
	* remote-nindy.c (nindy_xfer_inferior_memory):  Change param
	`write' to `should_write'.
	* utils.c (floatformat_to_long_double
	floatformat_from_long_double):  New routines that implement long
	double versions of functions in libiberty/floatformat.c.
	* config/i960/tm-i960.h (TARGET_LONG_DOUBLE_FORMAT):  Define this for
	i960 extended real (80 bit) numbers.
	* nindy-share/nindy.c (ninMemGet ninMemPut):  Return number of bytes
	actually read or written.
This commit is contained in:
Stu Grossman 1996-04-11 21:17:45 +00:00
parent 024e177923
commit a243a22f43
7 changed files with 551 additions and 66 deletions

View File

@ -1,3 +1,36 @@
Thu Apr 11 13:47:52 1996 Stu Grossman (grossman@critters.cygnus.com)
* dcache.c: Add prototypes. Make many functions static.
* (dcache_peek dcache_fetch dcache_poke): Make dcache_fetch and
dcache_poke call dcache_xfer_memory directly in order to fix
problems with turning off dcache. dcache_peek is now unnecessary,
so it goes away.
* defs.h: Define new macros HOST_{FLOAT DOUBLE LONG_DOUBLE}_FORMAT
and TARGET_{FLOAT DOUBLE LONG_DOUBLE}_FORMAT to specify a pointer
to a struct floatformat. This allows for better handling of
targets whose floating point formats differ from the host by more
than just byte order.
* (floatformat_to_long_double floatformat_from_long_double):
Prototypes for new functions in utils.c.
* (floatformat_to_doublest floatformat_from_doublest): Prototypes
for pointers to floating point conversion functions. The actual
function uses either double or long double if the host supports it.
* findvar.c (floatformat_to_doublest floatformat_from_doublest):
Initialize to point at correct function depending on HAVE_LONG_DOUBLE.
* (extract_floating store_floating): Rewrite. Now, if host fp
format is the same as the target, we just do a copy. Otherwise,
we call floatformat_{to from}_doublest.
* remote-nindy.c (nindy_xfer_inferior_memory): Change param
`write' to `should_write'.
* utils.c (floatformat_to_long_double
floatformat_from_long_double): New routines that implement long
double versions of functions in libiberty/floatformat.c.
* config/i960/tm-i960.h (TARGET_LONG_DOUBLE_FORMAT): Define this for
i960 extended real (80 bit) numbers.
* nindy-share/nindy.c (ninMemGet ninMemPut): Return number of bytes
actually read or written.
Wed Apr 10 02:56:06 1996 Wilfried Moser (Alcatel) <moser@rtl.cygnus.com>
* ch-valprint.c (chill_val_print): Remove call to calculate_array_length.

View File

@ -163,6 +163,8 @@ extern CORE_ADDR saved_pc_after_call ();
#include "floatformat.h"
#define TARGET_LONG_DOUBLE_FORMAT &floatformat_i960_ext
/* Convert data from raw format for register REGNUM in buffer FROM
to virtual format with type TYPE in buffer TO. */

View File

@ -148,6 +148,23 @@ struct dcache_struct
int cache_has_stuff;
} ;
static int
dcache_poke_byte PARAMS ((DCACHE *dcache, CORE_ADDR addr, char *ptr));
static int
dcache_peek_byte PARAMS ((DCACHE *dcache, CORE_ADDR addr, char *ptr));
static struct dcache_block *
dcache_hit PARAMS ((DCACHE *dcache, unsigned int addr));
static int dcache_write_line PARAMS ((DCACHE *dcache,struct dcache_block *db));
static struct dcache_block *dcache_alloc PARAMS ((DCACHE *dcache));
static int dcache_writeback PARAMS ((DCACHE *dcache));
static void dcache_info PARAMS ((char *exp, int tty));
int remote_dcache = 0;
DCACHE *last_cache; /* Used by info dcache */
@ -186,8 +203,8 @@ dcache_flush (dcache)
/* If addr is present in the dcache, return the address of the block
containing it. */
static
struct dcache_block *
static struct dcache_block *
dcache_hit (dcache, addr)
DCACHE *dcache;
unsigned int addr;
@ -261,8 +278,8 @@ dcache_write_line (dcache, db)
prevents errors from creeping in if a memory retrieval is
interrupted (which used to put garbage blocks in the valid
list...). */
static
struct dcache_block *
static struct dcache_block *
dcache_alloc (dcache)
DCACHE *dcache;
{
@ -302,7 +319,7 @@ dcache_alloc (dcache)
Returns 0 on error. */
int
static int
dcache_peek_byte (dcache, addr, ptr)
DCACHE *dcache;
CORE_ADDR addr;
@ -342,28 +359,6 @@ dcache_peek_byte (dcache, addr, ptr)
return ok;
}
/* Using the data cache DCACHE return the contents of the word at
address ADDR in the remote machine.
Returns 0 on error. */
int
dcache_peek (dcache, addr, data)
DCACHE *dcache;
CORE_ADDR addr;
int *data;
{
char *dp = (char *) data;
int i;
for (i = 0; i < (int) sizeof (int); i++)
{
if (!dcache_peek_byte (dcache, addr + i, dp + i))
return 0;
}
return 1;
}
/* Writeback any dirty lines to the remote. */
static int
dcache_writeback (dcache)
@ -391,7 +386,10 @@ dcache_fetch (dcache, addr)
CORE_ADDR addr;
{
int res;
dcache_peek (dcache, addr, &res);
if (dcache_xfer_memory (dcache, addr, (char *)&res, sizeof res, 0) != sizeof res)
memory_error (EIO, addr);
return res;
}
@ -400,7 +398,7 @@ dcache_fetch (dcache, addr)
Return zero on write error.
*/
int
static int
dcache_poke_byte (dcache, addr, ptr)
DCACHE *dcache;
CORE_ADDR addr;
@ -431,15 +429,10 @@ dcache_poke (dcache, addr, data)
CORE_ADDR addr;
int data;
{
char *dp = (char *) (&data);
int i;
for (i = 0; i < (int) sizeof (int); i++)
{
if (!dcache_poke_byte (dcache, addr + i, dp + i))
return 0;
}
dcache_writeback (dcache);
return 1;
if (dcache_xfer_memory (dcache, addr, (char *)&data, sizeof data, 1) != sizeof data)
return 0;
return dcache_writeback (dcache);
}

View File

@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifndef DEFS_H
#define DEFS_H
#include "config.h" /* Generated by configure */
#include <stdio.h>
#include <errno.h> /* System call error return status */
@ -811,6 +812,8 @@ extern LONGEST extract_signed_integer PARAMS ((void *, int));
extern unsigned LONGEST extract_unsigned_integer PARAMS ((void *, int));
extern int extract_long_unsigned_integer PARAMS ((void *, int, LONGEST *));
extern CORE_ADDR extract_address PARAMS ((void *, int));
extern void store_signed_integer PARAMS ((void *, int, LONGEST));
@ -819,9 +822,101 @@ extern void store_unsigned_integer PARAMS ((void *, int, unsigned LONGEST));
extern void store_address PARAMS ((void *, int, CORE_ADDR));
extern double extract_floating PARAMS ((void *, int));
/* Setup definitions for host and target floating point formats. We need to
consider the format for `float', `double', and `long double' for both target
and host. We need to do this so that we know what kind of conversions need
to be done when converting target numbers to and from the hosts DOUBLEST
data type. */
extern void store_floating PARAMS ((void *, int, double));
/* This is used to indicate that we don't know the format of the floating point
number. Typically, this is useful for native ports, where the actual format
is irrelevant, since no conversions will be taking place. */
extern const struct floatformat floatformat_unknown;
#if HOST_BYTE_ORDER == BIG_ENDIAN
# ifndef HOST_FLOAT_FORMAT
# define HOST_FLOAT_FORMAT &floatformat_ieee_single_big
# endif
# ifndef HOST_DOUBLE_FORMAT
# define HOST_DOUBLE_FORMAT &floatformat_ieee_double_big
# endif
#else /* LITTLE_ENDIAN */
# ifndef HOST_FLOAT_FORMAT
# define HOST_FLOAT_FORMAT &floatformat_ieee_single_little
# endif
# ifndef HOST_DOUBLE_FORMAT
# define HOST_DOUBLE_FORMAT &floatformat_ieee_double_little
# endif
#endif
#ifndef HOST_LONG_DOUBLE_FORMAT
#define HOST_LONG_DOUBLE_FORMAT &floatformat_unknown
#endif
#ifndef TARGET_BYTE_ORDER_SELECTABLE
# if TARGET_BYTE_ORDER == BIG_ENDIAN
# ifndef TARGET_FLOAT_FORMAT
# define TARGET_FLOAT_FORMAT &floatformat_ieee_single_big
# endif
# ifndef TARGET_DOUBLE_FORMAT
# define TARGET_DOUBLE_FORMAT &floatformat_ieee_double_big
# endif
# else /* LITTLE_ENDIAN */
# ifndef TARGET_FLOAT_FORMAT
# define TARGET_FLOAT_FORMAT &floatformat_ieee_single_little
# endif
# ifndef TARGET_DOUBLE_FORMAT
# define TARGET_DOUBLE_FORMAT &floatformat_ieee_double_little
# endif
# endif
# ifndef TARGET_LONG_DOUBLE_FORMAT
# define TARGET_LONG_DOUBLE_FORMAT &floatformat_unknown
# endif
#else /* TARGET_BYTE_ORDER_SELECTABLE */
# ifndef TARGET_FLOAT_FORMAT
Need a definition for target float format
# endif
# ifndef TARGET_DOUBLE_FORMAT
Need a definition for target double format
# endif
# ifndef TARGET_LONG_DOUBLE_FORMAT
Need a definition for target long double format
# endif
#endif
/* Use `long double' if the host compiler supports it. (Note that this is not
necessarily any longer than `double'. On SunOS/gcc, it's the same as
double.) This is necessary because GDB internally converts all floating
point values to the widest type supported by the host.
There are problems however, when the target `long double' is longer than the
host's `long double'. In general, we'll probably reduce the precision of
any such values and print a warning. */
#ifdef HAVE_LONG_DOUBLE
typedef long double DOUBLEST;
extern void floatformat_to_long_double PARAMS ((const struct floatformat *,
char *, DOUBLEST *));
extern void floatformat_from_long_double PARAMS ((const struct floatformat *,
DOUBLEST *, char *));
#else
typedef double DOUBLEST;
#endif
/* Pointer to appropriate conversion routine to convert between target floating
point format and DOUBLEST. */
extern void
(*floatformat_to_doublest) PARAMS ((const struct floatformat *,
char *, DOUBLEST *));
extern void
(*floatformat_from_doublest) PARAMS ((const struct floatformat *,
DOUBLEST *, char *));
extern DOUBLEST extract_floating PARAMS ((void *, int));
extern void store_floating PARAMS ((void *, int, DOUBLEST));
/* On some machines there are bits in addresses which are not really
part of the address, but are used by the kernel, the hardware, etc.

View File

@ -26,6 +26,29 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "inferior.h"
#include "target.h"
#include "gdb_string.h"
#include "floatformat.h"
/* This is used to indicate that we don't know the format of the floating point
number. Typically, this is useful for native ports, where the actual format
is irrelevant, since no conversions will be taking place. */
const struct floatformat floatformat_unknown;
#ifdef HAVE_LONG_DOUBLE
void (*floatformat_to_doublest)
PARAMS ((const struct floatformat *,
char *, DOUBLEST *)) = floatformat_to_long_double;
void (*floatformat_from_doublest)
PARAMS ((const struct floatformat *,
DOUBLEST *, char *)) = floatformat_from_long_double;
#else
void (*floatformat_to_doublest)
PARAMS ((const struct floatformat *,
char *, DOUBLEST *)) = floatformat_to_double;
void (*floatformat_from_doublest)
PARAMS ((const struct floatformat *,
DOUBLEST *, char *)) = floatformat_from_double;
#endif
/* Registers we shouldn't try to store. */
#if !defined (CANNOT_STORE_REGISTER)
@ -285,31 +308,50 @@ extract_floating (addr, len)
PTR addr;
int len;
{
DOUBLEST dretval;
if (len == sizeof (float))
{
float retval;
memcpy (&retval, addr, sizeof (retval));
SWAP_FLOATING (&retval, sizeof (retval));
return retval;
if (HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT)
{
float retval;
memcpy (&retval, addr, sizeof (retval));
return retval;
}
else
floatformat_to_doublest (TARGET_FLOAT_FORMAT, addr, &dretval);
}
else if (len == sizeof (double))
{
double retval;
memcpy (&retval, addr, sizeof (retval));
SWAP_FLOATING (&retval, sizeof (retval));
return retval;
if (HOST_DOUBLE_FORMAT == TARGET_DOUBLE_FORMAT)
{
double retval;
memcpy (&retval, addr, sizeof (retval));
return retval;
}
else
floatformat_to_doublest (TARGET_DOUBLE_FORMAT, addr, &dretval);
}
else if (len == sizeof (DOUBLEST))
{
DOUBLEST retval;
memcpy (&retval, addr, sizeof (retval));
SWAP_FLOATING (&retval, sizeof (retval));
return retval;
if (HOST_LONG_DOUBLE_FORMAT == TARGET_LONG_DOUBLE_FORMAT)
{
DOUBLEST retval;
memcpy (&retval, addr, sizeof (retval));
return retval;
}
else
floatformat_to_doublest (TARGET_LONG_DOUBLE_FORMAT, addr, &dretval);
}
else
{
error ("Can't deal with a floating point number of %d bytes.", len);
}
return dretval;
}
void
@ -320,21 +362,32 @@ store_floating (addr, len, val)
{
if (len == sizeof (float))
{
float floatval = val;
SWAP_FLOATING (&floatval, sizeof (floatval));
memcpy (addr, &floatval, sizeof (floatval));
if (HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT)
{
float floatval = val;
memcpy (addr, &floatval, sizeof (floatval));
}
else
floatformat_from_doublest (TARGET_FLOAT_FORMAT, &val, addr);
}
else if (len == sizeof (double))
{
double doubleval = val;
if (HOST_DOUBLE_FORMAT == TARGET_DOUBLE_FORMAT)
{
double doubleval = val;
SWAP_FLOATING (&doubleval, sizeof (doubleval));
memcpy (addr, &doubleval, sizeof (doubleval));
memcpy (addr, &doubleval, sizeof (doubleval));
}
else
floatformat_from_doublest (TARGET_DOUBLE_FORMAT, &val, addr);
}
else if (len == sizeof (DOUBLEST))
{
SWAP_FLOATING (&val, sizeof (val));
memcpy (addr, &val, sizeof (val));
if (HOST_LONG_DOUBLE_FORMAT == TARGET_LONG_DOUBLE_FORMAT)
memcpy (addr, &val, sizeof (val));
else
floatformat_from_doublest (TARGET_LONG_DOUBLE_FORMAT, &val, addr);
}
else
{

View File

@ -525,11 +525,11 @@ nindy_store_word (addr, word)
FIXME, rewrite this to not use the word-oriented routines. */
int
nindy_xfer_inferior_memory(memaddr, myaddr, len, write, target)
nindy_xfer_inferior_memory(memaddr, myaddr, len, should_write, target)
CORE_ADDR memaddr;
char *myaddr;
int len;
int write;
int should_write;
struct target_ops *target; /* ignored */
{
register int i;
@ -541,7 +541,7 @@ nindy_xfer_inferior_memory(memaddr, myaddr, len, write, target)
/* Allocate buffer of that many longwords. */
register int *buffer = (int *) alloca (count * sizeof (int));
if (write)
if (should_write)
{
/* Fill start and end extra bytes of buffer with existing memory data. */

View File

@ -18,7 +18,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "defs.h"
#if !defined(__GO32__) && !defined(__WIN32__)
#if !defined(__GO32__) && !defined(__WIN32__) && !defined(MPW)
#include <sys/ioctl.h>
#include <sys/param.h>
#include <pwd.h>
@ -603,9 +603,9 @@ request_quit (signo)
signal (signo, request_quit);
/* start-sanitize-gm */
#ifdef GENERAL_MAGIC_HACKS
#ifdef GENERAL_MAGIC
target_kill ();
#endif /* GENERAL_MAGIC_HACKS */
#endif /* GENERAL_MAGIC */
/* end-sanitize-gm */
#ifdef REQUEST_QUIT
@ -1950,4 +1950,313 @@ initialize_utils ()
#ifdef SIGWINCH_HANDLER_BODY
SIGWINCH_HANDLER_BODY
#endif
#ifdef HAVE_LONG_DOUBLE
/* Support for converting target fp numbers into host long double format. */
/* XXX - This code should really be in libiberty/floatformat.c, however
configuration issues with libiberty made this very difficult to do in the
available time. */
#include "floatformat.h"
#include <math.h> /* ldexp */
/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
going to bother with trying to muck around with whether it is defined in
a system header, what we do if not, etc. */
#define FLOATFORMAT_CHAR_BIT 8
static unsigned long get_field PARAMS ((unsigned char *,
enum floatformat_byteorders,
unsigned int,
unsigned int,
unsigned int));
/* Extract a field which starts at START and is LEN bytes long. DATA and
TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
static unsigned long
get_field (data, order, total_len, start, len)
unsigned char *data;
enum floatformat_byteorders order;
unsigned int total_len;
unsigned int start;
unsigned int len;
{
unsigned long result;
unsigned int cur_byte;
int cur_bitshift;
/* Start at the least significant part of the field. */
cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
cur_bitshift =
((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
result = *(data + cur_byte) >> (-cur_bitshift);
cur_bitshift += FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
++cur_byte;
else
--cur_byte;
/* Move towards the most significant part of the field. */
while (cur_bitshift < len)
{
if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
/* This is the last byte; zero out the bits which are not part of
this field. */
result |=
(*(data + cur_byte) & ((1 << (len - cur_bitshift)) - 1))
<< cur_bitshift;
else
result |= *(data + cur_byte) << cur_bitshift;
cur_bitshift += FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
++cur_byte;
else
--cur_byte;
}
return result;
}
/* Convert from FMT to a long double.
FROM is the address of the extended float.
Store the long double in *TO. */
void
floatformat_to_long_double (fmt, from, to)
const struct floatformat *fmt;
char *from;
long double *to;
{
unsigned char *ufrom = (unsigned char *)from;
long double dto;
long exponent;
unsigned long mant;
unsigned int mant_bits, mant_off;
int mant_bits_left;
exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
fmt->exp_start, fmt->exp_len);
/* Note that if exponent indicates a NaN, we can't really do anything useful
(not knowing if the host has NaN's, or how to build one). So it will
end up as an infinity or something close; that is OK. */
mant_bits_left = fmt->man_len;
mant_off = fmt->man_start;
dto = 0.0;
exponent -= fmt->exp_bias;
/* Build the result algebraically. Might go infinite, underflow, etc;
who cares. */
/* If this format uses a hidden bit, explicitly add it in now. Otherwise,
increment the exponent by one to account for the integer bit. */
if (fmt->intbit == floatformat_intbit_no)
dto = ldexp (1.0, exponent);
else
exponent++;
while (mant_bits_left > 0)
{
mant_bits = min (mant_bits_left, 32);
mant = get_field (ufrom, fmt->byteorder, fmt->totalsize,
mant_off, mant_bits);
dto += ldexp ((double)mant, exponent - mant_bits);
exponent -= mant_bits;
mant_off += mant_bits;
mant_bits_left -= mant_bits;
}
/* Negate it if negative. */
if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
dto = -dto;
memcpy (to, &dto, sizeof (dto));
}
static void put_field PARAMS ((unsigned char *, enum floatformat_byteorders,
unsigned int,
unsigned int,
unsigned int,
unsigned long));
/* Set a field which starts at START and is LEN bytes long. DATA and
TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
static void
put_field (data, order, total_len, start, len, stuff_to_put)
unsigned char *data;
enum floatformat_byteorders order;
unsigned int total_len;
unsigned int start;
unsigned int len;
unsigned long stuff_to_put;
{
unsigned int cur_byte;
int cur_bitshift;
/* Start at the least significant part of the field. */
cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
cur_bitshift =
((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
*(data + cur_byte) &=
~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1) << (-cur_bitshift));
*(data + cur_byte) |=
(stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift);
cur_bitshift += FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
++cur_byte;
else
--cur_byte;
/* Move towards the most significant part of the field. */
while (cur_bitshift < len)
{
if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
{
/* This is the last byte. */
*(data + cur_byte) &=
~((1 << (len - cur_bitshift)) - 1);
*(data + cur_byte) |= (stuff_to_put >> cur_bitshift);
}
else
*(data + cur_byte) = ((stuff_to_put >> cur_bitshift)
& ((1 << FLOATFORMAT_CHAR_BIT) - 1));
cur_bitshift += FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
++cur_byte;
else
--cur_byte;
}
}
/* Return the fractional part of VALUE, and put the exponent of VALUE in *EPTR.
The range of the returned value is >= 0.5 and < 1.0. This is equivalent to
frexp, but operates on the long double data type. */
static long double ldfrexp PARAMS ((long double value, int *eptr));
static long double
ldfrexp (value, eptr)
long double value;
int *eptr;
{
long double tmp;
int exp;
/* Unfortunately, there are no portable functions for extracting the exponent
of a long double, so we have to do it iteratively by multiplying or dividing
by two until the fraction is between 0.5 and 1.0. */
if (value < 0.0l)
value = -value;
tmp = 1.0l;
exp = 0;
if (value >= tmp) /* Value >= 1.0 */
while (value >= tmp)
{
tmp *= 2.0l;
exp++;
}
else if (value != 0.0l) /* Value < 1.0 and > 0.0 */
{
while (value < tmp)
{
tmp /= 2.0l;
exp--;
}
tmp *= 2.0l;
exp++;
}
*eptr = exp;
return value/tmp;
}
/* The converse: convert the long double *FROM to an extended float
and store where TO points. Neither FROM nor TO have any alignment
restrictions. */
void
floatformat_from_long_double (fmt, from, to)
CONST struct floatformat *fmt;
long double *from;
char *to;
{
long double dfrom;
int exponent;
long double mant;
unsigned int mant_bits, mant_off;
int mant_bits_left;
unsigned char *uto = (unsigned char *)to;
memcpy (&dfrom, from, sizeof (dfrom));
memset (uto, 0, fmt->totalsize / FLOATFORMAT_CHAR_BIT);
if (dfrom == 0)
return; /* Result is zero */
if (dfrom != dfrom)
{
/* From is NaN */
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
fmt->exp_len, fmt->exp_nan);
/* Be sure it's not infinity, but NaN value is irrel */
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->man_start,
32, 1);
return;
}
/* If negative, set the sign bit. */
if (dfrom < 0)
{
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1, 1);
dfrom = -dfrom;
}
/* How to tell an infinity from an ordinary number? FIXME-someday */
mant = ldfrexp (dfrom, &exponent);
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start, fmt->exp_len,
exponent + fmt->exp_bias - 1);
mant_bits_left = fmt->man_len;
mant_off = fmt->man_start;
while (mant_bits_left > 0)
{
unsigned long mant_long;
mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
mant *= 4294967296.0;
mant_long = (unsigned long)mant;
mant -= mant_long;
/* If the integer bit is implicit, then we need to discard it.
If we are discarding a zero, we should be (but are not) creating
a denormalized number which means adjusting the exponent
(I think). */
if (mant_bits_left == fmt->man_len
&& fmt->intbit == floatformat_intbit_no)
{
mant_long &= 0x7fffffff;
mant_bits -= 1;
}
else if (mant_bits < 32)
{
/* The bits we want are in the most significant MANT_BITS bits of
mant_long. Move them to the least significant. */
mant_long >>= 32 - mant_bits;
}
put_field (uto, fmt->byteorder, fmt->totalsize,
mant_off, mant_bits, mant_long);
mant_off += mant_bits;
mant_bits_left -= mant_bits;
}
}
#endif /* HAVE_LONG_DOUBLE */