linux/arch/x86/math-emu/reg_u_div.S
<<
>>
Prefs
   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)
 475