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