[SCSI] sd: Don't try to spin up drives that are connected to an inactive port
[linux-2.6/mini2440.git] / arch / x86 / math-emu / reg_ld_str.c
blobd597fe7423c98441f7ed52f8bcb0df3bd45646a9
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 l[0] = 0;
387 l[1] = 0;
388 if (st0_tag == TAG_Valid) {
389 reg_copy(st0_ptr, &tmp);
390 exp = exponent(&tmp);
392 if (exp < DOUBLE_Emin) { /* It may be a denormal */
393 addexponent(&tmp, -DOUBLE_Emin + 52); /* largest exp to be 51 */
394 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[1] = 0x7ff00000; /* Set to + INF */
481 } else {
482 if (precision_loss) {
483 if (increment)
484 set_precision_flag_up();
485 else
486 set_precision_flag_down();
488 /* Add the exponent */
489 l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
492 } else if (st0_tag == TAG_Zero) {
493 /* Number is zero */
494 } else if (st0_tag == TAG_Special) {
495 st0_tag = FPU_Special(st0_ptr);
496 if (st0_tag == TW_Denormal) {
497 /* A denormal will always underflow. */
498 #ifndef PECULIAR_486
499 /* An 80486 is supposed to be able to generate
500 a denormal exception here, but... */
501 /* Underflow has priority. */
502 if (control_word & CW_Underflow)
503 denormal_operand();
504 #endif /* PECULIAR_486 */
505 reg_copy(st0_ptr, &tmp);
506 goto denormal_arg;
507 } else if (st0_tag == TW_Infinity) {
508 l[1] = 0x7ff00000;
509 } else if (st0_tag == TW_NaN) {
510 /* Is it really a NaN ? */
511 if ((exponent(st0_ptr) == EXP_OVER)
512 && (st0_ptr->sigh & 0x80000000)) {
513 /* See if we can get a valid NaN from the FPU_REG */
514 l[0] =
515 (st0_ptr->sigl >> 11) | (st0_ptr->
516 sigh << 21);
517 l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
518 if (!(st0_ptr->sigh & 0x40000000)) {
519 /* It is a signalling NaN */
520 EXCEPTION(EX_Invalid);
521 if (!(control_word & CW_Invalid))
522 return 0;
523 l[1] |= (0x40000000 >> 11);
525 l[1] |= 0x7ff00000;
526 } else {
527 /* It is an unsupported data type */
528 EXCEPTION(EX_Invalid);
529 if (!(control_word & CW_Invalid))
530 return 0;
531 l[1] = 0xfff80000;
534 } else if (st0_tag == TAG_Empty) {
535 /* Empty register (stack underflow) */
536 EXCEPTION(EX_StackUnder);
537 if (control_word & CW_Invalid) {
538 /* The masked response */
539 /* Put out the QNaN indefinite */
540 RE_ENTRANT_CHECK_OFF;
541 FPU_access_ok(VERIFY_WRITE, dfloat, 8);
542 FPU_put_user(0, (unsigned long __user *)dfloat);
543 FPU_put_user(0xfff80000,
544 1 + (unsigned long __user *)dfloat);
545 RE_ENTRANT_CHECK_ON;
546 return 1;
547 } else
548 return 0;
550 if (getsign(st0_ptr))
551 l[1] |= 0x80000000;
553 RE_ENTRANT_CHECK_OFF;
554 FPU_access_ok(VERIFY_WRITE, dfloat, 8);
555 FPU_put_user(l[0], (unsigned long __user *)dfloat);
556 FPU_put_user(l[1], 1 + (unsigned long __user *)dfloat);
557 RE_ENTRANT_CHECK_ON;
559 return 1;
562 /* Put a float into user memory */
563 int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float __user *single)
565 long templ = 0;
566 unsigned long increment = 0; /* avoid gcc warnings */
567 int precision_loss;
568 int exp;
569 FPU_REG tmp;
571 if (st0_tag == TAG_Valid) {
573 reg_copy(st0_ptr, &tmp);
574 exp = exponent(&tmp);
576 if (exp < SINGLE_Emin) {
577 addexponent(&tmp, -SINGLE_Emin + 23); /* largest exp to be 22 */
579 denormal_arg:
581 if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
582 #ifdef PECULIAR_486
583 /* Did it round to a non-denormal ? */
584 /* This behaviour might be regarded as peculiar, it appears
585 that the 80486 rounds to the dest precision, then
586 converts to decide underflow. */
587 if (!((tmp.sigl == 0x00800000) &&
588 ((st0_ptr->sigh & 0x000000ff)
589 || st0_ptr->sigl)))
590 #endif /* PECULIAR_486 */
592 EXCEPTION(EX_Underflow);
593 /* This is a special case: see sec 16.2.5.1 of
594 the 80486 book */
595 if (!(control_word & CW_Underflow))
596 return 0;
598 EXCEPTION(precision_loss);
599 if (!(control_word & CW_Precision))
600 return 0;
602 templ = tmp.sigl;
603 } else {
604 if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
605 unsigned long sigh = tmp.sigh;
606 unsigned long sigl = tmp.sigl;
608 precision_loss = 1;
609 switch (control_word & CW_RC) {
610 case RC_RND:
611 increment = ((sigh & 0xff) > 0x80) /* more than half */
612 ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
613 ||((sigh & 0x180) == 0x180); /* round to even */
614 break;
615 case RC_DOWN: /* towards -infinity */
616 increment = signpositive(&tmp)
617 ? 0 : (sigl | (sigh & 0xff));
618 break;
619 case RC_UP: /* towards +infinity */
620 increment = signpositive(&tmp)
621 ? (sigl | (sigh & 0xff)) : 0;
622 break;
623 case RC_CHOP:
624 increment = 0;
625 break;
628 /* Truncate part of the mantissa */
629 tmp.sigl = 0;
631 if (increment) {
632 if (sigh >= 0xffffff00) {
633 /* The sigh part overflows */
634 tmp.sigh = 0x80000000;
635 exp++;
636 if (exp >= EXP_OVER)
637 goto overflow;
638 } else {
639 tmp.sigh &= 0xffffff00;
640 tmp.sigh += 0x100;
642 } else {
643 tmp.sigh &= 0xffffff00; /* Finish the truncation */
645 } else
646 precision_loss = 0;
648 templ = (tmp.sigh >> 8) & 0x007fffff;
650 if (exp > SINGLE_Emax) {
651 overflow:
652 EXCEPTION(EX_Overflow);
653 if (!(control_word & CW_Overflow))
654 return 0;
655 set_precision_flag_up();
656 if (!(control_word & CW_Precision))
657 return 0;
659 /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
660 /* Masked response is overflow to infinity. */
661 templ = 0x7f800000;
662 } else {
663 if (precision_loss) {
664 if (increment)
665 set_precision_flag_up();
666 else
667 set_precision_flag_down();
669 /* Add the exponent */
670 templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
673 } else if (st0_tag == TAG_Zero) {
674 templ = 0;
675 } else if (st0_tag == TAG_Special) {
676 st0_tag = FPU_Special(st0_ptr);
677 if (st0_tag == TW_Denormal) {
678 reg_copy(st0_ptr, &tmp);
680 /* A denormal will always underflow. */
681 #ifndef PECULIAR_486
682 /* An 80486 is supposed to be able to generate
683 a denormal exception here, but... */
684 /* Underflow has priority. */
685 if (control_word & CW_Underflow)
686 denormal_operand();
687 #endif /* PECULIAR_486 */
688 goto denormal_arg;
689 } else if (st0_tag == TW_Infinity) {
690 templ = 0x7f800000;
691 } else if (st0_tag == TW_NaN) {
692 /* Is it really a NaN ? */
693 if ((exponent(st0_ptr) == EXP_OVER)
694 && (st0_ptr->sigh & 0x80000000)) {
695 /* See if we can get a valid NaN from the FPU_REG */
696 templ = st0_ptr->sigh >> 8;
697 if (!(st0_ptr->sigh & 0x40000000)) {
698 /* It is a signalling NaN */
699 EXCEPTION(EX_Invalid);
700 if (!(control_word & CW_Invalid))
701 return 0;
702 templ |= (0x40000000 >> 8);
704 templ |= 0x7f800000;
705 } else {
706 /* It is an unsupported data type */
707 EXCEPTION(EX_Invalid);
708 if (!(control_word & CW_Invalid))
709 return 0;
710 templ = 0xffc00000;
713 #ifdef PARANOID
714 else {
715 EXCEPTION(EX_INTERNAL | 0x164);
716 return 0;
718 #endif
719 } else if (st0_tag == TAG_Empty) {
720 /* Empty register (stack underflow) */
721 EXCEPTION(EX_StackUnder);
722 if (control_word & EX_Invalid) {
723 /* The masked response */
724 /* Put out the QNaN indefinite */
725 RE_ENTRANT_CHECK_OFF;
726 FPU_access_ok(VERIFY_WRITE, single, 4);
727 FPU_put_user(0xffc00000,
728 (unsigned long __user *)single);
729 RE_ENTRANT_CHECK_ON;
730 return 1;
731 } else
732 return 0;
734 #ifdef PARANOID
735 else {
736 EXCEPTION(EX_INTERNAL | 0x163);
737 return 0;
739 #endif
740 if (getsign(st0_ptr))
741 templ |= 0x80000000;
743 RE_ENTRANT_CHECK_OFF;
744 FPU_access_ok(VERIFY_WRITE, single, 4);
745 FPU_put_user(templ, (unsigned long __user *)single);
746 RE_ENTRANT_CHECK_ON;
748 return 1;
751 /* Put a long long into user memory */
752 int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long __user *d)
754 FPU_REG t;
755 long long tll;
756 int precision_loss;
758 if (st0_tag == TAG_Empty) {
759 /* Empty register (stack underflow) */
760 EXCEPTION(EX_StackUnder);
761 goto invalid_operand;
762 } else if (st0_tag == TAG_Special) {
763 st0_tag = FPU_Special(st0_ptr);
764 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
765 EXCEPTION(EX_Invalid);
766 goto invalid_operand;
770 reg_copy(st0_ptr, &t);
771 precision_loss = FPU_round_to_int(&t, st0_tag);
772 ((long *)&tll)[0] = t.sigl;
773 ((long *)&tll)[1] = t.sigh;
774 if ((precision_loss == 1) ||
775 ((t.sigh & 0x80000000) &&
776 !((t.sigh == 0x80000000) && (t.sigl == 0) && signnegative(&t)))) {
777 EXCEPTION(EX_Invalid);
778 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
779 invalid_operand:
780 if (control_word & EX_Invalid) {
781 /* Produce something like QNaN "indefinite" */
782 tll = 0x8000000000000000LL;
783 } else
784 return 0;
785 } else {
786 if (precision_loss)
787 set_precision_flag(precision_loss);
788 if (signnegative(&t))
789 tll = -tll;
792 RE_ENTRANT_CHECK_OFF;
793 FPU_access_ok(VERIFY_WRITE, d, 8);
794 if (copy_to_user(d, &tll, 8))
795 FPU_abort;
796 RE_ENTRANT_CHECK_ON;
798 return 1;
801 /* Put a long into user memory */
802 int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long __user *d)
804 FPU_REG t;
805 int precision_loss;
807 if (st0_tag == TAG_Empty) {
808 /* Empty register (stack underflow) */
809 EXCEPTION(EX_StackUnder);
810 goto invalid_operand;
811 } else if (st0_tag == TAG_Special) {
812 st0_tag = FPU_Special(st0_ptr);
813 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
814 EXCEPTION(EX_Invalid);
815 goto invalid_operand;
819 reg_copy(st0_ptr, &t);
820 precision_loss = FPU_round_to_int(&t, st0_tag);
821 if (t.sigh ||
822 ((t.sigl & 0x80000000) &&
823 !((t.sigl == 0x80000000) && signnegative(&t)))) {
824 EXCEPTION(EX_Invalid);
825 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
826 invalid_operand:
827 if (control_word & EX_Invalid) {
828 /* Produce something like QNaN "indefinite" */
829 t.sigl = 0x80000000;
830 } else
831 return 0;
832 } else {
833 if (precision_loss)
834 set_precision_flag(precision_loss);
835 if (signnegative(&t))
836 t.sigl = -(long)t.sigl;
839 RE_ENTRANT_CHECK_OFF;
840 FPU_access_ok(VERIFY_WRITE, d, 4);
841 FPU_put_user(t.sigl, (unsigned long __user *)d);
842 RE_ENTRANT_CHECK_ON;
844 return 1;
847 /* Put a short into user memory */
848 int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short __user *d)
850 FPU_REG t;
851 int precision_loss;
853 if (st0_tag == TAG_Empty) {
854 /* Empty register (stack underflow) */
855 EXCEPTION(EX_StackUnder);
856 goto invalid_operand;
857 } else if (st0_tag == TAG_Special) {
858 st0_tag = FPU_Special(st0_ptr);
859 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
860 EXCEPTION(EX_Invalid);
861 goto invalid_operand;
865 reg_copy(st0_ptr, &t);
866 precision_loss = FPU_round_to_int(&t, st0_tag);
867 if (t.sigh ||
868 ((t.sigl & 0xffff8000) &&
869 !((t.sigl == 0x8000) && signnegative(&t)))) {
870 EXCEPTION(EX_Invalid);
871 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
872 invalid_operand:
873 if (control_word & EX_Invalid) {
874 /* Produce something like QNaN "indefinite" */
875 t.sigl = 0x8000;
876 } else
877 return 0;
878 } else {
879 if (precision_loss)
880 set_precision_flag(precision_loss);
881 if (signnegative(&t))
882 t.sigl = -t.sigl;
885 RE_ENTRANT_CHECK_OFF;
886 FPU_access_ok(VERIFY_WRITE, d, 2);
887 FPU_put_user((short)t.sigl, d);
888 RE_ENTRANT_CHECK_ON;
890 return 1;
893 /* Put a packed bcd array into user memory */
894 int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char __user *d)
896 FPU_REG t;
897 unsigned long long ll;
898 u_char b;
899 int i, precision_loss;
900 u_char sign = (getsign(st0_ptr) == SIGN_NEG) ? 0x80 : 0;
902 if (st0_tag == TAG_Empty) {
903 /* Empty register (stack underflow) */
904 EXCEPTION(EX_StackUnder);
905 goto invalid_operand;
906 } else if (st0_tag == TAG_Special) {
907 st0_tag = FPU_Special(st0_ptr);
908 if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
909 EXCEPTION(EX_Invalid);
910 goto invalid_operand;
914 reg_copy(st0_ptr, &t);
915 precision_loss = FPU_round_to_int(&t, st0_tag);
916 ll = significand(&t);
918 /* Check for overflow, by comparing with 999999999999999999 decimal. */
919 if ((t.sigh > 0x0de0b6b3) ||
920 ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
921 EXCEPTION(EX_Invalid);
922 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
923 invalid_operand:
924 if (control_word & CW_Invalid) {
925 /* Produce the QNaN "indefinite" */
926 RE_ENTRANT_CHECK_OFF;
927 FPU_access_ok(VERIFY_WRITE, d, 10);
928 for (i = 0; i < 7; i++)
929 FPU_put_user(0, d + i); /* These bytes "undefined" */
930 FPU_put_user(0xc0, d + 7); /* This byte "undefined" */
931 FPU_put_user(0xff, d + 8);
932 FPU_put_user(0xff, d + 9);
933 RE_ENTRANT_CHECK_ON;
934 return 1;
935 } else
936 return 0;
937 } else if (precision_loss) {
938 /* Precision loss doesn't stop the data transfer */
939 set_precision_flag(precision_loss);
942 RE_ENTRANT_CHECK_OFF;
943 FPU_access_ok(VERIFY_WRITE, d, 10);
944 RE_ENTRANT_CHECK_ON;
945 for (i = 0; i < 9; i++) {
946 b = FPU_div_small(&ll, 10);
947 b |= (FPU_div_small(&ll, 10)) << 4;
948 RE_ENTRANT_CHECK_OFF;
949 FPU_put_user(b, d + i);
950 RE_ENTRANT_CHECK_ON;
952 RE_ENTRANT_CHECK_OFF;
953 FPU_put_user(sign, d + 9);
954 RE_ENTRANT_CHECK_ON;
956 return 1;
959 /*===========================================================================*/
961 /* r gets mangled such that sig is int, sign:
962 it is NOT normalized */
963 /* The return value (in eax) is zero if the result is exact,
964 if bits are changed due to rounding, truncation, etc, then
965 a non-zero value is returned */
966 /* Overflow is signalled by a non-zero return value (in eax).
967 In the case of overflow, the returned significand always has the
968 largest possible value */
969 int FPU_round_to_int(FPU_REG *r, u_char tag)
971 u_char very_big;
972 unsigned eax;
974 if (tag == TAG_Zero) {
975 /* Make sure that zero is returned */
976 significand(r) = 0;
977 return 0; /* o.k. */
980 if (exponent(r) > 63) {
981 r->sigl = r->sigh = ~0; /* The largest representable number */
982 return 1; /* overflow */
985 eax = FPU_shrxs(&r->sigl, 63 - exponent(r));
986 very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */
987 #define half_or_more (eax & 0x80000000)
988 #define frac_part (eax)
989 #define more_than_half ((eax & 0x80000001) == 0x80000001)
990 switch (control_word & CW_RC) {
991 case RC_RND:
992 if (more_than_half /* nearest */
993 || (half_or_more && (r->sigl & 1))) { /* odd -> even */
994 if (very_big)
995 return 1; /* overflow */
996 significand(r)++;
997 return PRECISION_LOST_UP;
999 break;
1000 case RC_DOWN:
1001 if (frac_part && getsign(r)) {
1002 if (very_big)
1003 return 1; /* overflow */
1004 significand(r)++;
1005 return PRECISION_LOST_UP;
1007 break;
1008 case RC_UP:
1009 if (frac_part && !getsign(r)) {
1010 if (very_big)
1011 return 1; /* overflow */
1012 significand(r)++;
1013 return PRECISION_LOST_UP;
1015 break;
1016 case RC_CHOP:
1017 break;
1020 return eax ? PRECISION_LOST_DOWN : 0;
1024 /*===========================================================================*/
1026 u_char __user *fldenv(fpu_addr_modes addr_modes, u_char __user *s)
1028 unsigned short tag_word = 0;
1029 u_char tag;
1030 int i;
1032 if ((addr_modes.default_mode == VM86) ||
1033 ((addr_modes.default_mode == PM16)
1034 ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1035 RE_ENTRANT_CHECK_OFF;
1036 FPU_access_ok(VERIFY_READ, s, 0x0e);
1037 FPU_get_user(control_word, (unsigned short __user *)s);
1038 FPU_get_user(partial_status, (unsigned short __user *)(s + 2));
1039 FPU_get_user(tag_word, (unsigned short __user *)(s + 4));
1040 FPU_get_user(instruction_address.offset,
1041 (unsigned short __user *)(s + 6));
1042 FPU_get_user(instruction_address.selector,
1043 (unsigned short __user *)(s + 8));
1044 FPU_get_user(operand_address.offset,
1045 (unsigned short __user *)(s + 0x0a));
1046 FPU_get_user(operand_address.selector,
1047 (unsigned short __user *)(s + 0x0c));
1048 RE_ENTRANT_CHECK_ON;
1049 s += 0x0e;
1050 if (addr_modes.default_mode == VM86) {
1051 instruction_address.offset
1052 += (instruction_address.selector & 0xf000) << 4;
1053 operand_address.offset +=
1054 (operand_address.selector & 0xf000) << 4;
1056 } else {
1057 RE_ENTRANT_CHECK_OFF;
1058 FPU_access_ok(VERIFY_READ, s, 0x1c);
1059 FPU_get_user(control_word, (unsigned short __user *)s);
1060 FPU_get_user(partial_status, (unsigned short __user *)(s + 4));
1061 FPU_get_user(tag_word, (unsigned short __user *)(s + 8));
1062 FPU_get_user(instruction_address.offset,
1063 (unsigned long __user *)(s + 0x0c));
1064 FPU_get_user(instruction_address.selector,
1065 (unsigned short __user *)(s + 0x10));
1066 FPU_get_user(instruction_address.opcode,
1067 (unsigned short __user *)(s + 0x12));
1068 FPU_get_user(operand_address.offset,
1069 (unsigned long __user *)(s + 0x14));
1070 FPU_get_user(operand_address.selector,
1071 (unsigned long __user *)(s + 0x18));
1072 RE_ENTRANT_CHECK_ON;
1073 s += 0x1c;
1076 #ifdef PECULIAR_486
1077 control_word &= ~0xe080;
1078 #endif /* PECULIAR_486 */
1080 top = (partial_status >> SW_Top_Shift) & 7;
1082 if (partial_status & ~control_word & CW_Exceptions)
1083 partial_status |= (SW_Summary | SW_Backward);
1084 else
1085 partial_status &= ~(SW_Summary | SW_Backward);
1087 for (i = 0; i < 8; i++) {
1088 tag = tag_word & 3;
1089 tag_word >>= 2;
1091 if (tag == TAG_Empty)
1092 /* New tag is empty. Accept it */
1093 FPU_settag(i, TAG_Empty);
1094 else if (FPU_gettag(i) == TAG_Empty) {
1095 /* Old tag is empty and new tag is not empty. New tag is determined
1096 by old reg contents */
1097 if (exponent(&fpu_register(i)) == -EXTENDED_Ebias) {
1098 if (!
1099 (fpu_register(i).sigl | fpu_register(i).
1100 sigh))
1101 FPU_settag(i, TAG_Zero);
1102 else
1103 FPU_settag(i, TAG_Special);
1104 } else if (exponent(&fpu_register(i)) ==
1105 0x7fff - EXTENDED_Ebias) {
1106 FPU_settag(i, TAG_Special);
1107 } else if (fpu_register(i).sigh & 0x80000000)
1108 FPU_settag(i, TAG_Valid);
1109 else
1110 FPU_settag(i, TAG_Special); /* An Un-normal */
1112 /* Else old tag is not empty and new tag is not empty. Old tag
1113 remains correct */
1116 return s;
1119 void frstor(fpu_addr_modes addr_modes, u_char __user *data_address)
1121 int i, regnr;
1122 u_char __user *s = fldenv(addr_modes, data_address);
1123 int offset = (top & 7) * 10, other = 80 - offset;
1125 /* Copy all registers in stack order. */
1126 RE_ENTRANT_CHECK_OFF;
1127 FPU_access_ok(VERIFY_READ, s, 80);
1128 __copy_from_user(register_base + offset, s, other);
1129 if (offset)
1130 __copy_from_user(register_base, s + other, offset);
1131 RE_ENTRANT_CHECK_ON;
1133 for (i = 0; i < 8; i++) {
1134 regnr = (i + top) & 7;
1135 if (FPU_gettag(regnr) != TAG_Empty)
1136 /* The loaded data over-rides all other cases. */
1137 FPU_settag(regnr, FPU_tagof(&st(i)));
1142 u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d)
1144 if ((addr_modes.default_mode == VM86) ||
1145 ((addr_modes.default_mode == PM16)
1146 ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1147 RE_ENTRANT_CHECK_OFF;
1148 FPU_access_ok(VERIFY_WRITE, d, 14);
1149 #ifdef PECULIAR_486
1150 FPU_put_user(control_word & ~0xe080, (unsigned long __user *)d);
1151 #else
1152 FPU_put_user(control_word, (unsigned short __user *)d);
1153 #endif /* PECULIAR_486 */
1154 FPU_put_user(status_word(), (unsigned short __user *)(d + 2));
1155 FPU_put_user(fpu_tag_word, (unsigned short __user *)(d + 4));
1156 FPU_put_user(instruction_address.offset,
1157 (unsigned short __user *)(d + 6));
1158 FPU_put_user(operand_address.offset,
1159 (unsigned short __user *)(d + 0x0a));
1160 if (addr_modes.default_mode == VM86) {
1161 FPU_put_user((instruction_address.
1162 offset & 0xf0000) >> 4,
1163 (unsigned short __user *)(d + 8));
1164 FPU_put_user((operand_address.offset & 0xf0000) >> 4,
1165 (unsigned short __user *)(d + 0x0c));
1166 } else {
1167 FPU_put_user(instruction_address.selector,
1168 (unsigned short __user *)(d + 8));
1169 FPU_put_user(operand_address.selector,
1170 (unsigned short __user *)(d + 0x0c));
1172 RE_ENTRANT_CHECK_ON;
1173 d += 0x0e;
1174 } else {
1175 RE_ENTRANT_CHECK_OFF;
1176 FPU_access_ok(VERIFY_WRITE, d, 7 * 4);
1177 #ifdef PECULIAR_486
1178 control_word &= ~0xe080;
1179 /* An 80486 sets nearly all of the reserved bits to 1. */
1180 control_word |= 0xffff0040;
1181 partial_status = status_word() | 0xffff0000;
1182 fpu_tag_word |= 0xffff0000;
1183 I387->soft.fcs &= ~0xf8000000;
1184 I387->soft.fos |= 0xffff0000;
1185 #endif /* PECULIAR_486 */
1186 if (__copy_to_user(d, &control_word, 7 * 4))
1187 FPU_abort;
1188 RE_ENTRANT_CHECK_ON;
1189 d += 0x1c;
1192 control_word |= CW_Exceptions;
1193 partial_status &= ~(SW_Summary | SW_Backward);
1195 return d;
1198 void fsave(fpu_addr_modes addr_modes, u_char __user *data_address)
1200 u_char __user *d;
1201 int offset = (top & 7) * 10, other = 80 - offset;
1203 d = fstenv(addr_modes, data_address);
1205 RE_ENTRANT_CHECK_OFF;
1206 FPU_access_ok(VERIFY_WRITE, d, 80);
1208 /* Copy all registers in stack order. */
1209 if (__copy_to_user(d, register_base + offset, other))
1210 FPU_abort;
1211 if (offset)
1212 if (__copy_to_user(d + other, register_base, offset))
1213 FPU_abort;
1214 RE_ENTRANT_CHECK_ON;
1216 finit();
1219 /*===========================================================================*/