Metropoli BBS
VIEWER: math_com.asm MODE: TEXT (ASCII)
; Math Library to be used in C programs

;after finishing up the new func I found that BC LIBs suck!
; they had a few bugs, such as INF equal to 1.76645645e308 or so, and
; reports an error when pow(0,0) is used which returns 1 but should not
; be an error (at least according to their online help)

.data?
  align 4
  result REAL8 ?
  tmpd dd ?
  tmpr real8 ?
  tmpr2 real8 ?
  tmpw dw ?
  sign  db ?

.const
  _ZERO real8 0.0
  EXP_MAX real8 709.7827128933839
  ten REAL8 10.0
;FIX : v2.00 Beta #7 : these were all backwards and messed up
  rnd_0 label word    ;round towards 0
    db 7fh,1111b
  rnd_up label word   ;round up
    db 7fh,1011b
  rnd_dw label word   ;round down
    db 7fh,0111b
  cw_def label word   ;default rounding (to nearest or even)
    db 7fh,0011b 

.code
RETURN macro   ;saves return value (on FPU stack) in Watcom or Borland style
  ifdef _WC_
    fstp result
    fwait
    mov eax,dptr[result]
    mov edx,dptr[result+4]
    ret
  else
    fwait
    ret
  endif
endm

LOADF macro    ;loads Watcom return into FPU stack
  ifdef _WC_
    mov dptr[result],eax
    mov dptr[result+4],edx
    fld result
    fwait
  endif
endm

CHKF macro    ;sets errno based on value on top of FPU stack
  fxam
  fstsw ax
  fwait
  and ah,01000111b
  .if ah == 001b || ah == 011b
    ;+/-NAN
    mov errno,EDOM
  .elseif ah == 101b || ah == 111b
    ;+/-INF
    mov errno,ERANGE
  .endif
endm

sin proc,a:REAL8
  fld a
  fsin
  fstsw ax
  .if ax&4  ;mask C2
    mov errno,ERANGE
  .else
    mov errno,0
  .endif
  RETURN
sin endp

cos proc,a:REAL8
  fld a
  fcos
  fstsw ax
  .if ax&4  ;mask C2
    mov errno,ERANGE
  .else
    mov errno,0
  .endif
  fwait
  RETURN
cos endp

tan proc,a:REAL8
  fld a
  fsincos
  fstsw ax
  .if ax&4  ;mask C2
    mov errno,ERANGE
  .else
    mov errno,0
  .endif
  fdivp st(1),st
  RETURN
tan endp

ftol proc,a:REAL8
  fld a
__ftol::
  fistp dptr[result]
  fwait
  mov eax,dptr[result]
  ret
ftol endp

f_abs proc,a:REAL8
  and bptr [a+7],7fh
  fld a
  RETURN
f_abs endp

ceil proc,a:REAL8
  fldcw rnd_up
  fld a
  frndint
  fldcw cw_def
  RETURN  
ceil endp

floor proc,a:REAL8
  fldcw rnd_dw
  fld a
  frndint
  fldcw cw_def
  RETURN
floor endp

;FIX : v2.00 Beta #7 : now supports exp forms
atof proc,a:dword
  local _neg:byte

  pushad
  mov esi,a
  mov _neg,0
  fldz
  xor eax,eax  ;keep high part clear
@@:
  lodsb
  .if al==32
    jmp @b
  .endif
  .if al=='-'
    inc _neg
    lodsb
  .endif
  .if al=='e' || al=='E'
    jmp doexp  ; still have to get exp even though 0**anything is zero
  .endif       ;   cause _str2num_siz_ must be updated
  .if al=='.'
    jmp dodec
  .endif
@@:
  .if (al>='0') && (al<='9')
    fmul ten
    sub al,'0'
    mov tmpd,eax
    fiadd tmpd
    fwait
  .elseif al=='.'
    jmp dodec
  .elseif al=='e' || al=='E'
    jmp doexp
  .else
    jmp done
  .endif
  lodsb
  jmp @b
dodec:
  fldz  ;decimal part
  mov edi,esi    ;save EDI for stop pt
  lodsb
  .if ! ( (al>='0') && (al<='9') )
    fstp tmpr  ;ignore!
    fwait
    ;FIX v 2.00 Beta #8 : this was not poped off properly
    .if al=='e' || al=='E'
      jmp doexp
    .endif
    jmp done  ;no numbers after .
  .endif
@@:
  .if (al>='0') && (al<='9')
    lodsb
    jmp @b
  .endif
  push esi
  push ax  ;save last thing (if it's 'e' then there is an exp part)
  dec esi  ;go back
  dec esi  
@@:            ;go until ESI==EDI
  mov al,[esi]
  sub al,'0'
  mov tmpd,eax
  fiadd tmpd
  fdiv ten
  fwait
  .if esi>edi
    dec esi
    jmp @b
  .endif
  faddp st(1),st
  pop ax
  pop esi
  .if !(al=='e' || al=='E')
    jmp done
  .endif
doexp:
  ;esi=>char after e
  .if bptr[esi]=='-'
    mov ch,1
    inc esi
  .else
    mov ch,0
  .endif
  xor eax,eax
  xor ebx,ebx
@@:
  lodsb
  .if !((al >= '0') && (al<= '9'))
    jmp expdone
  .endif
  sub al,'0'
  imul ebx,ebx,10
  add ebx,eax
  jmp @b
expdone:
  fstp tmpr      ;save #
  mov tmpd,ebx
  fild tmpd
  fstp tmpr2
  fwait
  callp pow,ten,tmpr2
  LOADF    ;st(1)
  fld tmpr ;st   ;reload #
  .if ch==1  ;neg
    fxch
    fdivp st(1),st
  .else      ;pos
    fmulp st(1),st
  .endif
done:
  .if _neg
    fchs
  .endif
  mov eax,esi
  sub eax,a
  dec eax  ;minus the last char not used (ie: NULL)
  mov _str2num_siz_,eax  ;FIX : v2.00 Beta #7 : this was not set right
  popad
  RETURN
atof endp

exp PROC, val:REAL8
    mov     [errno], 0                          ; clear matherror
    fld     val                                 ; load the value
    fcom    EXP_MAX                             ; exceeded maximum?
    fstsw   ax                                  ; save status word in AX
    fwait                                       ; wait this shit be idle
    sahf                                        ; load flags with it
    jna @f
    mov     [errno], ERANGE                     ; set matherror from QLIB
@@:
    fldl2e                                      ; load log 2 (base e)
    fmulp   st(1), st(0)                        ; mul them
    fld     st(0)                               ; copy st(0) to st(1)
    frndint                                     ; rount st(0)
    fxch    st(1)                               ; xchange st(1) for st(0)
    fsub    st(0), st(1)                        ; sub rounded from unrounded
    f2xm1                                       ; calc 2^x-1
    fld1                                        ; load 1.00
    faddp   st(1), st(0)                        ; add 1.00 to st(1)
    fscale                                      ; exponential function of a
    ffree   st(1)                               ; remove rounded value
    RETURN
exp ENDP

log proc, val:REAL8
    mov     [errno], 0                          ; clear matherror
    fld     val                                 ; load the value
    ftst                                        ; compare with zero
    fstsw   ax                                  ; save status word in AX
    fwait                                       ; wait this shit be idle
    sahf                                        ; load flags with it
    .if carry?   ;jb      @@negative                          ; jump if value < 0
      mov     [errno], EDOM                       ; domain error
    .endif
    .if zero?    ;je      @@zero                              ; jump if zero
      mov     [errno], ERANGE                     ; range error
    .endif

    fldln2                                      ; load natural log of 2
    fxch    st(1)                               ; xchange it with value
    fyl2x                                       ; calc y=log2(x)

    RETURN
log ENDP

log10 PROC, val:REAL8
    mov     [errno], 0                          ; clear matherror
    fld     val                                 ; load the value
    ftst                                        ; compare with zero
    fstsw   ax                                  ; save status word in AX
    fwait                                       ; wait this shit be idle
    sahf                                        ; load flags with it
    .if carry?   ;jb      @@negative                          ; jump if value < 0
      mov     [errno], EDOM                       ; domain error
    .endif
    .if zero?    ;je      @@zero                              ; jump if zero
      mov     [errno], ERANGE                     ; range error
    .endif

    fldlg2                                      ; load log of 2
    fxch    st(1)                               ; xchange it wih value
    fyl2x                                       ; calc y=log2(x)
    RETURN
log10 ENDP

log2 PROC, val:REAL8
    mov     [errno], 0                          ; clear matherror
    fld     val                                 ; load the value
    ftst                                        ; compare with zero
    fstsw   ax                                  ; save status word in AX
    fwait                                       ; wait this shit be idle
    sahf                                        ; load flags with it
    .if carry?   ;jb      @@negative                          ; jump if value < 0
      mov     [errno], EDOM                       ; domain error
    .endif
    .if zero?    ;je      @@zero                              ; jump if zero
      mov     [errno], ERANGE                     ; range error
    .endif

    fld1                                        ; load 1
    fxch    st(1)                               ; xchange it with value
    fyl2x                                       ; calc y=log2(x)

    RETURN
log2 ENDP

pow PROC, val:REAL8, power:REAL8
    mov errno,0

    fld val
    fcomp _ZERO  
    fstsw ax
    fwait
    sahf
    .if zero?
      fld power
      fcomp _ZERO
      fstsw ax
      fwait
      sahf
      .if zero?      ;this is ANSI C specs
        fld1   ;load one!  (in reality 0**0 is INF - o well)
        RETURN
      .endif
      fldz     ;0**power = 0 always
      RETURN
    .endif
    fld power
    fcomp _ZERO
    fstsw ax
    fwait
    sahf
    .if zero?      ;this is ANSI C specs
      fld1   ;load one!
      RETURN
    .endif

    callp   log,val                         ; calculate log of val
    LOADF

    fld power                               ; load power

    fmulp   st(1), st(0)                        ; multiply them
    fstp    result                              ; save result

    callp   exp,result                          ; calculate exp func of result
    LOADF
    CHKF          ;setup error codes

    RETURN
pow  ENDP

sqrt PROC, val:REAL8
    mov     [errno], 0                          ; clear matherror
    fld     val                                 ; load val
    ftst                                        ; compare with zero
    fstsw   ax                                  ; save status word in AX
    fwait                                       ; wait this shit be idle
    sahf                                        ; load flags with it
    .if carry?                                  ; jump if value < 0
      mov     [errno], EDOM                       ; uh-oh. val < 0 = domain err!
      fsqrt     ;will return -NAN                 ; square root of st(0)
      fabs      ;make sure it's +NAN
    .else
      fsqrt                                       ; square root of st(0)
    .endif

    RETURN
sqrt ENDP


[ RETURN TO DIRECTORY ]