;*********************
format pe console      ;Flat PE
include 'win32axp.inc'
;include 'dbg32.inc'
;*********************

stdcall fpbinq,z,1     ;Display normalized binary (double)
stdcall fpbin,[x],1    ;Display normalized binary (single)
fst [y] 	       ;Attempt to store from empty ST0. Check flags
call fpu_sflag	       ;Display FPU status flag
fld [x] 	       ;Load a single precision into ST0
call fpu_stack	       ;View FPU stack
stdcall prtdbl,z,5,1   ;pass-by-reference. Print with 5 decimal places
stdcall prtflt,[x],0,1 ;pass-by-value. Display in full. New line
fld [z] 	       ;Load another variable to ST0
fldpi		       ;Load PI
fld [n] 	       ;load a NAN value
call fpu_stack	       ;See the stack
call fpu_cflag	       ;Display FPU control flag

invoke system,"pause>0"
invoke exit,0

x dd -0.000000000000000000045332
y dq 34.06565
z dq -67.004355
n dq 9221120237041090561 ;a NAN value

;---------------------------------
;DISPLAY FPU STACK
;Note: Use fclex where appropriate
;---------------------------------
proc fpu_stack
	locals
	   st dq 0.0
	endl
	pushad
	xor ecx,ecx
	lea edx,[st]
.again: fst [st]
	stdcall prtstr,'ST'
	add ecx,30h
	stdcall prtchr,ecx
	stdcall prtstr,': '
	sub ecx,30h
	;----------------------
	xor eax,eax
	fxam		 ;Read status flag
	fstsw ax
	and eax,4500h	 ;..for C3,C2 and C0
	.if eax=4100h ;
	    stdcall prtstr,"... \n"  ;empty
	    jmp .go
	;.elseif eax=4000h
	;    stdcall prtstr,"<0>\n"
	;    jmp .go
	.elseif eax=500h
	    stdcall prtstr,"<inf>\n"
	    jmp .go
	.elseif eax=100h
	    stdcall prtstr,"<NaN>\n"
	    jmp .go
	.elseif eax=0
	    stdcall prtstr,"<bad>\n"
	    jmp .go
	.endif
	fclex
	;----------------------
	stdcall prtdbl,edx,0,1
.go:	cmp ecx,7
	    je .out
	fincstp    ;rotate stack
	inc ecx
	jmp .again
.out:	fincstp
	popad
	ret
endp

;--------------------------------------
;Double-Precision to string
;DISPLAY FP (REAL8)
;info : dq address (initialize as FP)
;     : Takes and displays e format
;Note : Avoid using 'raw' EBX as argument
;pt   : Decimal places (max=16) rounding
;     :   0 - no rounding
;nline: 0-no line,1-new line
;--------------------------------------
proc prtdbl,val,pt,nline
	locals
	     dat dq 1.0,10.0,100.0,1000.0,10000.0,\
		    1e5,1e6,1e7,1e8,1e9,1e10,1e11,\
		    1e12,1e13,1e14,1e15,1e16,1e17
	     control dw 0
	     marker db 0
	     subn db 0
	     nega db 0
	     small db 0
	     big db 0
	     temp dq 0.0
	     val1 dq 0
	     val2 dq 0.0
	     cpy dq 0.0
	     copy dq 0.0
	     m dq 0.0
	     tmp dd 0
	     e dd 0
	     t dd 1.0
	     ten dd 10.0
	     one dd 1.0
	     idx dd 0
	     save rb 108
	endl
	pushad

	lea eax,[save]	;save FPU
	fsave [eax]
	push eax

	mov eax,[val]
	mov edx,dword[eax]
	mov dword[copy],edx
	mov edx,dword[eax+4]
	mov dword[copy+4],edx
	test edx,edx
	     jns .v
	stdcall prtchr,dword '-'
.v:	finit
	fld [copy]
	fst [cpy]
	fabs
	fst [copy]
