chipr32.asm

来自「开放源码的编译器open watcom 1.6.0版的源代码」· 汇编 代码 · 共 852 行 · 第 1/3 页

ASM
852
字号
        fld     tbyte ptr [MAIN_NUMER+esp]   ; load the numerator
        fprem                           ; and finally do a remainder
; prem_main_routine end
rem_done:
        test    edx, 03h
        jz      rem_exit
        fnstsw  [esp + MAIN_FPREM_SW]   ; save Q0 Q1 and Q2
        test    edx, 01h
        jz      do_not_de_scale
; De-scale the result. Go to pc=80 to prevent from fmul
; from user precision (fprem does not round the result).
        fnstcw  [esp + MAIN_PREV_CW]    ; save callers control word
        mov     eax, [esp + MAIN_PREV_CW]
        or      eax, 0300h              ; pc = 80
        mov     [esp + MAIN_PATCH_CW], eax
        fldcw   [esp + MAIN_PATCH_CW]
        fmul    qword ptr one_shr_64
        fldcw   [esp + MAIN_PREV_CW]    ; restore callers CW
do_not_de_scale:
        mov     eax, [esp + MAIN_FPREM_SW]
        fxch
        fstp    st
        fld     tbyte ptr[esp + MAIN_DENOM_SAVE]
        fxch
        and     eax, 04300h             ; restore saved Q0, Q1, Q2
        sub     esp, ENV_SIZE
        fnstenv [esp]
        and     [esp].STATUS_WORD, 0bcffh
        or      [esp].STATUS_WORD, eax
        fldenv  [esp]
        add     esp, ENV_SIZE
rem_exit:
        pop     ecx
        pop     ebx
        pop     eax
        CHECKSW                         ; debug only: save status
        ret
fprem_common    ENDP

comment ~****************************************************************

;
; float frem_chk (float numer, float denom)
;
        public  frem_chk
frem_chk        PROC    NEAR
        push    edx
        sub     esp, STACK_SIZE
        fld     dword ptr [STACK_SIZE+8+esp]
        fstp    tbyte ptr [NUMER+esp]
        fld     dword ptr [STACK_SIZE+12+esp]
        fstp    tbyte ptr [DENOM+esp]
        mov     edx, 0                  ; dx = 1 if denormal extended divisor
        call    fprem_common
        fxch
        fstp    st
        add     esp, STACK_SIZE
        pop     edx
        ret
frem_chk        ENDP
; end frem_chk

;
; double drem_chk (double numer, double denom)
;
        public  drem_chk
drem_chk        PROC    NEAR
        push    edx
        sub     esp, STACK_SIZE
        fld     qword ptr [STACK_SIZE+8+esp]
        fstp    tbyte ptr [NUMER+esp]
        fld     qword ptr [STACK_SIZE+16+esp]
        fstp    tbyte ptr [DENOM+esp]
        mov     edx, 0                  ; dx = 1 if denormal extended divisor
        call    fprem_common
        fxch
        fstp    st
        add     esp, STACK_SIZE
        pop     edx
        ret

drem_chk        ENDP
; end drem_chk

;
; long double lrem_chk(long double number,long double denom)
;
        public  lrem_chk
lrem_chk        PROC    NEAR
        fld     tbyte ptr [20+esp]
        fld     tbyte ptr [4+esp]
        call    fprem_chk
        fxch
        fstp    st
        ret
lrem_chk        ENDP

**********************************************************************~

;
; FPREM: ST = remainder(ST, ST(1))
;
; Compiler version of the FPREM must preserve the arguments in the floating
; point stack.

        public  __fprem_chk
        defpe   __fprem_chk
        push    edx
        sub     esp, STACK_SIZE
        fstp    tbyte ptr [NUMER+esp]
        fstp    tbyte ptr [DENOM+esp]
        xor     edx, edx
