📄 fp.asm
字号:
;
; GRDP
;
; Copyright(c) LADsoft
;
; David Lindauer, camille@bluegrass.net
;
;
; FP.ASM
;
; Function: Handle FP status commands
;
;MASM MODE
.MODEL SMALL
.386
public floatcheck, fpcommand
include eprints.inc
include einput.inc
include emtrap.inc
include ebreaks.inc
include eloader.inc
include edos.inc
include eexec.inc
;
; hack to help MASM versions that die on pop-fmath
;
.data
ten dw 10
pointone dt 0.1
nan dd 7fffffffh ; quiet nan
inf dd 7f800000h ; plus infinity
curdig dd ? ;current digit for input
work dw ? ;used in detecting FP hardware
fpflag db ? ;flag if we can use FP commands
floatstat dw 47 dup (0) ;FNSAVE/FNSTOR buffer
enames db "IDZOUP" ;exception names
.code
precmsg dw offset _text:prsing,offset _text:reserved
dw offset _text:prdbl,offset _text:prxt
roundmsg dw offset _text:rdnear,offset _text:rdminus
dw offset _text:rdplus,offset _text:rdzer
prsing db "Single",0
prdbl db "Double",0
prxt db "Extended",0
reserved db "Reserved",0
rdnear db "Nearest",0
rdzer db "Zero",0
rdplus db "Plus Infinity",0
rdminus db "Minus Infinity",0
;
; check for floating point unit
;
floatcheck proc
mov [fpflag],1
mov [work],4567h
fninit
fstsw [work]
cmp [work],0
jz hasfp
mov [fpflag],0
hasfp:
ret
floatcheck endp
;
; fp commands
;
fpcommand proc
test [fpflag],1
jnz hasfloat
PRINT_MESSAGE <13,10,"No FP unit">
clc
ret
hasfloat:
fnsave [floatstat]
fninit
fwait
call WadeSpace
jz dumpstack
cmp al,'s'
jnz flread
;
; status command
;
inc si
call WadeSpace
jnz flerr
frstor [floatstat]
PRINT_MESSAGE <13,10,"Masked exceptions: ">
mov dx,word ptr [floatstat]
call except
PRINT_MESSAGE <13,10,"Active exceptions: ">
mov dx,word ptr [floatstat+2]
call except
PRINT_MESSAGE <13,10,"Precision: ">
movzx bx,byte ptr [floatstat+1]
and bl,3
shl bl,1
mov bx,[bx+precmsg]
call olMessage
PRINT_MESSAGE <13,10,"Rounding: ">
movzx bx,byte ptr [floatstat+1]
and bl,0ch
shr bl,1
mov bx,[bx+roundmsg]
call olMessage
PRINT_MESSAGE <13,10,"FPU Status flags: ">
test word ptr [floatstat+2],100h
jz notc0
PRINT_MESSAGE "C0 "
notc0:
test word ptr [floatstat+2],200h
jz notc1
PRINT_MESSAGE "C1 "
notc1:
test word ptr [floatstat+2],400h
jz notc2
PRINT_MESSAGE "C2 "
notc2:
test word ptr [floatstat+2],4000h
jz notc3
PRINT_MESSAGE "C3 "
notc3:
clc
ret
;
; dump the FP stack
;
dumpstack:
mov ax,word ptr [floatstat+2]
shr ax,11
and ax,7
sub ax,7
neg ax
mul [ten]
mov bx,offset floatstat+14
add bx,ax
mov cx,8
dsl:
push bx
push cx
call dumpval
pop cx
pop bx
sub bx,10
loop dsl
frstor [floatstat]
clc
ret
;
; read an FP number into a reg
;
flread:
sub al,'0'
jc flerr
cmp al,8
jae flerr
push ax
inc si
call wadespace
jz flerr2
call ReadFpNumber
jc flerr2
pop ax
mov cl,al
call testtag
jz flerr3
mov bx,word ptr [floatstat+2]
shr bx,11
and bx,7
movzx ax,al
sub ax,bx
mul [ten]
mov bx,ax
fstp tbyte ptr [bx + floatstat + 14]
frstor [floatstat]
clc
ret
testtag PROC
and cl,7
add cl,cl
mov bx,[floatstat+4]
shr bx,cl
and bl,3
cmp bl,3
ret
testtag ENDP
flerr3:
frstor [floatstat]
PRINT_MESSAGE <13,10,"err - empty reg">
clc
ret
flerr2:
add sp,2
flerr:
frstor [floatstat]
stc
ret
fpcommand endp
except PROC
mov si,offset enames
mov cx,6
exlp:
shr dx,1
lodsb
jnc nhr
push dx
mov dl,al
call putchar
mov dl,'e'
call putchar
call printspace
pop dx
nhr:
loop exlp
ret
except ENDP
;
; dump the value of a stack register
;
dumpval PROC
call crlf
dec cl
mov dl,cl
add dl,'0'
call putchar
mov ax,word ptr [floatstat+2]
shr ax,11
and al,7
cmp al,cl
jnz nottop
mov dl,'*'
call putchar
jmp join
nottop:
call printspace
join:
mov dl,')'
call putchar
call printspace
call printspace
call printspace
mov ax,word ptr [floatstat+4]
shr ax,cl
shr ax,cl
and al,3
cmp al,3
jnz notempty
PRINT_MESSAGE "<Empty>"
clc
ret
notempty:
cmp al,2
jnz notnan
test byte ptr [bx + 9],80h
jz nsinf
mov dl,'-'
call putchar
nsinf:
fld tbyte ptr [bx]
fxam
fnstsw ax
fstp st(0)
sahf
jpe isinf
PRINT_MESSAGE "<Nan>"
clc
ret
isinf:
PRINT_MESSAGE "<Inf>"
clc
ret
notnan:
cmp al,1
jnz notzero
jnz notnan
test byte ptr [bx + 9],80h
jz nszer
mov dl,'-'
call putchar
nszer:
mov dl,'0'
call putchar
clc
ret
notzero:
fld tbyte ptr [bx]
call PrintFloating
fcomp
clc
ret
dumpval ENDP
;
; actual FP print routines
;
PrintFloating PROC
call fextract
or ax,ax
jz notdenorm
PRINT_MESSAGE "<Denorm>"
ret
notdenorm:
or dx,dx
jns noneg
call putneg
noneg:
cmp bx,8 ; getter be less than 16!!!!
jg highexp
cmp bx,-4
jle lowexp
or bx,bx
jge gte0
;
; print for less than one but not exponential
;
push bx
call putzer
call putper
pop cx
not cx
jcxz nozr
inc cx
zrl:
call putzer
loop zrl
nozr:
mov cx,16
nl2:
call fnd
call putdig
loop nl2
ret
;
; print for greater than or equal to one but not exponential
;
gte0:
push bx
mov cx,bx
call fnd
call putdig
jcxz gte0nl
gtel1:
call fnd
call putdig
loop gtel1
gte0nl:
call putper
pop cx
sub cx,16
neg cx
gtel2:
call fnd
call putdig
loop gtel2
ret
;
; hack to justify negative exponents
;
lowexp:
dec bx
call fnd
;
; exponential print
;
highexp:
push bx
call fnd
call putdig
call putper
mov cx,16
hel1:
call fnd
call putdig
loop hel1
mov dl,'e'
call putchar
pop ax
or ax,ax
jns nonegx
push ax
call putneg
pop ax
neg ax
nonegx:
sub cx,cx
elp1:
sub dx,dx
div [ten]
push dx
inc cx
or ax,ax
jnz elp1
elp2:
pop ax
call putdig
loop elp2
ret
putdig:
mov dl,al
add dl,'0'
call putchar
ret
putper:
mov dl,'.'
call putchar
ret
putneg:
mov dl,'-'
call putchar
ret
putzer:
mov dl,'0'
call putchar
ret
PrintFloating ENDP
;
; get exponent and mantissa and sign
;
; enter:
; st(0) = value
;
; returns:
; ax = 0 ; ok val
;
; st(0) = mantissa
; bx = exp
; dx = sign ( +-1)
;
; ax = -1 ; infinity or dnormal
;
fextract PROC
enter 6,0
fstcw [bp-2]
mov ax,[bp-2]
or ah,0ch ; round toward zero
mov [bp-4],ax
fldcw [bp-4]
ftst
fnstsw ax
fnclex ; just in case ...
sahf
mov eax,-1 ; first check for out of range
jp fxx
jnz dosign ; now check for zero
sub bx,bx
sub dx,dx
jmp fxx
dosign:
mov dx,1 ; nonzero, finally get sign
jnc pos
fabs ; we will work with positive nums hereafter
mov dx,-1
pos:
fldlg2 ; log to base 10
fxch
fyl2x
fld st(0) ; get int part
frndint
fist word ptr [bp-6]
pop bx
fsubp st(1),st(0) ; fraction
fldl2t ; convert back to base 2
fmulp st(1),st(0)
fld st(0) ; lovely exponentiation
frndint
fxch
fld st(1)
fsubp st(1),st(0)
f2xm1 ;
fld1
faddp st(1),st(0)
fscale
fxch
fcomp
sub eax,eax
fxx:
fnclex
fldcw [bp-2]
fwait
leave
ret
fextract ENDP
;
; get next digit from mantissa
;
; enter:
; mantissa from fextract on stack
;
; exit:
; eax = next digit (base 10)
; stack = new mantissa
;
fnd PROC
ENTER 6,0
fstcw [bp-2]
mov ax,[bp-2]
or ah,0ch ; round toward zero
mov [bp-4],ax
fldcw [bp-4]
fld st(0) ; next digit
frndint
fist word ptr [bp-6]
fsubp st(1),st(0) ; new mantissa
fimul word ptr [ten]
fnclex
fldcw [bp-2]
fwait
pop ax ; cute trick to get result :)
leave
ret
fnd ENDP
;
; read floating point number
readfpnumber PROC
sub dx,dx ; sign = +
cmp al,'+' ; get sign bit
jz rfn_gotsign
cmp al,'-'
jnz rfn_nosign
inc dx ; minus sign, sign = '-'
rfn_gotsign:
inc si ; step past sign
rfn_nosign:
call wadespace ; skip space
jz rfn_err
mov eax,[si] ; check for nan
and eax,0ffffffh
cmp eax,"nan"
jnz rfn_notnan
fld [nan] ; nan, load it
jmp rfn_ni
rfn_notnan:
cmp eax,"fni" ; check for inf
jnz rfn_notinf
fld [inf] ; inf load it
rfn_ni:
add si,3 ; skip past inf/nan
call wadespace ; err if more
jnz rfn_err2
jmp rfn_done ; else tag in sign and exit
;
rfn_notinf:
fldz ; return val
call getdig ; see if leading digits
jc rfn_err2
jz rfn_nostart ; go check for '.' if not digit
rfn_prelp:
fimul [ten] ; else shift digit in
fild [curdig]
faddp st(1),st(0)
call getdig ; loop till all digits got
jc rfn_err2
jnz rfn_prelp
cmp al,'.' ; now check for '.'
jz rfn_dot ; yes, do it
jmp rfn_exp
;
rfn_nostart:
cmp al,'.' ; no leading digit, must have dot
jnz rfn_err
rfn_dot:
call getdig ; if no digits get exponent
jc rfn_err2
jz rfn_exp
fld [pointone] ; else we have digits, load up 0.1
rfn_ptlp:
fild [curdig] ; load digit
fmul st(0),st(1) ; mul by placeholder and add it in
faddp st(2),st(0)
fld [pointone] ; adjust placeholder
fmulp st(1),st(0)
call getdig
jc rfn_err3
jnz rfn_ptlp
fstp st(0) ; get rid of placeholder
rfn_exp:
cmp al,'e' ; check for exponent
jnz rfn_done
sub bx,bx
sub cx,cx
rfn_exl:
call getdig ; first digit of exponent or sign
jc rfn_err2
jnz rfn_nexs
inc cx
call getdig
jz rfn_err2
jc rfn_err2
rfn_nexs:
xchg ax,bx
push dx
mul [ten]
pop dx
xchg ax,bx
movzx ax,al
add bx,ax
call getdig
jc rfn_err2
jnz rfn_nexs
or cx,cx
jz rfn_nexs2
neg bx
rfn_nexs2:
cmp bx,1024 ; verify range
jg rfn_err2
cmp bx,-1024
jl rfn_err2
enter 4,0 ; need temp space now
mov [bp-2],bx ; get exponent to fp stack
fild word ptr [bp-2]
fldl2t ; convert exponent to logarithmic base 2
fmulp st(1),st(0)
fnstcw [bp-4] ; set rounding mode to zero
mov ax,[bp-4]
and ah,0f3h
or ah,0ch
mov [bp-2],ax
fldcw [bp-2]
fld st(0) ; extract int and fraction of exponent
frndint
fxch st(1)
fsub st(0),st(1)
f2xm1 ; exponentiate the fraction
fld1
faddp st(1),st(0)
fscale ; scale in the integer part of exponent
fxch st(1) ; get rid of int part
fcomp st(1)
fldcw [bp-4] ; restore round mode
leave ; clear stack
fmulp st(1),st(0) ; multiply base * exponent (may result in
; an infinity)
rfn_done:
or dx,dx ; get the sign bit
jz rfn_x
fchs ; to the FPU
rfn_x:
clc
ret
rfn_err3:
fstp st(0)
rfn_err2:
fstp st(0)
rfn_err:
stc
ret
;
; routine to get a digit, returns ZR for a '.' or 'e' or CR
; and Carry for an error, else NZ means a digit
;
getdig PROC
lodsb ; get val
cmp al,'0' ; check low end of digits
jc gds ; no, check high end
cmp al,'9'
ja gds
sub al,'0'
mov byte ptr [curdig],al ; digit, store it for later
or si,si ; set nz
ret
gds:
cmp al,'-'
jz gdsk
cmp al,'e' ; check for 'e'
jz gdsk
cmp al,'.' ; '.'
jz gdsk
cmp al,13 ; CR
jnz gdsk
pushf ; if is CR bump char ptr back so
dec si ; we don't overrun
popf
gdsk:
clc
ret
getdig ENDP
readfpnumber ENDP
end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -