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 + -
显示快捷键?