1 /* SPDX-License-Identifier: GPL-2.0 */
3 /*---------------------------------------------------------------------------+
6 | Rounding/truncation/etc for FPU basic arithmetic functions. |
8 | Copyright (C) 1993,1995,1997 |
9 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
10 | Australia. E-mail billm@suburbia.net |
12 | This code has four possible entry points. |
13 | The following must be entered by a jmp instruction: |
14 | fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. |
16 | The FPU_round entry point is intended to be used by C code. |
18 | int FPU_round(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
20 | Return value is the tag of the answer, or-ed with FPU_Exception if |
21 | one was raised, or -1 on internal error. |
23 | For correct "up" and "down" rounding, the argument must have the correct |
26 +---------------------------------------------------------------------------*/
28 /*---------------------------------------------------------------------------+
29 | Four entry points. |
31 | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: |
32 | %eax:%ebx 64 bit significand |
33 | %edx 32 bit extension of the significand |
34 | %edi pointer to an FPU_REG for the result to be stored |
35 | stack calling function must have set up a C stack frame and |
36 | pushed %esi, %edi, and %ebx |
38 | Needed just for the fpu_reg_round_sqrt entry point: |
39 | %cx A control word in the same format as the FPU control word. |
40 | Otherwise, PARAM4 must give such a value. |
43 | The significand and its extension are assumed to be exact in the |
45 | If the significand by itself is the exact result then the significand |
46 | extension (%edx) must contain 0, otherwise the significand extension |
48 | If the significand extension is non-zero then the significand is |
49 | smaller than the magnitude of the correct exact result by an amount |
50 | greater than zero and less than one ls bit of the significand. |
51 | The significand extension is only required to have three possible |
53 | less than 0x80000000 <=> the significand is less than 1/2 an ls |
54 | bit smaller than the magnitude of the |
55 | true exact result. |
56 | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
57 | smaller than the magnitude of the true |
59 | greater than 0x80000000 <=> the significand is more than 1/2 an ls |
60 | bit smaller than the magnitude of the |
61 | true exact result. |
63 +---------------------------------------------------------------------------*/
65 /*---------------------------------------------------------------------------+
66 | The code in this module has become quite complex, but it should handle |
67 | all of the FPU flags which are set at this stage of the basic arithmetic |
69 | There are a few rare cases where the results are not set identically to |
70 | a real FPU. These require a bit more thought because at this stage the |
71 | results of the code here appear to be more consistent... |
72 | This may be changed in a future version. |
73 +---------------------------------------------------------------------------*/
77 #include "exception.h"
78 #include "control_w.h"
80 /* Flags for FPU_bits_lost */
84 /* Flags for FPU_denormal */
86 #define UNMASKED_UNDERFLOW $2
89 #ifndef NON_REENTRANT_FPU
90 /* Make the code re-entrant by putting
91 local storage on the stack: */
92 #define FPU_bits_lost (%esp)
93 #define FPU_denormal 1(%esp)
96 /* Not re-entrant, so we can gain speed by putting
97 local storage in a static area: */
104 #endif /* NON_REENTRANT_FPU */
109 .globl fpu_Arith_exit
111 /* Entry point when called from C */
124 fpu_reg_round: /* Normal entry point */
127 #ifndef NON_REENTRANT_FPU
128 pushl %ebx /* adjust the stack pointer */
129 #endif /* NON_REENTRANT_FPU */
132 /* Cannot use this here yet */
134 /* jns L_entry_bugged */
135 #endif /* PARANOID */
137 cmpw EXP_UNDER,EXP(%edi)
138 jle L_Make_denorm /* The number is a de-normal */
140 movb $0,FPU_denormal /* 0 -> not a de-normal */
143 movb $0,FPU_bits_lost /* No bits yet lost in rounding */
157 /* With the precision control bits set to 01 "(reserved)", a real 80486
158 behaves as if the precision control bits were set to 11 "64 bits" */
159 cmpl PR_RESERVED_BITS,%ecx
162 jmp L_bugged_denorm_486
163 #endif /* PARANOID */
166 jmp L_bugged_denorm /* There is no bug, just a bad control word */
167 #endif /* PARANOID */
168 #endif /* PECULIAR_486 */
171 /* Round etc to 24 bit precision */
179 je LCheck_truncate_24
181 cmpl RC_UP,%ecx /* Towards +infinity */
184 cmpl RC_DOWN,%ecx /* Towards -infinity */
189 #endif /* PARANOID */
193 jne LCheck_truncate_24 /* If negative then up==truncate */
195 jmp LCheck_24_round_up
199 je LCheck_truncate_24 /* If positive then down==truncate */
203 andl $0x000000ff,%ecx
210 /* Do rounding of the 24th bit if needed (nearest or even) */
212 andl $0x000000ff,%ecx
213 cmpl $0x00000080,%ecx
214 jc LCheck_truncate_24 /* less than half, no increment needed */
216 jne LGreater_Half_24 /* greater than half, increment needed */
218 /* Possibly half, we need to check the ls bits */
220 jnz LGreater_Half_24 /* greater than half, increment needed */
223 jnz LGreater_Half_24 /* greater than half, increment needed */
225 /* Exactly half, increment only if 24th bit is 1 (round to even) */
226 testl $0x00000100,%eax
229 LGreater_Half_24: /* Rounding: increment at the 24th bit */
231 andl $0xffffff00,%eax /* Truncate to 24 bits */
233 movb LOST_UP,FPU_bits_lost
234 addl $0x00000100,%eax
235 jmp LCheck_Round_Overflow
239 andl $0x000000ff,%ecx
242 jz L_Re_normalise /* No truncation needed */
245 andl $0xffffff00,%eax /* Truncate to 24 bits */
247 movb LOST_DOWN,FPU_bits_lost
251 /* Round etc to 53 bit precision */
259 je LCheck_truncate_53
261 cmpl RC_UP,%ecx /* Towards +infinity */
264 cmpl RC_DOWN,%ecx /* Towards -infinity */
269 #endif /* PARANOID */
273 jne LCheck_truncate_53 /* If negative then up==truncate */
275 jmp LCheck_53_round_up
279 je LCheck_truncate_53 /* If positive then down==truncate */
283 andl $0x000007ff,%ecx
289 /* Do rounding of the 53rd bit if needed (nearest or even) */
291 andl $0x000007ff,%ecx
292 cmpl $0x00000400,%ecx
293 jc LCheck_truncate_53 /* less than half, no increment needed */
295 jnz LGreater_Half_53 /* greater than half, increment needed */
297 /* Possibly half, we need to check the ls bits */
299 jnz LGreater_Half_53 /* greater than half, increment needed */
301 /* Exactly half, increment only if 53rd bit is 1 (round to even) */
302 testl $0x00000800,%ebx
305 LGreater_Half_53: /* Rounding: increment at the 53rd bit */
307 movb LOST_UP,FPU_bits_lost
308 andl $0xfffff800,%ebx /* Truncate to 53 bits */
309 addl $0x00000800,%ebx
311 jmp LCheck_Round_Overflow
315 andl $0x000007ff,%ecx
320 movb LOST_DOWN,FPU_bits_lost
321 andl $0xfffff800,%ebx /* Truncate to 53 bits */
325 /* Round etc to 64 bit precision */
333 je LCheck_truncate_64
335 cmpl RC_UP,%ecx /* Towards +infinity */
338 cmpl RC_DOWN,%ecx /* Towards -infinity */
343 #endif /* PARANOID */
347 jne LCheck_truncate_64 /* If negative then up==truncate */
355 je LCheck_truncate_64 /* If positive then down==truncate */
362 cmpl $0x80000000,%edx
363 jc LCheck_truncate_64
367 /* Now test for round-to-even */
369 jz LCheck_truncate_64
372 movb LOST_UP,FPU_bits_lost
376 LCheck_Round_Overflow:
379 /* Overflow, adjust the result (significand to 1.0) */
390 movb LOST_DOWN,FPU_bits_lost
393 testb $0xff,FPU_denormal
400 cmpb LOST_UP,FPU_bits_lost
401 je L_precision_lost_up
403 cmpb LOST_DOWN,FPU_bits_lost
404 je L_precision_lost_down
407 /* store the result */
413 cmpw EXP_OVER,EXP(%edi)
418 /* Convert the exponent to 80x87 form. */
419 addw EXTENDED_Ebias,EXP(%edi)
420 andw $0x7fff,EXP(%edi)
422 fpu_reg_round_signed_special_exit:
425 je fpu_reg_round_special_exit
427 orw $0x8000,EXP(%edi) /* Negative sign for the result. */
429 fpu_reg_round_special_exit:
431 #ifndef NON_REENTRANT_FPU
432 popl %ebx /* adjust the stack pointer */
433 #endif /* NON_REENTRANT_FPU */
444 * Set the FPU status flags to represent precision loss due to
450 call set_precision_flag_up
453 jmp L_no_precision_loss
456 * Set the FPU status flags to represent precision loss due to
459 L_precision_lost_down:
462 call set_precision_flag_down
465 jmp L_no_precision_loss
469 * The number is a denormal (which might get rounded up to a normal)
470 * Shift the number right the required number of bits, which will
471 * have to be undone later...
474 /* The action to be taken depends upon whether the underflow
475 exception is masked */
476 testb CW_Underflow,%cl /* Underflow mask. */
477 jz Unmasked_underflow /* Do not make a denormal. */
479 movb DENORMAL,FPU_denormal
481 pushl %ecx /* Save */
485 cmpw $64,%cx /* shrd only works for 0..31 bits */
486 jnc Denorm_shift_more_than_63
488 cmpw $32,%cx /* shrd only works for 0..31 bits */
489 jnc Denorm_shift_more_than_32
492 * We got here without jumps by assuming that the most common requirement
493 * is for a small de-normalising shift.
494 * Shift by [1..31] bits
497 orl %edx,%edx /* extension */
498 setne %ch /* Save whether %edx is non-zero */
507 /* Shift by [32..63] bits */
508 Denorm_shift_more_than_32:
518 orl %edx,%edx /* test these 32 bits */
528 /* Shift by [64..) bits */
529 Denorm_shift_more_than_63:
531 jne Denorm_shift_more_than_64
533 /* Exactly 64 bit shift */
548 Denorm_shift_more_than_64:
549 movw EXP_UNDER+1,EXP(%edi)
550 /* This is easy, %eax must be non-zero, so.. */
559 movb UNMASKED_UNDERFLOW,FPU_denormal
563 /* Undo the de-normalisation. */
565 cmpb UNMASKED_UNDERFLOW,FPU_denormal
568 /* The number must be a denormal if we got here. */
570 /* But check it... just in case. */
571 cmpw EXP_UNDER+1,EXP(%edi)
573 #endif /* PARANOID */
577 * This implements a special feature of 80486 behaviour.
578 * Underflow will be signalled even if the number is
579 * not a denormal after rounding.
580 * This difference occurs only for masked underflow, and not
581 * in the unmasked case.
582 * Actual 80486 behaviour differs from this in some circumstances.
584 orl %eax,%eax /* ms bits */
585 js LPseudoDenormal /* Will be masked underflow */
587 orl %eax,%eax /* ms bits */
588 js L_Normalised /* No longer a denormal */
589 #endif /* PECULIAR_486 */
591 jnz LDenormal_adj_exponent
594 jz L_underflow_to_zero /* The contents are zero */
596 LDenormal_adj_exponent:
600 testb $0xff,FPU_bits_lost /* bits lost == underflow */
601 movl TAG_Special,%edx
604 /* There must be a masked underflow */
610 movl TAG_Special,%edx
615 * The operations resulted in a number too small to represent.
620 call set_precision_flag_down
629 /* Reduce the exponent to EXP_UNDER */
630 movw EXP_UNDER,EXP(%edi)
632 jmp L_Store_significand
635 /* The operations resulted in a number too large to represent. */
637 addw EXTENDED_Ebias,EXP(%edi) /* Set for unmasked response. */
641 jmp fpu_reg_round_signed_special_exit
645 /* The number may have been changed to a non-denormal */
646 /* by the rounding operations. */
647 cmpw EXP_UNDER,EXP(%edi)
648 jle Do_unmasked_underflow
652 Do_unmasked_underflow:
653 /* Increase the exponent by the magic number */
654 addw $(3*(1<<13)),EXP(%edi)
666 pushl EX_INTERNAL|0x236
672 pushl EX_INTERNAL|0x230
676 #endif /* PECULIAR_486 */
679 pushl EX_INTERNAL|0x231
685 pushl EX_INTERNAL|0x232
691 pushl EX_INTERNAL|0x233
697 pushl EX_INTERNAL|0x234
703 pushl EX_INTERNAL|0x235
708 jmp fpu_reg_round_special_exit
709 #endif /* PARANOID */