diff options
Diffstat (limited to 'arch/x86/math-emu/div_Xsig.S')
-rw-r--r-- | arch/x86/math-emu/div_Xsig.S | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/arch/x86/math-emu/div_Xsig.S b/arch/x86/math-emu/div_Xsig.S new file mode 100644 index 000000000000..f77ba3058b31 --- /dev/null +++ b/arch/x86/math-emu/div_Xsig.S | |||
@@ -0,0 +1,365 @@ | |||
1 | .file "div_Xsig.S" | ||
2 | /*---------------------------------------------------------------------------+ | ||
3 | | div_Xsig.S | | ||
4 | | | | ||
5 | | Division subroutine for 96 bit quantities | | ||
6 | | | | ||
7 | | Copyright (C) 1994,1995 | | ||
8 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | ||
9 | | Australia. E-mail billm@jacobi.maths.monash.edu.au | | ||
10 | | | | ||
11 | | | | ||
12 | +---------------------------------------------------------------------------*/ | ||
13 | |||
14 | /*---------------------------------------------------------------------------+ | ||
15 | | Divide the 96 bit quantity pointed to by a, by that pointed to by b, and | | ||
16 | | put the 96 bit result at the location d. | | ||
17 | | | | ||
18 | | The result may not be accurate to 96 bits. It is intended for use where | | ||
19 | | a result better than 64 bits is required. The result should usually be | | ||
20 | | good to at least 94 bits. | | ||
21 | | The returned result is actually divided by one half. This is done to | | ||
22 | | prevent overflow. | | ||
23 | | | | ||
24 | | .aaaaaaaaaaaaaa / .bbbbbbbbbbbbb -> .dddddddddddd | | ||
25 | | | | ||
26 | | void div_Xsig(Xsig *a, Xsig *b, Xsig *dest) | | ||
27 | | | | ||
28 | +---------------------------------------------------------------------------*/ | ||
29 | |||
30 | #include "exception.h" | ||
31 | #include "fpu_emu.h" | ||
32 | |||
33 | |||
34 | #define XsigLL(x) (x) | ||
35 | #define XsigL(x) 4(x) | ||
36 | #define XsigH(x) 8(x) | ||
37 | |||
38 | |||
39 | #ifndef NON_REENTRANT_FPU | ||
40 | /* | ||
41 | Local storage on the stack: | ||
42 | Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 | ||
43 | */ | ||
44 | #define FPU_accum_3 -4(%ebp) | ||
45 | #define FPU_accum_2 -8(%ebp) | ||
46 | #define FPU_accum_1 -12(%ebp) | ||
47 | #define FPU_accum_0 -16(%ebp) | ||
48 | #define FPU_result_3 -20(%ebp) | ||
49 | #define FPU_result_2 -24(%ebp) | ||
50 | #define FPU_result_1 -28(%ebp) | ||
51 | |||
52 | #else | ||
53 | .data | ||
54 | /* | ||
55 | Local storage in a static area: | ||
56 | Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 | ||
57 | */ | ||
58 | .align 4,0 | ||
59 | FPU_accum_3: | ||
60 | .long 0 | ||
61 | FPU_accum_2: | ||
62 | .long 0 | ||
63 | FPU_accum_1: | ||
64 | .long 0 | ||
65 | FPU_accum_0: | ||
66 | .long 0 | ||
67 | FPU_result_3: | ||
68 | .long 0 | ||
69 | FPU_result_2: | ||
70 | .long 0 | ||
71 | FPU_result_1: | ||
72 | .long 0 | ||
73 | #endif /* NON_REENTRANT_FPU */ | ||
74 | |||
75 | |||
76 | .text | ||
77 | ENTRY(div_Xsig) | ||
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 PARAM1,%esi /* pointer to num */ | ||
89 | movl PARAM2,%ebx /* pointer to denom */ | ||
90 | |||
91 | #ifdef PARANOID | ||
92 | testl $0x80000000, XsigH(%ebx) /* Divisor */ | ||
93 | je L_bugged | ||
94 | #endif /* PARANOID */ | ||
95 | |||
96 | |||
97 | /*---------------------------------------------------------------------------+ | ||
98 | | Divide: Return arg1/arg2 to arg3. | | ||
99 | | | | ||
100 | | The maximum returned value is (ignoring exponents) | | ||
101 | | .ffffffff ffffffff | | ||
102 | | ------------------ = 1.ffffffff fffffffe | | ||
103 | | .80000000 00000000 | | ||
104 | | and the minimum is | | ||
105 | | .80000000 00000000 | | ||
106 | | ------------------ = .80000000 00000001 (rounded) | | ||
107 | | .ffffffff ffffffff | | ||
108 | | | | ||
109 | +---------------------------------------------------------------------------*/ | ||
110 | |||
111 | /* Save extended dividend in local register */ | ||
112 | |||
113 | /* Divide by 2 to prevent overflow */ | ||
114 | clc | ||
115 | movl XsigH(%esi),%eax | ||
116 | rcrl %eax | ||
117 | movl %eax,FPU_accum_3 | ||
118 | movl XsigL(%esi),%eax | ||
119 | rcrl %eax | ||
120 | movl %eax,FPU_accum_2 | ||
121 | movl XsigLL(%esi),%eax | ||
122 | rcrl %eax | ||
123 | movl %eax,FPU_accum_1 | ||
124 | movl $0,%eax | ||
125 | rcrl %eax | ||
126 | movl %eax,FPU_accum_0 | ||
127 | |||
128 | movl FPU_accum_2,%eax /* Get the current num */ | ||
129 | movl FPU_accum_3,%edx | ||
130 | |||
131 | /*----------------------------------------------------------------------*/ | ||
132 | /* Initialization done. | ||
133 | Do the first 32 bits. */ | ||
134 | |||
135 | /* We will divide by a number which is too large */ | ||
136 | movl XsigH(%ebx),%ecx | ||
137 | addl $1,%ecx | ||
138 | jnc LFirst_div_not_1 | ||
139 | |||
140 | /* here we need to divide by 100000000h, | ||
141 | i.e., no division at all.. */ | ||
142 | mov %edx,%eax | ||
143 | jmp LFirst_div_done | ||
144 | |||
145 | LFirst_div_not_1: | ||
146 | divl %ecx /* Divide the numerator by the augmented | ||
147 | denom ms dw */ | ||
148 | |||
149 | LFirst_div_done: | ||
150 | movl %eax,FPU_result_3 /* Put the result in the answer */ | ||
151 | |||
152 | mull XsigH(%ebx) /* mul by the ms dw of the denom */ | ||
153 | |||
154 | subl %eax,FPU_accum_2 /* Subtract from the num local reg */ | ||
155 | sbbl %edx,FPU_accum_3 | ||
156 | |||
157 | movl FPU_result_3,%eax /* Get the result back */ | ||
158 | mull XsigL(%ebx) /* now mul the ls dw of the denom */ | ||
159 | |||
160 | subl %eax,FPU_accum_1 /* Subtract from the num local reg */ | ||
161 | sbbl %edx,FPU_accum_2 | ||
162 | sbbl $0,FPU_accum_3 | ||
163 | je LDo_2nd_32_bits /* Must check for non-zero result here */ | ||
164 | |||
165 | #ifdef PARANOID | ||
166 | jb L_bugged_1 | ||
167 | #endif /* PARANOID */ | ||
168 | |||
169 | /* need to subtract another once of the denom */ | ||
170 | incl FPU_result_3 /* Correct the answer */ | ||
171 | |||
172 | movl XsigL(%ebx),%eax | ||
173 | movl XsigH(%ebx),%edx | ||
174 | subl %eax,FPU_accum_1 /* Subtract from the num local reg */ | ||
175 | sbbl %edx,FPU_accum_2 | ||
176 | |||
177 | #ifdef PARANOID | ||
178 | sbbl $0,FPU_accum_3 | ||
179 | jne L_bugged_1 /* Must check for non-zero result here */ | ||
180 | #endif /* PARANOID */ | ||
181 | |||
182 | /*----------------------------------------------------------------------*/ | ||
183 | /* Half of the main problem is done, there is just a reduced numerator | ||
184 | to handle now. | ||
185 | Work with the second 32 bits, FPU_accum_0 not used from now on */ | ||
186 | LDo_2nd_32_bits: | ||
187 | movl FPU_accum_2,%edx /* get the reduced num */ | ||
188 | movl FPU_accum_1,%eax | ||
189 | |||
190 | /* need to check for possible subsequent overflow */ | ||
191 | cmpl XsigH(%ebx),%edx | ||
192 | jb LDo_2nd_div | ||
193 | ja LPrevent_2nd_overflow | ||
194 | |||
195 | cmpl XsigL(%ebx),%eax | ||
196 | jb LDo_2nd_div | ||
197 | |||
198 | LPrevent_2nd_overflow: | ||
199 | /* The numerator is greater or equal, would cause overflow */ | ||
200 | /* prevent overflow */ | ||
201 | subl XsigL(%ebx),%eax | ||
202 | sbbl XsigH(%ebx),%edx | ||
203 | movl %edx,FPU_accum_2 | ||
204 | movl %eax,FPU_accum_1 | ||
205 | |||
206 | incl FPU_result_3 /* Reflect the subtraction in the answer */ | ||
207 | |||
208 | #ifdef PARANOID | ||
209 | je L_bugged_2 /* Can't bump the result to 1.0 */ | ||
210 | #endif /* PARANOID */ | ||
211 | |||
212 | LDo_2nd_div: | ||
213 | cmpl $0,%ecx /* augmented denom msw */ | ||
214 | jnz LSecond_div_not_1 | ||
215 | |||
216 | /* %ecx == 0, we are dividing by 1.0 */ | ||
217 | mov %edx,%eax | ||
218 | jmp LSecond_div_done | ||
219 | |||
220 | LSecond_div_not_1: | ||
221 | divl %ecx /* Divide the numerator by the denom ms dw */ | ||
222 | |||
223 | LSecond_div_done: | ||
224 | movl %eax,FPU_result_2 /* Put the result in the answer */ | ||
225 | |||
226 | mull XsigH(%ebx) /* mul by the ms dw of the denom */ | ||
227 | |||
228 | subl %eax,FPU_accum_1 /* Subtract from the num local reg */ | ||
229 | sbbl %edx,FPU_accum_2 | ||
230 | |||
231 | #ifdef PARANOID | ||
232 | jc L_bugged_2 | ||
233 | #endif /* PARANOID */ | ||
234 | |||
235 | movl FPU_result_2,%eax /* Get the result back */ | ||
236 | mull XsigL(%ebx) /* now mul the ls dw of the denom */ | ||
237 | |||
238 | subl %eax,FPU_accum_0 /* Subtract from the num local reg */ | ||
239 | sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */ | ||
240 | sbbl $0,FPU_accum_2 | ||
241 | |||
242 | #ifdef PARANOID | ||
243 | jc L_bugged_2 | ||
244 | #endif /* PARANOID */ | ||
245 | |||
246 | jz LDo_3rd_32_bits | ||
247 | |||
248 | #ifdef PARANOID | ||
249 | cmpl $1,FPU_accum_2 | ||
250 | jne L_bugged_2 | ||
251 | #endif /* PARANOID */ | ||
252 | |||
253 | /* need to subtract another once of the denom */ | ||
254 | movl XsigL(%ebx),%eax | ||
255 | movl XsigH(%ebx),%edx | ||
256 | subl %eax,FPU_accum_0 /* Subtract from the num local reg */ | ||
257 | sbbl %edx,FPU_accum_1 | ||
258 | sbbl $0,FPU_accum_2 | ||
259 | |||
260 | #ifdef PARANOID | ||
261 | jc L_bugged_2 | ||
262 | jne L_bugged_2 | ||
263 | #endif /* PARANOID */ | ||
264 | |||
265 | addl $1,FPU_result_2 /* Correct the answer */ | ||
266 | adcl $0,FPU_result_3 | ||
267 | |||
268 | #ifdef PARANOID | ||
269 | jc L_bugged_2 /* Must check for non-zero result here */ | ||
270 | #endif /* PARANOID */ | ||
271 | |||
272 | /*----------------------------------------------------------------------*/ | ||
273 | /* The division is essentially finished here, we just need to perform | ||
274 | tidying operations. | ||
275 | Deal with the 3rd 32 bits */ | ||
276 | LDo_3rd_32_bits: | ||
277 | /* We use an approximation for the third 32 bits. | ||
278 | To take account of the 3rd 32 bits of the divisor | ||
279 | (call them del), we subtract del * (a/b) */ | ||
280 | |||
281 | movl FPU_result_3,%eax /* a/b */ | ||
282 | mull XsigLL(%ebx) /* del */ | ||
283 | |||
284 | subl %edx,FPU_accum_1 | ||
285 | |||
286 | /* A borrow indicates that the result is negative */ | ||
287 | jnb LTest_over | ||
288 | |||
289 | movl XsigH(%ebx),%edx | ||
290 | addl %edx,FPU_accum_1 | ||
291 | |||
292 | subl $1,FPU_result_2 /* Adjust the answer */ | ||
293 | sbbl $0,FPU_result_3 | ||
294 | |||
295 | /* The above addition might not have been enough, check again. */ | ||
296 | movl FPU_accum_1,%edx /* get the reduced num */ | ||
297 | cmpl XsigH(%ebx),%edx /* denom */ | ||
298 | jb LDo_3rd_div | ||
299 | |||
300 | movl XsigH(%ebx),%edx | ||
301 | addl %edx,FPU_accum_1 | ||
302 | |||
303 | subl $1,FPU_result_2 /* Adjust the answer */ | ||
304 | sbbl $0,FPU_result_3 | ||
305 | jmp LDo_3rd_div | ||
306 | |||
307 | LTest_over: | ||
308 | movl FPU_accum_1,%edx /* get the reduced num */ | ||
309 | |||
310 | /* need to check for possible subsequent overflow */ | ||
311 | cmpl XsigH(%ebx),%edx /* denom */ | ||
312 | jb LDo_3rd_div | ||
313 | |||
314 | /* prevent overflow */ | ||
315 | subl XsigH(%ebx),%edx | ||
316 | movl %edx,FPU_accum_1 | ||
317 | |||
318 | addl $1,FPU_result_2 /* Reflect the subtraction in the answer */ | ||
319 | adcl $0,FPU_result_3 | ||
320 | |||
321 | LDo_3rd_div: | ||
322 | movl FPU_accum_0,%eax | ||
323 | movl FPU_accum_1,%edx | ||
324 | divl XsigH(%ebx) | ||
325 | |||
326 | movl %eax,FPU_result_1 /* Rough estimate of third word */ | ||
327 | |||
328 | movl PARAM3,%esi /* pointer to answer */ | ||
329 | |||
330 | movl FPU_result_1,%eax | ||
331 | movl %eax,XsigLL(%esi) | ||
332 | movl FPU_result_2,%eax | ||
333 | movl %eax,XsigL(%esi) | ||
334 | movl FPU_result_3,%eax | ||
335 | movl %eax,XsigH(%esi) | ||
336 | |||
337 | L_exit: | ||
338 | popl %ebx | ||
339 | popl %edi | ||
340 | popl %esi | ||
341 | |||
342 | leave | ||
343 | ret | ||
344 | |||
345 | |||
346 | #ifdef PARANOID | ||
347 | /* The logic is wrong if we got here */ | ||
348 | L_bugged: | ||
349 | pushl EX_INTERNAL|0x240 | ||
350 | call EXCEPTION | ||
351 | pop %ebx | ||
352 | jmp L_exit | ||
353 | |||
354 | L_bugged_1: | ||
355 | pushl EX_INTERNAL|0x241 | ||
356 | call EXCEPTION | ||
357 | pop %ebx | ||
358 | jmp L_exit | ||
359 | |||
360 | L_bugged_2: | ||
361 | pushl EX_INTERNAL|0x242 | ||
362 | call EXCEPTION | ||
363 | pop %ebx | ||
364 | jmp L_exit | ||
365 | #endif /* PARANOID */ | ||