au1550_spi: fix prototype of irq handler
[wrt350n-kernel.git] / arch / x86 / math-emu / reg_ld_str.c
blob799d4af5be66381da440d545d94e0d71ebf4bac5
1 /*---------------------------------------------------------------------------+
2 | reg_ld_str.c |
3 | |
4 | All of the functions which transfer data between user memory and FPU_REGs.|
5 | |
6 | Copyright (C) 1992,1993,1994,1996,1997 |
7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8 | E-mail billm@suburbia.net |
9 | |
10 | |
11 +---------------------------------------------------------------------------*/
13 /*---------------------------------------------------------------------------+
14 | Note: |
15 | The file contains code which accesses user memory. |
16 | Emulator static data may change when user memory is accessed, due to |
17 | other processes using the emulator while swapping is in progress. |
18 +---------------------------------------------------------------------------*/
20 #include "fpu_emu.h"
22 #include <asm/uaccess.h>
24 #include "fpu_system.h"
25 #include "exception.h"
26 #include "reg_constant.h"
27 #include "control_w.h"
28 #include "status_w.h"
30 #define DOUBLE_Emax 1023 /* largest valid exponent */
31 #define DOUBLE_Ebias 1023
32 #define DOUBLE_Emin (-1022) /* smallest valid exponent */
34 #define SINGLE_Emax 127 /* largest valid exponent */
35 #define SINGLE_Ebias 127
36 #define SINGLE_Emin (-126) /* smallest valid exponent */
38 static u_char normalize_no_excep(FPU_REG *r, int exp, int sign)
40 u_char tag;
42 setexponent16(r, exp);
44 tag = FPU_normalize_nuo(r);
45 stdexp(r);
46 if (sign)
47 setnegative(r);
49 return tag;
52 int FPU_tagof(FPU_REG *ptr)
54 int exp;
56 exp = exponent16(ptr) & 0x7fff;
57 if (exp == 0) {
58 if (!(ptr->sigh | ptr->sigl)) {
59 return TAG_Zero;
61 /* The number is a de-normal or pseudodenormal. */
62 return TAG_Special;
65 if (exp == 0x7fff) {
66 /* Is an Infinity, a NaN, or an unsupported data type. */
67 return TAG_Special;
70 if (!(ptr->sigh & 0x80000000)) {
71 /* Unsupported data type. */
72 /* Valid numbers have the ms bit set to 1. */
73 /* Unnormal. */
74 return TAG_Special;
77 return TAG_Valid;
80 /* Get a long double from user memory */
81 int FPU_load_extended(long double __user *s, int stnr)
83 FPU_REG *sti_ptr = &st(stnr);
85 RE_ENTRANT_CHECK_OFF;
86 FPU_access_ok(VERIFY_READ, s, 10);
87 __copy_from_user(sti_ptr, s, 10);
88 RE_ENTRANT_CHECK_ON;
90 return FPU_tagof(sti_ptr);
93 /* Get a double from user memory */
94 int FPU_load_double(double __user *dfloat, FPU_REG *loaded_data)
96 int exp, tag, negative;
97 unsigned m64, l64;
99 RE_ENTRANT_CHECK_OFF;
100 FPU_access_ok(VERIFY_READ, dfloat, 8);
101 FPU_get_user(m64, 1 + (unsigned long __user *)dfloat);
102 FPU_get_user(l64, (unsigned long __user *)dfloat);
103 RE_ENTRANT_CHECK_ON;
105 negative = (m64 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
106 exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias + EXTENDED_Ebias;
107 m64 &= 0xfffff;
108 if (exp > DOUBLE_Emax + EXTENDED_Ebias) {
109 /* Infinity or NaN */
110 if ((m64 == 0) && (l64 == 0)) {
111 /* +- infinity */
112 loaded_data->sigh = 0x80000000;
113 loaded_data->sigl = 0x00000000;
114 exp = EXP_Infinity + EXTENDED_Ebias;
115 tag = TAG_Special;
116 } else {
117 /* Must be a signaling or quiet NaN */
118 exp = EXP_NaN + EXTENDED_Ebias;
119 loaded_data->sigh = (m64 << 11) | 0x80000000;
120 loaded_data->sigh |= l64 >> 21;
121 loaded_data->sigl = l64 << 11;
122 tag = TAG_Special; /* The calling function must look for NaNs */
124 } else if (exp < DOUBLE_Emin + EXTENDED_Ebias) {
125 /* Zero or de-normal */
126 if ((m64 == 0) && (l64 == 0)) {
127 /* Zero */
128 reg_copy(&CONST_Z, loaded_data);
129 exp = 0;
130 tag = TAG_Zero;
131 } else {
132 /* De-normal */
133 loaded_data->sigh = m64 << 11;
134 loaded_data->sigh |= l64 >> 21;
135 loaded_data->sigl = l64 << 11;
137 return normalize_no_excep(loaded_data, DOUBLE_Emin,
138 negative)
139 | (denormal_operand() < 0 ? FPU_Exception : 0);
141 } else {
142 loaded_data->sigh = (m64 << 11) | 0x80000000;
143 loaded_data->sigh |= l64 >> 21;
144 loaded_data->sigl = l64 << 11;
146 tag = TAG_Valid;
149 setexponent16(loaded_data, exp | negative);
151 return tag;
154 /* Get a float from user memory */
155 int FPU_load_single(float __user *single, FPU_REG *loaded_data)
157 unsigned m32;
158 int exp, tag, negative;
160 RE_ENTRANT_CHECK_OFF;
161 FPU_access_ok(VERIFY_READ, single, 4);
162 FPU_get_user(m32, (unsigned long __user *)single);
163 RE_ENTRANT_CHECK_ON;
165 negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
167 if (!(m32 & 0x7fffffff)) {
168 /* Zero */
169 reg_copy(&CONST_Z, loaded_data);
170 addexponent(loaded_data, negative);
171 return TAG_Zero;
173 exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias;
174 m32 = (m32 & 0x7fffff) << 8;
175 if (exp < SINGLE_Emin + EXTENDED_Ebias) {
176 /* De-normals */
177 loaded_data->sigh = m32;
178 loaded_data->sigl = 0;
180 return normalize_no_excep(loaded_data, SINGLE_Emin, negative)
181 | (denormal_operand() < 0 ? FPU_Exception : 0);
182 } else if (exp > SINGLE_Emax + EXTENDED_Ebias) {
183 /* Infinity or NaN */
184 if (m32 == 0) {
185 /* +- infinity */
186 loaded_data->sigh = 0x80000000;
187 loaded_data->sigl = 0x00000000;
188 exp = EXP_Infinity + EXTENDED_Ebias;
189 tag = TAG_Special;
190 } else {
191 /* Must be a signaling or quiet NaN */
192 exp = EXP_NaN + EXTENDED_Ebias;
193 loaded_data->sigh = m32 | 0x80000000;
194 loaded_data->sigl = 0;
195 tag = TAG_Special; /* The calling function must look for NaNs */
197 } else {
198 loaded_data->sigh = m32 | 0x80000000;
199 loaded_data->sigl = 0;
200 tag = TAG_Valid;
203 setexponent16(loaded_data, exp | negative); /* Set the sign. */
205 return tag;
208 /* Get a long long from user memory */
209 int FPU_load_int64(long long __user *_s)
211 long long s;
212 int sign;
213 FPU_REG *st0_ptr = &st(0);
215 RE_ENTRANT_CHECK_OFF;
216 FPU_access_ok(VERIFY_READ, _s, 8);
217 if (copy_from_user(&s, _s, 8))
218 FPU_abort;
219 RE_ENTRANT_CHECK_ON;
221 if (s == 0) {
222 reg_copy(&CONST_Z, st0_ptr);
223 return TAG_Zero;
226 if (s > 0)
227 sign = SIGN_Positive;
228 else {
229 s = -s;
230 sign = SIGN_Negative;
233 significand(st0_ptr) = s;
235 return normalize_no_excep(st0_ptr, 63, sign);
238 /* Get a long from user memory */
239 int FPU_load_int32(long __user *_s, FPU_REG *loaded_data)
241 long s;
242 int negative;
244 RE_ENTRANT_CHECK_OFF;
245 FPU_access_ok(VERIFY_READ, _s, 4);
246 FPU_get_user(s, _s);
247 RE_ENTRANT_CHECK_ON;
249 if (s == 0) {
250 reg_copy(&CONST_Z, loaded_data);
251 return TAG_Zero;
254 if (s > 0)
255 negative = SIGN_Positive;
256 else {
257 s = -s;
258 negative = SIGN_Negative;
261 loaded_data->sigh = s;
262 loaded_data->sigl = 0;
264 return normalize_no_excep(loaded_data, 31, negative);
267 /* Get a short from user memory */
268 int FPU_load_int16(short __user *_s, FPU_REG *loaded_data)
270 int s, negative;
272 RE_ENTRANT_CHECK_OFF;
273 FPU_access_ok(VERIFY_READ, _s, 2);
274 /* Cast as short to get the sign extended. */
275 FPU_get_user(s, _s);
276 RE_ENTRANT_CHECK_ON;
278 if (s == 0) {
279 reg_copy(&CONST_Z, loaded_data);
280 return TAG_Zero;
283 if (s > 0)
284 negative = SIGN_Positive;
285 else {
286 s = -s;
287 negative = SIGN_Negative;
290 loaded_data->sigh = s << 16;
291 loaded_data->sigl = 0;
293 return normalize_no_excep(loaded_data, 15, negative);
296 /* Get a packed bcd array from user memory */
297 int FPU_load_bcd(u_char __user *s)
299 FPU_REG *st0_ptr = &st(0);
300 int pos;
301 u_char bcd;
302 long long l = 0;
303 int sign;
305 RE_ENTRANT_CHECK_OFF;
306 FPU_access_ok(VERIFY_READ, s, 10);
307 RE_ENTRANT_CHECK_ON;
308 for (pos = 8; pos >= 0; pos--) {
309 l *= 10;
310 RE_ENTRANT_CHECK_OFF;
311 FPU_get_user(bcd, s + pos);
312 RE_ENTRANT_CHECK_ON;
313 l += bcd >> 4;
314 l *= 10;
315 l += bcd & 0x0f;
318 RE_ENTRANT_CHECK_OFF;
319 FPU_get_user(sign, s + 9);
320 sign = sign & 0x80 ? SIGN_Negative : SIGN_Positive;
321 RE_ENTRANT_CHECK_ON;
323 if (l == 0) {
324 reg_copy(&CONST_Z, st0_ptr);
325 addexponent(st0_ptr, sign); /* Set the sign. */
326 return TAG_Zero;
327 } else {
328 significand(st0_ptr) = l;
329 return normalize_no_excep(st0_ptr, 63, sign);
333 /*===========================================================================*/
335 /* Put a long double into user memory */
336 int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
337 long double __user * d)
340 The only exception raised by an attempt to store to an
341 extended format is the Invalid Stack exception, i.e.
342 attempting to store from an empty register.
345 if (st0_tag != TAG_Empty) {
346 RE_ENTRANT_CHECK_OFF;
347 FPU_access_ok(VERIFY_WRITE, d, 10);
349 FPU_put_user(st0_ptr->sigl, (unsigned long __user *)d);
350 FPU_put_user(st0_ptr->sigh,
351 (unsigned long __user *)((u_char __user *) d + 4));
352 FPU_put_user(exponent16(st0_ptr),
353 (unsigned short __user *)((u_char __user *) d +
354 8));
355 RE_ENTRANT_CHECK_ON;
357 return 1;
360 /* Empty register (stack underflow) */
361 EXCEPTION(EX_StackUnder);
362 if (control_word & CW_Invalid) {
363 /* The masked response */
364 /* Put out the QNaN indefinite */
365 RE_ENTRANT_CHECK_OFF;
366 FPU_access_ok(VERIFY_WRITE, d, 10);
367 FPU_put_user(0, (unsigned long __user *)d);
368 FPU_put_user(0xc0000000, 1 + (unsigned long __user *)d);
369 FPU_put_user(0xffff, 4 + (short __user *)d);
370 RE_ENTRANT_CHECK_ON;
371 return 1;
372 } else
373 return 0;
377 /* Put a double into user memory */
378 int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double __user *dfloat)
380 unsigned long l[2];
381 unsigned long increment = 0; /* avoid gcc warnings */
382 int precision_loss;
383 int exp;
384 FPU_REG tmp;
386 if (st0_tag == TAG_Valid) {
387 reg_copy(st0_ptr, &tmp);
388 exp = exponent(&tmp);
390 if (exp < DOUBLE_Emin) { /* It may be a denormal */
391 addexponent(&tmp, -DOUBLE_Emin + 52); /* largest exp to be 51 */
393 denormal_arg:
395 if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
396 #ifdef PECULIAR_486
397 /* Did it round to a non-denormal ? */
398 /* This behaviour might be regarded as peculiar, it appears
399 that the 80486 rounds to the dest precision, then
400 converts to decide underflow. */
401 if (!
402 ((tmp.sigh == 0x00100000) && (tmp.sigl == 0)
403 && (st0_ptr->sigl & 0x000007ff)))
404 #endif /* PECULIAR_486 */
406 EXCEPTION(EX_Underflow);
407 /* This is a special case: see sec 16.2.5.1 of
408 the 80486 book */
409 if (!(control_word & CW_Underflow))
410 return 0;
412 EXCEPTION(precision_loss);
413 if (!(control_word & CW_Precision))
414 return 0;
416 l[0] = tmp.sigl;
417 l[1] = tmp.sigh;
418 } else {
419 if (tmp.sigl & 0x000007ff) {
420 precision_loss = 1;
421 switch (control_word & CW_RC) {
422 case RC_RND:
423 /* Rounding can get a little messy.. */
424 increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
425 ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
426 break;
427 case RC_DOWN: /* towards -infinity */
428 increment =
429 signpositive(&tmp) ? 0 : tmp.
430 sigl & 0x7ff;
431 break;
432 case RC_UP: /* towards +infinity */
433 increment =
434 signpositive(&tmp) ? tmp.
435 sigl & 0x7ff : 0;
436 break;
437 case RC_CHOP:
438 increment = 0;
439 break;
442 /* Truncate the mantissa */
443 tmp.sigl &= 0xfffff800;
445 if (increment) {
446 if (tmp.sigl >= 0xfffff800) {
447 /* the sigl part overflows */
448 if (tmp.sigh == 0xffffffff) {
449 /* The sigh part overflows */
450 tmp.sigh = 0x80000000;
451 exp++;
452 if (exp >= EXP_OVER)
453 goto overflow;
454 } else {
455 tmp.sigh++;
457 tmp.sigl = 0x00000000;
458 } else {
459 /* We only need to increment sigl */
460 tmp.sigl += 0x00000800;
463 } else
464 precision_loss = 0;
466 l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
467 l[1] = ((tmp.sigh >> 11) & 0xfffff);
469 if (exp > DOUBLE_Emax) {
470 overflow:
471 EXCEPTION(EX_Overflow);
472 if (!(control_word & CW_Overflow))
473 return 0;
474 set_precision_flag_up();
475 if (!(control_word & CW_Precision))
476 return 0;
478 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
479 /* Overflow to infinity */
480 l[0] = 0x00000000; /* Set to */
481 l[1] = 0x7ff00000; /* + INF */
482 } else {
483 if (precision_loss) {
484 if (increment)
485 set_precision_flag_up();
486 else
487 set_precision_flag_down();
489 /* Add the exponent */
490 l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
493 } else if (st0_tag == TAG_Zero) {
494 /* Number is zero */
495 l[0] = 0;
496 l[1] = 0;
497 } else if (st0_tag == TAG_Special) {
498 st0_tag = FPU_Special(st0_ptr);
499 if (st0_tag == TW_Denormal) {
500 /* A denormal will always underflow. */
501 #ifndef PECULIAR_486
502 /* An 80486 is supposed to be able to generate
503 a denormal exception here, but... */
504 /* Underflow has priority. */
505 if (control_word & CW_Underflow)
506 denormal_operand();
507 #endif /* PECULIAR_486 */
508 reg_copy(st0_ptr, &tmp);
509 goto denormal_arg;
510 } else if (st0_tag == TW_Infinity) {
511 l[0] = 0;
512 l[1] = 0x7ff00000;
513 } else if (st0_tag == TW_NaN) {
514 /* Is it really a NaN ? */
515 if ((exponent(st0_ptr) == EXP_OVER)
516 && (st0_ptr->sigh & 0x80000000)) {
517 /* See if we can get a valid NaN from the FPU_REG */
518 l[0] =
519 (st0_ptr->sigl >> 11) | (st0_ptr->
520 sigh << 21);
521 l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
522 if (!(st0_ptr->sigh & 0x40000000)) {
523 /* It is a signalling NaN */
524 EXCEPTION(EX_Invalid);
525 if (!(control_word & CW_Invalid))
526 return 0;
527 l[1] |= (0x40000000 >> 11);
529 l[1] |= 0x7ff00000;
530 } else {
531 /* It is an unsupported data type */
532 EXCEPTION(EX_Invalid);
533 if (!(control_word & CW_Invalid))
534 return 0;
535 l[0] = 0;
536 l[1] = 0xfff80000;
539 } else if (st0_tag == TAG_Empty) {
540 /* Empty register (stack underflow) */
541 EXCEPTION(EX_StackUnder);
542 if (control_word & CW_Invalid) {
543 /* The masked response */
544 /* Put out the QNaN indefinite */
545 RE_ENTRANT_CHECK_OFF;
546 FPU_access_ok(VERIFY_WRITE, dfloat, 8);
547 FPU_put_user(0, (unsigned long __user *)dfloat);
548 FPU_put_user(0xfff80000,
549 1 + (unsigned long __user *)dfloat);
550 RE_ENTRANT_CHECK_ON;
551 return 1;
552 } else
553 return 0;
555 if (getsign(st0_ptr))
556 l[1] |= 0x80000000;
558 RE_ENTRANT_CHECK_OFF;
559 FPU_access_ok(VERIFY_WRITE, dfloat, 8);
560 FPU_put_user(l[0], (unsigned long __user *)dfloat);
561 FPU_put_user(l[1], 1 + (unsigned long __user *)dfloat);
562 RE_ENTRANT_CHECK_ON;
564 return 1;
567 /* Put a float into user memory */
568 int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float __user *single)
570 long templ = 0;
571 unsigned long increment = 0; /* avoid gcc warnings */
572 int precision_loss;
573 int exp;
574 FPU_REG tmp;
576 if (st0_tag == TAG_Valid) {
578 reg_copy(st0_ptr, &tmp);
579 exp = exponent(&tmp);
581 if (exp < SINGLE_Emin) {
582 addexponent(&tmp, -SINGLE_Emin + 23); /* largest exp to be 22 */
584 denormal_arg:
586 if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
587 #ifdef PECULIAR_486
588 /* Did it round to a non-denormal ? */
589 /* This behaviour might be regarded as peculiar, it appears
590 that the 80486 rounds to the dest precision, then
591 converts to decide underflow. */
592 if (!((tmp.sigl == 0x00800000) &&
593 ((st0_ptr->sigh & 0x000000ff)
594 || st0_ptr->sigl)))
595 #endif /* PECULIAR_486 */
597 EXCEPTION(EX_Underflow);
598 /* This is a special case: see sec 16.2.5.1 of
599 the 80486 book */
600 if (!(control_word & CW_Underflow))
601 return 0;
603 EXCEPTION(precision_loss);
604 if (!(control_word & CW_Precision))
605 return 0;
607 templ = tmp.sigl;
608 } else {
609 if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
610 unsigned long sigh = tmp.sigh;
611 unsigned long sigl = tmp.sigl;
613 precision_loss = 1;
614 switch (control_word & CW_RC) {
615 case RC_RND:
616 increment = ((sigh & 0xff) > 0x80) /* more than half */
617 ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
618 ||((sigh & 0x180) == 0x180); /* round to even */
619 break;
620 case RC_DOWN: /* towards -infinity */
621 increment = signpositive(&tmp)
622 ? 0 : (sigl | (sigh & 0xff));
623 break;
624 case RC_UP: /* towards +infinity */
625 increment = signpositive(&tmp)
626 ? (sigl | (sigh & 0xff)) : 0;
627 break;
628 case RC_CHOP:
629 increment = 0;
630 break;
633 /* Truncate part of the mantissa */
634 tmp.sigl = 0;
636 if (increment) {
637 if (sigh >= 0xffffff00) {
638 /* The sigh part overflows */
639 tmp.sigh = 0x80000000;
640 exp++;
641 if (exp >= EXP_OVER)
642 goto overflow;
643 } else {
644 tmp.sigh &= 0xffffff00;
645 tmp.sigh += 0x100;
647 } else {
648 tmp.sigh &= 0xffffff00; /* Finish the truncation */
650 } else
651 precision_loss = 0;
653 templ = (tmp.sigh >> 8) & 0x007fffff;
655 if (exp > SINGLE_Emax) {
656 overflow:
657 EXCEPTION(EX_Overflow);
658 if (!(control_word & CW_Overflow))
659 return 0;
660 set_precision_flag_up();
661 if (!(control_word & CW_Precision))
662 return 0;
664 /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
665 /* Masked response is overflow to infinity. */
666 templ = 0x7f800000;
667 } else {
668 if (precision_loss) {
669 if (increment)
670 set_precision_flag_up();
671 else
672 set_precision_flag_down();
674 /* Add the exponent */
675 templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
678 } else if (st0_tag == TAG_Zero) {
679 templ = 0;
680 } else if (st0_tag == TAG_Special) {
681 st0_tag = FPU_Special(st0_ptr);
682 if (st0_tag == TW_Denormal) {
683 reg_copy(st0_ptr, &tmp);
685 /* A denormal will always underflow. */
686 #ifndef PECULIAR_486
687 /* An 80486 is supposed to be able to generate
688 a denormal exception here, but... */
689 /* Underflow has priority. */
690 if (control_word & CW_Underflow)
691 denormal_operand();
692 #endif /* PECULIAR_486 */
693 goto denormal_arg;
694 } else if (st0_tag == TW_Infinity) {
695 templ = 0x7f800000;
696 } else if (st0_tag == TW_NaN) {
697 /* Is it really a NaN ? */
698 if ((exponent(st0_ptr) == EXP_OVER)
699 && (st0_ptr->sigh & 0x80000000)) {
700 /* See if we can get a valid NaN from the FPU_REG */
701 templ = st0_ptr->sigh >> 8;
702 if (!(st0_ptr->sigh & 0x40000000)) {
703 /* It is a signalling NaN */
704 EXCEPTION(EX_Invalid);
705 if (!(control_word & CW_Invalid))
706 return 0;
707 templ |= (0x40000000 >> 8);
709 templ |= 0x7f800000;
710 } else {
711 /* It is an unsupported data type */
712 EXCEPTION(EX_Invalid);
713 if (!(control_word & CW_Invalid))
714 return 0;
715 templ = 0xffc00000;
718 #ifdef PARANOID
719 else {
720 EXCEPTION(EX_INTERNAL | 0x164);
721 return 0;
723 #endif
724 } else if (st0_tag == TAG_Empty) {
725 /* Empty register (stack underflow) */
726 EXCEPTION(EX_StackUnder);
727 if (control_word & EX_Invalid) {
728 /* The masked response */
729 /* Put out the QNaN indefinite */
730 RE_ENTRANT_CHECK_OFF;
731 FPU_access_ok(VERIFY_WRITE, single, 4);
732 FPU_put_user(0xffc00000,
733 (unsigned long __user *)single);
734 RE_ENTRANT_CHECK_ON;
735 return 1;
736 } else
737 return 0;
739 #ifdef PARANOID
740 else {
741 EXCEPTION(EX_INTERNAL | 0x163);
742 return 0;
744 #endif
745 if (getsign(st0_ptr))
746 templ |= 0x80000000;
748 RE_ENTRANT_CHECK_OFF;
749 FPU_access_ok(VERIFY_WRITE, single, 4);
750 FPU_put_user(templ, (unsigned long __user *)single);
751 RE_ENTRANT_CHECK_ON;
753 return 1;
756 /* Put a long long into user memory */
757 int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long __user *d)
759 FPU_REG t;
760 long long tll;
761 int precision_loss;
763 if (st0_tag == TAG_Empty) {
764 /* Empty register (stack underflow) */
765 EXCEPTION(EX_StackUnder);
766 goto invalid_operand;
767 } else if (st0_tag == TAG_Special) {
768 st0_tag = FPU_Special(st0_ptr);
769 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
770 EXCEPTION(EX_Invalid);
771 goto invalid_operand;
775 reg_copy(st0_ptr, &t);
776 precision_loss = FPU_round_to_int(&t, st0_tag);
777 ((long *)&tll)[0] = t.sigl;
778 ((long *)&tll)[1] = t.sigh;
779 if ((precision_loss == 1) ||
780 ((t.sigh & 0x80000000) &&
781 !((t.sigh == 0x80000000) && (t.sigl == 0) && signnegative(&t)))) {
782 EXCEPTION(EX_Invalid);
783 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
784 invalid_operand:
785 if (control_word & EX_Invalid) {
786 /* Produce something like QNaN "indefinite" */
787 tll = 0x8000000000000000LL;
788 } else
789 return 0;
790 } else {
791 if (precision_loss)
792 set_precision_flag(precision_loss);
793 if (signnegative(&t))
794 tll = -tll;
797 RE_ENTRANT_CHECK_OFF;
798 FPU_access_ok(VERIFY_WRITE, d, 8);
799 if (copy_to_user(d, &tll, 8))
800 FPU_abort;
801 RE_ENTRANT_CHECK_ON;
803 return 1;
806 /* Put a long into user memory */
807 int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long __user *d)
809 FPU_REG t;
810 int precision_loss;
812 if (st0_tag == TAG_Empty) {
813 /* Empty register (stack underflow) */
814 EXCEPTION(EX_StackUnder);
815 goto invalid_operand;
816 } else if (st0_tag == TAG_Special) {
817 st0_tag = FPU_Special(st0_ptr);
818 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
819 EXCEPTION(EX_Invalid);
820 goto invalid_operand;
824 reg_copy(st0_ptr, &t);
825 precision_loss = FPU_round_to_int(&t, st0_tag);
826 if (t.sigh ||
827 ((t.sigl & 0x80000000) &&
828 !((t.sigl == 0x80000000) && signnegative(&t)))) {
829 EXCEPTION(EX_Invalid);
830 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
831 invalid_operand:
832 if (control_word & EX_Invalid) {
833 /* Produce something like QNaN "indefinite" */
834 t.sigl = 0x80000000;
835 } else
836 return 0;
837 } else {
838 if (precision_loss)
839 set_precision_flag(precision_loss);
840 if (signnegative(&t))
841 t.sigl = -(long)t.sigl;
844 RE_ENTRANT_CHECK_OFF;
845 FPU_access_ok(VERIFY_WRITE, d, 4);
846 FPU_put_user(t.sigl, (unsigned long __user *)d);
847 RE_ENTRANT_CHECK_ON;
849 return 1;
852 /* Put a short into user memory */
853 int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short __user *d)
855 FPU_REG t;
856 int precision_loss;
858 if (st0_tag == TAG_Empty) {
859 /* Empty register (stack underflow) */
860 EXCEPTION(EX_StackUnder);
861 goto invalid_operand;
862 } else if (st0_tag == TAG_Special) {
863 st0_tag = FPU_Special(st0_ptr);
864 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
865 EXCEPTION(EX_Invalid);
866 goto invalid_operand;
870 reg_copy(st0_ptr, &t);
871 precision_loss = FPU_round_to_int(&t, st0_tag);
872 if (t.sigh ||
873 ((t.sigl & 0xffff8000) &&
874 !((t.sigl == 0x8000) && signnegative(&t)))) {
875 EXCEPTION(EX_Invalid);
876 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
877 invalid_operand:
878 if (control_word & EX_Invalid) {
879 /* Produce something like QNaN "indefinite" */
880 t.sigl = 0x8000;
881 } else
882 return 0;
883 } else {
884 if (precision_loss)
885 set_precision_flag(precision_loss);
886 if (signnegative(&t))
887 t.sigl = -t.sigl;
890 RE_ENTRANT_CHECK_OFF;
891 FPU_access_ok(VERIFY_WRITE, d, 2);
892 FPU_put_user((short)t.sigl, d);
893 RE_ENTRANT_CHECK_ON;
895 return 1;
898 /* Put a packed bcd array into user memory */
899 int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char __user *d)
901 FPU_REG t;
902 unsigned long long ll;
903 u_char b;
904 int i, precision_loss;
905 u_char sign = (getsign(st0_ptr) == SIGN_NEG) ? 0x80 : 0;
907 if (st0_tag == TAG_Empty) {
908 /* Empty register (stack underflow) */
909 EXCEPTION(EX_StackUnder);
910 goto invalid_operand;
911 } else if (st0_tag == TAG_Special) {
912 st0_tag = FPU_Special(st0_ptr);
913 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
914 EXCEPTION(EX_Invalid);
915 goto invalid_operand;
919 reg_copy(st0_ptr, &t);
920 precision_loss = FPU_round_to_int(&t, st0_tag);
921 ll = significand(&t);
923 /* Check for overflow, by comparing with 999999999999999999 decimal. */
924 if ((t.sigh > 0x0de0b6b3) ||
925 ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
926 EXCEPTION(EX_Invalid);
927 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
928 invalid_operand:
929 if (control_word & CW_Invalid) {
930 /* Produce the QNaN "indefinite" */
931 RE_ENTRANT_CHECK_OFF;
932 FPU_access_ok(VERIFY_WRITE, d, 10);
933 for (i = 0; i < 7; i++)
934 FPU_put_user(0, d + i); /* These bytes "undefined" */
935 FPU_put_user(0xc0, d + 7); /* This byte "undefined" */
936 FPU_put_user(0xff, d + 8);
937 FPU_put_user(0xff, d + 9);
938 RE_ENTRANT_CHECK_ON;
939 return 1;
940 } else
941 return 0;
942 } else if (precision_loss) {
943 /* Precision loss doesn't stop the data transfer */
944 set_precision_flag(precision_loss);
947 RE_ENTRANT_CHECK_OFF;
948 FPU_access_ok(VERIFY_WRITE, d, 10);
949 RE_ENTRANT_CHECK_ON;
950 for (i = 0; i < 9; i++) {
951 b = FPU_div_small(&ll, 10);
952 b |= (FPU_div_small(&ll, 10)) << 4;
953 RE_ENTRANT_CHECK_OFF;
954 FPU_put_user(b, d + i);
955 RE_ENTRANT_CHECK_ON;
957 RE_ENTRANT_CHECK_OFF;
958 FPU_put_user(sign, d + 9);
959 RE_ENTRANT_CHECK_ON;
961 return 1;
964 /*===========================================================================*/
966 /* r gets mangled such that sig is int, sign:
967 it is NOT normalized */
968 /* The return value (in eax) is zero if the result is exact,
969 if bits are changed due to rounding, truncation, etc, then
970 a non-zero value is returned */
971 /* Overflow is signalled by a non-zero return value (in eax).
972 In the case of overflow, the returned significand always has the
973 largest possible value */
974 int FPU_round_to_int(FPU_REG *r, u_char tag)
976 u_char very_big;
977 unsigned eax;
979 if (tag == TAG_Zero) {
980 /* Make sure that zero is returned */
981 significand(r) = 0;
982 return 0; /* o.k. */
985 if (exponent(r) > 63) {
986 r->sigl = r->sigh = ~0; /* The largest representable number */
987 return 1; /* overflow */
990 eax = FPU_shrxs(&r->sigl, 63 - exponent(r));
991 very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */
992 #define half_or_more (eax & 0x80000000)
993 #define frac_part (eax)
994 #define more_than_half ((eax & 0x80000001) == 0x80000001)
995 switch (control_word & CW_RC) {
996 case RC_RND:
997 if (more_than_half /* nearest */
998 || (half_or_more && (r->sigl & 1))) { /* odd -> even */
999 if (very_big)
1000 return 1; /* overflow */
1001 significand(r)++;
1002 return PRECISION_LOST_UP;
1004 break;
1005 case RC_DOWN:
1006 if (frac_part && getsign(r)) {
1007 if (very_big)
1008 return 1; /* overflow */
1009 significand(r)++;
1010 return PRECISION_LOST_UP;
1012 break;
1013 case RC_UP:
1014 if (frac_part && !getsign(r)) {
1015 if (very_big)
1016 return 1; /* overflow */
1017 significand(r)++;
1018 return PRECISION_LOST_UP;
1020 break;
1021 case RC_CHOP:
1022 break;
1025 return eax ? PRECISION_LOST_DOWN : 0;
1029 /*===========================================================================*/
1031 u_char __user *fldenv(fpu_addr_modes addr_modes, u_char __user *s)
1033 unsigned short tag_word = 0;
1034 u_char tag;
1035 int i;
1037 if ((addr_modes.default_mode == VM86) ||
1038 ((addr_modes.default_mode == PM16)
1039 ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1040 RE_ENTRANT_CHECK_OFF;
1041 FPU_access_ok(VERIFY_READ, s, 0x0e);
1042 FPU_get_user(control_word, (unsigned short __user *)s);
1043 FPU_get_user(partial_status, (unsigned short __user *)(s + 2));
1044 FPU_get_user(tag_word, (unsigned short __user *)(s + 4));
1045 FPU_get_user(instruction_address.offset,
1046 (unsigned short __user *)(s + 6));
1047 FPU_get_user(instruction_address.selector,
1048 (unsigned short __user *)(s + 8));
1049 FPU_get_user(operand_address.offset,
1050 (unsigned short __user *)(s + 0x0a));
1051 FPU_get_user(operand_address.selector,
1052 (unsigned short __user *)(s + 0x0c));
1053 RE_ENTRANT_CHECK_ON;
1054 s += 0x0e;
1055 if (addr_modes.default_mode == VM86) {
1056 instruction_address.offset
1057 += (instruction_address.selector & 0xf000) << 4;
1058 operand_address.offset +=
1059 (operand_address.selector & 0xf000) << 4;
1061 } else {
1062 RE_ENTRANT_CHECK_OFF;
1063 FPU_access_ok(VERIFY_READ, s, 0x1c);
1064 FPU_get_user(control_word, (unsigned short __user *)s);
1065 FPU_get_user(partial_status, (unsigned short __user *)(s + 4));
1066 FPU_get_user(tag_word, (unsigned short __user *)(s + 8));
1067 FPU_get_user(instruction_address.offset,
1068 (unsigned long __user *)(s + 0x0c));
1069 FPU_get_user(instruction_address.selector,
1070 (unsigned short __user *)(s + 0x10));
1071 FPU_get_user(instruction_address.opcode,
1072 (unsigned short __user *)(s + 0x12));
1073 FPU_get_user(operand_address.offset,
1074 (unsigned long __user *)(s + 0x14));
1075 FPU_get_user(operand_address.selector,
1076 (unsigned long __user *)(s + 0x18));
1077 RE_ENTRANT_CHECK_ON;
1078 s += 0x1c;
1081 #ifdef PECULIAR_486
1082 control_word &= ~0xe080;
1083 #endif /* PECULIAR_486 */
1085 top = (partial_status >> SW_Top_Shift) & 7;
1087 if (partial_status & ~control_word & CW_Exceptions)
1088 partial_status |= (SW_Summary | SW_Backward);
1089 else
1090 partial_status &= ~(SW_Summary | SW_Backward);
1092 for (i = 0; i < 8; i++) {
1093 tag = tag_word & 3;
1094 tag_word >>= 2;
1096 if (tag == TAG_Empty)
1097 /* New tag is empty. Accept it */
1098 FPU_settag(i, TAG_Empty);
1099 else if (FPU_gettag(i) == TAG_Empty) {
1100 /* Old tag is empty and new tag is not empty. New tag is determined
1101 by old reg contents */
1102 if (exponent(&fpu_register(i)) == -EXTENDED_Ebias) {
1103 if (!
1104 (fpu_register(i).sigl | fpu_register(i).
1105 sigh))
1106 FPU_settag(i, TAG_Zero);
1107 else
1108 FPU_settag(i, TAG_Special);
1109 } else if (exponent(&fpu_register(i)) ==
1110 0x7fff - EXTENDED_Ebias) {
1111 FPU_settag(i, TAG_Special);
1112 } else if (fpu_register(i).sigh & 0x80000000)
1113 FPU_settag(i, TAG_Valid);
1114 else
1115 FPU_settag(i, TAG_Special); /* An Un-normal */
1117 /* Else old tag is not empty and new tag is not empty. Old tag
1118 remains correct */
1121 return s;
1124 void frstor(fpu_addr_modes addr_modes, u_char __user *data_address)
1126 int i, regnr;
1127 u_char __user *s = fldenv(addr_modes, data_address);
1128 int offset = (top & 7) * 10, other = 80 - offset;
1130 /* Copy all registers in stack order. */
1131 RE_ENTRANT_CHECK_OFF;
1132 FPU_access_ok(VERIFY_READ, s, 80);
1133 __copy_from_user(register_base + offset, s, other);
1134 if (offset)
1135 __copy_from_user(register_base, s + other, offset);
1136 RE_ENTRANT_CHECK_ON;
1138 for (i = 0; i < 8; i++) {
1139 regnr = (i + top) & 7;
1140 if (FPU_gettag(regnr) != TAG_Empty)
1141 /* The loaded data over-rides all other cases. */
1142 FPU_settag(regnr, FPU_tagof(&st(i)));
1147 u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d)
1149 if ((addr_modes.default_mode == VM86) ||
1150 ((addr_modes.default_mode == PM16)
1151 ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1152 RE_ENTRANT_CHECK_OFF;
1153 FPU_access_ok(VERIFY_WRITE, d, 14);
1154 #ifdef PECULIAR_486
1155 FPU_put_user(control_word & ~0xe080, (unsigned long __user *)d);
1156 #else
1157 FPU_put_user(control_word, (unsigned short __user *)d);
1158 #endif /* PECULIAR_486 */
1159 FPU_put_user(status_word(), (unsigned short __user *)(d + 2));
1160 FPU_put_user(fpu_tag_word, (unsigned short __user *)(d + 4));
1161 FPU_put_user(instruction_address.offset,
1162 (unsigned short __user *)(d + 6));
1163 FPU_put_user(operand_address.offset,
1164 (unsigned short __user *)(d + 0x0a));
1165 if (addr_modes.default_mode == VM86) {
1166 FPU_put_user((instruction_address.
1167 offset & 0xf0000) >> 4,
1168 (unsigned short __user *)(d + 8));
1169 FPU_put_user((operand_address.offset & 0xf0000) >> 4,
1170 (unsigned short __user *)(d + 0x0c));
1171 } else {
1172 FPU_put_user(instruction_address.selector,
1173 (unsigned short __user *)(d + 8));
1174 FPU_put_user(operand_address.selector,
1175 (unsigned short __user *)(d + 0x0c));
1177 RE_ENTRANT_CHECK_ON;
1178 d += 0x0e;
1179 } else {
1180 RE_ENTRANT_CHECK_OFF;
1181 FPU_access_ok(VERIFY_WRITE, d, 7 * 4);
1182 #ifdef PECULIAR_486
1183 control_word &= ~0xe080;
1184 /* An 80486 sets nearly all of the reserved bits to 1. */
1185 control_word |= 0xffff0040;
1186 partial_status = status_word() | 0xffff0000;
1187 fpu_tag_word |= 0xffff0000;
1188 I387.soft.fcs &= ~0xf8000000;
1189 I387.soft.fos |= 0xffff0000;
1190 #endif /* PECULIAR_486 */
1191 if (__copy_to_user(d, &control_word, 7 * 4))
1192 FPU_abort;
1193 RE_ENTRANT_CHECK_ON;
1194 d += 0x1c;
1197 control_word |= CW_Exceptions;
1198 partial_status &= ~(SW_Summary | SW_Backward);
1200 return d;
1203 void fsave(fpu_addr_modes addr_modes, u_char __user *data_address)
1205 u_char __user *d;
1206 int offset = (top & 7) * 10, other = 80 - offset;
1208 d = fstenv(addr_modes, data_address);
1210 RE_ENTRANT_CHECK_OFF;
1211 FPU_access_ok(VERIFY_WRITE, d, 80);
1213 /* Copy all registers in stack order. */
1214 if (__copy_to_user(d, register_base + offset, other))
1215 FPU_abort;
1216 if (offset)
1217 if (__copy_to_user(d + other, register_base, offset))
1218 FPU_abort;
1219 RE_ENTRANT_CHECK_ON;
1221 finit();
1224 /*===========================================================================*/