.back:	fxtract
	fstp [m]     ;mantissa
	fistp [e]    ;exponent
	mov ecx,dword[cpy]
	mov edx,dword[cpy+4]
	.if ecx=0 & (edx=80000000h | edx=0)
	    stdcall prtchr,dword '0'  ;Signed 0.0
	    stdcall prtchr,dword '.'
	    stdcall prtchr,dword '0'
	    jmp .quit
	.endif
	shr edx,20
	.if edx = 07ffh | edx = 0fffh
	    stdcall prtchr,dword "#" ;NaN and friends
	    jmp .quit
	.endif
	xor edx,edx
	mov ecx,[e]
	mov dx,word[m+6]
	and edx,0fh	  ;clear higher bytes
	or edx,16	  ;+1.0
	mov word[m+6],dx  ;
	mov edx,dword[m+4];
	bsr esi,edx
	xor eax,eax
	test ecx,ecx
	     js .small
	cmp ecx,46
	    ja .big
;---------------------
;Extract integral part
.go:	bt edx,esi
	jc .pow
.ok:	test ecx,ecx
	     jz .half
	dec ecx
	.if esi=0 & ~[marker]
	    mov edx,dword[m]
	    mov esi,31
	    mov [marker],1
	    jmp .go
	.endif
	dec esi
	jmp .go
.pow:	mov [tmp],ecx
	finit
	fild [tmp]
	fld1
	fscale
	fld [val1]
	fadd st0,st1
	fstp [val1]
	jmp .ok
;---------------------
;Transition
.half:	dec esi
	mov ecx,1
	jmp .go1
;---------------------
;Extract fraction part
.go1:	bt edx,esi
	jc .pow1
.ok1:	.if ~esi & ~[marker]
	    mov edx,dword[m]
	    mov esi,31
	    mov [marker],1
	    inc ecx
	    jmp .go1
	.endif
	test esi,esi
	     jz .DISP
	dec esi
	inc ecx
	jmp .go1
.pow1:	finit
	mov [t],ecx
	fild [t]
	fchs
	fld1
	fscale
	fadd [val2]
	fstp [val2]
	jmp .ok1
;---------------------------------------
;Process Fraction with negative exponent
;And if ecx is too large (x.xxxxe-y format)
;else, proceed normally
.small: xor edi,edi
	finit
	fld [copy]
	fabs
.x:	fmul [ten]
	inc edi
	fcom [one]
	fstsw ax
	sahf
	jae .k
	jmp .x
.k:	mov [idx],edi
	fst [copy]
	.if edi < 15 & ~[pt]  ;normal display
	    fldz
	    fstp [val2]
	    neg ecx
	    mov [nega],1
	    jmp .go1
	.endif
	mov [small],1
	jmp .back
;------------------------------
;Process large integral part
;That requires x.xxxxe+y format
.big:	finit
	xor edi,edi
	fld [copy]
.f:	fdiv [ten]
	inc edi
	fcom [ten]
	fstsw ax
	sahf
	jb .g
	jmp .f
.g:	mov [idx],edi
	mov [big],1
	jmp .back
;---------------------------------
;By now......
;val1 should contain integral part
;val2 should contain fraction part
.DISP:	.if [nega]  ;if 0.xxxxx
	    stdcall prtchr,dword '0'
	    jmp .finish
	.endif
;----------------------------
;Display integral part
.DISP1: finit
	;---------------
	fstcw [control]
	bts [control],10	;Rounding: down
	fldcw [control] 	;for following DIVs
	;---------------
	xor edx,edx
	fld [val1]
	fstp [cpy]
	fld [cpy]		;single digit
	fcom [ten]
	fstsw ax
	sahf
	jb .w
.t:	fdiv [ten]		;Repeat DIV
	fcom [ten]		;to get the front digit
	fstsw ax
	sahf
	jb .s
	inc edx
	jmp .t
	;---------------
.s:	fst [temp]		;save digit
	frndint 		;round ST0 to x.000000
	fist [tmp]		;Display
	add [tmp],30h
	stdcall prtchr,[tmp]
	inc edx 		;Adjust post. value
	fmul [dat+edx*8]	;Get positional value * digit
	fld [val1]
	fsub st0,st1		;Subtract from previous value
	.if edx=1		;Display last digit
	    fmul [one]
	    fistp [tmp]
	    add [tmp],30h
	    stdcall prtchr,[tmp]
	    jmp .finish
	.endif
	.if edx > 1		;case: mid 0s. Related to .w
	    push edx		;and leading 0. How?
	    dec edx
.q:	    fcom [dat+edx*8]
	    fstsw ax
	    sahf
	    jb .midz
	    pop edx
	.endif
	fstp [val1]
	jmp .DISP1
	;--------------
