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