chipr32.asm
来自「开放源码的编译器open watcom 1.6.0版的源代码」· 汇编 代码 · 共 852 行 · 第 1/3 页
ASM
852 行
fstp st(0) ; pop the stack
jmp rem1_patch_loop
rem1_large:
test ebx, 02h ; is denominator already saved
jnz already_saved1
fld tbyte ptr[esp + MAIN_DENOM]
fstp tbyte ptr[esp + MAIN_DENOM_SAVE] ; save denominator
already_saved1:
; Save user's precision control and institute 80. The fp ops in
; rem1_large_loop must not round to user's precision (if it is less
; than 80) because the hardware would not have done so. We are
; aping the hardware here, which is all extended.
fnstcw [esp+MAIN_PREV_CW] ; save caller's control word
mov eax, dword ptr[esp + MAIN_PREV_CW]
or eax, 033fh ; mask exceptions, pc=80
mov [esp + MAIN_PATCH_CW], eax
fldcw [esp + MAIN_PATCH_CW]
mov eax, [MAIN_DENOM+8+esp] ; sign and exponent of y (denominator)
and eax, 07fffh ; clear sy
mov ebx, [MAIN_NUMER+8+esp] ; sign and exponent of x (numerator)
and ebx, 07fffh ; clear sx
sub ebx, eax ; evaluate the exponent difference
and ebx, 03fh
or ebx, 020h
add ebx, 1
mov ecx, ebx
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
and eax, 08000h ; keep sy
or ebx, eax ; merge the sign of y
mov dword ptr[MAIN_DENOM+8+esp], ebx ; make ey equal to ex (scaled denominator)
fld tbyte ptr [MAIN_DENOM+esp] ; load the scaled denominator
fabs
fld tbyte ptr [MAIN_NUMER+esp] ; load the numerator
fabs
rem1_large_loop:
fcom
fstsw ax
and eax, 00100h
jnz rem1_no_sub
fsub st, st(1)
rem1_no_sub:
fxch
fmul qword ptr half
fxch
sub ecx, 1 ; decrement the loop counter
jnz rem1_large_loop
mov ebx, [MAIN_NUMER+8+esp] ; sign and exponent of x (numerator)
fstp tbyte ptr[esp + MAIN_NUMER] ; save result
fstp st ; toss modified denom
fld tbyte ptr[esp + MAIN_DENOM_SAVE]
fld tbyte ptr[big_number] ; force C2 to be set
fprem1
fstp st
fld tbyte ptr[esp + MAIN_NUMER] ; restore saved result
fldcw [esp + MAIN_PREV_CW] ; restore caller's control word
and ebx, 08000h ; keep sx
jz rem1_done
fchs
jmp rem1_done
remainder1_hardware_ok:
fld tbyte ptr [MAIN_DENOM+esp] ; load the denominator
fld tbyte ptr [MAIN_NUMER+esp] ; load the numerator
fprem1 ; and finally do a remainder
; prem1_main_routine end
rem1_done:
test edx, 03h
jz rem1_exit
fnstsw [esp + 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 [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_scale1:
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
rem1_exit:
pop ecx
pop ebx
pop eax
CHECKSW ; debug only: save status
ret
fprem1_common ENDP
comment ~***************************************************************
;
; float frem1_chk (float numer, float denom)
;
public frem1_chk
frem1_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 fprem1_common
fxch
fstp st
add esp, STACK_SIZE
pop edx
ret
frem1_chk ENDP
; end frem1_chk
;
; double drem1_chk (double numer, double denom)
;
public drem1_chk
drem1_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 fprem1_common
fxch
fstp st
add esp, STACK_SIZE
pop edx
ret
drem1_chk ENDP
; end drem1_chk
;
; long double lrem1_chk(long double number,long double denom)
;
public lrem1_chk
lrem1_chk PROC NEAR
fld tbyte ptr [20+esp]
fld tbyte ptr [4+esp]
call fprem1_chk
fxch
fstp st
ret
lrem1_chk ENDP
********************************************************************~
;
; FPREM1: ST = remainder(ST, ST(1)) - IEEE version of rounding
;
; Compiler version of the FPREM must preserve the arguments in the floating
; point stack.
public __fprem1_chk
defpe __fprem1_chk
push edx
sub esp, STACK_SIZE
fstp tbyte ptr [NUMER+esp]
fstp tbyte ptr [DENOM+esp]
mov edx, 0
; prem1_main_routine begin
mov eax,[DENOM+6+esp] ; exponent and high 16 bits of mantissa
test eax,07fff0000h ; check for denormal
jz denormal1
call fprem1_common
add esp, STACK_SIZE
pop edx
ret
denormal1:
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 remainder1_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 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 eax, [esp + NUMER + 8] ; load numerator exponent
and eax, 7fffh ; isolate numerator exponent
cmp eax, 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 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_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+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_done1:
fldcw [esp + PREV_CW] ; restore callers control word
call fprem1_common
add esp, STACK_SIZE
pop edx
ret
remainder1_hardware_ok_l:
fprem ; and finally do a remainder
CHECKSW
add esp, STACK_SIZE
pop edx
ret
__fprem1_chk ENDP
; end fprem1_chk
ifdef DEBUG
public fpinit
fpinit PROC NEAR
fninit
ret
fpinit ENDP
endif
CODE32 ENDS
END
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?