root/kernel/FPU-emu/reg_u_div.S

/* [previous][next][first][last][top][bottom][index][help] */
   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 #ifdef PARANOID
  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 #ifdef PARANOID
 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 #ifdef PARANOID
 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 #ifdef PARANOID
 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 #ifdef PARANOID
 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 #ifdef PARANOID
 292         jc      L_bugged_2
 293 #endif PARANOID
 294 
 295         jz      LDo_3rd_32_bits
 296 
 297 #ifdef PARANOID
 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 #ifdef PARANOID
 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 #ifdef PARANOID
 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 #ifdef PARANOID
 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

/* [previous][next][first][last][top][bottom][index][help] */