diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/i386/math-emu/reg_u_div.S |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/i386/math-emu/reg_u_div.S')
-rw-r--r-- | arch/i386/math-emu/reg_u_div.S | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/arch/i386/math-emu/reg_u_div.S b/arch/i386/math-emu/reg_u_div.S new file mode 100644 index 000000000000..cc00654b6f9a --- /dev/null +++ b/arch/i386/math-emu/reg_u_div.S | |||
@@ -0,0 +1,471 @@ | |||
1 | .file "reg_u_div.S" | ||
2 | /*---------------------------------------------------------------------------+ | ||
3 | | reg_u_div.S | | ||
4 | | | | ||
5 | | Divide one FPU_REG by another and put the result in a destination FPU_REG.| | ||
6 | | | | ||
7 | | Copyright (C) 1992,1993,1995,1997 | | ||
8 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | | ||
9 | | E-mail billm@suburbia.net | | ||
10 | | | | ||
11 | | | | ||
12 | +---------------------------------------------------------------------------*/ | ||
13 | |||
14 | /*---------------------------------------------------------------------------+ | ||
15 | | Call from C as: | | ||
16 | | int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | | ||
17 | | unsigned int control_word, char *sign) | | ||
18 | | | | ||
19 | | Does not compute the destination exponent, but does adjust it. | | ||
20 | | | | ||
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 | +---------------------------------------------------------------------------*/ | ||
24 | |||
25 | #include "exception.h" | ||
26 | #include "fpu_emu.h" | ||
27 | #include "control_w.h" | ||
28 | |||
29 | |||
30 | /* #define dSIGL(x) (x) */ | ||
31 | /* #define dSIGH(x) 4(x) */ | ||
32 | |||
33 | |||
34 | #ifndef NON_REENTRANT_FPU | ||
35 | /* | ||
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 | ||
39 | */ | ||
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) | ||
47 | |||
48 | #else | ||
49 | .data | ||
50 | /* | ||
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 | ||
54 | */ | ||
55 | .align 4,0 | ||
56 | FPU_accum_3: | ||
57 | .long 0 | ||
58 | FPU_accum_2: | ||
59 | .long 0 | ||
60 | FPU_accum_1: | ||
61 | .long 0 | ||
62 | FPU_accum_0: | ||
63 | .long 0 | ||
64 | FPU_result_1: | ||
65 | .long 0 | ||
66 | FPU_result_2: | ||
67 | .long 0 | ||
68 | FPU_ovfl_flag: | ||
69 | .byte 0 | ||
70 | #endif /* NON_REENTRANT_FPU */ | ||
71 | |||
72 | #define REGA PARAM1 | ||
73 | #define REGB PARAM2 | ||
74 | #define DEST PARAM3 | ||
75 | |||
76 | .text | ||
77 | ENTRY(FPU_u_div) | ||
78 | pushl %ebp | ||
79 | movl %esp,%ebp | ||
80 | #ifndef NON_REENTRANT_FPU | ||
81 | subl $28,%esp | ||
82 | #endif /* NON_REENTRANT_FPU */ | ||
83 | |||
84 | pushl %esi | ||
85 | pushl %edi | ||
86 | pushl %ebx | ||
87 | |||
88 | movl REGA,%esi | ||
89 | movl REGB,%ebx | ||
90 | movl DEST,%edi | ||
91 | |||
92 | movswl EXP(%esi),%edx | ||
93 | movswl EXP(%ebx),%eax | ||
94 | subl %eax,%edx | ||
95 | addl EXP_BIAS,%edx | ||
96 | |||
97 | /* A denormal and a large number can cause an exponent underflow */ | ||
98 | cmpl EXP_WAY_UNDER,%edx | ||
99 | jg xExp_not_underflow | ||
100 | |||
101 | /* Set to a really low value allow correct handling */ | ||
102 | movl EXP_WAY_UNDER,%edx | ||
103 | |||
104 | xExp_not_underflow: | ||
105 | |||
106 | movw %dx,EXP(%edi) | ||
107 | |||
108 | #ifdef PARANOID | ||
109 | /* testl $0x80000000, SIGH(%esi) // Dividend */ | ||
110 | /* je L_bugged */ | ||
111 | testl $0x80000000, SIGH(%ebx) /* Divisor */ | ||
112 | je L_bugged | ||
113 | #endif /* PARANOID */ | ||
114 | |||
115 | /* Check if the divisor can be treated as having just 32 bits */ | ||
116 | cmpl $0,SIGL(%ebx) | ||
117 | jnz L_Full_Division /* Can't do a quick divide */ | ||
118 | |||
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 */ | ||
123 | |||
124 | cmpl %ecx,%edx | ||
125 | setaeb FPU_ovfl_flag /* Keep a record */ | ||
126 | jb L_no_adjust | ||
127 | |||
128 | subl %ecx,%edx /* Prevent the overflow */ | ||
129 | |||
130 | L_no_adjust: | ||
131 | /* Divide the 64 bit number by the 32 bit denominator */ | ||
132 | divl %ecx | ||
133 | movl %eax,FPU_result_2 | ||
134 | |||
135 | /* Work on the remainder of the first division */ | ||
136 | xorl %eax,%eax | ||
137 | divl %ecx | ||
138 | movl %eax,FPU_result_1 | ||
139 | |||
140 | /* Work on the remainder of the 64 bit division */ | ||
141 | xorl %eax,%eax | ||
142 | divl %ecx | ||
143 | |||
144 | testb $255,FPU_ovfl_flag /* was the num > denom ? */ | ||
145 | je L_no_overflow | ||
146 | |||
147 | /* Do the shifting here */ | ||
148 | /* increase the exponent */ | ||
149 | incw EXP(%edi) | ||
150 | |||
151 | /* shift the mantissa right one bit */ | ||
152 | stc /* To set the ms bit */ | ||
153 | rcrl FPU_result_2 | ||
154 | rcrl FPU_result_1 | ||
155 | rcrl %eax | ||
156 | |||
157 | L_no_overflow: | ||
158 | jmp LRound_precision /* Do the rounding as required */ | ||
159 | |||
160 | |||
161 | /*---------------------------------------------------------------------------+ | ||
162 | | Divide: Return arg1/arg2 to arg3. | | ||
163 | | | | ||
164 | | This routine does not use the exponents of arg1 and arg2, but does | | ||
165 | | adjust the exponent of arg3. | | ||
166 | | | | ||
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 | | ||
175 | | | | ||
176 | +---------------------------------------------------------------------------*/ | ||
177 | |||
178 | |||
179 | L_Full_Division: | ||
180 | /* Save extended dividend in local register */ | ||
181 | movl SIGL(%esi),%eax | ||
182 | movl %eax,FPU_accum_2 | ||
183 | movl SIGH(%esi),%eax | ||
184 | movl %eax,FPU_accum_3 | ||
185 | xorl %eax,%eax | ||
186 | movl %eax,FPU_accum_1 /* zero the extension */ | ||
187 | movl %eax,FPU_accum_0 /* zero the extension */ | ||
188 | |||
189 | movl SIGL(%esi),%eax /* Get the current num */ | ||
190 | movl SIGH(%esi),%edx | ||
191 | |||
192 | /*----------------------------------------------------------------------*/ | ||
193 | /* Initialization done. | ||
194 | Do the first 32 bits. */ | ||
195 | |||
196 | movb $0,FPU_ovfl_flag | ||
197 | cmpl SIGH(%ebx),%edx /* Test for imminent overflow */ | ||
198 | jb LLess_than_1 | ||
199 | ja LGreater_than_1 | ||
200 | |||
201 | cmpl SIGL(%ebx),%eax | ||
202 | jb LLess_than_1 | ||
203 | |||
204 | LGreater_than_1: | ||
205 | /* The dividend is greater or equal, would cause overflow */ | ||
206 | setaeb FPU_ovfl_flag /* Keep a record */ | ||
207 | |||
208 | subl SIGL(%ebx),%eax | ||
209 | sbbl SIGH(%ebx),%edx /* Prevent the overflow */ | ||
210 | movl %eax,FPU_accum_2 | ||
211 | movl %edx,FPU_accum_3 | ||
212 | |||
213 | LLess_than_1: | ||
214 | /* At this point, we have a dividend < divisor, with a record of | ||
215 | adjustment in FPU_ovfl_flag */ | ||
216 | |||
217 | /* We will divide by a number which is too large */ | ||
218 | movl SIGH(%ebx),%ecx | ||
219 | addl $1,%ecx | ||
220 | jnc LFirst_div_not_1 | ||
221 | |||
222 | /* here we need to divide by 100000000h, | ||
223 | i.e., no division at all.. */ | ||
224 | mov %edx,%eax | ||
225 | jmp LFirst_div_done | ||
226 | |||
227 | LFirst_div_not_1: | ||
228 | divl %ecx /* Divide the numerator by the augmented | ||
229 | denom ms dw */ | ||
230 | |||
231 | LFirst_div_done: | ||
232 | movl %eax,FPU_result_2 /* Put the result in the answer */ | ||
233 | |||
234 | mull SIGH(%ebx) /* mul by the ms dw of the denom */ | ||
235 | |||
236 | subl %eax,FPU_accum_2 /* Subtract from the num local reg */ | ||
237 | sbbl %edx,FPU_accum_3 | ||
238 | |||
239 | movl FPU_result_2,%eax /* Get the result back */ | ||
240 | mull SIGL(%ebx) /* now mul the ls dw of the denom */ | ||
241 | |||
242 | subl %eax,FPU_accum_1 /* Subtract from the num local reg */ | ||
243 | sbbl %edx,FPU_accum_2 | ||
244 | sbbl $0,FPU_accum_3 | ||
245 | je LDo_2nd_32_bits /* Must check for non-zero result here */ | ||
246 | |||
247 | #ifdef PARANOID | ||
248 | jb L_bugged_1 | ||
249 | #endif /* PARANOID */ | ||
250 | |||
251 | /* need to subtract another once of the denom */ | ||
252 | incl FPU_result_2 /* Correct the answer */ | ||
253 | |||
254 | movl SIGL(%ebx),%eax | ||
255 | movl SIGH(%ebx),%edx | ||
256 | subl %eax,FPU_accum_1 /* Subtract from the num local reg */ | ||
257 | sbbl %edx,FPU_accum_2 | ||
258 | |||
259 | #ifdef PARANOID | ||
260 | sbbl $0,FPU_accum_3 | ||
261 | jne L_bugged_1 /* Must check for non-zero result here */ | ||
262 | #endif /* PARANOID */ | ||
263 | |||
264 | /*----------------------------------------------------------------------*/ | ||
265 | /* Half of the main problem is done, there is just a reduced numerator | ||
266 | to handle now. | ||
267 | Work with the second 32 bits, FPU_accum_0 not used from now on */ | ||
268 | LDo_2nd_32_bits: | ||
269 | movl FPU_accum_2,%edx /* get the reduced num */ | ||
270 | movl FPU_accum_1,%eax | ||
271 | |||
272 | /* need to check for possible subsequent overflow */ | ||
273 | cmpl SIGH(%ebx),%edx | ||
274 | jb LDo_2nd_div | ||
275 | ja LPrevent_2nd_overflow | ||
276 | |||
277 | cmpl SIGL(%ebx),%eax | ||
278 | jb LDo_2nd_div | ||
279 | |||
280 | LPrevent_2nd_overflow: | ||
281 | /* The numerator is greater or equal, would cause overflow */ | ||
282 | /* prevent overflow */ | ||
283 | subl SIGL(%ebx),%eax | ||
284 | sbbl SIGH(%ebx),%edx | ||
285 | movl %edx,FPU_accum_2 | ||
286 | movl %eax,FPU_accum_1 | ||
287 | |||
288 | incl FPU_result_2 /* Reflect the subtraction in the answer */ | ||
289 | |||
290 | #ifdef PARANOID | ||
291 | je L_bugged_2 /* Can't bump the result to 1.0 */ | ||
292 | #endif /* PARANOID */ | ||
293 | |||
294 | LDo_2nd_div: | ||
295 | cmpl $0,%ecx /* augmented denom msw */ | ||
296 | jnz LSecond_div_not_1 | ||
297 | |||
298 | /* %ecx == 0, we are dividing by 1.0 */ | ||
299 | mov %edx,%eax | ||
300 | jmp LSecond_div_done | ||
301 | |||
302 | LSecond_div_not_1: | ||
303 | divl %ecx /* Divide the numerator by the denom ms dw */ | ||
304 | |||
305 | LSecond_div_done: | ||
306 | movl %eax,FPU_result_1 /* Put the result in the answer */ | ||
307 | |||
308 | mull SIGH(%ebx) /* mul by the ms dw of the denom */ | ||
309 | |||
310 | subl %eax,FPU_accum_1 /* Subtract from the num local reg */ | ||
311 | sbbl %edx,FPU_accum_2 | ||
312 | |||
313 | #ifdef PARANOID | ||
314 | jc L_bugged_2 | ||
315 | #endif /* PARANOID */ | ||
316 | |||
317 | movl FPU_result_1,%eax /* Get the result back */ | ||
318 | mull SIGL(%ebx) /* now mul the ls dw of the denom */ | ||
319 | |||
320 | subl %eax,FPU_accum_0 /* Subtract from the num local reg */ | ||
321 | sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ | ||
322 | sbbl $0,FPU_accum_2 | ||
323 | |||
324 | #ifdef PARANOID | ||
325 | jc L_bugged_2 | ||
326 | #endif /* PARANOID */ | ||
327 | |||
328 | jz LDo_3rd_32_bits | ||
329 | |||
330 | #ifdef PARANOID | ||
331 | cmpl $1,FPU_accum_2 | ||
332 | jne L_bugged_2 | ||
333 | #endif /* PARANOID */ | ||
334 | |||
335 | /* need to subtract another once of the denom */ | ||
336 | movl SIGL(%ebx),%eax | ||
337 | movl SIGH(%ebx),%edx | ||
338 | subl %eax,FPU_accum_0 /* Subtract from the num local reg */ | ||
339 | sbbl %edx,FPU_accum_1 | ||
340 | sbbl $0,FPU_accum_2 | ||
341 | |||
342 | #ifdef PARANOID | ||
343 | jc L_bugged_2 | ||
344 | jne L_bugged_2 | ||
345 | #endif /* PARANOID */ | ||
346 | |||
347 | addl $1,FPU_result_1 /* Correct the answer */ | ||
348 | adcl $0,FPU_result_2 | ||
349 | |||
350 | #ifdef PARANOID | ||
351 | jc L_bugged_2 /* Must check for non-zero result here */ | ||
352 | #endif /* PARANOID */ | ||
353 | |||
354 | /*----------------------------------------------------------------------*/ | ||
355 | /* The division is essentially finished here, we just need to perform | ||
356 | tidying operations. | ||
357 | Deal with the 3rd 32 bits */ | ||
358 | LDo_3rd_32_bits: | ||
359 | movl FPU_accum_1,%edx /* get the reduced num */ | ||
360 | movl FPU_accum_0,%eax | ||
361 | |||
362 | /* need to check for possible subsequent overflow */ | ||
363 | cmpl SIGH(%ebx),%edx /* denom */ | ||
364 | jb LRound_prep | ||
365 | ja LPrevent_3rd_overflow | ||
366 | |||
367 | cmpl SIGL(%ebx),%eax /* denom */ | ||
368 | jb LRound_prep | ||
369 | |||
370 | LPrevent_3rd_overflow: | ||
371 | /* prevent overflow */ | ||
372 | subl SIGL(%ebx),%eax | ||
373 | sbbl SIGH(%ebx),%edx | ||
374 | movl %edx,FPU_accum_1 | ||
375 | movl %eax,FPU_accum_0 | ||
376 | |||
377 | addl $1,FPU_result_1 /* Reflect the subtraction in the answer */ | ||
378 | adcl $0,FPU_result_2 | ||
379 | jne LRound_prep | ||
380 | jnc LRound_prep | ||
381 | |||
382 | /* This is a tricky spot, there is an overflow of the answer */ | ||
383 | movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */ | ||
384 | |||
385 | LRound_prep: | ||
386 | /* | ||
387 | * Prepare for rounding. | ||
388 | * To test for rounding, we just need to compare 2*accum with the | ||
389 | * denom. | ||
390 | */ | ||
391 | movl FPU_accum_0,%ecx | ||
392 | movl FPU_accum_1,%edx | ||
393 | movl %ecx,%eax | ||
394 | orl %edx,%eax | ||
395 | jz LRound_ovfl /* The accumulator contains zero. */ | ||
396 | |||
397 | /* Multiply by 2 */ | ||
398 | clc | ||
399 | rcll $1,%ecx | ||
400 | rcll $1,%edx | ||
401 | jc LRound_large /* No need to compare, denom smaller */ | ||
402 | |||
403 | subl SIGL(%ebx),%ecx | ||
404 | sbbl SIGH(%ebx),%edx | ||
405 | jnc LRound_not_small | ||
406 | |||
407 | movl $0x70000000,%eax /* Denom was larger */ | ||
408 | jmp LRound_ovfl | ||
409 | |||
410 | LRound_not_small: | ||
411 | jnz LRound_large | ||
412 | |||
413 | movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */ | ||
414 | jmp LRound_ovfl | ||
415 | |||
416 | LRound_large: | ||
417 | movl $0xff000000,%eax /* Denom was smaller */ | ||
418 | |||
419 | LRound_ovfl: | ||
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 ? */ | ||
423 | je LRound_precision | ||
424 | |||
425 | incw EXP(%edi) | ||
426 | |||
427 | /* shift the mantissa right one bit */ | ||
428 | stc /* Will set the ms bit */ | ||
429 | rcrl FPU_result_2 | ||
430 | rcrl FPU_result_1 | ||
431 | rcrl %eax | ||
432 | |||
433 | /* Round the result as required */ | ||
434 | LRound_precision: | ||
435 | decw EXP(%edi) /* binary point between 1st & 2nd bits */ | ||
436 | |||
437 | movl %eax,%edx | ||
438 | movl FPU_result_1,%ebx | ||
439 | movl FPU_result_2,%eax | ||
440 | jmp fpu_reg_round | ||
441 | |||
442 | |||
443 | #ifdef PARANOID | ||
444 | /* The logic is wrong if we got here */ | ||
445 | L_bugged: | ||
446 | pushl EX_INTERNAL|0x202 | ||
447 | call EXCEPTION | ||
448 | pop %ebx | ||
449 | jmp L_exit | ||
450 | |||
451 | L_bugged_1: | ||
452 | pushl EX_INTERNAL|0x203 | ||
453 | call EXCEPTION | ||
454 | pop %ebx | ||
455 | jmp L_exit | ||
456 | |||
457 | L_bugged_2: | ||
458 | pushl EX_INTERNAL|0x204 | ||
459 | call EXCEPTION | ||
460 | pop %ebx | ||
461 | jmp L_exit | ||
462 | |||
463 | L_exit: | ||
464 | movl $-1,%eax | ||
465 | popl %ebx | ||
466 | popl %edi | ||
467 | popl %esi | ||
468 | |||
469 | leave | ||
470 | ret | ||
471 | #endif /* PARANOID */ | ||