chipr16.asm

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

ASM
883
字号
        or      ax, 033fh               ; mask exceptions, pc=80
        mov     [bp + MAIN_PATCH_CW], ax
        fldcw   [bp + MAIN_PATCH_CW]
        mov     ax, [MAIN_DENOM+8+bp]   ; sign and exponent of y (denominator)
        and     ax, 07fffh              ; clear sy
        mov     bx, [MAIN_NUMER+8+bp]   ; sign and exponent of x (numerator)
        and     bx, 07fffh              ; clear sx
        sub     bx, ax                  ; evaluate the exponent difference
        and     bx, 03fh
        or      bx, 020h
        add     bx, 1
        mov     cx, bx
        mov     ax, [MAIN_DENOM+8+bp]   ; sign and exponent of y (denominator)
        mov     bx, [MAIN_NUMER+8+bp]   ; sign and exponent of x (numerator)
        and     bx, 07fffh              ; clear sx
        and     ax, 08000h              ; keep sy
        or      bx, ax                  ; merge the sign of y
        mov     word ptr[MAIN_DENOM+8+bp], bx   ; make ey equal to ex (scaled denominator)
        fld     tbyte ptr [MAIN_DENOM+bp]   ; load the scaled denominator
        fabs
        fld     tbyte ptr [MAIN_NUMER+bp]   ; load the numerator
        fabs
rem1_large_loop:
        fcom
        fstsw  ax
        and     ax, 00100h
        jnz     rem1_no_sub
        fsub    st, st(1)
rem1_no_sub:
        fxch
        fmul    qword ptr half
        fxch
        sub     cx, 1                   ; decrement the loop counter
        jnz     rem1_large_loop
        mov     bx, [MAIN_NUMER+8+bp]   ; sign and exponent of x (numerator)

        fstp    tbyte ptr[bp + MAIN_NUMER]      ; save result
        fstp    st                      ; toss modified denom
        fld     tbyte ptr[bp + MAIN_DENOM_SAVE]
        fld     tbyte ptr[big_number]   ; force C2 to be set
        fprem1
        fstp    st
        fld     tbyte ptr[bp + MAIN_NUMER]      ; restore saved result

        fldcw   [bp + MAIN_PREV_CW]     ; restore caller's control word
        and     bx, 08000h              ; keep sx
        jz      rem1_done
        fchs
        jmp     rem1_done
remainder1_hardware_ok:
        fld     tbyte ptr [MAIN_DENOM+bp]   ; load the denominator
        fld     tbyte ptr [MAIN_NUMER+bp]   ; load the numerator
        fprem1                           ; and finally do a remainder

; prem1_main_routine end
rem1_done:
        test    dx, 3h
        jz      rem1_exit
        fnstsw  [bp + MAIN_FPREM_SW]    ; save Q0 Q1 and Q2
        test    edx, 01h
        jz      do_not_de_scale1
; De-scale the result. Go to pc=80 to prevent from fmul
; from user precision (fprem does not round the result).
        fnstcw  [bp + MAIN_PREV_CW]     ; save callers control word
        mov     ax, [bp + MAIN_PREV_CW]
        or      eax, 0300h              ; pc = 80
        mov     [bp + MAIN_PATCH_CW], ax
        fldcw   [bp + MAIN_PATCH_CW]
        fmul    qword ptr one_shr_64
        fldcw   [bp + MAIN_PREV_CW]     ; restore callers CW
do_not_de_scale1:
        mov     ax, [bp + MAIN_FPREM_SW]
        fxch
        fstp    st
        fld     tbyte ptr[bp + MAIN_DENOM_SAVE]
        fxch
        and     ax, 04300h              ; restore saved Q0, Q1, Q2
        sub     sp, ENV_SIZE
        mov     bp, sp
        fnstenv [bp]
        and     [bp].STATUS_WORD, 0bcffh
        or      [bp].STATUS_WORD, ax
        fldenv  [bp]
        add     sp, ENV_SIZE
rem1_exit:
        pop     bp
        pop     cx
        pop     bx
        pop     ax
        CHECKSW                         ; debug only: save status
        ret
fprem1_common   ENDP


comment ~*****************************************************************
;
; float _frem1_chk (float numer, float denom)
;
        public  _frem1_chk
