1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "fpu_emu.h"
21
22#include <asm/uaccess.h>
23
24#include "fpu_system.h"
25#include "exception.h"
26#include "reg_constant.h"
27#include "control_w.h"
28#include "status_w.h"
29
30#define DOUBLE_Emax 1023
31#define DOUBLE_Ebias 1023
32#define DOUBLE_Emin (-1022)
33
34#define SINGLE_Emax 127
35#define SINGLE_Ebias 127
36#define SINGLE_Emin (-126)
37
38static u_char normalize_no_excep(FPU_REG *r, int exp, int sign)
39{
40 u_char tag;
41
42 setexponent16(r, exp);
43
44 tag = FPU_normalize_nuo(r);
45 stdexp(r);
46 if (sign)
47 setnegative(r);
48
49 return tag;
50}
51
52int FPU_tagof(FPU_REG *ptr)
53{
54 int exp;
55
56 exp = exponent16(ptr) & 0x7fff;
57 if (exp == 0) {
58 if (!(ptr->sigh | ptr->sigl)) {
59 return TAG_Zero;
60 }
61
62 return TAG_Special;
63 }
64
65 if (exp == 0x7fff) {
66
67 return TAG_Special;
68 }
69
70 if (!(ptr->sigh & 0x80000000)) {
71
72
73
74 return TAG_Special;
75 }
76
77 return TAG_Valid;
78}
79
80
81int FPU_load_extended(long double __user *s, int stnr)
82{
83 FPU_REG *sti_ptr = &st(stnr);
84
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;
89
90 return FPU_tagof(sti_ptr);
91}
92
93
94int FPU_load_double(double __user *dfloat, FPU_REG *loaded_data)
95{
96 int exp, tag, negative;
97 unsigned m64, l64;
98
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;
104
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
110 if ((m64 == 0) && (l64 == 0)) {
111
112 loaded_data->sigh = 0x80000000;
113 loaded_data->sigl = 0x00000000;
114 exp = EXP_Infinity + EXTENDED_Ebias;
115 tag = TAG_Special;
116 } else {
117
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;
123 }
124 } else if (exp < DOUBLE_Emin + EXTENDED_Ebias) {
125
126 if ((m64 == 0) && (l64 == 0)) {
127
128 reg_copy(&CONST_Z, loaded_data);
129 exp = 0;
130 tag = TAG_Zero;
131 } else {
132
133 loaded_data->sigh = m64 << 11;
134 loaded_data->sigh |= l64 >> 21;
135 loaded_data->sigl = l64 << 11;
136
137 return normalize_no_excep(loaded_data, DOUBLE_Emin,
138 negative)
139 | (denormal_operand() < 0 ? FPU_Exception : 0);
140 }
141 } else {
142 loaded_data->sigh = (m64 << 11) | 0x80000000;
143 loaded_data->sigh |= l64 >> 21;
144 loaded_data->sigl = l64 << 11;
145
146 tag = TAG_Valid;
147 }
148
149 setexponent16(loaded_data, exp | negative);
150
151 return tag;
152}
153
154
155int FPU_load_single(float __user *single, FPU_REG *loaded_data)
156{
157 unsigned m32;
158 int exp, tag, negative;
159
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;
164
165 negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
166
167 if (!(m32 & 0x7fffffff)) {
168
169 reg_copy(&CONST_Z, loaded_data);
170 addexponent(loaded_data, negative);
171 return TAG_Zero;
172 }
173 exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias;
174 m32 = (m32 & 0x7fffff) << 8;
175 if (exp < SINGLE_Emin + EXTENDED_Ebias) {
176
177 loaded_data->sigh = m32;
178 loaded_data->sigl = 0;
179
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
184 if (m32 == 0) {
185
186 loaded_data->sigh = 0x80000000;
187 loaded_data->sigl = 0x00000000;
188 exp = EXP_Infinity + EXTENDED_Ebias;
189 tag = TAG_Special;
190 } else {
191
192 exp = EXP_NaN + EXTENDED_Ebias;
193 loaded_data->sigh = m32 | 0x80000000;
194 loaded_data->sigl = 0;
195 tag = TAG_Special;
196 }
197 } else {
198 loaded_data->sigh = m32 | 0x80000000;
199 loaded_data->sigl = 0;
200 tag = TAG_Valid;
201 }
202
203 setexponent16(loaded_data, exp | negative);
204
205 return tag;
206}
207
208
209int FPU_load_int64(long long __user *_s)
210{
211 long long s;
212 int sign;
213 FPU_REG *st0_ptr = &st(0);
214
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;
220
221 if (s == 0) {
222 reg_copy(&CONST_Z, st0_ptr);
223 return TAG_Zero;
224 }
225
226 if (s > 0)
227 sign = SIGN_Positive;
228 else {
229 s = -s;
230 sign = SIGN_Negative;
231 }
232
233 significand(st0_ptr) = s;
234
235 return normalize_no_excep(st0_ptr, 63, sign);
236}
237
238
239int FPU_load_int32(long __user *_s, FPU_REG *loaded_data)
240{
241 long s;
242 int negative;
243
244 RE_ENTRANT_CHECK_OFF;
245 FPU_access_ok(VERIFY_READ, _s, 4);
246 FPU_get_user(s, _s);
247 RE_ENTRANT_CHECK_ON;
248
249 if (s == 0) {
250 reg_copy(&CONST_Z, loaded_data);
251 return TAG_Zero;
252 }
253
254 if (s > 0)
255 negative = SIGN_Positive;
256 else {
257 s = -s;
258 negative = SIGN_Negative;
259 }
260
261 loaded_data->sigh = s;
262 loaded_data->sigl = 0;
263
264 return normalize_no_excep(loaded_data, 31, negative);
265}
266
267
268int FPU_load_int16(short __user *_s, FPU_REG *loaded_data)
269{
270 int s, negative;
271
272 RE_ENTRANT_CHECK_OFF;
273 FPU_access_ok(VERIFY_READ, _s, 2);
274
275 FPU_get_user(s, _s);
276 RE_ENTRANT_CHECK_ON;
277
278 if (s == 0) {
279 reg_copy(&CONST_Z, loaded_data);
280 return TAG_Zero;
281 }
282
283 if (s > 0)
284 negative = SIGN_Positive;
285 else {
286 s = -s;
287 negative = SIGN_Negative;
288 }
289
290 loaded_data->sigh = s << 16;
291 loaded_data->sigl = 0;
292
293 return normalize_no_excep(loaded_data, 15, negative);
294}
295
296
297int FPU_load_bcd(u_char __user *s)
298{
299 FPU_REG *st0_ptr = &st(0);
300 int pos;
301 u_char bcd;
302 long long l = 0;
303 int sign;
304
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;
316 }
317
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;
322
323 if (l == 0) {
324 reg_copy(&CONST_Z, st0_ptr);
325 addexponent(st0_ptr, sign);
326 return TAG_Zero;
327 } else {
328 significand(st0_ptr) = l;
329 return normalize_no_excep(st0_ptr, 63, sign);
330 }
331}
332
333
334
335
336int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
337 long double __user * d)
338{
339
340
341
342
343
344
345 if (st0_tag != TAG_Empty) {
346 RE_ENTRANT_CHECK_OFF;
347 FPU_access_ok(VERIFY_WRITE, d, 10);
348
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;
356
357 return 1;
358 }
359
360
361 EXCEPTION(EX_StackUnder);
362 if (control_word & CW_Invalid) {
363
364
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;
374
375}
376
377
378int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double __user *dfloat)
379{
380 unsigned long l[2];
381 unsigned long increment = 0;
382 int precision_loss;
383 int exp;
384 FPU_REG tmp;
385
386 l[0] = 0;
387 l[1] = 0;
388 if (st0_tag == TAG_Valid) {
389 reg_copy(st0_ptr, &tmp);
390 exp = exponent(&tmp);
391
392 if (exp < DOUBLE_Emin) {
393 addexponent(&tmp, -DOUBLE_Emin + 52);
394denormal_arg:
395 if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
396#ifdef PECULIAR_486
397
398
399
400
401 if (!
402 ((tmp.sigh == 0x00100000) && (tmp.sigl == 0)
403 && (st0_ptr->sigl & 0x000007ff)))
404#endif
405 {
406 EXCEPTION(EX_Underflow);
407
408
409 if (!(control_word & CW_Underflow))
410 return 0;
411 }
412 EXCEPTION(precision_loss);
413 if (!(control_word & CW_Precision))
414 return 0;
415 }
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
424 increment = ((tmp.sigl & 0x7ff) > 0x400) |
425 ((tmp.sigl & 0xc00) == 0xc00);
426 break;
427 case RC_DOWN:
428 increment =
429 signpositive(&tmp) ? 0 : tmp.
430 sigl & 0x7ff;
431 break;
432 case RC_UP:
433 increment =
434 signpositive(&tmp) ? tmp.
435 sigl & 0x7ff : 0;
436 break;
437 case RC_CHOP:
438 increment = 0;
439 break;
440 }
441
442
443 tmp.sigl &= 0xfffff800;
444
445 if (increment) {
446 if (tmp.sigl >= 0xfffff800) {
447
448 if (tmp.sigh == 0xffffffff) {
449
450 tmp.sigh = 0x80000000;
451 exp++;
452 if (exp >= EXP_OVER)
453 goto overflow;
454 } else {
455 tmp.sigh++;
456 }
457 tmp.sigl = 0x00000000;
458 } else {
459
460 tmp.sigl += 0x00000800;
461 }
462 }
463 } else
464 precision_loss = 0;
465
466 l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
467 l[1] = ((tmp.sigh >> 11) & 0xfffff);
468
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;
477
478
479
480 l[1] = 0x7ff00000;
481 } else {
482 if (precision_loss) {
483 if (increment)
484 set_precision_flag_up();
485 else
486 set_precision_flag_down();
487 }
488
489 l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
490 }
491 }
492 } else if (st0_tag == TAG_Zero) {
493
494 } else if (st0_tag == TAG_Special) {
495 st0_tag = FPU_Special(st0_ptr);
496 if (st0_tag == TW_Denormal) {
497
498#ifndef PECULIAR_486
499
500
501
502 if (control_word & CW_Underflow)
503 denormal_operand();
504#endif
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
511 if ((exponent(st0_ptr) == EXP_OVER)
512 && (st0_ptr->sigh & 0x80000000)) {
513
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
520 EXCEPTION(EX_Invalid);
521 if (!(control_word & CW_Invalid))
522 return 0;
523 l[1] |= (0x40000000 >> 11);
524 }
525 l[1] |= 0x7ff00000;
526 } else {
527
528 EXCEPTION(EX_Invalid);
529 if (!(control_word & CW_Invalid))
530 return 0;
531 l[1] = 0xfff80000;
532 }
533 }
534 } else if (st0_tag == TAG_Empty) {
535
536 EXCEPTION(EX_StackUnder);
537 if (control_word & CW_Invalid) {
538
539
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;
549 }
550 if (getsign(st0_ptr))
551 l[1] |= 0x80000000;
552
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;
558
559 return 1;
560}
561
562
563int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float __user *single)
564{
565 long templ = 0;
566 unsigned long increment = 0;
567 int precision_loss;
568 int exp;
569 FPU_REG tmp;
570
571 if (st0_tag == TAG_Valid) {
572
573 reg_copy(st0_ptr, &tmp);
574 exp = exponent(&tmp);
575
576 if (exp < SINGLE_Emin) {
577 addexponent(&tmp, -SINGLE_Emin + 23);
578
579 denormal_arg:
580
581 if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
582#ifdef PECULIAR_486
583
584
585
586
587 if (!((tmp.sigl == 0x00800000) &&
588 ((st0_ptr->sigh & 0x000000ff)
589 || st0_ptr->sigl)))
590#endif
591 {
592 EXCEPTION(EX_Underflow);
593
594
595 if (!(control_word & CW_Underflow))
596 return 0;
597 }
598 EXCEPTION(precision_loss);
599 if (!(control_word & CW_Precision))
600 return 0;
601 }
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;
607
608 precision_loss = 1;
609 switch (control_word & CW_RC) {
610 case RC_RND:
611 increment = ((sigh & 0xff) > 0x80)
612 ||(((sigh & 0xff) == 0x80) && sigl)
613 ||((sigh & 0x180) == 0x180);
614 break;
615 case RC_DOWN:
616 increment = signpositive(&tmp)
617 ? 0 : (sigl | (sigh & 0xff));
618 break;
619 case RC_UP:
620 increment = signpositive(&tmp)
621 ? (sigl | (sigh & 0xff)) : 0;
622 break;
623 case RC_CHOP:
624 increment = 0;
625 break;
626 }
627
628
629 tmp.sigl = 0;
630
631 if (increment) {
632 if (sigh >= 0xffffff00) {
633
634 tmp.sigh = 0x80000000;
635 exp++;
636 if (exp >= EXP_OVER)
637 goto overflow;
638 } else {
639 tmp.sigh &= 0xffffff00;
640 tmp.sigh += 0x100;
641 }
642 } else {
643 tmp.sigh &= 0xffffff00;
644 }
645 } else
646 precision_loss = 0;
647
648 templ = (tmp.sigh >> 8) & 0x007fffff;
649
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;
658
659
660
661 templ = 0x7f800000;
662 } else {
663 if (precision_loss) {
664 if (increment)
665 set_precision_flag_up();
666 else
667 set_precision_flag_down();
668 }
669
670 templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
671 }
672 }
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);
679
680
681#ifndef PECULIAR_486
682
683
684
685 if (control_word & CW_Underflow)
686 denormal_operand();
687#endif
688 goto denormal_arg;
689 } else if (st0_tag == TW_Infinity) {
690 templ = 0x7f800000;
691 } else if (st0_tag == TW_NaN) {
692
693 if ((exponent(st0_ptr) == EXP_OVER)
694 && (st0_ptr->sigh & 0x80000000)) {
695
696 templ = st0_ptr->sigh >> 8;
697 if (!(st0_ptr->sigh & 0x40000000)) {
698
699 EXCEPTION(EX_Invalid);
700 if (!(control_word & CW_Invalid))
701 return 0;
702 templ |= (0x40000000 >> 8);
703 }
704 templ |= 0x7f800000;
705 } else {
706
707 EXCEPTION(EX_Invalid);
708 if (!(control_word & CW_Invalid))
709 return 0;
710 templ = 0xffc00000;
711 }
712 }
713#ifdef PARANOID
714 else {
715 EXCEPTION(EX_INTERNAL | 0x164);
716 return 0;
717 }
718#endif
719 } else if (st0_tag == TAG_Empty) {
720
721 EXCEPTION(EX_StackUnder);
722 if (control_word & EX_Invalid) {
723
724
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;
733 }
734#ifdef PARANOID
735 else {
736 EXCEPTION(EX_INTERNAL | 0x163);
737 return 0;
738 }
739#endif
740 if (getsign(st0_ptr))
741 templ |= 0x80000000;
742
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;
747
748 return 1;
749}
750
751
752int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long __user *d)
753{
754 FPU_REG t;
755 long long tll;
756 int precision_loss;
757
758 if (st0_tag == TAG_Empty) {
759
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;
767 }
768 }
769
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
779 invalid_operand:
780 if (control_word & EX_Invalid) {
781
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;
790 }
791
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;
797
798 return 1;
799}
800
801
802int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long __user *d)
803{
804 FPU_REG t;
805 int precision_loss;
806
807 if (st0_tag == TAG_Empty) {
808
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;
816 }
817 }
818
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
826 invalid_operand:
827 if (control_word & EX_Invalid) {
828
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;
837 }
838
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;
843
844 return 1;
845}
846
847
848int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short __user *d)
849{
850 FPU_REG t;
851 int precision_loss;
852
853 if (st0_tag == TAG_Empty) {
854
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;
862 }
863 }
864
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
872 invalid_operand:
873 if (control_word & EX_Invalid) {
874
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;
883 }
884
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;
889
890 return 1;
891}
892
893
894int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char __user *d)
895{
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;
901
902 if (st0_tag == TAG_Empty) {
903
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;
911 }
912 }
913
914 reg_copy(st0_ptr, &t);
915 precision_loss = FPU_round_to_int(&t, st0_tag);
916 ll = significand(&t);
917
918
919 if ((t.sigh > 0x0de0b6b3) ||
920 ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
921 EXCEPTION(EX_Invalid);
922
923 invalid_operand:
924 if (control_word & CW_Invalid) {
925
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);
930 FPU_put_user(0xc0, d + 7);
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
939 set_precision_flag(precision_loss);
940 }
941
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;
951 }
952 RE_ENTRANT_CHECK_OFF;
953 FPU_put_user(sign, d + 9);
954 RE_ENTRANT_CHECK_ON;
955
956 return 1;
957}
958
959
960
961
962
963
964
965
966
967
968
969int FPU_round_to_int(FPU_REG *r, u_char tag)
970{
971 u_char very_big;
972 unsigned eax;
973
974 if (tag == TAG_Zero) {
975
976 significand(r) = 0;
977 return 0;
978 }
979
980 if (exponent(r) > 63) {
981 r->sigl = r->sigh = ~0;
982 return 1;
983 }
984
985 eax = FPU_shrxs(&r->sigl, 63 - exponent(r));
986 very_big = !(~(r->sigh) | ~(r->sigl));
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
993 || (half_or_more && (r->sigl & 1))) {
994 if (very_big)
995 return 1;
996 significand(r)++;
997 return PRECISION_LOST_UP;
998 }
999 break;
1000 case RC_DOWN:
1001 if (frac_part && getsign(r)) {
1002 if (very_big)
1003 return 1;
1004 significand(r)++;
1005 return PRECISION_LOST_UP;
1006 }
1007 break;
1008 case RC_UP:
1009 if (frac_part && !getsign(r)) {
1010 if (very_big)
1011 return 1;
1012 significand(r)++;
1013 return PRECISION_LOST_UP;
1014 }
1015 break;
1016 case RC_CHOP:
1017 break;
1018 }
1019
1020 return eax ? PRECISION_LOST_DOWN : 0;
1021
1022}
1023
1024
1025
1026u_char __user *fldenv(fpu_addr_modes addr_modes, u_char __user *s)
1027{
1028 unsigned short tag_word = 0;
1029 u_char tag;
1030 int i;
1031
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;
1055 }
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;
1074 }
1075
1076#ifdef PECULIAR_486
1077 control_word &= ~0xe080;
1078#endif
1079
1080 top = (partial_status >> SW_Top_Shift) & 7;
1081
1082 if (partial_status & ~control_word & CW_Exceptions)
1083 partial_status |= (SW_Summary | SW_Backward);
1084 else
1085 partial_status &= ~(SW_Summary | SW_Backward);
1086
1087 for (i = 0; i < 8; i++) {
1088 tag = tag_word & 3;
1089 tag_word >>= 2;
1090
1091 if (tag == TAG_Empty)
1092
1093 FPU_settag(i, TAG_Empty);
1094 else if (FPU_gettag(i) == TAG_Empty) {
1095
1096
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);
1111 }
1112
1113
1114 }
1115
1116 return s;
1117}
1118
1119void frstor(fpu_addr_modes addr_modes, u_char __user *data_address)
1120{
1121 int i, regnr;
1122 u_char __user *s = fldenv(addr_modes, data_address);
1123 int offset = (top & 7) * 10, other = 80 - offset;
1124
1125
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;
1132
1133 for (i = 0; i < 8; i++) {
1134 regnr = (i + top) & 7;
1135 if (FPU_gettag(regnr) != TAG_Empty)
1136
1137 FPU_settag(regnr, FPU_tagof(&st(i)));
1138 }
1139
1140}
1141
1142u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d)
1143{
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
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));
1171 }
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
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
1186 if (__copy_to_user(d, &control_word, 7 * 4))
1187 FPU_abort;
1188 RE_ENTRANT_CHECK_ON;
1189 d += 0x1c;
1190 }
1191
1192 control_word |= CW_Exceptions;
1193 partial_status &= ~(SW_Summary | SW_Backward);
1194
1195 return d;
1196}
1197
1198void fsave(fpu_addr_modes addr_modes, u_char __user *data_address)
1199{
1200 u_char __user *d;
1201 int offset = (top & 7) * 10, other = 80 - offset;
1202
1203 d = fstenv(addr_modes, data_address);
1204
1205 RE_ENTRANT_CHECK_OFF;
1206 FPU_access_ok(VERIFY_WRITE, d, 80);
1207
1208
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;
1215
1216 finit();
1217}
1218
1219
1220