; prem_main_routine begin
        mov     eax,[DENOM+6+esp]       ; exponent and high 16 bits of mantissa
        test    eax,07fff0000h          ; check for denormal
        jz      denormal
        call    fprem_common
        add     esp, STACK_SIZE
        pop     edx
        ret

denormal:
        fld     tbyte ptr [DENOM+esp]   ; load the denominator
        fld     tbyte ptr [NUMER+esp]   ; load the numerator
        mov     eax, [DENOM+esp]        ; test for whole mantissa == 0
        or      eax, [DENOM+4+esp]      ; test for whole mantissa == 0
        jz      remainder_hardware_ok_l ; denominator is zero
        fxch
        fstp    tbyte ptr[esp + DENOM_SAVE]     ; save org denominator
        fld     tbyte ptr[esp + DENOM]
        fxch
        or      edx, 02h
;
; For this we need pc=80.  Also, mask exceptions so we don't take any
; denormal operand exceptions.  It is guaranteed that the descaling
; later on will take underflow, which is what the hardware would have done
; on a normal fprem.
;
        fnstcw  [PREV_CW+esp]           ; save caller's control word
        mov     eax, [PREV_CW+esp]
        or      eax, 0033fh             ; mask exceptions, pc=80
        mov     [PATCH_CW+esp], eax
        fldcw   [PATCH_CW+esp]          ; mask exceptions & pc=80

; The denominator is a denormal.  For most numerators, scale both numerator
; and denominator to get rid of denormals.  Then execute the common code
; with the flag set to indicate that the result must be de-scaled.
; For large numerators this won't work because the scaling would cause
; overflow.  In this case we know the numerator is large, the denominator
; is small (denormal), so the exponent difference is also large.  This means
; the rem_large code will be used and this code depends on the difference
; in exponents modulo 64.  Adding 64 to the denominators exponent
; doesn't change the modulo 64 difference.  So we can scale the denominator
; by 64, making it not denormal, and this won't effect the result.
;
; To start with, figure out if numerator is large

        mov     eax, [esp + NUMER + 8]  ; load numerator exponent
        and     eax, 7fffh              ; isolate numerator exponent
        cmp     eax, 7fbeh              ; compare Nexp to Maxexp-64
        ja      big_numer_rem_de        ; jif big numerator

; So the numerator is not large scale both numerator and denominator

        or      edx, 1                  ; edx = 1, if denormal extended divisor
        fmul    qword ptr one_shl_64    ; make numerator not denormal
        fstp    tbyte ptr[esp + NUMER]
        fmul    qword ptr one_shl_64    ; make denominator not denormal
        fstp    tbyte ptr[esp + DENOM]
        jmp     scaling_done

; The numerator is large.  Scale only the denominator, which will not
; change the result which we know will be partial.  Set the scale flag
; to false.
big_numer_rem_de:
        ; We must do this with pc=80 to avoid rounding to single/double.
        ; In this case we do not mask exceptions so that we will take
        ; denormal operand, as would the hardware.
        fnstcw  [PREV_CW+esp]           ; save caller's control word
        mov     eax, [PREV_CW+esp]
        or      eax, 00300h             ; pc=80
        mov     [PATCH_CW+esp], eax
        fldcw   [PATCH_CW+esp]          ;  pc=80

        fstp    st                      ; Toss numerator
        fmul    qword ptr one_shl_64    ; make denominator not denormal
        fstp    tbyte ptr[esp + DENOM]

; Restore the control word which was fiddled to scale at 80-bit precision.
; Then call the common code.
scaling_done:
        fldcw   [esp + PREV_CW]         ; restore callers control word
        call    fprem_common
        add     esp, STACK_SIZE
        pop     edx
        ret

remainder_hardware_ok_l:
        fprem                           ; and finally do a remainder

        CHECKSW

        add     esp, STACK_SIZE
        pop     edx
        ret
__fprem_chk       ENDP
; end fprem_chk


;
; FPREM1 code begins here
;


