* dcache.c: Add prototypes. Make many functions static.
authorStu Grossman <grossman@cygnus>
Thu, 11 Apr 1996 21:17:45 +0000 (21:17 +0000)
committerStu Grossman <grossman@cygnus>
Thu, 11 Apr 1996 21:17:45 +0000 (21:17 +0000)
* (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.

gdb/ChangeLog
gdb/config/i960/tm-i960.h
gdb/dcache.c
gdb/defs.h
gdb/findvar.c
gdb/remote-nindy.c
gdb/utils.c

index 037a696..beabdc2 100644 (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.
index 6c80f84..95a4143 100644 (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.  */
 
index 9f44e96..f110a0c 100644 (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);
 }
 
 
index 47de9f2..0aaac1d 100644 (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.  */
+
+/* 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));
 \f
 /* 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.
index 1dce905..7764a21 100644 (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
     {
index a3417f0..cb223a2 100644 (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.  */
 
index 97b6a9b..22e511c 100644 (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
+\f
+#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));
+}
+\f
+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 */