.midz:	stdcall prtchr,dword '0' ;middle 0s
	.if edx=0
	    pop edx
	    jmp .finish
	.endif
	dec edx
	jmp .q
.w:	frndint
	fist [tmp]
	add [tmp],30h
	stdcall prtchr,[tmp]
	jmp .finish
;------------------------------
;Transition
.finish:.if [subn]
	    jmp .quit
	.endif
	stdcall prtchr,dword '.'
;---------------------
;Display fraction part
.DISP2: xor edx,edx
	finit
	;---------------
	fstcw [control]
	bts [control],10   ;Rounding: down
	fldcw [control]    ;for next MULs
	;---------------
	fld [val2]
	fstp [cpy]
	fld [cpy]
.z:	fmul [ten]
	fst [temp]
	fld [temp]
	fist [tmp]
	frndint
	;Places Rounding ----------
	.if [pt]
	    inc edx
	    .if edx=[pt]      ;if final digit
		finit
		fld [temp]
		fistp [tmp]
		frndint
		add [tmp],30h
		stdcall prtchr,[tmp]
		jmp .done
	    .endif
	    dec edx
	.endif
	;---------------------------
	add [tmp],30h
	stdcall prtchr,[tmp]
	fxch
	fsub st0,st1
	ffree st1      ;NASTY bug!
	inc edx
	cmp edx,16
	    je .done
	jmp .z
;--------------------------
;Transition...
;Display exponential format
;If Big or Small numbers
.done:	.if [big]
	    stdcall prtchr,dword 'e'
	    stdcall prtchr,dword '+'
	    fild [idx]
	    fstp [val1] ;re-use
	    mov [subn],1
	    jmp .DISP1
	.endif
	.if [small]
	    stdcall prtchr,dword 'e'
	    stdcall prtchr,dword '-'
	    fild [idx]
	    fstp [val1] ;re-use
	    mov [subn],1
	    jmp .DISP1
	.endif
;--------------------------
.quit:	.if [nline]
	    call line
	.endif
	pop eax
	frstor [eax] ;restore FPU
	popad
	ret
endp

;--------------------------------------
;Float to String
;DISPLAY FP (REAL4)
;info : [dd] (initialize as FP)
;     : reg32,fp constant
;     : Takes and displays e format
;Note : Avoid using 'raw' EBX as argument
;pt   : Decimal places (max=6) rounding
;     : 0 - no rounding
;nline: 0-no line
;--------------------------------------
proc prtflt,info,pt,nline
	locals
	   tst dd 1,1000000,100000,\
		  10000,1000,100,10
	   frac db 0	;marker
	   subn db 0	;marker
	   num dd 0	;integral part
	   num2 dd 0	;fraction part
	   idx dd 0	;for small
	   ndx dd 1	;For big and large
	   expn dd 0
	   mant dd 0.0
	   copy dd 0.0
	   y dd 10.0
	   one dd 1.0
	   save rb 108
	endl
	pushad
	lea eax,[save]
	fsave [eax]
	push eax

	mov eax,[info]
	test eax,eax
	     jns .d
	stdcall prtchr,dword '-'
.d:	.if eax=0 | eax=80000000h
	     jmp .print   ;case: signed 0.0
	.endif
.back:	finit
	mov [copy],eax
	fld [copy]
	fabs
	fxtract
	fstp [mant]
	fistp [expn]
	mov ecx,[expn]
	mov edi,[mant]
	and edi,7fffffh
	or edi,800000h ;set bit 23
	bsr esi,edi
	xor eax,eax
	cmp [idx],1
	    je .dnorm
	test ecx,ecx
	     js .small
	mov edx,ecx
	cmp ecx,22
	    ja .norm
;DENORMALIZE ----------------
.dnorm: bt edi,esi
	   jc .plus
.ok:	dec esi
	test ecx,ecx
	     jz .big
	dec ecx
	jmp .dnorm
.plus:	mov edx,1
	shl edx,cl
	add eax,edx
	jmp .ok
;NORMALIZE ------------------
;Get: mantissa * 2^exp
.norm:	mov [copy],ecx
	fild [copy] ;exp
	fld1
	fscale
	fmul [mant]
	fstp [num]
	mov eax,[num]
