From a243a22f4359c557c08d4fd962391c9f70287fec Mon Sep 17 00:00:00 2001 From: Stu Grossman Date: Thu, 11 Apr 1996 21:17:45 +0000 Subject: * 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. --- gdb/ChangeLog | 33 +++++ gdb/config/i960/tm-i960.h | 2 + gdb/dcache.c | 69 +++++----- gdb/defs.h | 99 ++++++++++++++- gdb/findvar.c | 93 +++++++++++--- gdb/remote-nindy.c | 6 +- gdb/utils.c | 315 +++++++++++++++++++++++++++++++++++++++++++++- 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) * 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 #include /* 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 #include #include @@ -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 /* 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 */ -- cgit v1.1