;FpuAtoFL    PROTO :DWORD,:DWORD,:DWORD
;FpuFLtoA    PROTO :DWORD,:DWORD,:DWORD,:DWORD

;FpuAdd      PROTO :DWORD,:DWORD,:DWORD,:DWORD
;FpuSub      PROTO :DWORD,:DWORD,:DWORD,:DWORD
;FpuMul      PROTO :DWORD,:DWORD,:DWORD,:DWORD
;FpuDiv      PROTO :DWORD,:DWORD,:DWORD,:DWORD
;FpuSqrt     PROTO :DWORD,:DWORD,:DWORD
;FpuXexpY    PROTO :DWORD,:DWORD,:DWORD,:DWORD
;FpuAbs      PROTO :DWORD,:DWORD,:DWORD
;FpuTrunc    PROTO :DWORD,:DWORD,:DWORD
;FpuRound    PROTO :DWORD,:DWORD,:DWORD
;FpuChs      PROTO :DWORD,:DWORD,:DWORD

;FpuLnx      PROTO :DWORD,:DWORD,:DWORD
;FpuLogx     PROTO :DWORD,:DWORD,:DWORD
;FpuEexpX    PROTO :DWORD,:DWORD,:DWORD
;FpuTexpX    PROTO :DWORD,:DWORD,:DWORD

;FpuSin      PROTO :DWORD,:DWORD,:DWORD
;FpuCos      PROTO :DWORD,:DWORD,:DWORD
;FpuTan      PROTO :DWORD,:DWORD,:DWORD
;FpuArcsin   PROTO :DWORD,:DWORD,:DWORD
;FpuArccos   PROTO :DWORD,:DWORD,:DWORD
;FpuArctan   PROTO :DWORD,:DWORD,:DWORD

;FpuSinh     PROTO :DWORD,:DWORD,:DWORD
;FpuCosh     PROTO :DWORD,:DWORD,:DWORD
;FpuTanh     PROTO :DWORD,:DWORD,:DWORD
;FpuArcsinh  PROTO :DWORD,:DWORD,:DWORD
;FpuArccosh  PROTO :DWORD,:DWORD,:DWORD
;FpuArctanh  PROTO :DWORD,:DWORD,:DWORD

;FpuSize     PROTO :DWORD,:DWORD,:DWORD
;FpuComp     PROTO :DWORD,:DWORD,:DWORD
;FpuExam     PROTO :DWORD,:DWORD
;FpuState    PROTO :DWORD

SRC1_FPU    EQU   1
SRC1_REAL   EQU   2
SRC1_DMEM   EQU   4
SRC1_DIMM   EQU   8
SRC1_CONST  EQU   16

ANG_DEG     EQU   0
ANG_RAD     EQU   32

DEST_MEM    EQU   0
DEST_IMEM   EQU   64
DEST_FPU    EQU   128

SRC2_FPU    EQU   256
SRC2_REAL   EQU   512
SRC2_DMEM   EQU   1024
SRC2_DIMM   EQU   2048
SRC2_CONST  EQU   4096

STR_REG     EQU   0
STR_SCI     EQU   32768

FPU_PI      EQU   1
FPU_NAPIER  EQU   2

XAM_VALID   EQU   1
XAM_ZERO    EQU   2
XAM_NEG     EQU   4
XAM_SMALL   EQU   8
XAM_INFINIT EQU   16

CMP_EQU     EQU   1
CMP_GREATER EQU   2
CMP_LOWER   EQU   4




; #########################################################################
;
;                             FpuFLtoA
;
;##########################################################################

  ; -----------------------------------------------------------------------
  ; This procedure was written by Raymond Filiatreault, December 2002
  ; and modified April 2003. A minor flaw was corrected in July 2003.
  ; Modified March 2004 to avoid any potential data loss from the FPU
  ;
  ; This FpuFLtoA function converts an 80-bit REAL number (Src) to its
  ; decimal representation as a zero terminated alphanumeric string which
  ; is returned at the specified memory destination unless an invalid
  ; operation is reported by the FPU or the definition of the parameters
  ; (with uID) is invalid.
  ;
  ; The format of the string can be specified as regular (default) or
  ; scientific notation. The number of decimal places returned must also be
  ; specified but the total number of significant digits must not exceed 16.
  ; When the regular format is specified, the integer portion can also be
  ; padded with preceding spaces to position the decimal point at a
  ; specified location from the start of the string.
  ;
  ; The source can be an 80-bit REAL number from the FPU itself or from
  ; memory.
  ;
  ; The source is not checked for validity. This is the programmer's
  ; responsibility.
  ;
  ; This procedure is based on using an FPU instruction to convert the
  ; REAL number into a specific packed decimal format. After unpacking,
  ; the decimal point is positioned as required.
  ;
  ; Only EAX is used to return error or success. All other CPU registers
  ; are preserved. All FPU registers are preserved.
  ;
  ; -----------------------------------------------------------------------


; #########################################################################
align 4
proc  FpuFLtoA stdcall, lpSrc:DWORD, lpDecimal:DWORD, lpDest:DWORD, uID:DWORD

locals
 tempdw dd ?
 esize dd ?
 Padding dd ?
 Decimal dd ?
 content: times 108 db ?
 tempst dt ?
 bcdstr dt ?
 oldcw dw ?
 truncw dw ?
 unpacked: times 20 db ?
endl

;get the specified number of decimals for result
;and make corrections if necessary

      mov   eax,[lpDecimal]
      test  [uID],SRC2_DMEM
      jz    @F
      mov   eax,[eax]	      ;get the decimals from memory
   @@:
      push  eax
      movzx eax,al	      ;low byte - number of decimal digits
      cmp   eax,15
      jbe   @F
      mov   eax,15	      ;a maximum of 15 decimals is allowed
   @@:
      mov   [Decimal],eax
      pop   eax
      movzx eax,ah	      ;2nd byte - number of char before decimal point
      cmp   eax,17
      jbe   @F
      mov   eax,17	      ;a maximum of 17 characters is allowed
   @@:
      mov   [Padding],eax

      test  [uID],SRC1_FPU	;is data taken from FPU?
      jz    @F		      ;continue if not

;-------------------------------
;check if top register is empty
;-------------------------------

      fxam		      ;examine its content
      fstsw ax		      ;store results in AX
      fwait		      ;for precaution
      sahf		      ;transfer result bits to CPU flag
      jnc   @F		      ;not empty if Carry flag not set
      jpe   @F		      ;not empty if Parity flag set
      jz    srcerr1	      ;empty if Zero flag set

   @@:
      fsave [content]

;----------------------------------------
;check source for Src and load it to FPU
;----------------------------------------

      test  [uID],SRC1_FPU
      jz    @F
      lea   eax,[content]
      fld   tbyte [eax+28]
      jmp   dest0

   @@:
      test  [uID],SRC1_REAL	;is Src an 80-bit REAL in memory?
      jz    srcerr	      ;no proper source identificaiton
      mov   eax,[lpSrc]
      fld   tbyte [eax]
      jmp   dest0	      ;go complete process
      
srcerr:
      frstor [content]
srcerr1:
      push  edi
      mov   edi,[lpDest]
      mov   eax,"ERRO";ORRE"
      stosd
      mov   ax,"R"
      stosw
      pop   edi
      xor   eax,eax
      ret

dest0:

;-------------------------------------------
;first examine the value on FPU for validity
;-------------------------------------------

      fxam		      ;examine value on FPU
      fstsw ax		      ;get result
      fwait
      sahf		      ;transfer to CPU flags
      jz    maybezero
      jpo   srcerr	      ;C3=0 and C2=0 would be NAN or unsupported
      jnc   getnumsize	      ;continue if normal finite number

;--------------------------------
;value to be converted = INFINITY
;--------------------------------

      push  ecx
      push  esi
      push  edi
      mov   edi,[lpDest]
      mov   al,"+"
      test  ah,2	      ;C1 field = sign
      jz    @F
      mov   al,"-"
   @@:
      stosb
      mov   eax,"INFI";IFNI"
      stosd
      mov   eax,"NITY";YTIN"
      stosd
      jmp   finish	

;-------------------------
;value to be converted = 0
;-------------------------

maybezero:
      jpe   getnumsize	      ;would be denormalized number
      fstp  st0 	    ;flush that 0 value off the FPU
      push  ecx
      push  esi
      push  edi
      mov   edi,[lpDest]
      test  [uID],STR_SCI	;scientific notation?
      jnz   @F		      ;no padding
      mov   ecx,[Padding]
      sub   ecx,2
      jle   @F		      ;no padding specified or necessary
      mov   al," "
      rep   stosb
   @@:
      mov   ax,3020h	      ;" 0" szstring
      stosw		      ;write it
      jmp   finish
      
;---------------------------
; get the size of the number
;---------------------------

getnumsize:
      fldlg2		      ;log10(2)
      fld   st1 	    ;copy Src
      fabs		      ;insures a positive value
      fyl2x		      ;->[log2(Src)]*[log10(2)] = log10(Src)
      
      fstcw [oldcw]		;get current control word
      fwait
      mov   ax,[oldcw]
      or    ax,0c00h	      ;code it for truncating
      mov   [truncw],ax
      fldcw [truncw]		;insure rounding code of FPU to truncating
      
      fist  [esize]		;store characteristic of logarithm
      fldcw [oldcw]		;load back the former control word

      ftst		      ;test logarithm for its sign
      fstsw ax		      ;get result
      fwait
      sahf		      ;transfer to CPU flags
      sbb   [esize],0		;decrement esize if log is negative
      fstp  st0 	    ;get rid of the logarithm

;-----------------------------------------------------------------------
; get the power of 10 required to generate an integer with the specified
; number of significant digits
;-----------------------------------------------------------------------

      mov   eax,[uID]
      and   eax,STR_SCI
	jnz .els0	 ;regular decimal notation
	    mov   eax,[esize]
	    or	  eax,eax     ;check if number is < 1
	    js	  @F
;            .if   eax > 15    ;if number is >= 10^16
		cmp eax,15
		jbe .endif0
		  or	[uID],STR_SCI ;switch to scientific notation
		  mov	[Decimal],15  ;insures 15 decimal places in result
		  jmp	scific
	    .endif0:
	    add   eax,[Decimal]
;            .if   eax > 15    ;if integer + decimal digits > 16
		cmp eax,15
		jbe .endif1
		  sub	eax,15
		  sub	[Decimal],eax ;reduce decimal digits as required
	    .endif1:
	 @@:
	    push  [Decimal]
	    pop   [tempdw]
		jmp @f
      .els0:		       ;scientific notation
      scific:
	    mov   eax,[Decimal]
	    sub   eax,[esize]
	    mov   [tempdw],eax
;      .endif
	@@:

;----------------------------------------------------------------------------------------
; multiply the number by the power of 10 to generate required integer and store it as BCD
;----------------------------------------------------------------------------------------

;.if   tempdw != 0
	cmp [tempdw],0
	je @f
      fild  [tempdw]
      fldl2t
      fmulp st1,st0		     ;->log2(10)*exponent
      fld   st0
      frndint		      ;get the characteristic of the log
      fxch
      fsub st,st1	    ;get only the fractional part but keep the characteristic
      f2xm1		      ;->2^(fractional part)-1
      fld1
      faddp st1,st0		     ;add 1 back
      fscale		      ;re-adjust the exponent part of the REAL number
      fstp  st1 	    ;get rid of the characteristic of the log
      fmulp st1,st0		     ;->16-digit integer