;FOR BIG EXPONENT------------
.big:	mov [num],eax
	xor eax,eax
	cmp [num],10000000
	    jb .frctn
	mov edx,1
	finit
	fld [num]
.f:	fdiv [y]
	fcom [y]
	fstsw ax
	sahf
	jb .g
	inc edx
	jmp .f
.g:	fstp [num]
	mov eax,[num]
	mov [subn],1  ;marker up
	mov [ndx],edx
	jmp .back
;FOR SMALL NEGATIVE EXPONENT---------
.small: mov eax,[info]
	mov [num2],eax
	xor edx,edx
	fld [num2]
	fabs
.s:	fmul [y]
	inc edx
	fcom [one]
	fstsw ax
	sahf
	jae .k
	jmp .s
.k:	fstp [num2]
	mov [idx],edx
	cmp edx,6 ;7 ???
	    jl .nexp
	mov eax,[num2]
	mov [subn],1  ;marker up
	jmp .back     ;normalize to x.xxxxe-y
;NEGATIVE EXPONENT-------------------
.nexp:	neg ecx
	shr edi,cl
	bsr esi,edi
	dec ecx
	xor eax,eax
;FRACTION ---------------------------
.frctn: bt edi,esi
	   jc .pow1
.ko:	inc ecx
	test esi,esi
	     jz .round
	dec esi
	jmp .frctn
.pow1:	mov edx,5000000 ;negative pow of 2
	shr edx,cl	;at position CL
	add eax,edx
	jmp .ko
;DONE & ROUNDING --------------------
.round: mov [num2],eax
	mov edx,[pt]
	.if edx > 6
	    xor edx,edx
	.endif
	fild [num2]
	fidiv [tst+edx*4]
	frndint
	fimul [tst+edx*4]
	fistp [num2]
	;cmp edx,1
	;    jne .print
	cmp [num2],10000000
	    jb .print
	mov [num2],eax
;DISPLAY & FORMATING-----------------
.print: xor esi,esi
	mov ecx,[num]
	cmp [frac],1
	    jne .start
	mov ecx,[num2]
.start: mov eax,ecx
	mov edi,0cccccccdh
	mul edi
	shr edx,3
	mov eax,edx
	mov edi,edx
	mov edx,10
	mul edx
	sub ecx,eax
	push ecx
	mov ecx,edi
	inc esi
	.if [frac]=1 ;zero padding
	    .if esi = 7
		xor edi,edi
		jmp .prt
	    .endif
	.else
	    test edi,edi
	    jz .prt
	.endif
	jmp .start
.prt:	pop eax
	.if [pt] & [frac]=1
	    inc edi
	    cmp [pt],edi ;keep popping
	    jb .m	 ;trailing 0s
	.endif		 ;add esp??
	add al,30h
	stdcall prtchr,eax
.m:	dec esi
	test esi,esi
	     jz .what
	jmp .prt
.what:	cmp [frac],2 ;if both done
	    je .quit
	.if ~ [frac]
	    stdcall prtchr,dword '.'
	    mov [frac],1
	    jmp .print
	.endif
	;Scientific E -------
	.if [subn]
	    inc [frac]
	    stdcall prtchr,dword 'e'
	    .if [idx]
		stdcall prtchr,dword '-'
		mov ecx,[idx]
	    .else
		stdcall prtchr,dword '+'
		mov ecx,[ndx]
	    .endif
	    jmp .start
	.endif
.quit:	.if [nline]
	    call line
	.endif
	pop eax
	frstor [eax]
	popad
	ret
endp

;-------------------------
;DISPLAY FPU CONTROL FLAGS
;-------------------------
proc fpu_cflag
	locals
	    controls dw 0
	endl
	push eax ecx
	fstcw [controls]
	mov ecx,15
.go:	add ecx,30h
	.if ecx > 39h
	    add ecx,7h
	.endif
	stdcall prtchr,ecx
	stdcall prtchr,dword ' '
	stdcall prtchr,dword ' '
	.if ecx > 39h
	    sub ecx,7h
	.endif
	sub ecx,30h
	test ecx,ecx
	    je .done
	dec ecx
	jmp .go
