aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorStu Grossman <grossman@cygnus>1996-04-11 21:17:45 +0000
committerStu Grossman <grossman@cygnus>1996-04-11 21:17:45 +0000
commita243a22f4359c557c08d4fd962391c9f70287fec (patch)
tree018f21e57d8d9f12db5fe5f608d8536beb5e34e0 /gdb
parent024e1779233ad7121b2bf40741cfe21b4047770e (diff)
downloadgdb-a243a22f4359c557c08d4fd962391c9f70287fec.zip
gdb-a243a22f4359c557c08d4fd962391c9f70287fec.tar.gz
gdb-a243a22f4359c557c08d4fd962391c9f70287fec.tar.bz2
* 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.
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog33
-rw-r--r--gdb/config/i960/tm-i960.h2
-rw-r--r--gdb/dcache.c69
-rw-r--r--gdb/defs.h99
-rw-r--r--gdb/findvar.c93
-rw-r--r--gdb/remote-nindy.c6
-rw-r--r--gdb/utils.c315
7 files changed, 551 insertions, 66 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 037a696..beabdc2 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -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.
diff --git a/gdb/config/i960/tm-i960.h b/gdb/config/i960/tm-i960.h
index 6c80f84..95a4143 100644
--- a/gdb/config/i960/tm-i960.h
+++ b/gdb/config/i960/tm-i960.h
@@ -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. */
diff --git a/gdb/dcache.c b/gdb/dcache.c
index 9f44e96..f110a0c0 100644
--- a/gdb/dcache.c
+++ b/gdb/dcache.c
@@ -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);
}
diff --git a/gdb/defs.h b/gdb/defs.h
index 47de9f2..0aaac1d 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -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. */
+
+/* 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, double));
+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.
diff --git a/gdb/findvar.c b/gdb/findvar.c
index 1dce905..7764a21 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -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
{
diff --git a/gdb/remote-nindy.c b/gdb/remote-nindy.c
index a3417f0..cb223a2 100644
--- a/gdb/remote-nindy.c
+++ b/gdb/remote-nindy.c
@@ -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. */
diff --git a/gdb/utils.c b/gdb/utils.c
index 97b6a9b..22e511c 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -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 */