1 .file "reg_u_div.S"
2 /*---------------------------------------------------------------------------+ 3 | reg_u_div.S | 4 | | 5 | Core division routines | 6 | | 7 | Copyright (C) 1992,1993 | 8 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | 9 | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | 10 | | 11 | | 12 +---------------------------------------------------------------------------*/ 13
14 /*---------------------------------------------------------------------------+ 15 | Kernel for the division routines. | 16 | | 17 | void reg_u_div(FPU_REG *a, FPU_REG *a, | 18 | FPU_REG *dest, unsigned int control_word) | 19 | | 20 | Does not compute the destination exponent, but does adjust it. | 21 +---------------------------------------------------------------------------*/ 22
23 #include "exception.h"
24 #include "fpu_asm.h"
25 #include "control_w.h"
26
27
28 // #define dSIGL(x) (x) 29 // #define dSIGH(x) 4(x) 30
31
32 .data
33 /* 34 Local storage: 35 Result: accum_3:accum_2:accum_1:accum_0 36 Overflow flag: ovfl_flag 37 */ 38 .align 2,0
39 accum_3:
40 .long 0
41 accum_2:
42 .long 0
43 accum_1:
44 .long 0
45 accum_0:
46 .long 0
47 result_1:
48 .long 0
49 result_2:
50 .long 0
51 ovfl_flag:
52 .byte 0
53
54
55 .text
56 .align 2,144
57
58 .globl _reg_u_div
59
60 .globl _divide_kernel
61
62 _reg_u_div:
63 pushl %ebp
64 movl %esp,%ebp
65
66 pushl %esi
67 pushl %edi
68 pushl %ebx
69
70 movl PARAM1,%esi /* pointer to num */ 71 movl PARAM2,%ebx /* pointer to denom */ 72 movl PARAM3,%edi /* pointer to answer */ 73
74 _divide_kernel:
75 #ifdefPARANOID 76 // testl $0x80000000, SIGH(%esi) // Dividend 77 // je L_bugged 78 testl $0x80000000, SIGH(%ebx) // Divisor 79 je L_bugged
80 #endif PARANOID
81
82 /* Check if the divisor can be treated as having just 32 bits */ 83 cmpl $0,SIGL(%ebx)
84 jnz L_Full_Division /* Can't do a quick divide */ 85
86 /* We should be able to zip through the division here */ 87 movl SIGH(%ebx),%ecx /* The divisor */ 88 movl SIGH(%esi),%edx /* Dividend */ 89 movl SIGL(%esi),%eax /* Dividend */ 90
91 cmpl %ecx,%edx
92 setaeb ovfl_flag /* Keep a record */ 93 jb L_no_adjust
94
95 subl %ecx,%edx /* Prevent the overflow */ 96
97 L_no_adjust:
98 /* Divide the 64 bit number by the 32 bit denominator */ 99 divl %ecx
100 movl %eax,result_2
101
102 /* Work on the remainder of the first division */ 103 xorl %eax,%eax
104 divl %ecx
105 movl %eax,result_1
106
107 /* Work on the remainder of the 64 bit division */ 108 xorl %eax,%eax
109 divl %ecx
110
111 testb $255,ovfl_flag /* was the num > denom ? */ 112 je L_no_overflow
113
114 /* Do the shifting here */ 115 /* increase the exponent */ 116 incl EXP(%edi)
117
118 /* shift the mantissa right one bit */ 119 stc /* To set the ms bit */ 120 rcrl result_2
121 rcrl result_1
122 rcrl %eax
123
124 L_no_overflow:
125 jmp LRound_precision // Do the rounding as required 126
127
128 /*---------------------------------------------------------------------------+ 129 | Divide: Return arg1/arg2 to arg3. | 130 | | 131 | This routine does not use the exponents of arg1 and arg2, but does | 132 | adjust the exponent of arg3. | 133 | | 134 | The maximum returned value is (ignoring exponents) | 135 | .ffffffff ffffffff | 136 | ------------------ = 1.ffffffff fffffffe | 137 | .80000000 00000000 | 138 | and the minimum is | 139 | .80000000 00000000 | 140 | ------------------ = .80000000 00000001 (rounded) | 141 | .ffffffff ffffffff | 142 | | 143 +---------------------------------------------------------------------------*/ 144
145
146 L_Full_Division:
147 // Save extended dividend in local register 148 movl SIGL(%esi),%eax
149 movl %eax,accum_2
150 movl SIGH(%esi),%eax
151 movl %eax,accum_3
152 xorl %eax,%eax
153 movl %eax,accum_1 /* zero the extension */ 154 movl %eax,accum_0 /* zero the extension */ 155
156 movl SIGL(%esi),%eax /* Get the current num */ 157 movl SIGH(%esi),%edx
158
159 /*----------------------------------------------------------------------*/ 160 /* Initialization done */ 161 /* Do the first 32 bits */ 162
163 movb $0,ovfl_flag
164 cmpl SIGH(%ebx),%edx /* Test for imminent overflow */ 165 jb LLess_than_1
166 ja LGreater_than_1
167
168 cmpl SIGL(%ebx),%eax
169 jb LLess_than_1
170
171 LGreater_than_1:
172 /* The dividend is greater or equal, would cause overflow */ 173 setaeb ovfl_flag /* Keep a record */ 174
175 subl SIGL(%ebx),%eax
176 sbbl SIGH(%ebx),%edx /* Prevent the overflow */ 177 movl %eax,accum_2
178 movl %edx,accum_3
179
180 LLess_than_1:
181 /* At this point, we have a dividend < divisor, with a record of 182 adjustment in ovfl_flag */ 183
184 /* We will divide by a number which is too large */ 185 movl SIGH(%ebx),%ecx
186 addl $1,%ecx
187 jnc LFirst_div_not_1
188
189 /* here we need to divide by 100000000h, 190 i.e., no division at all.. */ 191 mov %edx,%eax
192 jmp LFirst_div_done
193
194 LFirst_div_not_1:
195 divl %ecx /* Divide the numerator by the augmented 196 denom ms dw */ 197
198 LFirst_div_done:
199 movl %eax,result_2 /* Put the result in the answer */ 200
201 mull SIGH(%ebx) /* mul by the ms dw of the denom */ 202
203 subl %eax,accum_2 /* Subtract from the num local reg */ 204 sbbl %edx,accum_3
205
206 movl result_2,%eax /* Get the result back */ 207 mull SIGL(%ebx) /* now mul the ls dw of the denom */ 208
209 subl %eax,accum_1 /* Subtract from the num local reg */ 210 sbbl %edx,accum_2
211 sbbl $0,accum_3
212 je LDo_2nd_32_bits /* Must check for non-zero result here */ 213
214 #ifdefPARANOID 215 jb L_bugged_1
216 #endif PARANOID
217
218 /* need to subtract another once of the denom */ 219 incl result_2 /* Correct the answer */ 220
221 movl SIGL(%ebx),%eax
222 movl SIGH(%ebx),%edx
223 subl %eax,accum_1 /* Subtract from the num local reg */ 224 sbbl %edx,accum_2
225
226 #ifdefPARANOID 227 sbbl $0,accum_3
228 jne L_bugged_1 /* Must check for non-zero result here */ 229 #endif PARANOID
230
231 /*----------------------------------------------------------------------*/ 232 /* Half of the main problem is done, there is just a reduced numerator 233 to handle now */ 234 /* Work with the second 32 bits, accum_0 not used from now on */ 235 LDo_2nd_32_bits:
236 movl accum_2,%edx /* get the reduced num */ 237 movl accum_1,%eax
238
239 /* need to check for possible subsequent overflow */ 240 cmpl SIGH(%ebx),%edx
241 jb LDo_2nd_div
242 ja LPrevent_2nd_overflow
243
244 cmpl SIGL(%ebx),%eax
245 jb LDo_2nd_div
246
247 LPrevent_2nd_overflow:
248 /* The numerator is greater or equal, would cause overflow */ 249 /* prevent overflow */ 250 subl SIGL(%ebx),%eax
251 sbbl SIGH(%ebx),%edx
252 movl %edx,accum_2
253 movl %eax,accum_1
254
255 incl result_2 /* Reflect the subtraction in the answer */ 256
257 #ifdefPARANOID 258 je L_bugged_2 /* Can't bump the result to 1.0 */ 259 #endif PARANOID
260
261 LDo_2nd_div:
262 cmpl $0,%ecx // augmented denom msw 263 jnz LSecond_div_not_1
264
265 /* %ecx == 0, we are dividing by 1.0 */ 266 mov %edx,%eax
267 jmp LSecond_div_done
268
269 LSecond_div_not_1:
270 divl %ecx /* Divide the numerator by the denom ms dw */ 271
272 LSecond_div_done:
273 movl %eax,result_1 /* Put the result in the answer */ 274
275 mull SIGH(%ebx) /* mul by the ms dw of the denom */ 276
277 subl %eax,accum_1 /* Subtract from the num local reg */ 278 sbbl %edx,accum_2
279
280 #ifdefPARANOID 281 jc L_bugged_2
282 #endif PARANOID
283
284 movl result_1,%eax /* Get the result back */ 285 mull SIGL(%ebx) /* now mul the ls dw of the denom */ 286
287 subl %eax,accum_0 /* Subtract from the num local reg */ 288 sbbl %edx,accum_1 /* Subtract from the num local reg */ 289 sbbl $0,accum_2
290
291 #ifdefPARANOID 292 jc L_bugged_2
293 #endif PARANOID
294
295 jz LDo_3rd_32_bits
296
297 #ifdefPARANOID 298 cmpl $1,accum_2
299 jne L_bugged_2
300 #endif PARANOID
301
302 /* need to subtract another once of the denom */ 303 movl SIGL(%ebx),%eax
304 movl SIGH(%ebx),%edx
305 subl %eax,accum_0 /* Subtract from the num local reg */ 306 sbbl %edx,accum_1
307 sbbl $0,accum_2
308
309 #ifdefPARANOID 310 jc L_bugged_2
311 jne L_bugged_2
312 #endif PARANOID
313
314 addl $1,result_1 /* Correct the answer */ 315 adcl $0,result_2
316
317 #ifdefPARANOID 318 jc L_bugged_2 /* Must check for non-zero result here */ 319 #endif PARANOID
320
321 /*----------------------------------------------------------------------*/ 322 /* The division is essentially finished here, we just need to perform 323 tidying operations. */ 324 /* deal with the 3rd 32 bits */ 325 LDo_3rd_32_bits:
326 movl accum_1,%edx /* get the reduced num */ 327 movl accum_0,%eax
328
329 /* need to check for possible subsequent overflow */ 330 cmpl SIGH(%ebx),%edx // denom 331 jb LRound_prep
332 ja LPrevent_3rd_overflow
333
334 cmpl SIGL(%ebx),%eax // denom 335 jb LRound_prep
336
337 LPrevent_3rd_overflow:
338 /* prevent overflow */ 339 subl SIGL(%ebx),%eax
340 sbbl SIGH(%ebx),%edx
341 movl %edx,accum_1
342 movl %eax,accum_0
343
344 addl $1,result_1 /* Reflect the subtraction in the answer */ 345 adcl $0,result_2
346 jne LRound_prep
347 jnc LRound_prep
348
349 /* This is a tricky spot, there is an overflow of the answer */ 350 movb $255,ovfl_flag /* Overflow -> 1.000 */ 351
352 LRound_prep:
353 // Prepare for rounding. 354 // To test for rounding, we just need to compare 2*accum with the 355 // denom. 356 movl accum_0,%ecx
357 movl accum_1,%edx
358 movl %ecx,%eax
359 orl %edx,%eax
360 jz LRound_ovfl // The accumulator contains zero. 361
362 // Multiply by 2 363 clc
364 rcll $1,%ecx
365 rcll $1,%edx
366 jc LRound_large // No need to compare, denom smaller 367
368 subl SIGL(%ebx),%ecx
369 sbbl SIGH(%ebx),%edx
370 jnc LRound_not_small
371
372 movl $0x70000000,%eax // Denom was larger 373 jmp LRound_ovfl
374
375 LRound_not_small:
376 jnz LRound_large
377
378 movl $0x80000000,%eax // Remainder was exactly 1/2 denom 379 jmp LRound_ovfl
380
381 LRound_large:
382 movl $0xff000000,%eax // Denom was smaller 383
384 LRound_ovfl:
385 /* We are now ready to deal with rounding, but first we must get 386 the bits properly aligned */ 387 testb $255,ovfl_flag /* was the num > denom ? */ 388 je LRound_precision
389
390 incl EXP(%edi)
391
392 /* shift the mantissa right one bit */ 393 stc // Will set the ms bit 394 rcrl result_2
395 rcrl result_1
396 rcrl %eax
397
398 // Round the result as required 399 LRound_precision:
400 decl EXP(%edi) /* binary point between 1st & 2nd bits */ 401
402 movl %eax,%edx
403 movl result_1,%ebx
404 movl result_2,%eax
405 jmp FPU_round
406
407
408 #ifdefPARANOID 409 /* The logic is wrong if we got here */ 410 L_bugged:
411 pushl EX_INTERNAL|0x202
412 call EXCEPTION
413 pop %ebx
414 jmp L_exit
415
416 L_bugged_1:
417 pushl EX_INTERNAL|0x203
418 call EXCEPTION
419 pop %ebx
420 jmp L_exit
421
422 L_bugged_2:
423 pushl EX_INTERNAL|0x204
424 call EXCEPTION
425 pop %ebx
426 jmp L_exit
427
428 L_exit:
429 popl %ebx
430 popl %edi
431 popl %esi
432
433 leave
434 ret
435 #endif PARANOID