.done:	stdcall prtstr,"\n----------------------------------------------"
	stdcall prtstr,"\n        IC RC RC PC PC IEM   PM UM OM ZM DM IM\n"
	stdcall prtstr,"----------------------------------------------\n"
	mov ecx,15
	xor eax,eax
	movzx eax,[controls]
.go1:	bt eax,ecx
	   jc .one
	stdcall prtchr,dword '0'
.y:	test ecx,ecx
	     jz .out
	dec ecx
	stdcall prtchr,dword ' '
	stdcall prtchr,dword ' '
	jmp .go1
.one:	stdcall prtchr,dword '1'
	jmp .y
.out:	stdcall prtchr,dword ' '
	stdcall prthex,eax,0
	call line
	call line
	fldcw [controls]  ;restore control flag
	pop ecx eax
	ret
endp

;------------------------
;DISPLAY FPU STATUS FLAGS
;------------------------
proc fpu_sflag
	push eax ecx
	fstsw ax
	fwait
	push eax
	mov ecx,15
.go:	add ecx,30h
	.if ecx > 39h
	    add ecx,7h
	.endif
	stdcall prtchr,ecx
	stdcall prtchr,dword ' '
	stdcall prtchr,dword ' '
	.if ecx > 39h
	    sub ecx,7h
	.endif
	sub ecx,30h
	test ecx,ecx
	    je .done
	dec ecx
	jmp .go
.done:	stdcall prtstr,"\n----------------------------------------------"
	stdcall prtstr,"\nB C3 TP TP TP C2 C1 C0 IR SF  P  U  O  Z  D  I\n"
	stdcall prtstr,"----------------------------------------------\n"
	mov ecx,15
	pop eax
.go1:	bt eax,ecx
	   jc .one
	stdcall prtchr,dword '0'
.y:	test ecx,ecx
	     jz .out
	dec ecx
	stdcall prtchr,dword ' '
	stdcall prtchr,dword ' '
	jmp .go1
.one:	stdcall prtchr,dword '1'
	jmp .y
.out:	stdcall prtchr,dword ' '
	stdcall prthex,eax,0
	call line
	call line
	pop ecx eax
	ret
endp

;---------------------------------
;String to float
;GET FP (REAL4) FROM KEYBOARD
;Convert and save to info
;info: dd (initialize as FP)
;    : Does not take e-format
;    : Must use FP format as input
;---------------------------------
proc getflt,info
	locals
	     m dd 0
	     pow10 dd 10.0
	     power dd 1.0
	     val dd 0.0
	     sgn db 0
	endl
	xor esi,esi
	xor edi,edi
.get:	cinvoke getchar
	.if al = '-'
	    mov [sgn],1
	    jmp .get
	.endif
	cmp al,0ah
	    je .start
	.if al = '.'
	    mov edi,esi
	    jmp .get
	.endif
	sub eax,30h
	push eax
	inc esi
	jmp .get
;----------------------
.start: finit
	pop [m]
	fild [m]
	fstp [val]
	mov ecx,esi
	dec ecx
.rr:	finit
	fld [power]
	fmul [pow10]
	fst [power]
	pop [m]
	fild [m]
	fmul st0,st1
	fld [val]
	fadd st0,st1
	fstp [val]
	loop .rr
;----------------------
	mov ecx,esi
	sub ecx,edi
	finit
	fld [val]
.d:	fdiv [pow10]
	loop .d
;----------------------
.quit:	fstp [val]
	mov eax,[info]
	mov ecx,[val]
	.if [sgn]
	    or ecx,80000000h
	.endif
	mov [eax],ecx
	ret
endp

;---------------------------------
;String to double
;GET FP (REAL8) FROM KEYBOARD
;Convert and save to info
;info: dq (initialize as FP)
;    : Does not take e-format
;    : Must use FP format as input
;---------------------------------
proc getdbl,info
	locals
	     m dd 0
	     pow10 dd 10.0
	     power dq 1.0
	     val dq 0.0
	     sgn db 0
	endl
	xor esi,esi
	xor edi,edi
.get:	cinvoke getchar
	.if al = '-'
	    mov [sgn],1
	    jmp .get
	.endif
	cmp al,0ah
	    je .start
	.if al = '.'
	    mov edi,esi
	    jmp .get
	.endif
	sub eax,30h
	push eax
	inc esi
	jmp .get