;.endif
	@@:

      fbstp [bcdstr]		;->TBYTE containing the packed digits
      fstsw ax		      ;retrieve exception flags from FPU
      fwait
      shr   eax,1	      ;test for invalid operation
      jc    srcerr	      ;clean-up and return error

;------------------------------------------------------------------------------
; unpack BCD, the 10 bytes returned by the FPU being in the little-endian style
;------------------------------------------------------------------------------

      push  ecx
      push  esi
      push  edi
      lea   esi,[bcdstr+9]      ;go to the most significant byte (sign byte)
      lea   edi,[unpacked]
      mov   eax,3020h
      mov   cl,byte [esi]  ;sign byte
;      .if   cl == 80h
	cmp cl,0x80
	jne @f
	    mov   al,"-"      ;insert sign if negative number
;      .endif
	@@:
      stosw
      mov   ecx,9
   @@:
      dec   esi
      movzx eax,byte [esi]
      ror   ax,4
      ror   ah,4
      add   ax,3030h
      stosw
      dec   ecx
      jnz   @B

      mov   edi,[lpDest]
      lea   esi,[unpacked]
      test  [uID],STR_SCI	;scientific notation?
      jnz   scientific

;************************
; REGULAR STRING NOTATION
;************************

;------------------------------
; check if padding is specified
;------------------------------

      mov   ecx,[Padding]
      or    ecx,ecx	      ;check if padding is specified
      jz    nopadding
      
      mov   edx,2	      ;at least 1 integer + sign
      mov   eax,[esize]
      or    eax,eax
      js    @F		      ;only 1 integer digit if size is < 1
      add   edx,eax	      ;->number of integer digits
   @@:
      sub   ecx,edx
      jle   nopadding
      mov   al," "
      rep   stosb

nopadding:
      pushfd		      ;save padding flags
      movsb		      ;insert sign
      mov   ecx,1	      ;at least 1 integer digit
      mov   eax,[esize]
      or    eax,eax	      ;is size negative (i.e. number smaller than 1)
      js    @F
      add   ecx,eax
   @@:
      mov   eax,[Decimal]
      add   eax,ecx	      ;->total number of digits to be displayed
      sub   eax,19
      sub   esi,eax	      ;address of 1st digit to be displayed
      pop   eax 	      ;retrieve padding flags in EAX
;      .if   byte ptr[esi-1] == "1"
	cmp byte [esi-1],"1"
	jne @f
	    dec   esi
	    inc   ecx
	    push  eax	      ;transfer padding flags through stack
	    popfd	      ;retrieve padding flags
	    jle   @F	      ;no padding was necessary
	    dec   edi	      ;adjust for one less padding byte
;      .endif
	@@:
   @@:
      rep   movsb	      ;copy required integer digits
      mov   ecx,[Decimal]
      or    ecx,ecx
      jz    @F
      mov   al,"."
      stosb
      rep   movsb	      ;copy required decimal digits
   @@:
      jmp   finish

;********************
; SCIENTIFIC NOTATION
;********************

scientific:
      movsb		      ;insert sign
      mov   ecx,[Decimal]
      mov   eax,18
      sub   eax,ecx
      add   esi,eax
      cmp   byte [esi-1],"1"
      pushfd		      ;save flags for extra "1"
      jnz   @F
      dec   esi
   @@:
      movsb		      ;copy the integer
      mov   al,"."
      stosb
      rep   movsb	      ;copy the decimal digits

      mov   al,"E"
      stosb
      mov   al,"+"
      mov   ecx,[esize]
      popfd		      ;retrieve flags for extra "1"
      jnz   @F		      ;no extra "1"
      inc   ecx 	      ;adjust exponent
   @@:
      or    ecx,ecx
      jns   @F
      mov   al,"-"
      neg   ecx 	      ;make number positive
   @@:
      stosb		      ;insert proper sign

