Merge from vendor branch OPENSSL:
[dragonfly.git] / sys / i386 / gnu / fpemul / reg_ld_str.c
1 /*
2  *  reg_ld_str.c
3  *
4  * All of the functions which transfer data between user memory and FPU_REGs.
5  *
6  *
7  * Copyright (C) 1992,1993,1994
8  *                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
9  *                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au
10  * All rights reserved.
11  *
12  * This copyright notice covers the redistribution and use of the
13  * FPU emulator developed by W. Metzenthen. It covers only its use
14  * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
15  * use is not permitted under this copyright.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must include information specifying
23  *    that source code for the emulator is freely available and include
24  *    either:
25  *      a) an offer to provide the source code for a nominal distribution
26  *         fee, or
27  *      b) list at least two alternative methods whereby the source
28  *         can be obtained, e.g. a publically accessible bulletin board
29  *         and an anonymous ftp site from which the software can be
30  *         downloaded.
31  * 3. All advertising materials specifically mentioning features or use of
32  *    this emulator must acknowledge that it was developed by W. Metzenthen.
33  * 4. The name of W. Metzenthen may not be used to endorse or promote
34  *    products derived from this software without specific prior written
35  *    permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
38  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
40  * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
41  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
42  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
43  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
44  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
45  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
46  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  *
48  *
49  * The purpose of this copyright, based upon the Berkeley copyright, is to
50  * ensure that the covered software remains freely available to everyone.
51  *
52  * The software (with necessary differences) is also available, but under
53  * the terms of the GNU copyleft, for the Linux operating system and for
54  * the djgpp ms-dos extender.
55  *
56  * W. Metzenthen   June 1994.
57  *
58  *
59  * $FreeBSD: src/sys/gnu/i386/fpemul/reg_ld_str.c,v 1.13 1999/08/28 00:42:56 peter Exp $
60  * $DragonFly: src/sys/i386/gnu/fpemul/Attic/reg_ld_str.c,v 1.3 2003/08/07 21:17:20 dillon Exp $
61  *
62  */
63
64
65 /*---------------------------------------------------------------------------+
66  | Note:                                                                     |
67  |    The file contains code which accesses user memory.                     |
68  |    Emulator static data may change when user memory is accessed, due to   |
69  |    other processes using the emulator while swapping is in progress.      |
70  +---------------------------------------------------------------------------*/
71 #include <sys/param.h>
72 #include <sys/proc.h>
73 #include <sys/systm.h>
74 #include <machine/pcb.h>
75
76 #include "fpu_emu.h"
77 #include "fpu_system.h"
78 #include "exception.h"
79 #include "reg_constant.h"
80 #include "control_w.h"
81 #include "status_w.h"
82
83
84 #define EXTENDED_Emax 0x3fff    /* largest valid exponent */
85 #define EXTENDED_Ebias 0x3fff
86 #define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */
87
88 #define DOUBLE_Emax 1023        /* largest valid exponent */
89 #define DOUBLE_Ebias 1023
90 #define DOUBLE_Emin (-1022)     /* smallest valid exponent */
91
92 #define SINGLE_Emax 127         /* largest valid exponent */
93 #define SINGLE_Ebias 127
94 #define SINGLE_Emin (-126)      /* smallest valid exponent */
95
96 #define LOST_UP    (EX_Precision | SW_C1)
97 #define LOST_DOWN  EX_Precision
98
99 FPU_REG FPU_loaded_data;
100
101
102 /* Get a long double from user memory */
103 void
104 reg_load_extended(void)
105 {
106         long double *s = (long double *) FPU_data_address;
107         unsigned long sigl, sigh, exp;
108
109         REENTRANT_CHECK(OFF);
110         /* Use temporary variables here because FPU_loaded data is static and
111          * hence re-entrancy problems can arise */
112         sigl = fuword((unsigned long *) s);
113         sigh = fuword(1 + (unsigned long *) s);
114         exp = fusword(4 + (unsigned short *) s);
115         REENTRANT_CHECK(ON);
116
117         FPU_loaded_data.sigl = sigl;
118         FPU_loaded_data.sigh = sigh;
119         FPU_loaded_data.exp = exp;
120
121         if (FPU_loaded_data.exp & 0x8000)
122                 FPU_loaded_data.sign = SIGN_NEG;
123         else
124                 FPU_loaded_data.sign = SIGN_POS;
125         if ((FPU_loaded_data.exp &= 0x7fff) == 0) {
126                 if (!(FPU_loaded_data.sigl | FPU_loaded_data.sigh)) {
127                         FPU_loaded_data.tag = TW_Zero;
128                         return;
129                 }
130                 /* The number is a de-normal or pseudodenormal. */
131                 /* The 80486 doesn't regard pseudodenormals as denormals here. */
132                 if (!(FPU_loaded_data.sigh & 0x80000000))
133                         EXCEPTION(EX_Denormal);
134                 FPU_loaded_data.exp++;
135
136                 /* The default behaviour will now take care of it. */
137         } else
138                 if (FPU_loaded_data.exp == 0x7fff) {
139                         FPU_loaded_data.exp = EXTENDED_Emax;
140                         if ((FPU_loaded_data.sigh == 0x80000000)
141                             && (FPU_loaded_data.sigl == 0)) {
142                                 FPU_loaded_data.tag = TW_Infinity;
143                                 return;
144                         } else
145                                 if (!(FPU_loaded_data.sigh & 0x80000000)) {
146                                         /* Unsupported NaN data type */
147                                         EXCEPTION(EX_Invalid);
148                                         FPU_loaded_data.tag = TW_NaN;
149                                         return;
150                                 }
151                         FPU_loaded_data.tag = TW_NaN;
152                         return;
153                 }
154         FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias
155             + EXP_BIAS;
156         FPU_loaded_data.tag = TW_Valid;
157
158         if (!(sigh & 0x80000000)) {
159                 /* Unsupported data type */
160                 EXCEPTION(EX_Invalid);
161                 normalize_nuo(&FPU_loaded_data);
162         }
163 }
164
165
166 /* Get a double from user memory */
167 void
168 reg_load_double(void)
169 {
170         double *dfloat = (double *) FPU_data_address;
171         int     exp;
172         unsigned m64, l64;
173
174         REENTRANT_CHECK(OFF);
175         m64 = fuword(1 + (unsigned long *) dfloat);
176         l64 = fuword((unsigned long *) dfloat);
177         REENTRANT_CHECK(ON);
178
179         if (m64 & 0x80000000)
180                 FPU_loaded_data.sign = SIGN_NEG;
181         else
182                 FPU_loaded_data.sign = SIGN_POS;
183         exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
184         m64 &= 0xfffff;
185         if (exp > DOUBLE_Emax) {
186                 /* Infinity or NaN */
187                 if ((m64 == 0) && (l64 == 0)) {
188                         /* +- infinity */
189                         FPU_loaded_data.exp = EXTENDED_Emax;
190                         FPU_loaded_data.tag = TW_Infinity;
191                         return;
192                 } else {
193                         /* Must be a signaling or quiet NaN */
194                         FPU_loaded_data.exp = EXTENDED_Emax;
195                         FPU_loaded_data.tag = TW_NaN;
196                         FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
197                         FPU_loaded_data.sigh |= l64 >> 21;
198                         FPU_loaded_data.sigl = l64 << 11;
199                         return;
200                 }
201         } else
202                 if (exp < DOUBLE_Emin) {
203                         /* Zero or de-normal */
204                         if ((m64 == 0) && (l64 == 0)) {
205                                 /* Zero */
206                                 int     c = FPU_loaded_data.sign;
207                                 reg_move(&CONST_Z, &FPU_loaded_data);
208                                 FPU_loaded_data.sign = c;
209                                 return;
210                         } else {
211                                 /* De-normal */
212                                 EXCEPTION(EX_Denormal);
213                                 FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
214                                 FPU_loaded_data.tag = TW_Valid;
215                                 FPU_loaded_data.sigh = m64 << 11;
216                                 FPU_loaded_data.sigh |= l64 >> 21;
217                                 FPU_loaded_data.sigl = l64 << 11;
218                                 normalize_nuo(&FPU_loaded_data);
219                                 return;
220                         }
221                 } else {
222                         FPU_loaded_data.exp = exp + EXP_BIAS;
223                         FPU_loaded_data.tag = TW_Valid;
224                         FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
225                         FPU_loaded_data.sigh |= l64 >> 21;
226                         FPU_loaded_data.sigl = l64 << 11;
227
228                         return;
229                 }
230 }
231
232
233 /* Get a float from user memory */
234 void
235 reg_load_single(void)
236 {
237         float  *single = (float *) FPU_data_address;
238         unsigned m32;
239         int     exp;
240
241         REENTRANT_CHECK(OFF);
242         m32 = fuword((unsigned long *) single);
243         REENTRANT_CHECK(ON);
244
245         if (m32 & 0x80000000)
246                 FPU_loaded_data.sign = SIGN_NEG;
247         else
248                 FPU_loaded_data.sign = SIGN_POS;
249         if (!(m32 & 0x7fffffff)) {
250                 /* Zero */
251                 int     c = FPU_loaded_data.sign;
252                 reg_move(&CONST_Z, &FPU_loaded_data);
253                 FPU_loaded_data.sign = c;
254                 return;
255         }
256         exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
257         m32 = (m32 & 0x7fffff) << 8;
258         if (exp < SINGLE_Emin) {
259                 /* De-normals */
260                 EXCEPTION(EX_Denormal);
261                 FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
262                 FPU_loaded_data.tag = TW_Valid;
263                 FPU_loaded_data.sigh = m32;
264                 FPU_loaded_data.sigl = 0;
265                 normalize_nuo(&FPU_loaded_data);
266                 return;
267         } else
268                 if (exp > SINGLE_Emax) {
269                         /* Infinity or NaN */
270                         if (m32 == 0) {
271                                 /* +- infinity */
272                                 FPU_loaded_data.exp = EXTENDED_Emax;
273                                 FPU_loaded_data.tag = TW_Infinity;
274                                 return;
275                         } else {
276                                 /* Must be a signaling or quiet NaN */
277                                 FPU_loaded_data.exp = EXTENDED_Emax;
278                                 FPU_loaded_data.tag = TW_NaN;
279                                 FPU_loaded_data.sigh = m32 | 0x80000000;
280                                 FPU_loaded_data.sigl = 0;
281                                 return;
282                         }
283                 } else {
284                         FPU_loaded_data.exp = exp + EXP_BIAS;
285                         FPU_loaded_data.sigh = m32 | 0x80000000;
286                         FPU_loaded_data.sigl = 0;
287                         FPU_loaded_data.tag = TW_Valid;
288                 }
289 }
290
291
292 /* Get a long long from user memory */
293 void
294 reg_load_int64(void)
295 {
296         long long *_s = (long long *) FPU_data_address;
297         int     e;
298         long long s;
299
300         REENTRANT_CHECK(OFF);
301         ((unsigned long *) &s)[0] = fuword((unsigned long *) _s);
302         ((unsigned long *) &s)[1] = fuword(1 + (unsigned long *) _s);
303         REENTRANT_CHECK(ON);
304
305         if (s == 0) {
306                 reg_move(&CONST_Z, &FPU_loaded_data);
307                 return;
308         }
309         if (s > 0)
310                 FPU_loaded_data.sign = SIGN_POS;
311         else {
312                 s = -s;
313                 FPU_loaded_data.sign = SIGN_NEG;
314         }
315
316         e = EXP_BIAS + 63;
317         *((long long *) &FPU_loaded_data.sigl) = s;
318         FPU_loaded_data.exp = e;
319         FPU_loaded_data.tag = TW_Valid;
320         normalize_nuo(&FPU_loaded_data);
321 }
322
323
324 /* Get a long from user memory */
325 void
326 reg_load_int32(void)
327 {
328         long   *_s = (long *) FPU_data_address;
329         long    s;
330         int     e;
331
332         REENTRANT_CHECK(OFF);
333         s = (long) fuword((unsigned long *) _s);
334         REENTRANT_CHECK(ON);
335
336         if (s == 0) {
337                 reg_move(&CONST_Z, &FPU_loaded_data);
338                 return;
339         }
340         if (s > 0)
341                 FPU_loaded_data.sign = SIGN_POS;
342         else {
343                 s = -s;
344                 FPU_loaded_data.sign = SIGN_NEG;
345         }
346
347         e = EXP_BIAS + 31;
348         FPU_loaded_data.sigh = s;
349         FPU_loaded_data.sigl = 0;
350         FPU_loaded_data.exp = e;
351         FPU_loaded_data.tag = TW_Valid;
352         normalize_nuo(&FPU_loaded_data);
353 }
354
355
356 /* Get a short from user memory */
357 void
358 reg_load_int16(void)
359 {
360         short  *_s = (short *) FPU_data_address;
361         int     s, e;
362
363         REENTRANT_CHECK(OFF);
364         /* Cast as short to get the sign extended. */
365         s = (short) fusword((unsigned short *) _s);
366         REENTRANT_CHECK(ON);
367
368         if (s == 0) {
369                 reg_move(&CONST_Z, &FPU_loaded_data);
370                 return;
371         }
372         if (s > 0)
373                 FPU_loaded_data.sign = SIGN_POS;
374         else {
375                 s = -s;
376                 FPU_loaded_data.sign = SIGN_NEG;
377         }
378
379         e = EXP_BIAS + 15;
380         FPU_loaded_data.sigh = s << 16;
381
382         FPU_loaded_data.sigl = 0;
383         FPU_loaded_data.exp = e;
384         FPU_loaded_data.tag = TW_Valid;
385         normalize_nuo(&FPU_loaded_data);
386 }
387
388
389 /* Get a packed bcd array from user memory */
390 void
391 reg_load_bcd(void)
392 {
393         char   *s = (char *) FPU_data_address;
394         int     pos;
395         unsigned char bcd;
396         long long l = 0;
397
398         for (pos = 8; pos >= 0; pos--) {
399                 l *= 10;
400                 REENTRANT_CHECK(OFF);
401                 bcd = (unsigned char) fubyte((unsigned char *) s + pos);
402                 REENTRANT_CHECK(ON);
403                 l += bcd >> 4;
404                 l *= 10;
405                 l += bcd & 0x0f;
406         }
407
408         /* Finish all access to user memory before putting stuff into the
409          * static FPU_loaded_data */
410         REENTRANT_CHECK(OFF);
411         FPU_loaded_data.sign =
412             ((unsigned char) fubyte((unsigned char *) s + 9)) & 0x80 ?
413             SIGN_NEG : SIGN_POS;
414         REENTRANT_CHECK(ON);
415
416         if (l == 0) {
417                 char    sign = FPU_loaded_data.sign;
418                 reg_move(&CONST_Z, &FPU_loaded_data);
419                 FPU_loaded_data.sign = sign;
420         } else {
421                 *((long long *) &FPU_loaded_data.sigl) = l;
422                 FPU_loaded_data.exp = EXP_BIAS + 63;
423                 FPU_loaded_data.tag = TW_Valid;
424                 normalize_nuo(&FPU_loaded_data);
425         }
426 }
427 /*===========================================================================*/
428
429 /* Put a long double into user memory */
430 int
431 reg_store_extended(void)
432 {
433         long double *d = (long double *) FPU_data_address;
434         long    e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias;
435         unsigned short sign = FPU_st0_ptr->sign * 0x8000;
436         unsigned long ls, ms;
437
438
439         if (FPU_st0_tag == TW_Valid) {
440                 if (e >= 0x7fff) {
441                         EXCEPTION(EX_Overflow); /* Overflow */
442                         /* This is a special case: see sec 16.2.5.1 of the
443                          * 80486 book */
444                         if (control_word & EX_Overflow) {
445                                 /* Overflow to infinity */
446                                 ls = 0;
447                                 ms = 0x80000000;
448                                 e = 0x7fff;
449                         } else
450                                 return 0;
451                 } else
452                         if (e <= 0) {
453                                 if (e > -63) {
454                                         /* Correctly format the de-normal */
455                                         int     precision_loss;
456                                         FPU_REG tmp;
457
458                                         EXCEPTION(EX_Denormal);
459                                         reg_move(FPU_st0_ptr, &tmp);
460                                         tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
461                                         if ((precision_loss = round_to_int(&tmp))) {
462                                                 EXCEPTION(EX_Underflow | precision_loss);
463                                                 /* This is a special case: see
464                                                  * sec 16.2.5.1 of the 80486
465                                                  * book */
466                                                 if (!(control_word & EX_Underflow))
467                                                         return 0;
468                                         }
469                                         e = 0;
470                                         ls = tmp.sigl;
471                                         ms = tmp.sigh;
472                                 } else {
473                                         /* ****** ??? This should not be
474                                          * possible */
475                                         EXCEPTION(EX_Underflow);        /* Underflow */
476                                         /* This is a special case: see sec
477                                          * 16.2.5.1 of the 80486 book */
478                                         if (control_word & EX_Underflow) {
479                                                 /* Underflow to zero */
480                                                 ls = 0;
481                                                 ms = 0;
482                                                 e = FPU_st0_ptr->sign == SIGN_POS ? 0x7fff : 0xffff;
483                                         } else
484                                                 return 0;
485                                 }
486                         } else {
487                                 ls = FPU_st0_ptr->sigl;
488                                 ms = FPU_st0_ptr->sigh;
489                         }
490         } else
491                 if (FPU_st0_tag == TW_Zero) {
492                         ls = ms = 0;
493                         e = 0;
494                 } else
495                         if (FPU_st0_tag == TW_Infinity) {
496                                 ls = 0;
497                                 ms = 0x80000000;
498                                 e = 0x7fff;
499                         } else
500                                 if (FPU_st0_tag == TW_NaN) {
501                                         ls = FPU_st0_ptr->sigl;
502                                         ms = FPU_st0_ptr->sigh;
503                                         e = 0x7fff;
504                                 } else
505                                         if (FPU_st0_tag == TW_Empty) {
506                                                 /* Empty register (stack
507                                                  * underflow) */
508                                                 EXCEPTION(EX_StackUnder);
509                                                 if (control_word & EX_Invalid) {
510                                                         /* The masked response */
511                                                         /* Put out the QNaN
512                                                          * indefinite */
513                                                         ls = 0;
514                                                         ms = 0xc0000000;
515                                                         e = 0xffff;
516                                                 } else
517                                                         return 0;
518                                         } else {
519                                                 /* We don't use TW_Denormal
520                                                  * yet ... perhaps never! */
521                                                 EXCEPTION(EX_Invalid);
522                                                 /* Store a NaN */
523                                                 e = 0x7fff;
524                                                 ls = 1;
525                                                 ms = 0x80000000;
526                                         }
527         REENTRANT_CHECK(OFF);
528 /*          verify_area(VERIFY_WRITE, d, 10); */
529         suword((unsigned long *) d, ls);
530         suword(1 + (unsigned long *) d, ms);
531         susword(4 + (short *) d, (unsigned short) e | sign);
532         REENTRANT_CHECK(ON);
533
534         return 1;
535
536 }
537
538
539 /* Put a double into user memory */
540 int
541 reg_store_double(void)
542 {
543         double *dfloat = (double *) FPU_data_address;
544         unsigned long l[2];
545         if (FPU_st0_tag == TW_Valid) {
546                 int     exp;
547                 FPU_REG tmp;
548
549                 reg_move(FPU_st0_ptr, &tmp);
550                 exp = tmp.exp - EXP_BIAS;
551
552                 if (exp < DOUBLE_Emin) {        /* It may be a denormal */
553                         /* Make a de-normal */
554                         int     precision_loss;
555
556                         if (exp <= -EXTENDED_Ebias)
557                                 EXCEPTION(EX_Denormal);
558
559                         tmp.exp += -DOUBLE_Emin + 52;   /* largest exp to be 51 */
560
561                         if ((precision_loss = round_to_int(&tmp))) {
562 #ifdef PECULIAR_486
563                                 /* Did it round to a non-denormal ? */
564                                 /* This behaviour might be regarded as
565                                  * peculiar, it appears that the 80486 rounds
566                                  * to the dest precision, then converts to
567                                  * decide underflow. */
568                                 if ((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
569                                     (FPU_st0_ptr->sigl & 0x000007ff))
570                                         EXCEPTION(precision_loss);
571                                 else
572 #endif                          /* PECULIAR_486 */
573                                 {
574                                         EXCEPTION(EX_Underflow | precision_loss);
575                                         /* This is a special case: see sec
576                                          * 16.2.5.1 of the 80486 book */
577                                         if (!(control_word & EX_Underflow))
578                                                 return 0;
579                                 }
580                         }
581                         l[0] = tmp.sigl;
582                         l[1] = tmp.sigh;
583                 } else {
584                         if (tmp.sigl & 0x000007ff) {
585                                 unsigned long increment = 0;    /* avoid gcc warnings */
586
587                                 switch (control_word & CW_RC) {
588                                 case RC_RND:
589                                         /* Rounding can get a little messy.. */
590                                         increment = ((tmp.sigl & 0x7ff) > 0x400) |      /* nearest */
591                                             ((tmp.sigl & 0xc00) == 0xc00);      /* odd -> even */
592                                         break;
593                                 case RC_DOWN:   /* towards -infinity */
594                                         increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
595                                         break;
596                                 case RC_UP:     /* towards +infinity */
597                                         increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
598                                         break;
599                                 case RC_CHOP:
600                                         increment = 0;
601                                         break;
602                                 }
603
604                                 /* Truncate the mantissa */
605                                 tmp.sigl &= 0xfffff800;
606
607                                 if (increment) {
608                                         set_precision_flag_up();
609
610                                         if (tmp.sigl >= 0xfffff800) {
611                                                 /* the sigl part overflows */
612                                                 if (tmp.sigh == 0xffffffff) {
613                                                         /* The sigh part
614                                                          * overflows */
615                                                         tmp.sigh = 0x80000000;
616                                                         exp++;
617                                                         if (exp >= EXP_OVER)
618                                                                 goto overflow;
619                                                 } else {
620                                                         tmp.sigh++;
621                                                 }
622                                                 tmp.sigl = 0x00000000;
623                                         } else {
624                                                 /* We only need to increment
625                                                  * sigl */
626                                                 tmp.sigl += 0x00000800;
627                                         }
628                                 } else
629                                         set_precision_flag_down();
630                         }
631                         l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
632                         l[1] = ((tmp.sigh >> 11) & 0xfffff);
633
634                         if (exp > DOUBLE_Emax) {
635                 overflow:
636                                 EXCEPTION(EX_Overflow);
637                                 /* This is a special case: see sec 16.2.5.1 of
638                                  * the 80486 book */
639                                 if (control_word & EX_Overflow) {
640                                         /* Overflow to infinity */
641                                         l[0] = 0x00000000;      /* Set to */
642                                         l[1] = 0x7ff00000;      /* + INF */
643                                 } else
644                                         return 0;
645                         } else {
646                                 /* Add the exponent */
647                                 l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
648                         }
649                 }
650         } else
651                 if (FPU_st0_tag == TW_Zero) {
652                         /* Number is zero */
653                         l[0] = 0;
654                         l[1] = 0;
655                 } else
656                         if (FPU_st0_tag == TW_Infinity) {
657                                 l[0] = 0;
658                                 l[1] = 0x7ff00000;
659                         } else
660                                 if (FPU_st0_tag == TW_NaN) {
661                                         /* See if we can get a valid NaN from
662                                          * the FPU_REG */
663                                         l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21);
664                                         l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
665                                         if (!(l[0] | l[1])) {
666                                                 /* This case does not seem to
667                                                  * be handled by the 80486
668                                                  * specs */
669                                                 EXCEPTION(EX_Invalid);
670                                                 /* Make the quiet NaN "real
671                                                  * indefinite" */
672                                                 goto put_indefinite;
673                                         }
674                                         l[1] |= 0x7ff00000;
675                                 } else
676                                         if (FPU_st0_tag == TW_Empty) {
677                                                 /* Empty register (stack
678                                                  * underflow) */
679                                                 EXCEPTION(EX_StackUnder);
680                                                 if (control_word & EX_Invalid) {
681                                                         /* The masked response */
682                                                         /* Put out the QNaN
683                                                          * indefinite */
684                                         put_indefinite:
685                                                         REENTRANT_CHECK(OFF);
686                                                         /* verify_area(VERIFY_W
687                                                          * RITE, (void *)
688                                                          * dfloat, 8); */
689                                                         suword((unsigned long *) dfloat, 0);
690                                                         suword(1 + (unsigned long *) dfloat, 0xfff80000);
691                                                         REENTRANT_CHECK(ON);
692                                                         return 1;
693                                                 } else
694                                                         return 0;
695                                         }
696 #if 0                           /* TW_Denormal is not used yet, and probably
697                                  * won't be */
698                                         else
699                                                 if (FPU_st0_tag == TW_Denormal) {
700                                                         /* Extended real ->
701                                                          * double real will
702                                                          * always underflow */
703                                                         l[0] = l[1] = 0;
704                                                         EXCEPTION(EX_Underflow);
705                                                 }
706 #endif
707         if (FPU_st0_ptr->sign)
708                 l[1] |= 0x80000000;
709
710         REENTRANT_CHECK(OFF);
711 /*          verify_area(VERIFY_WRITE, (void *) dfloat, 8);*/
712         suword((u_long *) dfloat, l[0]);
713         suword((u_long *) dfloat + 1, l[1]);
714 /*
715         suword(l[0], (unsigned long *) dfloat);
716         suword(l[1], 1 + (unsigned long *) dfloat);*/
717         REENTRANT_CHECK(ON);
718
719         return 1;
720 }
721
722
723 /* Put a float into user memory */
724 int
725 reg_store_single(void)
726 {
727         float  *single = (float *) FPU_data_address;
728         long    templ = 0;
729
730         if (FPU_st0_tag == TW_Valid) {
731                 int     exp;
732                 FPU_REG tmp;
733
734                 reg_move(FPU_st0_ptr, &tmp);
735                 exp = tmp.exp - EXP_BIAS;
736
737                 if (exp < SINGLE_Emin) {
738                         /* Make a de-normal */
739                         int     precision_loss;
740
741                         if (exp <= -EXTENDED_Ebias)
742                                 EXCEPTION(EX_Denormal);
743
744                         tmp.exp += -SINGLE_Emin + 23;   /* largest exp to be 22 */
745
746                         if ((precision_loss = round_to_int(&tmp))) {
747 #ifdef PECULIAR_486
748                                 /* Did it round to a non-denormal ? */
749                                 /* This behaviour might be regarded as
750                                  * peculiar, it appears that the 80486 rounds
751                                  * to the dest precision, then converts to
752                                  * decide underflow. */
753                                 if ((tmp.sigl == 0x00800000) &&
754                                     ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl))
755                                         EXCEPTION(precision_loss);
756                                 else
757 #endif                          /* PECULIAR_486 */
758                                 {
759                                         EXCEPTION(EX_Underflow | precision_loss);
760                                         /* This is a special case: see sec
761                                          * 16.2.5.1 of the 80486 book */
762                                         if (!(control_word & EX_Underflow))
763                                                 return 0;
764                                 }
765                         }
766                         templ = tmp.sigl;
767                 } else {
768                         if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
769                                 unsigned long increment = 0;    /* avoid gcc warnings */
770                                 unsigned long sigh = tmp.sigh;
771                                 unsigned long sigl = tmp.sigl;
772
773                                 switch (control_word & CW_RC) {
774                                 case RC_RND:
775                                         increment = ((sigh & 0xff) > 0x80)      /* more than half */
776                                             ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
777                                             ||((sigh & 0x180) == 0x180);        /* round to even */
778                                         break;
779                                 case RC_DOWN:   /* towards -infinity */
780                                         increment = (tmp.sign == SIGN_POS)
781                                             ? 0 : (sigl | (sigh & 0xff));
782                                         break;
783                                 case RC_UP:     /* towards +infinity */
784                                         increment = (tmp.sign == SIGN_POS)
785                                             ? (sigl | (sigh & 0xff)) : 0;
786                                         break;
787                                 case RC_CHOP:
788                                         increment = 0;
789                                         break;
790                                 }
791
792                                 /* Truncate part of the mantissa */
793                                 tmp.sigl = 0;
794
795                                 if (increment) {
796                                         set_precision_flag_up();
797
798                                         if (sigh >= 0xffffff00) {
799                                                 /* The sigh part overflows */
800                                                 tmp.sigh = 0x80000000;
801                                                 exp++;
802                                                 if (exp >= EXP_OVER)
803                                                         goto overflow;
804                                         } else {
805                                                 tmp.sigh &= 0xffffff00;
806                                                 tmp.sigh += 0x100;
807                                         }
808                                 } else {
809                                         set_precision_flag_down();
810                                         tmp.sigh &= 0xffffff00; /* Finish the truncation */
811                                 }
812                         }
813                         templ = (tmp.sigh >> 8) & 0x007fffff;
814
815                         if (exp > SINGLE_Emax) {
816                 overflow:
817                                 EXCEPTION(EX_Overflow);
818                                 /* This is a special case: see sec 16.2.5.1 of
819                                  * the 80486 book */
820                                 if (control_word & EX_Overflow) {
821                                         /* Overflow to infinity */
822                                         templ = 0x7f800000;
823                                 } else
824                                         return 0;
825                         } else
826                                 templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
827                 }
828         } else
829                 if (FPU_st0_tag == TW_Zero) {
830                         templ = 0;
831                 } else
832                         if (FPU_st0_tag == TW_Infinity) {
833                                 templ = 0x7f800000;
834                         } else
835                                 if (FPU_st0_tag == TW_NaN) {
836                                         /* See if we can get a valid NaN from
837                                          * the FPU_REG */
838                                         templ = FPU_st0_ptr->sigh >> 8;
839                                         if (!(templ & 0x3fffff)) {
840                                                 /* This case does not seem to
841                                                  * be handled by the 80486
842                                                  * specs */
843                                                 EXCEPTION(EX_Invalid);
844                                                 /* Make the quiet NaN "real
845                                                  * indefinite" */
846                                                 goto put_indefinite;
847                                         }
848                                         templ |= 0x7f800000;
849                                 } else
850                                         if (FPU_st0_tag == TW_Empty) {
851                                                 /* Empty register (stack
852                                                  * underflow) */
853                                                 EXCEPTION(EX_StackUnder);
854                                                 if (control_word & EX_Invalid) {
855                                                         /* The masked response */
856                                                         /* Put out the QNaN
857                                                          * indefinite */
858                                         put_indefinite:
859                                                         REENTRANT_CHECK(OFF);
860 /*                                                          verify_area(VERIFY_WRITE, (void *) single, 4); */
861                                                         suword((unsigned long *) single, 0xffc00000);
862                                                         REENTRANT_CHECK(ON);
863                                                         return 1;
864                                                 } else
865                                                         return 0;
866                                         }
867 #if 0                           /* TW_Denormal is not used yet, and probably
868                                  * won't be */
869                                         else
870                                                 if (FPU_st0_tag == TW_Denormal) {
871                                                         /* Extended real ->
872                                                          * real will always
873                                                          * underflow */
874                                                         templ = 0;
875                                                         EXCEPTION(EX_Underflow);
876                                                 }
877 #endif
878 #ifdef PARANOID
879                                                 else {
880                                                         EXCEPTION(EX_INTERNAL | 0x106);
881                                                         return 0;
882                                                 }
883 #endif
884         if (FPU_st0_ptr->sign)
885                 templ |= 0x80000000;
886
887         REENTRANT_CHECK(OFF);
888 /*          verify_area(VERIFY_WRITE, (void *) single, 4); */
889         suword((unsigned long *) single, templ);
890         REENTRANT_CHECK(ON);
891
892         return 1;
893 }
894
895
896 /* Put a long long into user memory */
897 int
898 reg_store_int64(void)
899 {
900         long long *d = (long long *) FPU_data_address;
901         FPU_REG t;
902         long long tll;
903
904         if (FPU_st0_tag == TW_Empty) {
905                 /* Empty register (stack underflow) */
906                 EXCEPTION(EX_StackUnder);
907                 if (control_word & EX_Invalid) {
908                         /* The masked response */
909                         /* Put out the QNaN indefinite */
910                         goto put_indefinite;
911                 } else
912                         return 0;
913         }
914         reg_move(FPU_st0_ptr, &t);
915         round_to_int(&t);
916         ((long *) &tll)[0] = t.sigl;
917         ((long *) &tll)[1] = t.sigh;
918         if ((t.sigh & 0x80000000) &&
919             !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG))) {
920                 EXCEPTION(EX_Invalid);
921                 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
922                 if (control_word & EX_Invalid) {
923                         /* Produce "indefinite" */
924         put_indefinite:
925                         ((long *) &tll)[1] = 0x80000000;
926                         ((long *) &tll)[0] = 0;
927                 } else
928                         return 0;
929         } else
930                 if (t.sign)
931                         tll = -tll;
932
933         REENTRANT_CHECK(OFF);
934 /*          verify_area(VERIFY_WRITE, (void *) d, 8); */
935         suword((unsigned long *) d, ((long *) &tll)[0]);
936         suword(1 + (unsigned long *) d, ((long *) &tll)[1]);
937         REENTRANT_CHECK(ON);
938
939         return 1;
940 }
941
942
943 /* Put a long into user memory */
944 int
945 reg_store_int32(void)
946 {
947         long   *d = (long *) FPU_data_address;
948         FPU_REG t;
949
950         if (FPU_st0_tag == TW_Empty) {
951                 /* Empty register (stack underflow) */
952                 EXCEPTION(EX_StackUnder);
953                 if (control_word & EX_Invalid) {
954                         /* The masked response */
955                         /* Put out the QNaN indefinite */
956                         REENTRANT_CHECK(OFF);
957 /*                          verify_area(VERIFY_WRITE, d, 4);*/
958                         suword((unsigned long *) d, 0x80000000);
959                         REENTRANT_CHECK(ON);
960                         return 1;
961                 } else
962                         return 0;
963         }
964         reg_move(FPU_st0_ptr, &t);
965         round_to_int(&t);
966         if (t.sigh ||
967             ((t.sigl & 0x80000000) &&
968                 !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG)))) {
969                 EXCEPTION(EX_Invalid);
970                 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
971                 if (control_word & EX_Invalid) {
972                         /* Produce "indefinite" */
973                         t.sigl = 0x80000000;
974                 } else
975                         return 0;
976         } else
977                 if (t.sign)
978                         t.sigl = -(long) t.sigl;
979
980         REENTRANT_CHECK(OFF);
981 /*          verify_area(VERIFY_WRITE, d, 4); */
982         suword((unsigned long *) d, t.sigl);
983         REENTRANT_CHECK(ON);
984
985         return 1;
986 }
987
988
989 /* Put a short into user memory */
990 int
991 reg_store_int16(void)
992 {
993         short  *d = (short *) FPU_data_address;
994         FPU_REG t;
995         short   ts;
996
997         if (FPU_st0_tag == TW_Empty) {
998                 /* Empty register (stack underflow) */
999                 EXCEPTION(EX_StackUnder);
1000                 if (control_word & EX_Invalid) {
1001                         /* The masked response */
1002                         /* Put out the QNaN indefinite */
1003                         REENTRANT_CHECK(OFF);
1004 /*                          verify_area(VERIFY_WRITE, d, 2);*/
1005                         susword((unsigned short *) d, 0x8000);
1006                         REENTRANT_CHECK(ON);
1007                         return 1;
1008                 } else
1009                         return 0;
1010         }
1011         reg_move(FPU_st0_ptr, &t);
1012         round_to_int(&t);
1013         if (t.sigh ||
1014             ((t.sigl & 0xffff8000) &&
1015                 !((t.sigl == 0x8000) && (t.sign == SIGN_NEG)))) {
1016                 EXCEPTION(EX_Invalid);
1017                 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
1018                 if (control_word & EX_Invalid) {
1019                         /* Produce "indefinite" */
1020                         ts = 0x8000;
1021                 } else
1022                         return 0;
1023         } else
1024                 if (t.sign)
1025                         t.sigl = -t.sigl;
1026
1027         REENTRANT_CHECK(OFF);
1028 /*          verify_area(VERIFY_WRITE, d, 2); */
1029         susword((short *) d, (short) t.sigl);
1030         REENTRANT_CHECK(ON);
1031
1032         return 1;
1033 }
1034
1035
1036 /* Put a packed bcd array into user memory */
1037 int
1038 reg_store_bcd(void)
1039 {
1040         char   *d = (char *) FPU_data_address;
1041         FPU_REG t;
1042         long long ll;
1043         unsigned char b;
1044         int     i;
1045         unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
1046
1047         if (FPU_st0_tag == TW_Empty) {
1048                 /* Empty register (stack underflow) */
1049                 EXCEPTION(EX_StackUnder);
1050                 if (control_word & EX_Invalid) {
1051                         /* The masked response */
1052                         /* Put out the QNaN indefinite */
1053                         goto put_indefinite;
1054                 } else
1055                         return 0;
1056         }
1057         reg_move(FPU_st0_ptr, &t);
1058         round_to_int(&t);
1059         ll = *(long long *) (&t.sigl);
1060
1061         /* Check for overflow, by comparing with 999999999999999999 decimal. */
1062         if ((t.sigh > 0x0de0b6b3) ||
1063             ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
1064                 EXCEPTION(EX_Invalid);
1065                 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
1066                 if (control_word & EX_Invalid) {
1067         put_indefinite:
1068                         /* Produce "indefinite" */
1069                         REENTRANT_CHECK(OFF);
1070 /*                          verify_area(VERIFY_WRITE, d, 10);*/
1071                         subyte((unsigned char *) d + 7, 0xff);
1072                         subyte((unsigned char *) d + 8, 0xff);
1073                         subyte((unsigned char *) d + 9, 0xff);
1074                         REENTRANT_CHECK(ON);
1075                         return 1;
1076                 } else
1077                         return 0;
1078         }
1079 /*      verify_area(VERIFY_WRITE, d, 10);*/
1080         for (i = 0; i < 9; i++) {
1081                 b = div_small(&ll, 10);
1082                 b |= (div_small(&ll, 10)) << 4;
1083                 REENTRANT_CHECK(OFF);
1084                 subyte((unsigned char *) d + i, b);
1085                 REENTRANT_CHECK(ON);
1086         }
1087         REENTRANT_CHECK(OFF);
1088         subyte((unsigned char *) d + 9, sign);
1089         REENTRANT_CHECK(ON);
1090
1091         return 1;
1092 }
1093 /*===========================================================================*/
1094
1095 /* r gets mangled such that sig is int, sign:
1096    it is NOT normalized */
1097 /* The return value (in eax) is zero if the result is exact,
1098    if bits are changed due to rounding, truncation, etc, then
1099    a non-zero value is returned */
1100 /* Overflow is signalled by a non-zero return value (in eax).
1101    In the case of overflow, the returned significand always has the
1102    the largest possible value */
1103 /* The value returned in eax is never actually needed :-) */
1104 int
1105 round_to_int(FPU_REG * r)
1106 {
1107         char    very_big;
1108         unsigned eax;
1109
1110         if (r->tag == TW_Zero) {
1111                 /* Make sure that zero is returned */
1112                 *(long long *) &r->sigl = 0;
1113                 return 0;       /* o.k. */
1114         }
1115         if (r->exp > EXP_BIAS + 63) {
1116                 r->sigl = r->sigh = ~0; /* The largest representable number */
1117                 return 1;       /* overflow */
1118         }
1119         eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
1120         very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
1121 #define half_or_more    (eax & 0x80000000)
1122 #define frac_part       (eax)
1123 #define more_than_half  ((eax & 0x80000001) == 0x80000001)
1124         switch (control_word & CW_RC) {
1125         case RC_RND:
1126                 if (more_than_half      /* nearest */
1127                     || (half_or_more && (r->sigl & 1))) {       /* odd -> even */
1128                         if (very_big)
1129                                 return 1;       /* overflow */
1130                         (*(long long *) (&r->sigl))++;
1131                         return LOST_UP;
1132                 }
1133                 break;
1134         case RC_DOWN:
1135                 if (frac_part && r->sign) {
1136                         if (very_big)
1137                                 return 1;       /* overflow */
1138                         (*(long long *) (&r->sigl))++;
1139                         return LOST_UP;
1140                 }
1141                 break;
1142         case RC_UP:
1143                 if (frac_part && !r->sign) {
1144                         if (very_big)
1145                                 return 1;       /* overflow */
1146                         (*(long long *) (&r->sigl))++;
1147                         return LOST_UP;
1148                 }
1149                 break;
1150         case RC_CHOP:
1151                 break;
1152         }
1153
1154         return eax ? LOST_DOWN : 0;
1155
1156 }
1157 /*===========================================================================*/
1158
1159 char   *
1160 fldenv(void)
1161 {
1162         char   *s = (char *) FPU_data_address;
1163         unsigned short tag_word = 0;
1164         unsigned char tag;
1165         int     i;
1166
1167         REENTRANT_CHECK(OFF);
1168         control_word = fusword((unsigned short *) s);
1169         status_word = fusword((unsigned short *) (s + 4));
1170         tag_word = fusword((unsigned short *) (s + 8));
1171         ip_offset = fuword((unsigned long *) (s + 0x0c));
1172         cs_selector = fuword((unsigned long *) (s + 0x10));
1173         data_operand_offset = fuword((unsigned long *) (s + 0x14));
1174         operand_selector = fuword((unsigned long *) (s + 0x18));
1175         REENTRANT_CHECK(ON);
1176
1177         top = (status_word >> SW_Top_Shift) & 7;
1178
1179         for (i = 0; i < 8; i++) {
1180                 tag = tag_word & 3;
1181                 tag_word >>= 2;
1182
1183                 switch (tag) {
1184                 case 0:
1185                         regs[i].tag = TW_Valid;
1186                         break;
1187                 case 1:
1188                         regs[i].tag = TW_Zero;
1189                         break;
1190                 case 2:
1191                         regs[i].tag = TW_NaN;
1192                         break;
1193                 case 3:
1194                         regs[i].tag = TW_Empty;
1195                         break;
1196                 }
1197         }
1198
1199         /* We want no net effect: */
1200         FPU_data_address = (void *) (intptr_t) data_operand_offset;
1201         FPU_entry_eip = ip_offset;      /* We want no net effect */
1202
1203         return s + 0x1c;
1204 }
1205
1206
1207 void
1208 frstor(void)
1209 {
1210         int     i, stnr;
1211         unsigned char tag;
1212         unsigned short saved_status, saved_control;
1213         char   *s = (char *) fldenv();
1214
1215         saved_status = status_word;
1216         saved_control = control_word;
1217         control_word = 0x037f;  /* Mask all interrupts while we load. */
1218         for (i = 0; i < 8; i++) {
1219                 /* load each register */
1220                 FPU_data_address = (void *) (s + i * 10);
1221                 reg_load_extended();
1222                 stnr = (i + top) & 7;
1223                 tag = regs[stnr].tag;   /* derived from the loaded tag word */
1224                 reg_move(&FPU_loaded_data, &regs[stnr]);
1225                 if (tag == TW_NaN) {
1226                         /* The current data is a special, i.e. NaN,
1227                          * unsupported, infinity, or denormal */
1228                         unsigned char t = regs[stnr].tag;       /* derived from the new
1229                                                                  * data */
1230                         if ( /* (t == TW_Valid) || *** */ (t == TW_Zero))
1231                                 regs[stnr].tag = TW_NaN;
1232                 } else
1233                         regs[stnr].tag = tag;
1234         }
1235         control_word = saved_control;
1236         status_word = saved_status;
1237
1238         /* We want no net effect: */
1239         FPU_data_address = (void *) (intptr_t) data_operand_offset;
1240 }
1241
1242
1243 unsigned short
1244 tag_word(void)
1245 {
1246         unsigned short word = 0;
1247         unsigned char tag;
1248         int     i;
1249
1250         for (i = 7; i >= 0; i--) {
1251                 switch (tag = regs[i].tag) {
1252 #if 0                           /* TW_Denormal is not used yet, and probably
1253                                  * won't be */
1254                 case TW_Denormal:
1255 #endif
1256                 case TW_Valid:
1257                         if (regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias))
1258                                 tag = 2;
1259                         break;
1260                 case TW_Infinity:
1261                 case TW_NaN:
1262                         tag = 2;
1263                         break;
1264                 case TW_Empty:
1265                         tag = 3;
1266                         break;
1267                         /* TW_Valid and TW_Zero already have the correct value */
1268                 }
1269                 word <<= 2;
1270                 word |= tag;
1271         }
1272         return word;
1273 }
1274
1275
1276 char   *
1277 fstenv(void)
1278 {
1279         char   *d = (char *) FPU_data_address;
1280
1281 /*      verify_area(VERIFY_WRITE, d, 28);*/
1282
1283 #if 0                           /****/
1284         *(unsigned short *) &cs_selector = fpu_cs;
1285         *(unsigned short *) &operand_selector = fpu_os;
1286 #endif                          /****/
1287
1288         REENTRANT_CHECK(OFF);
1289         susword((unsigned short *) d, control_word);
1290         susword((unsigned short *) (d + 4), (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift));
1291         susword((unsigned short *) (d + 8), tag_word());
1292         suword((unsigned long *) (d + 0x0c), ip_offset);
1293         suword((unsigned long *) (d + 0x10), cs_selector);
1294         suword((unsigned long *) (d + 0x14), data_operand_offset);
1295         suword((unsigned long *) (d + 0x18), operand_selector);
1296         REENTRANT_CHECK(ON);
1297
1298         return d + 0x1c;
1299 }
1300
1301
1302 void
1303 fsave(void)
1304 {
1305         char   *d;
1306         FPU_REG tmp, *rp;
1307         int     i;
1308         short   e;
1309
1310         d = fstenv();
1311 /*      verify_area(VERIFY_WRITE, d, 80);*/
1312         for (i = 0; i < 8; i++) {
1313                 /* Store each register in the order: st(0), st(1), ... */
1314                 rp = &regs[(top + i) & 7];
1315
1316                 e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
1317
1318                 if (rp->tag == TW_Valid) {
1319                         if (e >= 0x7fff) {
1320                                 /* Overflow to infinity */
1321                                 REENTRANT_CHECK(OFF);
1322                                 suword((unsigned long *) (d + i * 10), 0);
1323                                 suword((unsigned long *) (d + i * 10 + 4), 0);
1324                                 REENTRANT_CHECK(ON);
1325                                 e = 0x7fff;
1326                         } else
1327                                 if (e <= 0) {
1328                                         if (e > -63) {
1329                                                 /* Make a de-normal */
1330                                                 reg_move(rp, &tmp);
1331                                                 tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 62 */
1332                                                 round_to_int(&tmp);
1333                                                 REENTRANT_CHECK(OFF);
1334                                                 suword((unsigned long *) (d + i * 10), tmp.sigl);
1335                                                 suword((unsigned long *) (d + i * 10 + 4), tmp.sigh);
1336                                                 REENTRANT_CHECK(ON);
1337                                         } else {
1338                                                 /* Underflow to zero */
1339                                                 REENTRANT_CHECK(OFF);
1340                                                 suword((unsigned long *) (d + i * 10), 0);
1341                                                 suword((unsigned long *) (d + i * 10 + 4), 0);
1342                                                 REENTRANT_CHECK(ON);
1343                                         }
1344                                         e = 0;
1345                                 } else {
1346                                         REENTRANT_CHECK(OFF);
1347                                         suword((unsigned long *) (d + i * 10), rp->sigl);
1348                                         suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
1349                                         REENTRANT_CHECK(ON);
1350                                 }
1351                 } else
1352                         if (rp->tag == TW_Zero) {
1353                                 REENTRANT_CHECK(OFF);
1354                                 suword((unsigned long *) (d + i * 10), 0);
1355                                 suword((unsigned long *) (d + i * 10 + 4), 0);
1356                                 REENTRANT_CHECK(ON);
1357                                 e = 0;
1358                         } else
1359                                 if (rp->tag == TW_Infinity) {
1360                                         REENTRANT_CHECK(OFF);
1361                                         suword((unsigned long *) (d + i * 10), 0);
1362                                         suword((unsigned long *) (d + i * 10 + 4), 0x80000000);
1363                                         REENTRANT_CHECK(ON);
1364                                         e = 0x7fff;
1365                                 } else
1366                                         if (rp->tag == TW_NaN) {
1367                                                 REENTRANT_CHECK(OFF);
1368                                                 suword((unsigned long *) (d + i * 10), rp->sigl);
1369                                                 suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
1370                                                 REENTRANT_CHECK(ON);
1371                                                 e = 0x7fff;
1372                                         } else
1373                                                 if (rp->tag == TW_Empty) {
1374                                                         /* just copy the reg */
1375                                                         REENTRANT_CHECK(OFF);
1376                                                         suword((unsigned long *) (d + i * 10), rp->sigl);
1377                                                         suword((unsigned long *) (d + i * 10 + 4), rp->sigh);
1378                                                         REENTRANT_CHECK(ON);
1379                                                 }
1380                 e |= rp->sign == SIGN_POS ? 0 : 0x8000;
1381                 REENTRANT_CHECK(OFF);
1382                 susword((unsigned short *) (d + i * 10 + 8), e);
1383                 REENTRANT_CHECK(ON);
1384         }
1385
1386         finit();
1387
1388 }
1389 /*===========================================================================*/