fprem1_common   PROC    NEAR

        push    eax
        push    ebx
        push    ecx
        mov     eax, [MAIN_DENOM+6+esp] ; exponent and high 16 bits of mantissa
        xor     eax, ONESMASK           ; invert bits that have to be one
        test    eax, ONESMASK           ; check bits that have to be one
        jnz     remainder1_hardware_ok
        shr     eax, 11
        and     eax, 0fh
        cmp     byte ptr fprem_risc_table[eax], 0     ; check for (1,4,7,a,d)
        jz      remainder1_hardware_ok

; The denominator has the bit pattern. Weed out the funny cases like NaNs
; before applying the software version. Our caller guarantees that the
; denominator is not a denormal. Here we check for:
;       denominator     inf, NaN, unnormal
;       numerator       inf, NaN, unnormal, denormal

        mov     eax, [MAIN_DENOM+6+esp] ; exponent and high 16 bits of mantissa
        and     eax, 07fff0000h         ; mask the exponent only
        cmp     eax, 07fff0000h         ; check for INF or NaN
        je      remainder1_hardware_ok
        mov     eax, [MAIN_NUMER+6+esp] ; exponent and high 16 bits of mantissa
        and     eax, 07fff0000h         ; mask the exponent only
        jz      remainder1_hardware_ok  ; jif numerator denormal
        cmp     eax, 07fff0000h         ; check for INF or NaN
        je      remainder1_hardware_ok
        mov     eax, [esp + MAIN_NUMER + 4]     ; high mantissa bits - numerator
        add     eax, eax                ; set carry if explicit bit set
        jnz     remainder1_hardware_ok  ; jmp if numerator is unnormal
        mov     eax, [esp + MAIN_DENOM + 4] ; high mantissa bits - denominator
        add     eax, eax                ; set carry if explicit bit set
        jnz     remainder1_hardware_ok  ; jmp if denominator is unnormal

rem1_patch:
        mov     eax, [MAIN_DENOM+8+esp] ; sign and exponent of y (denominator)
        and     eax, 07fffh              ; clear sy
        add     eax, 63                  ; evaluate ey + 63
        mov     ebx, [MAIN_NUMER+8+esp]  ; sign and exponent of x (numerator)
        and     ebx, 07fffh              ; clear sx
        sub     ebx, eax                 ; evaluate the exponent difference (ex - ey)
        ja      rem1_large              ; if ex > ey + 63, case of large arguments
rem1_patch_loop:
        mov     eax, [MAIN_DENOM+8+esp]  ; sign and exponent of y (denominator)
        and     eax, 07fffh             ; clear sy
        add     eax, 10                 ; evaluate ey + 10
        mov     ebx, [MAIN_NUMER+8+esp] ; sign and exponent of x (numerator)
        and     ebx, 07fffh             ; clear sx
        sub     ebx, eax                ; evaluate the exponent difference (ex - ey)
        js      remainder1_hardware_ok  ; safe if ey + 10 > ex
        fld     tbyte ptr [MAIN_NUMER+esp]   ; load the numerator
        mov     eax, [MAIN_DENOM+8+esp] ; sign and exponent of y (denominator)
        mov     ebx, [MAIN_NUMER+8+esp] ; sign and exponent of x (numerator)
        and     ebx, 07fffh             ; clear sx
        mov     ecx, ebx
        sub     ebx, eax
        and     ebx, 07h
        or      ebx, 04h
        sub     ecx, ebx
        mov     ebx, eax
        and     ebx, 08000h             ; keep sy
        or      ecx, ebx                ; merge the sign of y
        mov     dword ptr [MAIN_DENOM+8+esp], ecx
        fld     tbyte ptr [MAIN_DENOM+esp]   ; load the shifted denominator
        mov     dword ptr [MAIN_DENOM+8+esp], eax       ; restore the initial denominator
        fxch
        fprem                           ; this rem is safe
        fstp    tbyte ptr [MAIN_NUMER+esp]      ; update the numerator

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?