2 /*---------------------------------------------------------------------------+
5 | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
7 | Copyright (C) 1992,1993,1995,1997 |
8 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9 | E-mail billm@suburbia.net |
12 +---------------------------------------------------------------------------*/
14 /*---------------------------------------------------------------------------+
16 | int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |
17 | unsigned int control_word, char *sign) |
19 | Does not compute the destination exponent, but does adjust it. |
21 | Return value is the tag of the answer, or-ed with FPU_Exception if |
22 | one was raised, or -1 on internal error. |
23 +---------------------------------------------------------------------------*/
25 #include "exception.h"
27 #include "control_w.h"
30 /* #define dSIGL(x) (x) */
31 /* #define dSIGH(x) 4(x) */
34 #ifndef NON_REENTRANT_FPU
36 Local storage on the stack:
37 Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
38 Overflow flag: ovfl_flag
40 #define FPU_accum_3 -4(%ebp)
41 #define FPU_accum_2 -8(%ebp)
42 #define FPU_accum_1 -12(%ebp)
43 #define FPU_accum_0 -16(%ebp)
44 #define FPU_result_1 -20(%ebp)
45 #define FPU_result_2 -24(%ebp)
46 #define FPU_ovfl_flag -28(%ebp)
51 Local storage in a static area:
52 Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
53 Overflow flag: ovfl_flag
70 #endif /* NON_REENTRANT_FPU */
80 #ifndef NON_REENTRANT_FPU
82 #endif /* NON_REENTRANT_FPU */
97 /* A denormal and a large number can cause an exponent underflow */
98 cmpl EXP_WAY_UNDER,%edx
101 /* Set to a really low value allow correct handling */
102 movl EXP_WAY_UNDER,%edx
109 /* testl $0x80000000, SIGH(%esi) // Dividend */
111 testl $0x80000000, SIGH(%ebx) /* Divisor */
113 #endif /* PARANOID */
115 /* Check if the divisor can be treated as having just 32 bits */
117 jnz L_Full_Division /* Can't do a quick divide */
119 /* We should be able to zip through the division here */
120 movl SIGH(%ebx),%ecx /* The divisor */
121 movl SIGH(%esi),%edx /* Dividend */
122 movl SIGL(%esi),%eax /* Dividend */
125 setaeb FPU_ovfl_flag /* Keep a record */
128 subl %ecx,%edx /* Prevent the overflow */
131 /* Divide the 64 bit number by the 32 bit denominator */
133 movl %eax,FPU_result_2
135 /* Work on the remainder of the first division */
138 movl %eax,FPU_result_1
140 /* Work on the remainder of the 64 bit division */
144 testb $255,FPU_ovfl_flag /* was the num > denom ? */
147 /* Do the shifting here */
148 /* increase the exponent */
151 /* shift the mantissa right one bit */
152 stc /* To set the ms bit */
158 jmp LRound_precision /* Do the rounding as required */
161 /*---------------------------------------------------------------------------+
162 | Divide: Return arg1/arg2 to arg3. |
164 | This routine does not use the exponents of arg1 and arg2, but does |
165 | adjust the exponent of arg3. |
167 | The maximum returned value is (ignoring exponents) |
168 | .ffffffff ffffffff |
169 | ------------------ = 1.ffffffff fffffffe |
170 | .80000000 00000000 |
171 | and the minimum is |
172 | .80000000 00000000 |
173 | ------------------ = .80000000 00000001 (rounded) |
174 | .ffffffff ffffffff |
176 +---------------------------------------------------------------------------*/
180 /* Save extended dividend in local register */
182 movl %eax,FPU_accum_2
184 movl %eax,FPU_accum_3
186 movl %eax,FPU_accum_1 /* zero the extension */
187 movl %eax,FPU_accum_0 /* zero the extension */
189 movl SIGL(%esi),%eax /* Get the current num */
192 /*----------------------------------------------------------------------*/
193 /* Initialization done.
194 Do the first 32 bits. */
196 movb $0,FPU_ovfl_flag
197 cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
205 /* The dividend is greater or equal, would cause overflow */
206 setaeb FPU_ovfl_flag /* Keep a record */
209 sbbl SIGH(%ebx),%edx /* Prevent the overflow */
210 movl %eax,FPU_accum_2
211 movl %edx,FPU_accum_3
214 /* At this point, we have a dividend < divisor, with a record of
215 adjustment in FPU_ovfl_flag */
217 /* We will divide by a number which is too large */
222 /* here we need to divide by 100000000h,
223 i.e., no division at all.. */
228 divl %ecx /* Divide the numerator by the augmented
232 movl %eax,FPU_result_2 /* Put the result in the answer */
234 mull SIGH(%ebx) /* mul by the ms dw of the denom */
236 subl %eax,FPU_accum_2 /* Subtract from the num local reg */
237 sbbl %edx,FPU_accum_3
239 movl FPU_result_2,%eax /* Get the result back */
240 mull SIGL(%ebx) /* now mul the ls dw of the denom */
242 subl %eax,FPU_accum_1 /* Subtract from the num local reg */
243 sbbl %edx,FPU_accum_2
245 je LDo_2nd_32_bits /* Must check for non-zero result here */
249 #endif /* PARANOID */
251 /* need to subtract another once of the denom */
252 incl FPU_result_2 /* Correct the answer */
256 subl %eax,FPU_accum_1 /* Subtract from the num local reg */
257 sbbl %edx,FPU_accum_2
261 jne L_bugged_1 /* Must check for non-zero result here */
262 #endif /* PARANOID */
264 /*----------------------------------------------------------------------*/
265 /* Half of the main problem is done, there is just a reduced numerator
267 Work with the second 32 bits, FPU_accum_0 not used from now on */
269 movl FPU_accum_2,%edx /* get the reduced num */
270 movl FPU_accum_1,%eax
272 /* need to check for possible subsequent overflow */
275 ja LPrevent_2nd_overflow
280 LPrevent_2nd_overflow:
281 /* The numerator is greater or equal, would cause overflow */
282 /* prevent overflow */
285 movl %edx,FPU_accum_2
286 movl %eax,FPU_accum_1
288 incl FPU_result_2 /* Reflect the subtraction in the answer */
291 je L_bugged_2 /* Can't bump the result to 1.0 */
292 #endif /* PARANOID */
295 cmpl $0,%ecx /* augmented denom msw */
296 jnz LSecond_div_not_1
298 /* %ecx == 0, we are dividing by 1.0 */
303 divl %ecx /* Divide the numerator by the denom ms dw */
306 movl %eax,FPU_result_1 /* Put the result in the answer */
308 mull SIGH(%ebx) /* mul by the ms dw of the denom */
310 subl %eax,FPU_accum_1 /* Subtract from the num local reg */
311 sbbl %edx,FPU_accum_2
315 #endif /* PARANOID */
317 movl FPU_result_1,%eax /* Get the result back */
318 mull SIGL(%ebx) /* now mul the ls dw of the denom */
320 subl %eax,FPU_accum_0 /* Subtract from the num local reg */
321 sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */
326 #endif /* PARANOID */
333 #endif /* PARANOID */
335 /* need to subtract another once of the denom */
338 subl %eax,FPU_accum_0 /* Subtract from the num local reg */
339 sbbl %edx,FPU_accum_1
345 #endif /* PARANOID */
347 addl $1,FPU_result_1 /* Correct the answer */
351 jc L_bugged_2 /* Must check for non-zero result here */
352 #endif /* PARANOID */
354 /*----------------------------------------------------------------------*/
355 /* The division is essentially finished here, we just need to perform
357 Deal with the 3rd 32 bits */
359 movl FPU_accum_1,%edx /* get the reduced num */
360 movl FPU_accum_0,%eax
362 /* need to check for possible subsequent overflow */
363 cmpl SIGH(%ebx),%edx /* denom */
365 ja LPrevent_3rd_overflow
367 cmpl SIGL(%ebx),%eax /* denom */
370 LPrevent_3rd_overflow:
371 /* prevent overflow */
374 movl %edx,FPU_accum_1
375 movl %eax,FPU_accum_0
377 addl $1,FPU_result_1 /* Reflect the subtraction in the answer */
382 /* This is a tricky spot, there is an overflow of the answer */
383 movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */
387 * Prepare for rounding.
388 * To test for rounding, we just need to compare 2*accum with the
391 movl FPU_accum_0,%ecx
392 movl FPU_accum_1,%edx
395 jz LRound_ovfl /* The accumulator contains zero. */
401 jc LRound_large /* No need to compare, denom smaller */
407 movl $0x70000000,%eax /* Denom was larger */
413 movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
417 movl $0xff000000,%eax /* Denom was smaller */
420 /* We are now ready to deal with rounding, but first we must get
421 the bits properly aligned */
422 testb $255,FPU_ovfl_flag /* was the num > denom ? */
427 /* shift the mantissa right one bit */
428 stc /* Will set the ms bit */
433 /* Round the result as required */
435 decw EXP(%edi) /* binary point between 1st & 2nd bits */
438 movl FPU_result_1,%ebx
439 movl FPU_result_2,%eax
444 /* The logic is wrong if we got here */
446 pushl EX_INTERNAL|0x202
452 pushl EX_INTERNAL|0x203
458 pushl EX_INTERNAL|0x204
471 #endif /* PARANOID */