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