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