_frem1_chk      PROC    FAR
        push    dx
        push    bp
        sub     sp, STACK_SIZE
        mov     bp, sp
        fld     dword ptr [STACK_SIZE+8+bp]
        fstp    tbyte ptr [NUMER+bp]
        fld     dword ptr [STACK_SIZE+12+bp]
        fstp    tbyte ptr [DENOM+bp]
        mov     dx, 0                   ; dx = 1 if denormal extended divisor
        call    fprem1_common

        fstp    dword ptr [__fprem1_result]
        mov     dx,ds
        mov     ax, offset __fprem1_result

        fstp    st                      ; clean FP stack
        add     sp, STACK_SIZE
        pop     bp
        pop     dx
        ret
_frem1_chk      ENDP
; end _frem1_chk

;
; double _drem1_chk (double numer, double denom)
;
        public  _drem1_chk
_drem1_chk      PROC    FAR
        push    dx
        push    bp
        sub     sp, STACK_SIZE
        mov     bp, sp
        fld     qword ptr [STACK_SIZE+8+bp]
        fstp    tbyte ptr [NUMER+bp]
        fld     qword ptr [STACK_SIZE+16+bp]
        fstp    tbyte ptr [DENOM+bp]
        mov     dx, 0                   ; dx = 1 if denormal extended divisor
        call    fprem1_common
        fstp    qword ptr [__fprem1_result]
        mov     dx,ds
        mov     ax, offset __fprem1_result
        fstp    st                      ; clean FP stack
        add     sp, STACK_SIZE
        pop     bp
        pop     dx
        ret

_drem1_chk      ENDP
; end drem1_chk

;
; long double _lrem1_chk(long double number,long double denom)
;
        public  _lrem1_chk
_lrem1_chk      PROC    NEAR
        push    bp
        mov     bp, sp
        fld     tbyte ptr [16+bp]       ; assumes long double push
                                        ; is 10 bytes, 18 = 8 + long double push                                        ; size.
        fld     tbyte ptr [6+bp]
        call    _fprem1_chk
        fxch
        fstp    st
        pop     bp
        ret
_lrem1_chk      ENDP
**********************************************************************~

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

        public  __fprem1_chk
        defpe   __fprem1_chk
        push    dx
        push    bp
        sub     sp, STACK_SIZE
        mov     bp, sp
        fstp    tbyte ptr [NUMER+bp]
        fstp    tbyte ptr [DENOM+bp]
        xor     dx, dx
; prem1_main_routine begin
        mov     ax,[DENOM+8+bp]         ; exponent
        test    ax,07fffh               ; check for denormal
        jz      denormal1
        call    fprem1_common
        add     sp, STACK_SIZE
        pop     bp
        pop     dx
        ret

denormal1:
        fld     tbyte ptr [DENOM+bp]    ; load the denominator
        fld     tbyte ptr [NUMER+bp]    ; load the numerator
        mov     eax, dword ptr[DENOM+bp]        ; test for whole mantissa == 0
        or      eax, dword ptr[DENOM+4+bp]      ; test for whole mantissa == 0
        jz      remainder1_hardware_ok_l ; denominator is zero
        fxch
        fstp    tbyte ptr[bp + DENOM_SAVE]      ; save org denominator
        fld     tbyte ptr[bp + DENOM]
        fxch
        or      dx, 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+bp]            ; save caller's control word
        mov     ax, [PREV_CW+bp]
        or      ax, 0033fh              ; mask exceptions, pc=80
        mov     [PATCH_CW+bp], ax
        fldcw   [PATCH_CW+bp]           ; 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 rem1_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     ax, [bp + NUMER + 8]    ; load numerator exponent
        and     ax, 7fffh               ; isolate numerator exponent
        cmp     ax, 7fbeh               ; compare Nexp to Maxexp-64
        ja      big_numer_rem1_de       ; jif big numerator

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

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

; 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_rem1_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+bp]            ; save caller's control word
        mov     ax, [PREV_CW+bp]
        or      ax, 00300h              ; pc=80
        mov     [PATCH_CW+bp], ax
        fldcw   [PATCH_CW+bp]           ; pc=80

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

; Restore the control word which was fiddled to scale at 80-bit precision.
; Then call the common code.
scaling_done1:
        fldcw   [bp + PREV_CW]          ; restore callers control word
        call    fprem1_common
        add     sp, STACK_SIZE
        pop     bp
        pop     dx
        ret

remainder1_hardware_ok_l:
        fprem                           ; and finally do a remainder

        CHECKSW

        add     sp, STACK_SIZE
        pop     bp
        pop     dx
        ret
__fprem1_chk     ENDP
; end _fprem1_chk

ifdef   DEBUG
        public  _fpinit
_fpinit PROC    FAR
        fninit
        ret
_fpinit ENDP
endif

_TEXT ENDS
       END

⌨️ 快捷键说明

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