;Note: the absolute value of the size could not exceed 4931
      
      mov   eax,ecx
      mov   cl,100
      div   cl		      ;->thousands & hundreds in AL, tens & units in AH
      push  eax
      and   eax,0ffh	      ;keep only the thousands & hundreds
      mov   cl,10
      div   cl		      ;->thousands in AL, hundreds in AH
      add   ax,3030h	      ;convert to characters
      stosw		      ;insert them
      pop   eax
      shr   eax,8	      ;get the tens & units in AL
      div   cl		      ;tens in AL, units in AH
      add   ax,3030h	      ;convert to characters
      stosw		      ;insert them

finish:
      xor   eax,eax
      stosb		      ;string terminating 0
      pop   edi
      pop   esi
      pop   ecx

      frstor [content]

      or    al,1	      ;to insure EAX!=0
      ret
      
endp

; #########################################################################


 align 4

; #########################################################################
;                             FpuCos
;##########################################################################
  ;
  ;                           cos(Src) -> Dest
FpuCos:
	test [flags],(1 shl 30)
	jz @f			;jump if angle already in radians
	fldpi			;load pi (3.14159...) on FPU
	fmulp
	test [flags],(1 shl 31)
	jnz .1
	pushd 200
	jmp .2
.1:	pushd 180
.2:	fidiv dword [esp]	;value now in radians
	fwait
	add esp,4		;clean the stack
@@:	fldpi
	fadd  st,st		;->2pi
	fxch

@@:	fprem			;reduce the angle
	fcos
	fstsw ax		;retrieve exception flags from FPU
	fwait
	shr al,1		;test for invalid operation
	sahf			;transfer to the CPU flags
	jpe	@B		;reduce angle again if necessary
	fstp st1		;get rid of the 2pi
ret

 align 4
; #########################################################################
;                             FpuSin
;##########################################################################
  ;
  ;                       sin(Src) -> Dest
FpuSin:
	test [flags],(1 shl 30)
	jz @f			;jump if angle already in radians
	fldpi			;load pi (3.14159...) on FPU
	fmulp
	test [flags],(1 shl 31)
	jnz .1
	pushd 200
	jmp .2
.1:	pushd 180
.2:	fidiv dword [esp]	;value now in radians
	fwait
	add esp,4		;clean the stack
   @@:
	fldpi
	fadd  st,st		;->2pi
	fxch

	fprem                   ;reduce the angle
	fsin
	fstp  st1             ;get rid of the 2pi
ret

 align 4
; #########################################################################
;                             FpuArctan
;##########################################################################
  ;
  ;                       atan(Src) -> Dest
FpuArctan:
	fld1
	fpatan
	test [flags],(1 shl 30)
	jz @F			;jump if angle is required in radians
	test [flags],(1 shl 31)
	jnz .1
	pushd 200
	jmp .2
.1:	pushd 180
.2:	fimul dword [esp]	;*180 degrees
	fldpi			;load pi (3.14159...) on FPU
	fdivp			;*180/pi, angle now in degrees
	add esp,4		;clean CPU stack

	ftst			;check for negative angle
	fstsw ax		;retrieve status word from FPU
	fwait
	sahf
	jnc  @F			;jump if positive number
	test [flags],(1 shl 31)
	jnz .3
	pushd 400
	jmp .4
.3:	pushd 360
.4:	fiadd dword [esp]	;angle now 0-360
	fwait
	add esp,4			;clean CPU stack
      
@@:
	ret

 align 4
; #########################################################################
;                             FpuTan
;##########################################################################

  ;                           a = tan(x)
FpuTan:
	fldpi			;load pi (3.14159...) on FPU
	fadd  st,st		;->2pi
	fxch
	test [flags],(1 shl 30)
	jz @F			;jump if angle already in radians
	test [flags],(1 shl 31)
	jnz .1
	pushd 400
	jmp .2
.1:	pushd 360
.2:	fmul st,st1
	fidiv dword [esp]	;value now in radians
	pop eax			;clean the stack
