2 /*---------------------------------------------------------------------------+
5 | Rounding/truncation/etc for FPU basic arithmetic functions. |
7 | Copyright (C) 1993,1995,1997 |
8 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
9 | Australia. E-mail billm@suburbia.net |
11 | This code has four possible entry points. |
12 | The following must be entered by a jmp instruction: |
13 | fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. |
15 | The FPU_round entry point is intended to be used by C code. |
17 | int FPU_round(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
19 | Return value is the tag of the answer, or-ed with FPU_Exception if |
20 | one was raised, or -1 on internal error. |
22 | For correct "up" and "down" rounding, the argument must have the correct |
25 +---------------------------------------------------------------------------*/
27 /*---------------------------------------------------------------------------+
28 | Four entry points. |
30 | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: |
31 | %eax:%ebx 64 bit significand |
32 | %edx 32 bit extension of the significand |
33 | %edi pointer to an FPU_REG for the result to be stored |
34 | stack calling function must have set up a C stack frame and |
35 | pushed %esi, %edi, and %ebx |
37 | Needed just for the fpu_reg_round_sqrt entry point: |
38 | %cx A control word in the same format as the FPU control word. |
39 | Otherwise, PARAM4 must give such a value. |
42 | The significand and its extension are assumed to be exact in the |
44 | If the significand by itself is the exact result then the significand |
45 | extension (%edx) must contain 0, otherwise the significand extension |
47 | If the significand extension is non-zero then the significand is |
48 | smaller than the magnitude of the correct exact result by an amount |
49 | greater than zero and less than one ls bit of the significand. |
50 | The significand extension is only required to have three possible |
52 | less than 0x80000000 <=> the significand is less than 1/2 an ls |
53 | bit smaller than the magnitude of the |
54 | true exact result. |
55 | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
56 | smaller than the magnitude of the true |
58 | greater than 0x80000000 <=> the significand is more than 1/2 an ls |
59 | bit smaller than the magnitude of the |
60 | true exact result. |
62 +---------------------------------------------------------------------------*/
64 /*---------------------------------------------------------------------------+
65 | The code in this module has become quite complex, but it should handle |
66 | all of the FPU flags which are set at this stage of the basic arithmetic |
68 | There are a few rare cases where the results are not set identically to |
69 | a real FPU. These require a bit more thought because at this stage the |
70 | results of the code here appear to be more consistent... |
71 | This may be changed in a future version. |
72 +---------------------------------------------------------------------------*/
76 #include "exception.h"
77 #include "control_w.h"
79 /* Flags for FPU_bits_lost */
83 /* Flags for FPU_denormal */
85 #define UNMASKED_UNDERFLOW $2
88 #ifndef NON_REENTRANT_FPU
89 /* Make the code re-entrant by putting
90 local storage on the stack: */
91 #define FPU_bits_lost (%esp)
92 #define FPU_denormal 1(%esp)
95 /* Not re-entrant, so we can gain speed by putting
96 local storage in a static area: */
103 #endif /* NON_REENTRANT_FPU */
108 .globl fpu_Arith_exit
110 /* Entry point when called from C */
123 fpu_reg_round: /* Normal entry point */
126 #ifndef NON_REENTRANT_FPU
127 pushl %ebx /* adjust the stack pointer */
128 #endif /* NON_REENTRANT_FPU */
131 /* Cannot use this here yet */
133 /* jns L_entry_bugged */
134 #endif /* PARANOID */
136 cmpw EXP_UNDER,EXP(%edi)
137 jle L_Make_denorm /* The number is a de-normal */
139 movb $0,FPU_denormal /* 0 -> not a de-normal */
142 movb $0,FPU_bits_lost /* No bits yet lost in rounding */
156 /* With the precision control bits set to 01 "(reserved)", a real 80486
157 behaves as if the precision control bits were set to 11 "64 bits" */
158 cmpl PR_RESERVED_BITS,%ecx
161 jmp L_bugged_denorm_486
162 #endif /* PARANOID */
165 jmp L_bugged_denorm /* There is no bug, just a bad control word */
166 #endif /* PARANOID */
167 #endif /* PECULIAR_486 */
170 /* Round etc to 24 bit precision */
178 je LCheck_truncate_24
180 cmpl RC_UP,%ecx /* Towards +infinity */
183 cmpl RC_DOWN,%ecx /* Towards -infinity */
188 #endif /* PARANOID */
192 jne LCheck_truncate_24 /* If negative then up==truncate */
194 jmp LCheck_24_round_up
198 je LCheck_truncate_24 /* If positive then down==truncate */
202 andl $0x000000ff,%ecx
209 /* Do rounding of the 24th bit if needed (nearest or even) */
211 andl $0x000000ff,%ecx
212 cmpl $0x00000080,%ecx
213 jc LCheck_truncate_24 /* less than half, no increment needed */
215 jne LGreater_Half_24 /* greater than half, increment needed */
217 /* Possibly half, we need to check the ls bits */
219 jnz LGreater_Half_24 /* greater than half, increment needed */
222 jnz LGreater_Half_24 /* greater than half, increment needed */
224 /* Exactly half, increment only if 24th bit is 1 (round to even) */
225 testl $0x00000100,%eax
228 LGreater_Half_24: /* Rounding: increment at the 24th bit */
230 andl $0xffffff00,%eax /* Truncate to 24 bits */
232 movb LOST_UP,FPU_bits_lost
233 addl $0x00000100,%eax
234 jmp LCheck_Round_Overflow
238 andl $0x000000ff,%ecx
241 jz L_Re_normalise /* No truncation needed */
244 andl $0xffffff00,%eax /* Truncate to 24 bits */
246 movb LOST_DOWN,FPU_bits_lost
250 /* Round etc to 53 bit precision */
258 je LCheck_truncate_53
260 cmpl RC_UP,%ecx /* Towards +infinity */
263 cmpl RC_DOWN,%ecx /* Towards -infinity */
268 #endif /* PARANOID */
272 jne LCheck_truncate_53 /* If negative then up==truncate */
274 jmp LCheck_53_round_up
278 je LCheck_truncate_53 /* If positive then down==truncate */
282 andl $0x000007ff,%ecx
288 /* Do rounding of the 53rd bit if needed (nearest or even) */
290 andl $0x000007ff,%ecx
291 cmpl $0x00000400,%ecx
292 jc LCheck_truncate_53 /* less than half, no increment needed */
294 jnz LGreater_Half_53 /* greater than half, increment needed */
296 /* Possibly half, we need to check the ls bits */
298 jnz LGreater_Half_53 /* greater than half, increment needed */
300 /* Exactly half, increment only if 53rd bit is 1 (round to even) */
301 testl $0x00000800,%ebx
304 LGreater_Half_53: /* Rounding: increment at the 53rd bit */
306 movb LOST_UP,FPU_bits_lost
307 andl $0xfffff800,%ebx /* Truncate to 53 bits */
308 addl $0x00000800,%ebx
310 jmp LCheck_Round_Overflow
314 andl $0x000007ff,%ecx
319 movb LOST_DOWN,FPU_bits_lost
320 andl $0xfffff800,%ebx /* Truncate to 53 bits */
324 /* Round etc to 64 bit precision */
332 je LCheck_truncate_64
334 cmpl RC_UP,%ecx /* Towards +infinity */
337 cmpl RC_DOWN,%ecx /* Towards -infinity */
342 #endif /* PARANOID */
346 jne LCheck_truncate_64 /* If negative then up==truncate */
354 je LCheck_truncate_64 /* If positive then down==truncate */
361 cmpl $0x80000000,%edx
362 jc LCheck_truncate_64
366 /* Now test for round-to-even */
368 jz LCheck_truncate_64
371 movb LOST_UP,FPU_bits_lost
375 LCheck_Round_Overflow:
378 /* Overflow, adjust the result (significand to 1.0) */
389 movb LOST_DOWN,FPU_bits_lost
392 testb $0xff,FPU_denormal
399 cmpb LOST_UP,FPU_bits_lost
400 je L_precision_lost_up
402 cmpb LOST_DOWN,FPU_bits_lost
403 je L_precision_lost_down
406 /* store the result */
412 cmpw EXP_OVER,EXP(%edi)
417 /* Convert the exponent to 80x87 form. */
418 addw EXTENDED_Ebias,EXP(%edi)
419 andw $0x7fff,EXP(%edi)
421 fpu_reg_round_signed_special_exit:
424 je fpu_reg_round_special_exit
426 orw $0x8000,EXP(%edi) /* Negative sign for the result. */
428 fpu_reg_round_special_exit:
430 #ifndef NON_REENTRANT_FPU
431 popl %ebx /* adjust the stack pointer */
432 #endif /* NON_REENTRANT_FPU */
443 * Set the FPU status flags to represent precision loss due to
449 call set_precision_flag_up
452 jmp L_no_precision_loss
455 * Set the FPU status flags to represent precision loss due to
458 L_precision_lost_down:
461 call set_precision_flag_down
464 jmp L_no_precision_loss
468 * The number is a denormal (which might get rounded up to a normal)
469 * Shift the number right the required number of bits, which will
470 * have to be undone later...
473 /* The action to be taken depends upon whether the underflow
474 exception is masked */
475 testb CW_Underflow,%cl /* Underflow mask. */
476 jz Unmasked_underflow /* Do not make a denormal. */
478 movb DENORMAL,FPU_denormal
480 pushl %ecx /* Save */
484 cmpw $64,%cx /* shrd only works for 0..31 bits */
485 jnc Denorm_shift_more_than_63
487 cmpw $32,%cx /* shrd only works for 0..31 bits */
488 jnc Denorm_shift_more_than_32
491 * We got here without jumps by assuming that the most common requirement
492 * is for a small de-normalising shift.
493 * Shift by [1..31] bits
496 orl %edx,%edx /* extension */
497 setne %ch /* Save whether %edx is non-zero */
506 /* Shift by [32..63] bits */
507 Denorm_shift_more_than_32:
517 orl %edx,%edx /* test these 32 bits */
527 /* Shift by [64..) bits */
528 Denorm_shift_more_than_63:
530 jne Denorm_shift_more_than_64
532 /* Exactly 64 bit shift */
547 Denorm_shift_more_than_64:
548 movw EXP_UNDER+1,EXP(%edi)
549 /* This is easy, %eax must be non-zero, so.. */
558 movb UNMASKED_UNDERFLOW,FPU_denormal
562 /* Undo the de-normalisation. */
564 cmpb UNMASKED_UNDERFLOW,FPU_denormal
567 /* The number must be a denormal if we got here. */
569 /* But check it... just in case. */
570 cmpw EXP_UNDER+1,EXP(%edi)
572 #endif /* PARANOID */
576 * This implements a special feature of 80486 behaviour.
577 * Underflow will be signalled even if the number is
578 * not a denormal after rounding.
579 * This difference occurs only for masked underflow, and not
580 * in the unmasked case.
581 * Actual 80486 behaviour differs from this in some circumstances.
583 orl %eax,%eax /* ms bits */
584 js LPseudoDenormal /* Will be masked underflow */
586 orl %eax,%eax /* ms bits */
587 js L_Normalised /* No longer a denormal */
588 #endif /* PECULIAR_486 */
590 jnz LDenormal_adj_exponent
593 jz L_underflow_to_zero /* The contents are zero */
595 LDenormal_adj_exponent:
599 testb $0xff,FPU_bits_lost /* bits lost == underflow */
600 movl TAG_Special,%edx
603 /* There must be a masked underflow */
609 movl TAG_Special,%edx
614 * The operations resulted in a number too small to represent.
619 call set_precision_flag_down
628 /* Reduce the exponent to EXP_UNDER */
629 movw EXP_UNDER,EXP(%edi)
631 jmp L_Store_significand
634 /* The operations resulted in a number too large to represent. */
636 addw EXTENDED_Ebias,EXP(%edi) /* Set for unmasked response. */
640 jmp fpu_reg_round_signed_special_exit
644 /* The number may have been changed to a non-denormal */
645 /* by the rounding operations. */
646 cmpw EXP_UNDER,EXP(%edi)
647 jle Do_unmasked_underflow
651 Do_unmasked_underflow:
652 /* Increase the exponent by the magic number */
653 addw $(3*(1<<13)),EXP(%edi)
665 pushl EX_INTERNAL|0x236
671 pushl EX_INTERNAL|0x230
675 #endif /* PECULIAR_486 */
678 pushl EX_INTERNAL|0x231
684 pushl EX_INTERNAL|0x232
690 pushl EX_INTERNAL|0x233
696 pushl EX_INTERNAL|0x234
702 pushl EX_INTERNAL|0x235
707 jmp fpu_reg_round_special_exit
708 #endif /* PARANOID */