2 /*---------------------------------------------------------------------------+
5 | Rounding/truncation/etc for FPU basic arithmetic functions. |
8 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
9 | Australia. E-mail billm@vaxc.cc.monash.edu.au |
11 | This code has four possible entry points. |
12 | The following must be entered by a jmp intruction: |
13 | fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. |
15 | The _round_reg entry point is intended to be used by C code. |
17 | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
19 | For correct "up" and "down" rounding, the argument must have the correct |
22 +---------------------------------------------------------------------------*/
24 /*---------------------------------------------------------------------------+
25 | Four entry points. |
27 | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: |
28 | %eax:%ebx 64 bit significand |
29 | %edx 32 bit extension of the significand |
30 | %edi pointer to an FPU_REG for the result to be stored |
31 | stack calling function must have set up a C stack frame and |
32 | pushed %esi, %edi, and %ebx |
34 | Needed just for the fpu_reg_round_sqrt entry point: |
35 | %cx A control word in the same format as the FPU control word. |
36 | Otherwise, PARAM4 must give such a value. |
39 | The significand and its extension are assumed to be exact in the |
41 | If the significand by itself is the exact result then the significand |
42 | extension (%edx) must contain 0, otherwise the significand extension |
44 | If the significand extension is non-zero then the significand is |
45 | smaller than the magnitude of the correct exact result by an amount |
46 | greater than zero and less than one ls bit of the significand. |
47 | The significand extension is only required to have three possible |
49 | less than 0x80000000 <=> the significand is less than 1/2 an ls |
50 | bit smaller than the magnitude of the |
51 | true exact result. |
52 | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
53 | smaller than the magnitude of the true |
55 | greater than 0x80000000 <=> the significand is more than 1/2 an ls |
56 | bit smaller than the magnitude of the |
57 | true exact result. |
59 +---------------------------------------------------------------------------*/
61 /*---------------------------------------------------------------------------+
62 | The code in this module has become quite complex, but it should handle |
63 | all of the FPU flags which are set at this stage of the basic arithmetic |
65 | There are a few rare cases where the results are not set identically to |
66 | a real FPU. These require a bit more thought because at this stage the |
67 | results of the code here appear to be more consistent... |
68 | This may be changed in a future version. |
69 +---------------------------------------------------------------------------*/
73 #include "exception.h"
74 #include "control_w.h"
76 /* Flags for FPU_bits_lost */
80 /* Flags for FPU_denormal */
82 #define UNMASKED_UNDERFLOW $2
86 /* Make the code re-entrant by putting
87 local storage on the stack: */
88 #define FPU_bits_lost (%esp)
89 #define FPU_denormal 1(%esp)
92 /* Not re-entrant, so we can gain speed by putting
93 local storage in a static area: */
106 .globl fpu_reg_round_sqrt
107 .globl fpu_Arith_exit
110 /* Entry point when called from C */
123 jmp fpu_reg_round_sqrt
125 fpu_reg_round: /* Normal entry point */
128 fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */
131 pushl %ebx /* adjust the stack pointer */
135 /* Cannot use this here yet */
137 /* jns L_entry_bugged */
140 cmpl EXP_UNDER,EXP(%edi)
141 jle xMake_denorm /* The number is a de-normal */
143 movb $0,FPU_denormal /* 0 -> not a de-normal */
146 movb $0,FPU_bits_lost /* No bits yet lost in rounding */
160 jmp L_bugged /* There is no bug, just a bad control word */
164 /* Round etc to 24 bit precision */
172 je LCheck_truncate_24
174 cmpl RC_UP,%ecx /* Towards +infinity */
177 cmpl RC_DOWN,%ecx /* Towards -infinity */
185 cmpb SIGN_POS,SIGN(%edi)
186 jne LCheck_truncate_24 /* If negative then up==truncate */
188 jmp LCheck_24_round_up
191 cmpb SIGN_POS,SIGN(%edi)
192 je LCheck_truncate_24 /* If positive then down==truncate */
196 andl $0x000000ff,%ecx
203 /* Do rounding of the 24th bit if needed (nearest or even) */
205 andl $0x000000ff,%ecx
206 cmpl $0x00000080,%ecx
207 jc LCheck_truncate_24 /* less than half, no increment needed */
209 jne LGreater_Half_24 /* greater than half, increment needed */
211 /* Possibly half, we need to check the ls bits */
213 jnz LGreater_Half_24 /* greater than half, increment needed */
216 jnz LGreater_Half_24 /* greater than half, increment needed */
218 /* Exactly half, increment only if 24th bit is 1 (round to even) */
219 testl $0x00000100,%eax
222 LGreater_Half_24: /* Rounding: increment at the 24th bit */
224 andl $0xffffff00,%eax /* Truncate to 24 bits */
226 movb LOST_UP,FPU_bits_lost
227 addl $0x00000100,%eax
228 jmp LCheck_Round_Overflow
232 andl $0x000000ff,%ecx
235 jz LRe_normalise /* No truncation needed */
238 andl $0xffffff00,%eax /* Truncate to 24 bits */
240 movb LOST_DOWN,FPU_bits_lost
244 /* Round etc to 53 bit precision */
252 je LCheck_truncate_53
254 cmpl RC_UP,%ecx /* Towards +infinity */
257 cmpl RC_DOWN,%ecx /* Towards -infinity */
265 cmpb SIGN_POS,SIGN(%edi)
266 jne LCheck_truncate_53 /* If negative then up==truncate */
268 jmp LCheck_53_round_up
271 cmpb SIGN_POS,SIGN(%edi)
272 je LCheck_truncate_53 /* If positive then down==truncate */
276 andl $0x000007ff,%ecx
282 /* Do rounding of the 53rd bit if needed (nearest or even) */
284 andl $0x000007ff,%ecx
285 cmpl $0x00000400,%ecx
286 jc LCheck_truncate_53 /* less than half, no increment needed */
288 jnz LGreater_Half_53 /* greater than half, increment needed */
290 /* Possibly half, we need to check the ls bits */
292 jnz LGreater_Half_53 /* greater than half, increment needed */
294 /* Exactly half, increment only if 53rd bit is 1 (round to even) */
295 testl $0x00000800,%ebx
298 LGreater_Half_53: /* Rounding: increment at the 53rd bit */
300 movb LOST_UP,FPU_bits_lost
301 andl $0xfffff800,%ebx /* Truncate to 53 bits */
302 addl $0x00000800,%ebx
304 jmp LCheck_Round_Overflow
308 andl $0x000007ff,%ecx
313 movb LOST_DOWN,FPU_bits_lost
314 andl $0xfffff800,%ebx /* Truncate to 53 bits */
318 /* Round etc to 64 bit precision */
326 je LCheck_truncate_64
328 cmpl RC_UP,%ecx /* Towards +infinity */
331 cmpl RC_DOWN,%ecx /* Towards -infinity */
339 cmpb SIGN_POS,SIGN(%edi)
340 jne LCheck_truncate_64 /* If negative then up==truncate */
347 cmpb SIGN_POS,SIGN(%edi)
348 je LCheck_truncate_64 /* If positive then down==truncate */
355 cmpl $0x80000000,%edx
356 jc LCheck_truncate_64
360 /* Now test for round-to-even */
362 jz LCheck_truncate_64
365 movb LOST_UP,FPU_bits_lost
369 LCheck_Round_Overflow:
372 /* Overflow, adjust the result (significand to 1.0) */
383 movb LOST_DOWN,FPU_bits_lost
386 testb $0xff,FPU_denormal
387 jnz xNormalise_result
390 cmpb LOST_UP,FPU_bits_lost
391 je xL_precision_lost_up
393 cmpb LOST_DOWN,FPU_bits_lost
394 je xL_precision_lost_down
396 xL_no_precision_loss:
397 /* store the result */
398 movb TW_Valid,TAG(%edi)
400 xL_Store_significand:
404 xorl %eax,%eax /* No errors detected. */
406 cmpl EXP_OVER,EXP(%edi)
411 popl %ebx /* adjust the stack pointer */
423 * Set the FPU status flags to represent precision loss due to
426 xL_precision_lost_up:
428 call _set_precision_flag_up
430 jmp xL_no_precision_loss
433 * Set the FPU status flags to represent precision loss due to
436 xL_precision_lost_down:
438 call _set_precision_flag_down
440 jmp xL_no_precision_loss
444 * The number is a denormal (which might get rounded up to a normal)
445 * Shift the number right the required number of bits, which will
446 * have to be undone later...
449 /* The action to be taken depends upon whether the underflow
450 exception is masked */
451 testb CW_Underflow,%cl /* Underflow mask. */
452 jz xUnmasked_underflow /* Do not make a denormal. */
454 movb DENORMAL,FPU_denormal
456 pushl %ecx /* Save */
457 movl EXP_UNDER+1,%ecx
460 cmpl $64,%ecx /* shrd only works for 0..31 bits */
461 jnc xDenorm_shift_more_than_63
463 cmpl $32,%ecx /* shrd only works for 0..31 bits */
464 jnc xDenorm_shift_more_than_32
467 * We got here without jumps by assuming that the most common requirement
468 * is for a small de-normalising shift.
469 * Shift by [1..31] bits
472 orl %edx,%edx /* extension */
473 setne %ch /* Save whether %edx is non-zero */
482 /* Shift by [32..63] bits */
483 xDenorm_shift_more_than_32:
493 orl %edx,%edx /* test these 32 bits */
503 /* Shift by [64..) bits */
504 xDenorm_shift_more_than_63:
506 jne xDenorm_shift_more_than_64
508 /* Exactly 64 bit shift */
523 xDenorm_shift_more_than_64:
524 movl EXP_UNDER+1,EXP(%edi)
525 /* This is easy, %eax must be non-zero, so.. */
534 movb UNMASKED_UNDERFLOW,FPU_denormal
538 /* Undo the de-normalisation. */
540 cmpb UNMASKED_UNDERFLOW,FPU_denormal
543 /* The number must be a denormal if we got here. */
545 /* But check it... just in case. */
546 cmpl EXP_UNDER+1,EXP(%edi)
552 * This implements a special feature of 80486 behaviour.
553 * Underflow will be signalled even if the number is
554 * not a denormal after rounding.
555 * This difference occurs only for masked underflow, and not
556 * in the unmasked case.
557 * Actual 80486 behaviour differs from this in some circumstances.
559 orl %eax,%eax /* ms bits */
560 js LNormalise_shift_done /* Will be masked underflow */
563 orl %eax,%eax /* ms bits */
564 js xL_Normalised /* No longer a denormal */
566 jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits */
569 jz L_underflow_to_zero /* The contents are zero */
571 /* Shift left 32 - 63 bits */
576 LNormalise_shift_up_to_31:
577 bsrl %eax,%ecx /* get the required shift in %ecx */
584 LNormalise_shift_done:
585 testb $0xff,FPU_bits_lost /* bits lost == underflow */
588 /* There must be a masked underflow */
598 * The operations resulted in a number too small to represent.
603 call _set_precision_flag_down
612 /* Reduce the exponent to EXP_UNDER */
613 movl EXP_UNDER,EXP(%edi)
614 movb TW_Zero,TAG(%edi)
615 jmp xL_Store_significand
618 /* The operations resulted in a number too large to represent. */
623 jmp fpu_reg_round_exit
627 /* The number may have been changed to a non-denormal */
628 /* by the rounding operations. */
629 cmpl EXP_UNDER,EXP(%edi)
630 jle xDo_unmasked_underflow
634 xDo_unmasked_underflow:
635 /* Increase the exponent by the magic number */
636 addl $(3*(1<<13)),EXP(%edi)
646 /* If we ever get here then we have problems! */
648 pushl EX_INTERNAL|0x201
654 pushl EX_INTERNAL|0x216
660 pushl EX_INTERNAL|0x217
665 jmp fpu_reg_round_exit