Initial import from FreeBSD RELENG_4:
[games.git] / sys / platform / pc32 / 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  *
70  */
71
72
73 /*---------------------------------------------------------------------------+
74  | Four entry points.                                                        |
75  |                                                                           |
76  | Needed by both the FPU_round and FPU_round_sqrt entry points:             |
77  |  %eax:%ebx  64 bit significand                                            |
78  |  %edx       32 bit extension of the significand                           |
79  |  %edi       pointer to an FPU_REG for the result to be stored             |
80  |  stack      calling function must have set up a C stack frame and         |
81  |             pushed %esi, %edi, and %ebx                                   |
82  |                                                                           |
83  | Needed just for the FPU_round_sqrt entry point:                           |
84  |  %cx  A control word in the same format as the FPU control word.          |
85  | Otherwise, PARAM4 must give such a value.                                 |
86  |                                                                           |
87  |                                                                           |
88  | The significand and its extension are assumed to be exact in the          |
89  | following sense:                                                          |
90  |   If the significand by itself is the exact result then the significand   |
91  |   extension (%edx) must contain 0, otherwise the significand extension    |
92  |   must be non-zero.                                                       |
93  |   If the significand extension is non-zero then the significand is        |
94  |   smaller than the magnitude of the correct exact result by an amount     |
95  |   greater than zero and less than one ls bit of the significand.          |
96  |   The significand extension is only required to have three possible       |
97  |   non-zero values:                                                        |
98  |       less than 0x80000000  <=> the significand is less than 1/2 an ls    |
99  |                                 bit smaller than the magnitude of the     |
100  |                                 true exact result.                        |
101  |         exactly 0x80000000  <=> the significand is exactly 1/2 an ls bit  |
102  |                                 smaller than the magnitude of the true    |
103  |                                 exact result.                             |
104  |    greater than 0x80000000  <=> the significand is more than 1/2 an ls    |
105  |                                 bit smaller than the magnitude of the     |
106  |                                 true exact result.                        |
107  |                                                                           |
108  +---------------------------------------------------------------------------*/
109
110 /*---------------------------------------------------------------------------+
111  |  The code in this module has become quite complex, but it should handle   |
112  |  all of the FPU flags which are set at this stage of the basic arithmetic |
113  |  computations.                                                            |
114  |  There are a few rare cases where the results are not set identically to  |
115  |  a real FPU. These require a bit more thought because at this stage the   |
116  |  results of the code here appear to be more consistent...                 |
117  |  This may be changed in a future version.                                 |
118  +---------------------------------------------------------------------------*/
119
120
121 #include <gnu/i386/fpemul/exception.h>
122 #include <gnu/i386/fpemul/control_w.h>
123
124 #define LOST_DOWN       $1
125 #define LOST_UP         $2
126 #define DENORMAL        $1
127 #define UNMASKED_UNDERFLOW $2
128
129 .data
130         ALIGN_DATA
131 FPU_bits_lost:
132         .byte   0
133 FPU_denormal:
134         .byte   0
135
136 .text
137 .globl FPU_round
138 .globl FPU_round_sqrt
139 .globl FPU_Arith_exit
140
141 /* Entry point when called from C */
142 ENTRY(round_reg)
143         pushl   %ebp
144         movl    %esp,%ebp
145         pushl   %esi
146         pushl   %edi
147         pushl   %ebx
148
149         movl    PARAM1,%edi
150         movl    SIGH(%edi),%eax
151         movl    SIGL(%edi),%ebx
152         movl    PARAM2,%edx
153         movl    PARAM3,%ecx
154         jmp     FPU_round_sqrt
155
156 FPU_round:              /* Normal entry point */
157         movl    PARAM4,%ecx
158
159 FPU_round_sqrt:         /* Entry point from wm_sqrt.S */
160
161 #ifdef PARANOID
162 /* Cannot use this here yet */
163 /*      orl     %eax,%eax */
164 /*      jns     L_entry_bugged */
165 #endif PARANOID
166
167         cmpl    EXP_UNDER,EXP(%edi)
168         jle     xMake_denorm                    /* The number is a de-normal*/
169
170         movb    $0,FPU_denormal                 /* 0 -> not a de-normal*/
171
172 xDenorm_done:
173         movb    $0,FPU_bits_lost                /*No bits yet lost in rounding*/
174
175         movl    %ecx,%esi
176         andl    CW_PC,%ecx
177         cmpl    PR_64_BITS,%ecx
178         je      LRound_To_64
179
180         cmpl    PR_53_BITS,%ecx
181         je      LRound_To_53
182
183         cmpl    PR_24_BITS,%ecx
184         je      LRound_To_24
185
186 #ifdef PARANOID
187         jmp     L_bugged        /* There is no bug, just a bad control word */
188 #endif PARANOID
189
190
191 /* Round etc to 24 bit precision */
192 LRound_To_24:
193         movl    %esi,%ecx
194         andl    CW_RC,%ecx
195         cmpl    RC_RND,%ecx
196         je      LRound_nearest_24
197
198         cmpl    RC_CHOP,%ecx
199         je      LCheck_truncate_24
200
201         cmpl    RC_UP,%ecx              /* Towards +infinity */
202         je      LUp_24
203
204         cmpl    RC_DOWN,%ecx            /* Towards -infinity */
205         je      LDown_24
206
207 #ifdef PARANOID
208         jmp     L_bugged
209 #endif PARANOID
210
211 LUp_24:
212         cmpb    SIGN_POS,SIGN(%edi)
213         jne     LCheck_truncate_24      /* If negative then  up==truncate */
214
215         jmp     LCheck_24_round_up
216
217 LDown_24:
218         cmpb    SIGN_POS,SIGN(%edi)
219         je      LCheck_truncate_24      /* If positive then  down==truncate */
220
221 LCheck_24_round_up:
222         movl    %eax,%ecx
223         andl    $0x000000ff,%ecx
224         orl     %ebx,%ecx
225         orl     %edx,%ecx
226         jnz     LDo_24_round_up
227         jmp     LRe_normalise
228
229 LRound_nearest_24:
230         /* Do rounding of the 24th bit if needed (nearest or even) */
231         movl    %eax,%ecx
232         andl    $0x000000ff,%ecx
233         cmpl    $0x00000080,%ecx
234         jc      LCheck_truncate_24      /*less than half, no increment needed*/
235
236         jne     LGreater_Half_24        /* greater than half, increment needed*/
237
238         /* Possibly half, we need to check the ls bits */
239         orl     %ebx,%ebx
240         jnz     LGreater_Half_24        /* greater than half, increment needed*/
241
242         orl     %edx,%edx
243         jnz     LGreater_Half_24        /* greater than half, increment needed*/
244
245         /* Exactly half, increment only if 24th bit is 1 (round to even)*/
246         testl   $0x00000100,%eax
247         jz      LDo_truncate_24
248
249 LGreater_Half_24:                       /*Rounding: increment at the 24th bit*/
250 LDo_24_round_up:
251         andl    $0xffffff00,%eax        /*Truncate to 24 bits*/
252         xorl    %ebx,%ebx
253         movb    LOST_UP,FPU_bits_lost
254         addl    $0x00000100,%eax
255         jmp     LCheck_Round_Overflow
256
257 LCheck_truncate_24:
258         movl    %eax,%ecx
259         andl    $0x000000ff,%ecx
260         orl     %ebx,%ecx
261         orl     %edx,%ecx
262         jz      LRe_normalise                   /* No truncation needed*/
263
264 LDo_truncate_24:
265         andl    $0xffffff00,%eax        /* Truncate to 24 bits*/
266         xorl    %ebx,%ebx
267         movb    LOST_DOWN,FPU_bits_lost
268         jmp     LRe_normalise
269
270
271 /* Round etc to 53 bit precision */
272 LRound_To_53:
273         movl    %esi,%ecx
274         andl    CW_RC,%ecx
275         cmpl    RC_RND,%ecx
276         je      LRound_nearest_53
277
278         cmpl    RC_CHOP,%ecx
279         je      LCheck_truncate_53
280
281         cmpl    RC_UP,%ecx              /* Towards +infinity*/
282         je      LUp_53
283
284         cmpl    RC_DOWN,%ecx            /* Towards -infinity*/
285         je      LDown_53
286
287 #ifdef PARANOID
288         jmp     L_bugged
289 #endif PARANOID
290
291 LUp_53:
292         cmpb    SIGN_POS,SIGN(%edi)
293         jne     LCheck_truncate_53      /* If negative then  up==truncate*/
294
295         jmp     LCheck_53_round_up
296
297 LDown_53:
298         cmpb    SIGN_POS,SIGN(%edi)
299         je      LCheck_truncate_53      /* If positive then  down==truncate*/
300
301 LCheck_53_round_up:
302         movl    %ebx,%ecx
303         andl    $0x000007ff,%ecx
304         orl     %edx,%ecx
305         jnz     LDo_53_round_up
306         jmp     LRe_normalise
307
308 LRound_nearest_53:
309         /*Do rounding of the 53rd bit if needed (nearest or even)*/
310         movl    %ebx,%ecx
311         andl    $0x000007ff,%ecx
312         cmpl    $0x00000400,%ecx
313         jc      LCheck_truncate_53      /* less than half, no increment needed*/
314
315         jnz     LGreater_Half_53        /* greater than half, increment needed*/
316
317         /*Possibly half, we need to check the ls bits*/
318         orl     %edx,%edx
319         jnz     LGreater_Half_53        /* greater than half, increment needed*/
320
321         /* Exactly half, increment only if 53rd bit is 1 (round to even)*/
322         testl   $0x00000800,%ebx
323         jz      LTruncate_53
324
325 LGreater_Half_53:                       /*Rounding: increment at the 53rd bit*/
326 LDo_53_round_up:
327         movb    LOST_UP,FPU_bits_lost
328         andl    $0xfffff800,%ebx        /* Truncate to 53 bits*/
329         addl    $0x00000800,%ebx
330         adcl    $0,%eax
331         jmp     LCheck_Round_Overflow
332
333 LCheck_truncate_53:
334         movl    %ebx,%ecx
335         andl    $0x000007ff,%ecx
336         orl     %edx,%ecx
337         jz      LRe_normalise
338
339 LTruncate_53:
340         movb    LOST_DOWN,FPU_bits_lost
341         andl    $0xfffff800,%ebx        /* Truncate to 53 bits*/
342         jmp     LRe_normalise
343
344
345 /* Round etc to 64 bit precision*/
346 LRound_To_64:
347         movl    %esi,%ecx
348         andl    CW_RC,%ecx
349         cmpl    RC_RND,%ecx
350         je      LRound_nearest_64
351
352         cmpl    RC_CHOP,%ecx
353         je      LCheck_truncate_64
354
355         cmpl    RC_UP,%ecx              /* Towards +infinity*/
356         je      LUp_64
357
358         cmpl    RC_DOWN,%ecx            /* Towards -infinity*/
359         je      LDown_64
360
361 #ifdef PARANOID
362         jmp     L_bugged
363 #endif PARANOID
364
365 LUp_64:
366         cmpb    SIGN_POS,SIGN(%edi)
367         jne     LCheck_truncate_64      /* If negative then  up==truncate*/
368
369         orl     %edx,%edx
370         jnz     LDo_64_round_up
371         jmp     LRe_normalise
372
373 LDown_64:
374         cmpb    SIGN_POS,SIGN(%edi)
375         je      LCheck_truncate_64      /*If positive then  down==truncate*/
376
377         orl     %edx,%edx
378         jnz     LDo_64_round_up
379         jmp     LRe_normalise
380
381 LRound_nearest_64:
382         cmpl    $0x80000000,%edx
383         jc      LCheck_truncate_64
384
385         jne     LDo_64_round_up
386
387         /* Now test for round-to-even */
388         testb   $1,%bl
389         jz      LCheck_truncate_64
390
391 LDo_64_round_up:
392         movb    LOST_UP,FPU_bits_lost
393         addl    $1,%ebx
394         adcl    $0,%eax
395
396 LCheck_Round_Overflow:
397         jnc     LRe_normalise           /* Rounding done, no overflow */
398
399         /* Overflow, adjust the result (to 1.0) */
400         rcrl    $1,%eax
401         rcrl    $1,%ebx
402         incl    EXP(%edi)
403         jmp     LRe_normalise
404
405 LCheck_truncate_64:
406         orl     %edx,%edx
407         jz      LRe_normalise
408
409 LTruncate_64:
410         movb    LOST_DOWN,FPU_bits_lost
411
412 LRe_normalise:
413         testb   $0xff,FPU_denormal
414         jnz     xNormalise_result
415
416 xL_Normalised:
417         cmpb    LOST_UP,FPU_bits_lost
418         je      xL_precision_lost_up
419
420         cmpb    LOST_DOWN,FPU_bits_lost
421         je      xL_precision_lost_down
422
423 xL_no_precision_loss:
424         cmpl    EXP_OVER,EXP(%edi)
425         jge     L_overflow
426
427         /* store the result */
428         movb    TW_Valid,TAG(%edi)
429
430 xL_Store_significand:
431         movl    %eax,SIGH(%edi)
432         movl    %ebx,SIGL(%edi)
433
434 FPU_Arith_exit:
435         popl    %ebx
436         popl    %edi
437         popl    %esi
438         leave
439         ret
440
441
442 /* Set the FPU status flags to represent precision loss due to*/
443 /* round-up.*/
444 xL_precision_lost_up:
445         push    %eax
446         call    _set_precision_flag_up
447         popl    %eax
448         jmp     xL_no_precision_loss
449
450 /* Set the FPU status flags to represent precision loss due to*/
451 /* truncation.*/
452 xL_precision_lost_down:
453         push    %eax
454         call    _set_precision_flag_down
455         popl    %eax
456         jmp     xL_no_precision_loss
457
458
459 /* The number is a denormal (which might get rounded up to a normal)
460 // Shift the number right the required number of bits, which will
461 // have to be undone later...*/
462 xMake_denorm:
463         /* The action to be taken depends upon whether the underflow
464         // exception is masked*/
465         testb   CW_Underflow,%cl                /* Underflow mask.*/
466         jz      xUnmasked_underflow             /* Do not make a denormal.*/
467
468         movb    DENORMAL,FPU_denormal
469
470         pushl   %ecx            /* Save*/
471         movl    EXP(%edi),%ecx
472         subl    EXP_UNDER+1,%ecx
473         negl    %ecx
474
475         cmpl    $64,%ecx        /* shrd only works for 0..31 bits */
476         jnc     xDenorm_shift_more_than_63
477
478         cmpl    $32,%ecx        /* shrd only works for 0..31 bits */
479         jnc     xDenorm_shift_more_than_32
480
481 /* We got here without jumps by assuming that the most common requirement
482 //   is for a small de-normalising shift.
483 // Shift by [1..31] bits */
484         addl    %ecx,EXP(%edi)
485         orl     %edx,%edx       /* extension*/
486         setne   %ch
487         xorl    %edx,%edx
488         shrd    %cl,%ebx,%edx
489         shrd    %cl,%eax,%ebx
490         shr     %cl,%eax
491         orb     %ch,%dl
492         popl    %ecx
493         jmp     xDenorm_done
494
495 /* Shift by [32..63] bits*/
496 xDenorm_shift_more_than_32:
497         addl    %ecx,EXP(%edi)
498         subb    $32,%cl
499         orl     %edx,%edx
500         setne   %ch
501         orb     %ch,%bl
502         xorl    %edx,%edx
503         shrd    %cl,%ebx,%edx
504         shrd    %cl,%eax,%ebx
505         shr     %cl,%eax
506         orl     %edx,%edx               /*test these 32 bits*/
507         setne   %cl
508         orb     %ch,%bl
509         orb     %cl,%bl
510         movl    %ebx,%edx
511         movl    %eax,%ebx
512         xorl    %eax,%eax
513         popl    %ecx
514         jmp     xDenorm_done
515
516 /* Shift by [64..) bits*/
517 xDenorm_shift_more_than_63:
518         cmpl    $64,%ecx
519         jne     xDenorm_shift_more_than_64
520
521 /* Exactly 64 bit shift*/
522         addl    %ecx,EXP(%edi)
523         xorl    %ecx,%ecx
524         orl     %edx,%edx
525         setne   %cl
526         orl     %ebx,%ebx
527         setne   %ch
528         orb     %ch,%cl
529         orb     %cl,%al
530         movl    %eax,%edx
531         xorl    %eax,%eax
532         xorl    %ebx,%ebx
533         popl    %ecx
534         jmp     xDenorm_done
535
536 xDenorm_shift_more_than_64:
537         movl    EXP_UNDER+1,EXP(%edi)
538 /* This is easy, %eax must be non-zero, so..*/
539         movl    $1,%edx
540         xorl    %eax,%eax
541         xorl    %ebx,%ebx
542         popl    %ecx
543         jmp     xDenorm_done
544
545
546 xUnmasked_underflow:
547         /* Increase the exponent by the magic number*/
548         addl    $(3*(1<<13)),EXP(%edi)
549         movb    UNMASKED_UNDERFLOW,FPU_denormal
550         jmp     xDenorm_done
551
552
553 /* Undo the de-normalisation.*/
554 xNormalise_result:
555         cmpb    UNMASKED_UNDERFLOW,FPU_denormal
556         je      xSignal_underflow
557
558 /* The number must be a denormal if we got here.*/
559 #ifdef PARANOID
560         /* But check it... just in case.*/
561         cmpl    EXP_UNDER+1,EXP(%edi)
562         jne     L_norm_bugged
563 #endif PARANOID
564
565         orl     %eax,%eax       /* ms bits*/
566         jnz     LNormalise_shift_up_to_31       /* Shift left 0 - 31 bits*/
567
568         orl     %ebx,%ebx
569         jz      L_underflow_to_zero     /* The contents are zero*/
570
571 /* Shift left 32 - 63 bits*/
572         movl    %ebx,%eax
573         xorl    %ebx,%ebx
574         subl    $32,EXP(%edi)
575
576 LNormalise_shift_up_to_31:
577         bsrl    %eax,%ecx       /* get the required shift in %ecx */
578         subl    $31,%ecx
579         negl    %ecx
580         shld    %cl,%ebx,%eax
581         shl     %cl,%ebx
582         subl    %ecx,EXP(%edi)
583
584 LNormalise_shift_done:
585         testb   $0xff,FPU_bits_lost     /* bits lost == underflow*/
586         jz      xL_Normalised
587
588         /* There must be a masked underflow*/
589         push    %eax
590         pushl   EX_Underflow
591         call    _exception
592         popl    %eax
593         popl    %eax
594         jmp     xL_Normalised
595
596
597 /* The operations resulted in a number too small to represent.
598 // Masked response.*/
599 L_underflow_to_zero:
600         push    %eax
601         call    _set_precision_flag_down
602         popl    %eax
603
604         push    %eax
605         pushl   EX_Underflow
606         call    _exception
607         popl    %eax
608         popl    %eax
609
610         movb    TW_Zero,TAG(%edi)
611         jmp     xL_Store_significand
612
613
614 /* The operations resulted in a number too large to represent.*/
615 L_overflow:
616         push    %edi
617         call    _arith_overflow
618         pop     %edi
619         jmp     FPU_Arith_exit
620
621
622 xSignal_underflow:
623         push    %eax
624         pushl   EX_Underflow
625         call    EXCEPTION
626         popl    %eax
627         popl    %eax
628         jmp     xL_Normalised
629
630
631 #ifdef PARANOID
632 /* If we ever get here then we have problems! */
633 L_bugged:
634         pushl   EX_INTERNAL|0x201
635         call    EXCEPTION
636         popl    %ebx
637         jmp     FPU_Arith_exit
638
639 L_norm_bugged:
640         pushl   EX_INTERNAL|0x216
641         call    EXCEPTION
642         popl    %ebx
643         jmp     FPU_Arith_exit
644
645 L_entry_bugged:
646         pushl   EX_INTERNAL|0x217
647         call    EXCEPTION
648         popl    %ebx
649         jmp     FPU_Arith_exit
650 #endif PARANOID