;----------------------
.start: finit
	pop [m]
	fild [m]
	fstp [val]
	mov ecx,esi
	dec ecx
.rr:	finit
	fld [power]
	fmul [pow10]
	fst [power]
	pop [m]
	fild [m]
	fmul st0,st1
	fld [val]
	fadd st0,st1
	fstp [val]
	loop .rr
;----------------------
	mov ecx,esi
	sub ecx,edi
	finit
	fld [val]
.d:	fdiv [pow10]
	loop .d
;----------------------
.quit:	fstp [val]
	mov eax,[info]
	mov ecx,dword[val+4]
	mov edx,dword[val]
	.if [sgn]
	    or ecx,80000000h
	.endif
	mov dword[eax+4],ecx
	mov dword[eax],edx
	ret
endp

;----------------------------
;CONVERT & DISPLAY HEX STRING
;info = reg32,imm32,[dd]
;nline = 1-newline,0-no line
;----------------------------
proc prthex,info,nline
	pushad
	mov eax,[info]
	xor esi,esi
	mov ecx,16
.start: xor edx,edx
	div ecx
	push edx
	inc esi
	test eax,eax
	    jz .conv
	jmp .start
.conv:	dec esi
	pop eax
	add al,30h
	cmp al,39h
	    jbe .prt
	add al,7
.prt:	stdcall prtchr,eax
	test esi,esi
	    jz .done
	jmp .conv
.done:	cmp [nline],0
	    je .quit
	call line
.quit:	popad
	ret
endp

;---------------------------
;DISPLAY FP (REAL4) BINARY
;info  : r32,[dd],real4
;nline : 1 - newline, 0-none
;---------------------------
proc fpbin,info,nline
	pushad
	pushfd
	mov edx,[info]
	mov ecx,32
.go:	shl edx,1
	    jc .one
	stdcall prtchr,dword '0'
	jmp .ok
.one:	stdcall prtchr,dword '1'
.ok:	.if ecx=24 | ecx=32
	    stdcall prtchr,dword '.'
	.endif
	loop .go
.done:	.if [nline]
	   call line
	.endif
	popfd
	popad
	ret
endp

;-------------------------------
;DISPLAY DOUBLE PRECISION BINARY
;info	 :dq address only
;nline	 :1-new line. 0-no line
;-------------------------------
proc fpbinq,info,nline
	pushad
	mov ecx,[info]
	mov edx,[ecx+4]
	mov eax,63
.start: shl edx,1
	jc .one
	stdcall prtchr,dword '0'
.ok:	.if eax=63 | eax=52
	    stdcall prtchr,dword '.'
	.endif
	dec eax
	cmp eax,31
	    je .next
	test eax,eax
	     js .done
	jmp .start
.one:	stdcall prtchr,dword '1'
	jmp .ok
.next:	stdcall prtchr,dword '-'
	mov edx,[ecx]
	jmp .start
.done:	.if [nline]
	    call line
	.endif
	popad
	ret
endp

;----------------------------------
;Display a character
;info: char in reg32,dword[db],[dd]
;      for constant, use dword
;----------------------------------
proc prtchr,info
	pushad
	cinvoke putchar,dword[info]
	popad
	ret
endp

;-----------------------------
;DISPLAY STRING
;Takes string buffer with dup
;Takes 0-ended string buffers
;takes #-ended string constant
;-----------------------------
proc prtstr,info
	pushad
	mov esi,[info]
.prt:	;cmp byte[esi],'#'
	;    je .done
	cmp byte[esi],0ah
	    je .done
	cmp byte[esi],0
	    je .done
	cmp word[esi],'\n'
	    je .ok
	stdcall prtchr,dword[esi]
	inc esi
	jmp .prt
.ok:	call line
	add esi,2
	jmp .prt
.done:	popad
	ret
endp

;--------
proc line
;--------
	pushad
	stdcall prtchr,0dh
	stdcall prtchr,0ah
	popad
	ret
endp

;IMPORT TABLE
data import
     library msvcrt,'msvcrt.dll',\
     kernel32,'kernel32.dll'
	import msvcrt,\
	   system,'system',exit,'exit',\
	   putchar,'putchar',getchar,'getchar'
end data

;fasmnewbie / soffianabdulrasad @ gmail . com
;Sept 24rd, 2014
