Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / arch / x86 / math-emu / wm_sqrt.S
1 /* SPDX-License-Identifier: GPL-2.0 */
2         .file   "wm_sqrt.S"
3 /*---------------------------------------------------------------------------+
4  |  wm_sqrt.S                                                                |
5  |                                                                           |
6  | Fixed point arithmetic square root evaluation.                            |
7  |                                                                           |
8  | Copyright (C) 1992,1993,1995,1997                                         |
9  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
10  |                       Australia.  E-mail billm@suburbia.net               |
11  |                                                                           |
12  | Call from C as:                                                           |
13  |    int wm_sqrt(FPU_REG *n, unsigned int control_word)                     |
14  |                                                                           |
15  +---------------------------------------------------------------------------*/
16
17 /*---------------------------------------------------------------------------+
18  |  wm_sqrt(FPU_REG *n, unsigned int control_word)                           |
19  |    returns the square root of n in n.                                     |
20  |                                                                           |
21  |  Use Newton's method to compute the square root of a number, which must   |
22  |  be in the range  [1.0 .. 4.0),  to 64 bits accuracy.                     |
23  |  Does not check the sign or tag of the argument.                          |
24  |  Sets the exponent, but not the sign or tag of the result.                |
25  |                                                                           |
26  |  The guess is kept in %esi:%edi                                           |
27  +---------------------------------------------------------------------------*/
28
29 #include "exception.h"
30 #include "fpu_emu.h"
31
32
33 #ifndef NON_REENTRANT_FPU
34 /*      Local storage on the stack: */
35 #define FPU_accum_3     -4(%ebp)        /* ms word */
36 #define FPU_accum_2     -8(%ebp)
37 #define FPU_accum_1     -12(%ebp)
38 #define FPU_accum_0     -16(%ebp)
39
40 /*
41  * The de-normalised argument:
42  *                  sq_2                  sq_1              sq_0
43  *        b b b b b b b ... b b b   b b b .... b b b   b 0 0 0 ... 0
44  *           ^ binary point here
45  */
46 #define FPU_fsqrt_arg_2 -20(%ebp)       /* ms word */
47 #define FPU_fsqrt_arg_1 -24(%ebp)
48 #define FPU_fsqrt_arg_0 -28(%ebp)       /* ls word, at most the ms bit is set */
49
50 #else
51 /*      Local storage in a static area: */
52 .data
53         .align 4,0
54 FPU_accum_3:
55         .long   0               /* ms word */
56 FPU_accum_2:
57         .long   0
58 FPU_accum_1:
59         .long   0
60 FPU_accum_0:
61         .long   0
62
63 /* The de-normalised argument:
64                     sq_2                  sq_1              sq_0
65           b b b b b b b ... b b b   b b b .... b b b   b 0 0 0 ... 0
66              ^ binary point here
67  */
68 FPU_fsqrt_arg_2:
69         .long   0               /* ms word */
70 FPU_fsqrt_arg_1:
71         .long   0
72 FPU_fsqrt_arg_0:
73         .long   0               /* ls word, at most the ms bit is set */
74 #endif /* NON_REENTRANT_FPU */ 
75
76
77 .text
78 ENTRY(wm_sqrt)
79         pushl   %ebp
80         movl    %esp,%ebp
81 #ifndef NON_REENTRANT_FPU
82         subl    $28,%esp
83 #endif /* NON_REENTRANT_FPU */
84         pushl   %esi
85         pushl   %edi
86         pushl   %ebx
87
88         movl    PARAM1,%esi
89
90         movl    SIGH(%esi),%eax
91         movl    SIGL(%esi),%ecx
92         xorl    %edx,%edx
93
94 /* We use a rough linear estimate for the first guess.. */
95
96         cmpw    EXP_BIAS,EXP(%esi)
97         jnz     sqrt_arg_ge_2
98
99         shrl    $1,%eax                 /* arg is in the range  [1.0 .. 2.0) */
100         rcrl    $1,%ecx
101         rcrl    $1,%edx
102
103 sqrt_arg_ge_2:
104 /* From here on, n is never accessed directly again until it is
105    replaced by the answer. */
106
107         movl    %eax,FPU_fsqrt_arg_2            /* ms word of n */
108         movl    %ecx,FPU_fsqrt_arg_1
109         movl    %edx,FPU_fsqrt_arg_0
110
111 /* Make a linear first estimate */
112         shrl    $1,%eax
113         addl    $0x40000000,%eax
114         movl    $0xaaaaaaaa,%ecx
115         mull    %ecx
116         shll    %edx                    /* max result was 7fff... */
117         testl   $0x80000000,%edx        /* but min was 3fff... */
118         jnz     sqrt_prelim_no_adjust
119
120         movl    $0x80000000,%edx        /* round up */
121
122 sqrt_prelim_no_adjust:
123         movl    %edx,%esi       /* Our first guess */
124
125 /* We have now computed (approx)   (2 + x) / 3, which forms the basis
126    for a few iterations of Newton's method */
127
128         movl    FPU_fsqrt_arg_2,%ecx    /* ms word */
129
130 /*
131  * From our initial estimate, three iterations are enough to get us
132  * to 30 bits or so. This will then allow two iterations at better
133  * precision to complete the process.
134  */
135
136 /* Compute  (g + n/g)/2  at each iteration (g is the guess). */
137         shrl    %ecx            /* Doing this first will prevent a divide */
138                                 /* overflow later. */
139
140         movl    %ecx,%edx       /* msw of the arg / 2 */
141         divl    %esi            /* current estimate */
142         shrl    %esi            /* divide by 2 */
143         addl    %eax,%esi       /* the new estimate */
144
145         movl    %ecx,%edx
146         divl    %esi
147         shrl    %esi
148         addl    %eax,%esi
149
150         movl    %ecx,%edx
151         divl    %esi
152         shrl    %esi
153         addl    %eax,%esi
154
155 /*
156  * Now that an estimate accurate to about 30 bits has been obtained (in %esi),
157  * we improve it to 60 bits or so.
158  *
159  * The strategy from now on is to compute new estimates from
160  *      guess := guess + (n - guess^2) / (2 * guess)
161  */
162
163 /* First, find the square of the guess */
164         movl    %esi,%eax
165         mull    %esi
166 /* guess^2 now in %edx:%eax */
167
168         movl    FPU_fsqrt_arg_1,%ecx
169         subl    %ecx,%eax
170         movl    FPU_fsqrt_arg_2,%ecx    /* ms word of normalized n */
171         sbbl    %ecx,%edx
172         jnc     sqrt_stage_2_positive
173
174 /* Subtraction gives a negative result,
175    negate the result before division. */
176         notl    %edx
177         notl    %eax
178         addl    $1,%eax
179         adcl    $0,%edx
180
181         divl    %esi
182         movl    %eax,%ecx
183
184         movl    %edx,%eax
185         divl    %esi
186         jmp     sqrt_stage_2_finish
187
188 sqrt_stage_2_positive:
189         divl    %esi
190         movl    %eax,%ecx
191
192         movl    %edx,%eax
193         divl    %esi
194
195         notl    %ecx
196         notl    %eax
197         addl    $1,%eax
198         adcl    $0,%ecx
199
200 sqrt_stage_2_finish:
201         sarl    $1,%ecx         /* divide by 2 */
202         rcrl    $1,%eax
203
204         /* Form the new estimate in %esi:%edi */
205         movl    %eax,%edi
206         addl    %ecx,%esi
207
208         jnz     sqrt_stage_2_done       /* result should be [1..2) */
209
210 #ifdef PARANOID
211 /* It should be possible to get here only if the arg is ffff....ffff */
212         cmp     $0xffffffff,FPU_fsqrt_arg_1
213         jnz     sqrt_stage_2_error
214 #endif /* PARANOID */
215
216 /* The best rounded result. */
217         xorl    %eax,%eax
218         decl    %eax
219         movl    %eax,%edi
220         movl    %eax,%esi
221         movl    $0x7fffffff,%eax
222         jmp     sqrt_round_result
223
224 #ifdef PARANOID
225 sqrt_stage_2_error:
226         pushl   EX_INTERNAL|0x213
227         call    EXCEPTION
228 #endif /* PARANOID */ 
229
230 sqrt_stage_2_done:
231
232 /* Now the square root has been computed to better than 60 bits. */
233
234 /* Find the square of the guess. */
235         movl    %edi,%eax               /* ls word of guess */
236         mull    %edi
237         movl    %edx,FPU_accum_1
238
239         movl    %esi,%eax
240         mull    %esi
241         movl    %edx,FPU_accum_3
242         movl    %eax,FPU_accum_2
243
244         movl    %edi,%eax
245         mull    %esi
246         addl    %eax,FPU_accum_1
247         adcl    %edx,FPU_accum_2
248         adcl    $0,FPU_accum_3
249
250 /*      movl    %esi,%eax */
251 /*      mull    %edi */
252         addl    %eax,FPU_accum_1
253         adcl    %edx,FPU_accum_2
254         adcl    $0,FPU_accum_3
255
256 /* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */
257
258         movl    FPU_fsqrt_arg_0,%eax            /* get normalized n */
259         subl    %eax,FPU_accum_1
260         movl    FPU_fsqrt_arg_1,%eax
261         sbbl    %eax,FPU_accum_2
262         movl    FPU_fsqrt_arg_2,%eax            /* ms word of normalized n */
263         sbbl    %eax,FPU_accum_3
264         jnc     sqrt_stage_3_positive
265
266 /* Subtraction gives a negative result,
267    negate the result before division */
268         notl    FPU_accum_1
269         notl    FPU_accum_2
270         notl    FPU_accum_3
271         addl    $1,FPU_accum_1
272         adcl    $0,FPU_accum_2
273
274 #ifdef PARANOID
275         adcl    $0,FPU_accum_3  /* This must be zero */
276         jz      sqrt_stage_3_no_error
277
278 sqrt_stage_3_error:
279         pushl   EX_INTERNAL|0x207
280         call    EXCEPTION
281
282 sqrt_stage_3_no_error:
283 #endif /* PARANOID */
284
285         movl    FPU_accum_2,%edx
286         movl    FPU_accum_1,%eax
287         divl    %esi
288         movl    %eax,%ecx
289
290         movl    %edx,%eax
291         divl    %esi
292
293         sarl    $1,%ecx         /* divide by 2 */
294         rcrl    $1,%eax
295
296         /* prepare to round the result */
297
298         addl    %ecx,%edi
299         adcl    $0,%esi
300
301         jmp     sqrt_stage_3_finished
302
303 sqrt_stage_3_positive:
304         movl    FPU_accum_2,%edx
305         movl    FPU_accum_1,%eax
306         divl    %esi
307         movl    %eax,%ecx
308
309         movl    %edx,%eax
310         divl    %esi
311
312         sarl    $1,%ecx         /* divide by 2 */
313         rcrl    $1,%eax
314
315         /* prepare to round the result */
316
317         notl    %eax            /* Negate the correction term */
318         notl    %ecx
319         addl    $1,%eax
320         adcl    $0,%ecx         /* carry here ==> correction == 0 */
321         adcl    $0xffffffff,%esi
322
323         addl    %ecx,%edi
324         adcl    $0,%esi
325
326 sqrt_stage_3_finished:
327
328 /*
329  * The result in %esi:%edi:%esi should be good to about 90 bits here,
330  * and the rounding information here does not have sufficient accuracy
331  * in a few rare cases.
332  */
333         cmpl    $0xffffffe0,%eax
334         ja      sqrt_near_exact_x
335
336         cmpl    $0x00000020,%eax
337         jb      sqrt_near_exact
338
339         cmpl    $0x7fffffe0,%eax
340         jb      sqrt_round_result
341
342         cmpl    $0x80000020,%eax
343         jb      sqrt_get_more_precision
344
345 sqrt_round_result:
346 /* Set up for rounding operations */
347         movl    %eax,%edx
348         movl    %esi,%eax
349         movl    %edi,%ebx
350         movl    PARAM1,%edi
351         movw    EXP_BIAS,EXP(%edi)      /* Result is in  [1.0 .. 2.0) */
352         jmp     fpu_reg_round
353
354
355 sqrt_near_exact_x:
356 /* First, the estimate must be rounded up. */
357         addl    $1,%edi
358         adcl    $0,%esi
359
360 sqrt_near_exact:
361 /*
362  * This is an easy case because x^1/2 is monotonic.
363  * We need just find the square of our estimate, compare it
364  * with the argument, and deduce whether our estimate is
365  * above, below, or exact. We use the fact that the estimate
366  * is known to be accurate to about 90 bits.
367  */
368         movl    %edi,%eax               /* ls word of guess */
369         mull    %edi
370         movl    %edx,%ebx               /* 2nd ls word of square */
371         movl    %eax,%ecx               /* ls word of square */
372
373         movl    %edi,%eax
374         mull    %esi
375         addl    %eax,%ebx
376         addl    %eax,%ebx
377
378 #ifdef PARANOID
379         cmp     $0xffffffb0,%ebx
380         jb      sqrt_near_exact_ok
381
382         cmp     $0x00000050,%ebx
383         ja      sqrt_near_exact_ok
384
385         pushl   EX_INTERNAL|0x214
386         call    EXCEPTION
387
388 sqrt_near_exact_ok:
389 #endif /* PARANOID */ 
390
391         or      %ebx,%ebx
392         js      sqrt_near_exact_small
393
394         jnz     sqrt_near_exact_large
395
396         or      %ebx,%edx
397         jnz     sqrt_near_exact_large
398
399 /* Our estimate is exactly the right answer */
400         xorl    %eax,%eax
401         jmp     sqrt_round_result
402
403 sqrt_near_exact_small:
404 /* Our estimate is too small */
405         movl    $0x000000ff,%eax
406         jmp     sqrt_round_result
407         
408 sqrt_near_exact_large:
409 /* Our estimate is too large, we need to decrement it */
410         subl    $1,%edi
411         sbbl    $0,%esi
412         movl    $0xffffff00,%eax
413         jmp     sqrt_round_result
414
415
416 sqrt_get_more_precision:
417 /* This case is almost the same as the above, except we start
418    with an extra bit of precision in the estimate. */
419         stc                     /* The extra bit. */
420         rcll    $1,%edi         /* Shift the estimate left one bit */
421         rcll    $1,%esi
422
423         movl    %edi,%eax               /* ls word of guess */
424         mull    %edi
425         movl    %edx,%ebx               /* 2nd ls word of square */
426         movl    %eax,%ecx               /* ls word of square */
427
428         movl    %edi,%eax
429         mull    %esi
430         addl    %eax,%ebx
431         addl    %eax,%ebx
432
433 /* Put our estimate back to its original value */
434         stc                     /* The ms bit. */
435         rcrl    $1,%esi         /* Shift the estimate left one bit */
436         rcrl    $1,%edi
437
438 #ifdef PARANOID
439         cmp     $0xffffff60,%ebx
440         jb      sqrt_more_prec_ok
441
442         cmp     $0x000000a0,%ebx
443         ja      sqrt_more_prec_ok
444
445         pushl   EX_INTERNAL|0x215
446         call    EXCEPTION
447
448 sqrt_more_prec_ok:
449 #endif /* PARANOID */ 
450
451         or      %ebx,%ebx
452         js      sqrt_more_prec_small
453
454         jnz     sqrt_more_prec_large
455
456         or      %ebx,%ecx
457         jnz     sqrt_more_prec_large
458
459 /* Our estimate is exactly the right answer */
460         movl    $0x80000000,%eax
461         jmp     sqrt_round_result
462
463 sqrt_more_prec_small:
464 /* Our estimate is too small */
465         movl    $0x800000ff,%eax
466         jmp     sqrt_round_result
467         
468 sqrt_more_prec_large:
469 /* Our estimate is too large */
470         movl    $0x7fffff00,%eax
471         jmp     sqrt_round_result
472 ENDPROC(wm_sqrt)