Merge from vendor branch BINUTILS:
[dragonfly.git] / sys / i386 / gnu / fpemul / reg_round.s
1         .file "reg_round.S"
2 /*
3  *  reg_round.S
4  *
5  * Rounding/truncation/etc for FPU basic arithmetic functions.
6  *
7  * This code has four possible entry points.
8  * The following must be entered by a jmp intruction:
9  *   FPU_round, FPU_round_sqrt, and FPU_Arith_exit.
10  *
11  * The _round_reg entry point is intended to be used by C code.
12  * From C, call as:
13  * void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w)
14  *
15  *
16  * Copyright (C) 1992,1993,1994
17  *                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
18  *                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au
19  * All rights reserved.
20  *
21  * This copyright notice covers the redistribution and use of the
22  * FPU emulator developed by W. Metzenthen. It covers only its use
23  * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
24  * use is not permitted under this copyright.
25  *
26  * Redistribution and use in source and binary forms, with or without
27  * modification, are permitted provided that the following conditions
28  * are met:
29  * 1. Redistributions of source code must retain the above copyright
30  *    notice, this list of conditions and the following disclaimer.
31  * 2. Redistributions in binary form must include information specifying
32  *    that source code for the emulator is freely available and include
33  *    either:
34  *      a) an offer to provide the source code for a nominal distribution
35  *         fee, or
36  *      b) list at least two alternative methods whereby the source
37  *         can be obtained, e.g. a publically accessible bulletin board
38  *         and an anonymous ftp site from which the software can be
39  *         downloaded.
40  * 3. All advertising materials specifically mentioning features or use of
41  *    this emulator must acknowledge that it was developed by W. Metzenthen.
42  * 4. The name of W. Metzenthen may not be used to endorse or promote
43  *    products derived from this software without specific prior written
44  *    permission.
45  *
46  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
47  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
48  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
49  * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
50  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56  *
57  *
58  * The purpose of this copyright, based upon the Berkeley copyright, is to
59  * ensure that the covered software remains freely available to everyone.
60  *
61  * The software (with necessary differences) is also available, but under
62  * the terms of the GNU copyleft, for the Linux operating system and for
63  * the djgpp ms-dos extender.
64  *
65  * W. Metzenthen   June 1994.
66  *
67  *
68  * $FreeBSD: src/sys/gnu/i386/fpemul/reg_round.s,v 1.9.2.1 2000/07/07 00:38:42 obrien Exp $
69  * $DragonFly: src/sys/i386/gnu/fpemul/Attic/reg_round.s,v 1.4 2003/08/07 21:17:20 dillon Exp $
70  *
71  */
72
73
74 /*---------------------------------------------------------------------------+
75  | Four entry points.                                                        |
76  |                                                                           |
77  | Needed by both the FPU_round and FPU_round_sqrt entry points:             |
78  |  %eax:%ebx  64 bit significand                                            |
79  |  %edx       32 bit extension of the significand                           |
80  |  %edi       pointer to an FPU_REG for the result to be stored             |
81  |  stack      calling function must have set up a C stack frame and         |
82  |             pushed %esi, %edi, and %ebx                                   |
83  |                                                                           |
84  | Needed just for the FPU_round_sqrt entry point:                           |
85  |  %cx  A control word in the same format as the FPU control word.          |
86  | Otherwise, PARAM4 must give such a value.                                 |
87  |                                                                           |
88  |                                                                           |
89  | The significand and its extension are assumed to be exact in the          |
90  | following sense:                                                          |
91  |   If the significand by itself is the exact result then the significand   |
92  |   extension (%edx) must contain 0, otherwise the significand extension    |
93  |   must be non-zero.                                                       |
94  |   If the significand extension is non-zero then the significand is        |
95  |   smaller than the magnitude of the correct exact result by an amount     |
96  |   greater than zero and less than one ls bit of the significand.          |
97  |   The significand extension is only required to have three possible       |
98  |   non-zero values:                                                        |
99  |       less than 0x80000000  <=> the significand is less than 1/2 an ls    |
100  |                                 bit smaller than the magnitude of the     |
101  |                                 true exact result.                        |
102  |         exactly 0x80000000  <=> the significand is exactly 1/2 an ls bit  |
103  |                                 smaller than the magnitude of the true    |
104  |                                 exact result.                             |
105  |    greater than 0x80000000  <=> the significand is more than 1/2 an ls    |
106  |                                 bit smaller than the magnitude of the     |
107  |                                 true exact result.                        |
108  |                                                                           |
109  +---------------------------------------------------------------------------*/
110
111 /*---------------------------------------------------------------------------+
112  |  The code in this module has become quite complex, but it should handle   |
113  |  all of the FPU flags which are set at this stage of the basic arithmetic |
114  |  computations.                                                            |
115  |  There are a few rare cases where the results are not set identically to  |
116  |  a real FPU. These require a bit more thought because at this stage the   |
117  |  results of the code here appear to be more consistent...                 |
118  |  This may be changed in a future version.                                 |
119  +---------------------------------------------------------------------------*/
120
121
122 #include "exception.h"
123 #include "control_w.h"
124
125 #define LOST_DOWN       $1
126 #define LOST_UP         $2
127 #define DENORMAL        $1
128 #define UNMASKED_UNDERFLOW $2
129
130 .data
131         ALIGN_DATA
132 FPU_bits_lost:
133         .byte   0
134 FPU_denormal:
135         .byte   0
136
137 .text
138 .globl FPU_round
139 .globl FPU_round_sqrt
140 .globl FPU_Arith_exit
141
142 /* Entry point when called from C */
143 ENTRY(round_reg)
144         pushl   %ebp
145         movl    %esp,%ebp
146         pushl   %esi
147         pushl   %edi
148         pushl   %ebx
149
150         movl    PARAM1,%edi
151         movl    SIGH(%edi),%eax
152         movl    SIGL(%edi),%ebx
153         movl    PARAM2,%edx
154         movl    PARAM3,%ecx
155         jmp     FPU_round_sqrt
156
157 FPU_round:              /* Normal entry point */
158         movl    PARAM4,%ecx
159
160 FPU_round_sqrt:         /* Entry point from wm_sqrt.S */
161
162 #ifdef PARANOID
163 /* Cannot use this here yet */
164 /*      orl     %eax,%eax */
165 /*      jns     L_entry_bugged */
166 #endif PARANOID
167
168         cmpl    EXP_UNDER,EXP(%edi)
169         jle     xMake_denorm                    /* The number is a de-normal*/
170
171         movb    $0,FPU_denormal                 /* 0 -> not a de-normal*/
172
173 xDenorm_done:
174         movb    $0,FPU_bits_lost                /*No bits yet lost in rounding*/
175
176         movl    %ecx,%esi
177         andl    CW_PC,%ecx
178         cmpl    PR_64_BITS,%ecx
179         je      LRound_To_64
180
181         cmpl    PR_53_BITS,%ecx
182         je      LRound_To_53
183
184         cmpl    PR_24_BITS,%ecx
185         je      LRound_To_24
186
187 #ifdef PARANOID
188         jmp     L_bugged        /* There is no bug, just a bad control word */
189 #endif PARANOID
190
191
192 /* Round etc to 24 bit precision */
193 LRound_To_24:
194         movl    %esi,%ecx
195         andl    CW_RC,%ecx
196         cmpl    RC_RND,%ecx
197         je      LRound_nearest_24
198
199         cmpl    RC_CHOP,%ecx
200         je      LCheck_truncate_24
201
202         cmpl    RC_UP,%ecx              /* Towards +infinity */
203         je      LUp_24
204
205         cmpl    RC_DOWN,%ecx            /* Towards -infinity */
206         je      LDown_24
207
208 #ifdef PARANOID
209         jmp     L_bugged
210 #endif PARANOID
211
212 LUp_24:
213         cmpb    SIGN_POS,SIGN(%edi)
214         jne     LCheck_truncate_24      /* If negative then  up==truncate */
215
216         jmp     LCheck_24_round_up
217
218 LDown_24:
219         cmpb    SIGN_POS,SIGN(%edi)
220         je      LCheck_truncate_24      /* If positive then  down==truncate */
221
222 LCheck_24_round_up:
223         movl    %eax,%ecx
224         andl    $0x000000ff,%ecx
225         orl     %ebx,%ecx
226         orl     %edx,%ecx
227         jnz     LDo_24_round_up
228         jmp     LRe_normalise
229
230 LRound_nearest_24:
231         /* Do rounding of the 24th bit if needed (nearest or even) */
232         movl    %eax,%ecx
233         andl    $0x000000ff,%ecx
234         cmpl    $0x00000080,%ecx
235         jc      LCheck_truncate_24      /*less than half, no increment needed*/
236
237         jne     LGreater_Half_24        /* greater than half, increment needed*/
238
239         /* Possibly half, we need to check the ls bits */
240         orl     %ebx,%ebx
241         jnz     LGreater_Half_24        /* greater than half, increment needed*/
242
243         orl     %edx,%edx
244         jnz     LGreater_Half_24        /* greater than half, increment needed*/
245
246         /* Exactly half, increment only if 24th bit is 1 (round to even)*/
247         testl   $0x00000100,%eax
248         jz      LDo_truncate_24
249
250 LGreater_Half_24:                       /*Rounding: increment at the 24th bit*/
251 LDo_24_round_up:
252         andl    $0xffffff00,%eax        /*Truncate to 24 bits*/
253         xorl    %ebx,%ebx
254         movb    LOST_UP,FPU_bits_lost
255         addl    $0x00000100,%eax
256         jmp     LCheck_Round_Overflow
257
258 LCheck_truncate_24:
259         movl    %eax,%ecx
260         andl    $0x000000ff,%ecx
261         orl     %ebx,%ecx
262         orl     %edx,%ecx
263         jz      LRe_normalise                   /* No truncation needed*/
264
265 LDo_truncate_24:
266         andl    $0xffffff00,%eax        /* Truncate to 24 bits*/
267         xorl    %ebx,%ebx
268         movb    LOST_DOWN,FPU_bits_lost
269         jmp     LRe_normalise
270
271
272 /* Round etc to 53 bit precision */
273 LRound_To_53:
274         movl    %esi,%ecx
275         andl    CW_RC,%ecx
276         cmpl    RC_RND,%ecx
277         je      LRound_nearest_53
278
279         cmpl    RC_CHOP,%ecx
280         je      LCheck_truncate_53
281
282         cmpl    RC_UP,%ecx              /* Towards +infinity*/
283         je      LUp_53
284
285         cmpl    RC_DOWN,%ecx            /* Towards -infinity*/
286         je      LDown_53
287
288 #ifdef PARANOID
289         jmp     L_bugged
290 #endif PARANOID
291
292 LUp_53:
293         cmpb    SIGN_POS,SIGN(%edi)
294         jne     LCheck_truncate_53      /* If negative then  up==truncate*/
295
296         jmp     LCheck_53_round_up
297
298 LDown_53:
299         cmpb    SIGN_POS,SIGN(%edi)
300         je      LCheck_truncate_53      /* If positive then  down==truncate*/
301
302 LCheck_53_round_up:
303         movl    %ebx,%ecx
304         andl    $0x000007ff,%ecx
305         orl     %edx,%ecx
306         jnz     LDo_53_round_up
307         jmp     LRe_normalise
308
309 LRound_nearest_53:
310         /*Do rounding of the 53rd bit if needed (nearest or even)*/
311         movl    %ebx,%ecx
312         andl    $0x000007ff,%ecx
313         cmpl    $0x00000400,%ecx
314         jc      LCheck_truncate_53      /* less than half, no increment needed*/
315
316         jnz     LGreater_Half_53        /* greater than half, increment needed*/
317
318         /*Possibly half, we need to check the ls bits*/
319         orl     %edx,%edx
320         jnz     LGreater_Half_53        /* greater than half, increment needed*/
321
322         /* Exactly half, increment only if 53rd bit is 1 (round to even)*/
323         testl   $0x00000800,%ebx
324         jz      LTruncate_53
325
326 LGreater_Half_53:                       /*Rounding: increment at the 53rd bit*/
327 LDo_53_round_up:
328         movb    LOST_UP,FPU_bits_lost
329         andl    $0xfffff800,%ebx        /* Truncate to 53 bits*/
330         addl    $0x00000800,%ebx
331         adcl    $0,%eax
332         jmp     LCheck_Round_Overflow
333
334 LCheck_truncate_53:
335         movl    %ebx,%ecx
336         andl    $0x000007ff,%ecx
337         orl     %edx,%ecx
338         jz      LRe_normalise
339
340 LTruncate_53:
341         movb    LOST_DOWN,FPU_bits_lost
342         andl    $0xfffff800,%ebx        /* Truncate to 53 bits*/
343         jmp     LRe_normalise
344
345
346 /* Round etc to 64 bit precision*/
347 LRound_To_64:
348         movl    %esi,%ecx
349         andl    CW_RC,%ecx
350         cmpl    RC_RND,%ecx
351         je      LRound_nearest_64
352
353         cmpl    RC_CHOP,%ecx
354         je      LCheck_truncate_64
355
356         cmpl    RC_UP,%ecx              /* Towards +infinity*/
357         je      LUp_64
358
359         cmpl    RC_DOWN,%ecx            /* Towards -infinity*/
360         je      LDown_64
361
362 #ifdef PARANOID
363         jmp     L_bugged
364 #endif PARANOID
365
366 LUp_64:
367         cmpb    SIGN_POS,SIGN(%edi)
368         jne     LCheck_truncate_64      /* If negative then  up==truncate*/
369
370         orl     %edx,%edx
371         jnz     LDo_64_round_up
372         jmp     LRe_normalise
373
374 LDown_64:
375         cmpb    SIGN_POS,SIGN(%edi)
376         je      LCheck_truncate_64      /*If positive then  down==truncate*/
377
378         orl     %edx,%edx
379         jnz     LDo_64_round_up
380         jmp     LRe_normalise
381
382 LRound_nearest_64:
383         cmpl    $0x80000000,%edx
384         jc      LCheck_truncate_64
385
386         jne     LDo_64_round_up
387
388         /* Now test for round-to-even */
389         testb   $1,%bl
390         jz      LCheck_truncate_64
391
392 LDo_64_round_up:
393         movb    LOST_UP,FPU_bits_lost
394         addl    $1,%ebx
395         adcl    $0,%eax
396
397 LCheck_Round_Overflow:
398         jnc     LRe_normalise           /* Rounding done, no overflow */
399
400         /* Overflow, adjust the result (to 1.0) */
401         rcrl    $1,%eax
402         rcrl    $1,%ebx
403         incl    EXP(%edi)
404         jmp     LRe_normalise
405
406 LCheck_truncate_64:
407         orl     %edx,%edx
408         jz      LRe_normalise
409
410 LTruncate_64:
411         movb    LOST_DOWN,FPU_bits_lost
412
413 LRe_normalise:
414         testb   $0xff,FPU_denormal
415         jnz     xNormalise_result
416
417 xL_Normalised:
418         cmpb    LOST_UP,FPU_bits_lost
419         je      xL_precision_lost_up
420
421         cmpb    LOST_DOWN,FPU_bits_lost
422         je      xL_precision_lost_down
423
424 xL_no_precision_loss:
425         cmpl    EXP_OVER,EXP(%edi)
426         jge     L_overflow
427
428         /* store the result */
429         movb    TW_Valid,TAG(%edi)
430
431 xL_Store_significand:
432         movl    %eax,SIGH(%edi)
433         movl    %ebx,SIGL(%edi)
434
435 FPU_Arith_exit:
436         popl    %ebx
437         popl    %edi
438         popl    %esi
439         leave
440         ret
441
442
443 /* Set the FPU status flags to represent precision loss due to*/
444 /* round-up.*/
445 xL_precision_lost_up:
446         push    %eax
447         call    set_precision_flag_up
448         popl    %eax
449         jmp     xL_no_precision_loss
450
451 /* Set the FPU status flags to represent precision loss due to*/
452 /* truncation.*/
453 xL_precision_lost_down:
454         push    %eax
455         call    set_precision_flag_down
456         popl    %eax
457         jmp     xL_no_precision_loss
458
459
460 /* The number is a denormal (which might get rounded up to a normal)
461 // Shift the number right the required number of bits, which will
462 // have to be undone later...*/
463 xMake_denorm:
464         /* The action to be taken depends upon whether the underflow
465         // exception is masked*/
466         testb   CW_Underflow,%cl                /* Underflow mask.*/
467         jz      xUnmasked_underflow             /* Do not make a denormal.*/
468
469         movb    DENORMAL,FPU_denormal
470
471         pushl   %ecx            /* Save*/
472         movl    EXP(%edi),%ecx
473         subl    EXP_UNDER+1,%ecx
474         negl    %ecx
475
476         cmpl    $64,%ecx        /* shrd only works for 0..31 bits */
477         jnc     xDenorm_shift_more_than_63
478
479         cmpl    $32,%ecx        /* shrd only works for 0..31 bits */
480         jnc     xDenorm_shift_more_than_32
481
482 /* We got here without jumps by assuming that the most common requirement
483 //   is for a small de-normalising shift.
484 // Shift by [1..31] bits */
485         addl    %ecx,EXP(%edi)
486         orl     %edx,%edx       /* extension*/
487         setne   %ch
488         xorl    %edx,%edx
489         shrd    %cl,%ebx,%edx
490         shrd    %cl,%eax,%ebx
491         shr     %cl,%eax
492         orb     %ch,%dl
493         popl    %ecx
494         jmp     xDenorm_done
495
496 /* Shift by [32..63] bits*/
497 xDenorm_shift_more_than_32:
498         addl    %ecx,EXP(%edi)
499         subb    $32,%cl
500         orl     %edx,%edx
501         setne   %ch
502         orb     %ch,%bl
503         xorl    %edx,%edx
504         shrd    %cl,%ebx,%edx
505         shrd    %cl,%eax,%ebx
506         shr     %cl,%eax
507         orl     %edx,%edx               /*test these 32 bits*/
508         setne   %cl
509         orb     %ch,%bl
510         orb     %cl,%bl
511         movl    %ebx,%edx
512         movl    %eax,%ebx
513         xorl    %eax,%eax
514         popl    %ecx
515         jmp     xDenorm_done
516
517 /* Shift by [64..) bits*/
518 xDenorm_shift_more_than_63:
519         cmpl    $64,%ecx
520         jne     xDenorm_shift_more_than_64
521
522 /* Exactly 64 bit shift*/
523         addl    %ecx,EXP(%edi)
524         xorl    %ecx,%ecx
525         orl     %edx,%edx
526         setne   %cl
527         orl     %ebx,%ebx
528         setne   %ch
529         orb     %ch,%cl
530         orb     %cl,%al
531         movl    %eax,%edx
532         xorl    %eax,%eax
533         xorl    %ebx,%ebx
534         popl    %ecx
535         jmp     xDenorm_done
536
537 xDenorm_shift_more_than_64:
538         movl    EXP_UNDER+1,EXP(%edi)
539 /* This is easy, %eax must be non-zero, so..*/
540         movl    $1,%edx
541         xorl    %eax,%eax
542         xorl    %ebx,%ebx
543         popl    %ecx
544         jmp     xDenorm_done
545
546
547 xUnmasked_underflow:
548         /* Increase the exponent by the magic number*/
549         addl    $(3*(1<<13)),EXP(%edi)
550         movb    UNMASKED_UNDERFLOW,FPU_denormal
551         jmp     xDenorm_done
552
553
554 /* Undo the de-normalisation.*/
555 xNormalise_result:
556         cmpb    UNMASKED_UNDERFLOW,FPU_denormal
557         je      xSignal_underflow
558
559 /* The number must be a denormal if we got here.*/
560 #ifdef PARANOID
561         /* But check it... just in case.*/
562         cmpl    EXP_UNDER+1,EXP(%edi)
563         jne     L_norm_bugged
564 #endif PARANOID
565
566         orl     %eax,%eax       /* ms bits*/
567         jnz     LNormalise_shift_up_to_31       /* Shift left 0 - 31 bits*/
568
569         orl     %ebx,%ebx
570         jz      L_underflow_to_zero     /* The contents are zero*/
571
572 /* Shift left 32 - 63 bits*/
573         movl    %ebx,%eax
574         xorl    %ebx,%ebx
575         subl    $32,EXP(%edi)
576
577 LNormalise_shift_up_to_31:
578         bsrl    %eax,%ecx       /* get the required shift in %ecx */
579         subl    $31,%ecx
580         negl    %ecx
581         shld    %cl,%ebx,%eax
582         shl     %cl,%ebx
583         subl    %ecx,EXP(%edi)
584
585 LNormalise_shift_done:
586         testb   $0xff,FPU_bits_lost     /* bits lost == underflow*/
587         jz      xL_Normalised
588
589         /* There must be a masked underflow*/
590         push    %eax
591         pushl   EX_Underflow
592         call    exception
593         popl    %eax
594         popl    %eax
595         jmp     xL_Normalised
596
597
598 /* The operations resulted in a number too small to represent.
599 // Masked response.*/
600 L_underflow_to_zero:
601         push    %eax
602         call    set_precision_flag_down
603         popl    %eax
604
605         push    %eax
606         pushl   EX_Underflow
607         call    exception
608         popl    %eax
609         popl    %eax
610
611         movb    TW_Zero,TAG(%edi)
612         jmp     xL_Store_significand
613
614
615 /* The operations resulted in a number too large to represent.*/
616 L_overflow:
617         push    %edi
618         call    arith_overflow
619         pop     %edi
620         jmp     FPU_Arith_exit
621
622
623 xSignal_underflow:
624         push    %eax
625         pushl   EX_Underflow
626         call    EXCEPTION
627         popl    %eax
628         popl    %eax
629         jmp     xL_Normalised
630
631
632 #ifdef PARANOID
633 /* If we ever get here then we have problems! */
634 L_bugged:
635         pushl   EX_INTERNAL|0x201
636         call    EXCEPTION
637         popl    %ebx
638         jmp     FPU_Arith_exit
639
640 L_norm_bugged:
641         pushl   EX_INTERNAL|0x216
642         call    EXCEPTION
643         popl    %ebx
644         jmp     FPU_Arith_exit
645
646 L_entry_bugged:
647         pushl   EX_INTERNAL|0x217
648         call    EXCEPTION
649         popl    %ebx
650         jmp     FPU_Arith_exit
651 #endif PARANOID