@@:	fprem			;reduce the angle
	fptan

	fstp st			;get rid of the 1
	fstp st1		;get rid of the 2pi
ret

 align 4
; #########################################################################
;                             FpuArccos
;##########################################################################
  ;                                sqrt(1-Src^2)
  ;               acos(Src) = atan -------------  -> Dest
  ;                                     Src

FpuArccos:
	fld st			;copy cosine value
	fmul st0,st1			;cos^2
	fld1
	fsubrp			;1-cos^2 = sin^2
	fsqrt			;->sin
	fxch
	fpatan			;i.e. arctan(sin/cos)

	test [flags],(1 shl 30)
	jz @F			;jump if angle is required in radians
	test [flags],(1 shl 31)
	jnz .1
	pushd 200
	jmp .2
.1:	pushd 180
.2:	fimul dword [esp]	;*180 degrees
	fldpi			;load pi (3.14159...) on FPU
	fdivp			;*180/pi, angle now in degrees
	pop eax			;clean CPU stack
@@:
ret

 align 4
; #########################################################################
;                             FpuArcsin
;##########################################################################
  ;                                  Src
  ;            asin(Src) = atan -------------  -> Dest
  ;                             sqrt(1-Src^2)
FpuArcsin:
	fld st0			;copy sine value
	fmul st0,st0			;sin^2
	fld1
	fsubrp			;1-sin^2 = cos^2
	fsqrt			;->cos
	fpatan			;i.e. arctan(sin/cos) = arcsin
	test [flags],(1 shl 30)
	jz @F			;jump if angle is required in radians
	test [flags],(1 shl 31)
	jnz .1
	pushd 200
	jmp .2
.1:	pushd 180
.2:	fimul dword [esp]	;*180 degrees
	fldpi			;load pi (3.14159...) on FPU
	fdivrp			;*180/pi, angle now in degrees
	add esp,4		;clean CPU stack
@@:
	ret

 align 4
; #########################################################################
;                             FpuEexpX
;##########################################################################

  ;              e^(Src) = antilog2[ log2(e) * Src ] -> Dest

FpuEexpX:

      fldl2e                  ;->log2(e)
      fmulp                    ;->log2(e)*Src
      
      fld   st0             ;copy the logarithm
      frndint                 ;keep only the characteristic
      fsub  st1,st          ;keeps only the mantissa
      fxch                    ;get the mantissa on top

      f2xm1                   ;->2^(mantissa)-1
      fld1
      faddp                    ;add 1 back

      fscale                  ;scale it with the characteristic
      
      fstp  st1             ;get rid of the characteristic
ret

 align 4
; #########################################################################
;                             FpuXexpY
;##########################################################################

  ;            Src1^Src2 = antilog2[ log2(Src1) * Src2 ] -> Dest

FpuXexpY:
      fxch                    ;set up FPU registers for next operation
      fyl2x                   ;->log2(Src1)*exponent
      
      fld   st0             ;copy the logarithm
      frndint                 ;keep only the characteristic
      fsub  st1,st          ;keeps only the mantissa
      fxch                    ;get the mantissa on top

      f2xm1                   ;->2^(mantissa)-1
      fld1
      faddp                    ;add 1 back

      fscale                  ;scale it with the characteristic
      
      fstp  st1             ;overwrite the characteristic
ret


if 0
exp:
.N equ 10
	xor eax,eax
	mov ecx,.N
	fld1
.0:	dec ecx
	jz .end
	mov eax,.N
	sub eax,ecx
	mov [.d],eax
	fild [.d]
@@:	dec eax
	jz @f
	mov [.d],eax
	fild [.d]
	fmulp
	jmp @b
@@:	mov eax,.N
	sub eax,ecx
	fld st2
@@:	dec eax
	jz @f
	fld st3
	fmulp
	jmp @b
@@:	fdivrp
	faddp
	jmp .0
.end:	fxch
	fstp st0
ret
.d dd 0
endf