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