The MASM Forum

General => The Workshop => Topic started by: clamicun on February 08, 2016, 08:32:53 AM

Title: A question on FPU
Post by: clamicun on February 08, 2016, 08:32:53 AM
;this does it - why ... I still am on problems with "FPU"

include \masm32\include\masm32rt.inc
include Convert8DR.inc   ;You have to download the zipFile to make it working

;=========
show_string MACRO arg1,arg2
LOCAL ws_msg
LOCAL stringlabel

.data
ws_msg    TCHAR 60 dup(?),0
stringlabel TCHAR '%s',0

.code
    pushad
    INVOKE wsprintf,addr ws_msg,addr stringlabel,reparg(arg2)
    INVOKE MessageBox,0,addr arg1,addr ws_msg,0
    popad
ENDM
;==========

.data
startvalue db "0.00",0
endvalue   db 20 dup(?),0

value1 db "1.11",0
value2 db "2.22",0
value3 db "3.33",0
value4 db "4.44",0
value5 db "5.55",0

FPU_calc    REAL8 ?
;-----------------

.code
start:

call Calculate


INVOKE ExitProcess,0

;============
Calculate proc

FINIT  ; It is good programming practice to take the precaution of initializing FPU before starting any computation
FFREE st(0)

show_string startvalue,"start value"

INVOKE crt_atof,offset startvalue
INVOKE crt_atof,offset value1
fadd st(0),st(1)                 ; add ST(0)+ST(1)

INVOKE crt_atof,offset value2
fadd st(0),st(1)                

INVOKE crt_atof,offset value3
fadd st(0),st(1)             

INVOKE crt_atof,offset value4
fadd st(0),st(1)   
             
INVOKE crt_atof,offset value5
fadd st(0),st(1)

fstp FPU_calc         ;Store real number and pop ST(0)

;So the result is in st(0)
;I use 'Convert8DR.inc' written by RuiLoureiro,- it works well, but I do not understand his macros... 
;Is there an "easier" way to convert the content of 'FPU_calc REAL8 ?' into a string - MasmBasics ??
 
INVOKE  ConvertReal8DR, offset FPU_calc, offset endvalue

show_string endvalue,"result add"

ret
Calculate endp
;============

end start
Title: Re: A question on FPU
Post by: jj2007 on February 08, 2016, 08:39:57 AM
;Is there an "easier" way to convert the content of 'FPU_calc REAL8 ?' into a string - MasmBasics ??
deb 4, "Result", FPU_calc

Or, more flexible:
Let esi=Str$("The result is %4f", FPU_calc)
PrintLine "[", esi, "]"


Simplest version:
Let some$=Str$(FPU_calc)
Title: Re: A question on FPU
Post by: clamicun on February 08, 2016, 10:23:45 AM
Hi jj
Did you really check this ?

;my code
;So the result is in st(0)
INVOKE  ConvertReal8DR, offset FPU_calc, offset endvalue
show_string endvalue,"result add"   ;correct result

;and now what ?
;===
;your codes
;deb 4, "Result", FPU_calc       ; ???

;Or, more flexible:
;Let esi=Str$("The result is %4f", FPU_calc)             ;no result !!!
;PrintLine "[", esi, "]"  ;assembled as console           -Nothing

;Simplest version:
;Let some$=Str$(FPU_calc)                                    ;Error "some" not defined

;===
Title: Re: A question on FPU
Post by: jj2007 on February 08, 2016, 11:05:01 AM
Quote from: clamicun on February 08, 2016, 10:23:45 AM
Hi jj
Did you really check this ?

Of course. But if you use some$, you must define it previously ;-)
Full code attached. I wonder what ConvertReal8DR does.

> ;So the result is in st(0)
Well, not really. You popped it, right?
Title: Re: A question on FPU
Post by: HSE on February 08, 2016, 12:39:40 PM
I think very littlle is very good!

include \masm32\include\masm32rt.inc
   
.data
    startvalue qword 0.0
    value1 qword 1.11
    value2 qword 2.22   
    value3 qword 3.33
    value4 qword 4.44
    value5 qword 5.55
    FPU_calc qword 0.0

.code

start:
    call main
    inkey
    exit

main proc

    finit
    fld startvalue
    fadd value1
    fadd value2
    fadd value3
    fadd value4
    fadd value5
    fstp FPU_calc

    cls
    print real8$(FPU_calc), 13, 10

  ret

main endp
end start
Title: Re: A question on FPU
Post by: jj2007 on February 08, 2016, 06:40:04 PM
    cls
    print real8$(FPU_calc), 13, 10
    PrintLine Str$(FPU_calc)


Output:
16.650000
4625379776044361319

Guess why...
Title: Re: A question on FPU
Post by: dedndave on February 09, 2016, 12:40:39 AM
many decimal values cannot be precisely represented in the FPU binary formats
what you are seeing is the closest FPU format value
Title: Re: A question on FPU
Post by: jj2007 on February 09, 2016, 01:10:04 AM
In this case, the explanation is much simpler: Str$() thinks it's a qword integer:

FPU_calc qword 0.0  ; if the assembler was mean, it would throw an error for this abuse of types ;-)
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 02:02:55 AM
To HSE,
sometimes very little is not good enough...

The values can not be qwords or dwords. They are "strings" and are downloaded from a database.

print real8$(FPU_calc), 13, 10 is fine, but I need the "endvalue".
Thank you
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 02:06:45 AM
To jj,
Convert8DR.inc. Written by:   RuiLoureiro         @masmforum
What it does?
It is attached. So check it out.
Like I said. I do not get it entirely.
Title: Re: A question on FPU
Post by: qWord on February 09, 2016, 02:12:33 AM
REAL8 to string? =>

LOCAL buf[32]:CHAR,r8Value:REAL8
...
fn crt_sprintf, &buf, "%.15E", r8Value
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 03:09:08 AM
jj,

assembled "floats.zip"

Works perfectly.
Thanks a lot.
Mic
btw... Did you take a look on Convert8DR.inc ?
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 03:52:07 AM
jj,
one last qestion..

Let esi=Str$("%4f",FPU_calc)
PrintLine  esi

Now I need the value as string for further use
Title: Re: A question on FPU
Post by: jj2007 on February 09, 2016, 03:56:26 AM
Quote from: clamicun on February 09, 2016, 03:09:08 AM
btw... Did you take a look on Convert8DR.inc ?

I took a look. Resembles my Float2Asc routine - 500+ lines of scarcely commented code ::)

Quote from: clamicun on February 09, 2016, 02:02:55 AMThey are "strings" and are downloaded from a database.

So you want to process a textfile? Can you post an example of that database, and pseudo code what you want to do with it? StringToArray (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1130) is pretty powerful for such things.

> Now I need the value as string for further use
esi is a string. You can print it, use it in a MsgBox, concatenate it, put it in an array, write it to disk...

Let esi=Str$("%4f",FPU_calc)
FileWrite "SomeFile.txt", esi
Let esi="Hello "+esi+" World"
Let My$(0)="["+esi+"]"
...
Title: Re: A question on FPU
Post by: HSE on February 09, 2016, 05:21:48 AM
Hi Clamicun!

Of course, just a game. The clue was "real8$".

qWord help you showing what is behind real8$.

The solution is pretty simple:

fn crt_sprintf, addr endvalue, "%lf", FPU_calc



Regards. HSE
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 05:39:32 AM
Hi HSE,

" fn crt_sprintf, addr endvalue, "%lf", FPU_calc..."

ok. but as I said...
Seeing it is good, but I nedd it for further use.
Thank you
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 05:51:14 AM
jj,

"I took a look. Resembles my Float2Asc routine - 500+ lines of scarcely commented code ::)"

That is what I meant ..
I do not understand it entirely (lots of code for little output).

Let esi=Str$("%4f",FPU_calc)
If esi is a string...
well I find out

...
Title: Re: A question on FPU
Post by: HSE on February 09, 2016, 05:53:12 AM
Quote from: clamicun on February 09, 2016, 02:02:55 AM
but I need the "endvalue".

Have you tested the function in your code?
Title: Re: A question on FPU
Post by: jj2007 on February 09, 2016, 06:21:35 AM
Quote from: clamicun on February 09, 2016, 05:51:14 AMLet esi=Str$("%4f",FPU_calc)
If esi is a string...

In C/C++ and assembler, a string is technically speaking a pointer to an area of memory that is delimited by a zero byte (or word in case of Unicode). You can use a register like esi, edi, ebx and ecx if you want smaller code, but Let my$=Str$(123) is valid as well, if my$ has been declared either in the .data? section or via SetGlobals (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1015).

On a very technical note,
Let my$="Hello"
Let my$=my$+" world"
is possible in MasmBasic. When my$ is assigned in the second line, the old my$ gets HeapFree'd after the new one was created. The same applies to esi, edi, ebx and ecx, but attention: If they pointed to something else before the first assignment, the heap might not agree with what you were doing. No such risk for a global variable like my$, of course, and no problem for arrays like x$(0). If you really want to use registers, the correct way is as follows:
xor esi, esi   ; make sure it doesn't point to the heap
Let esi="Hello"
Let esi=esi+" world"
PrintLine esi  ; do something useful
Clr$ esi   ; release the memory (->HeapFree)
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 09:19:47 AM
"Problem" resolved

MasmBasics...
fstp FPU_calculation
Let esi=Str$("%4f",FPU_calculation)
INVOKE lstrcpy,offset where_it_should_be,esi  ;for further use

Relatively small code  - using MasmBasics  "str$ MACRO" 

Nothing against the code of "RuiLoureiro" ,but...

as always - Thanks a lot jj
Mic

P.S.
One of these days I am going to understand "MasmBasics"



Title: Re: A question on FPU
Post by: raymond on February 09, 2016, 10:03:07 AM
clamicum

QuoteINVOKE crt_atof,offset startvalue
INVOKE crt_atof,offset value1
fadd st(0),st(1)                 ; add ST(0)+ST(1)

I don't know how the crt_atof works but I would suspect that it converts the input string
- with a clean FPU after saving all current FPU registers,
- saving its converted result in local memory,
- restoring the saved FPU registers,
- and finally loading the saved result onto the FPU.

The detail for which I have no information is if it blindly frees the st(7) register (which could still contain valuable data) before loading the saved result on the FPU. If not, you would have been lucky that your test included no more than 8 input strings; otherwise, your final output value would have been TRASH.

Maybe you should have a look at the FPU tutorial at http://www.ray.masmcode.com/fpu.html, which you can read on-line or download its content.
Trying to program the FPU without sufficient knowledge of its various pitfalls can only lead to a lot of frustration.

You also seemed curious about how the Convert8DR (or Float2Asc) routines worked. I don't know how those arrive at the intended result but, if you have the MASM32 package, the source code for the FpuFLtoA function from the fpulib library is well commented. Although it contains almost 500 lines, the first 195 lines are the overhead to verify the source and validity of the input. You can then easily follow the logic used for the conversion and eventual display according to user preferences.
Title: Re: A question on FPU
Post by: jj2007 on February 09, 2016, 10:59:02 AM
Quote from: raymond on February 09, 2016, 10:03:07 AMThe detail for which I have no information is if it blindly frees the st(7) register (which could still contain valuable data) before loading the saved result on the FPU. If not, you would have been lucky that your test included no more than 8 input strings; otherwise, your final output value would have been TRASH.

That is indeed a problem with this code:
Quote from: clamicun on February 08, 2016, 08:32:53 AMINVOKE crt_atof,offset startvalue
INVOKE crt_atof,offset value1
fadd st(0),st(1)                 ; add ST(0)+ST(1)

INVOKE crt_atof,offset value2
fadd st(0),st(1)                

INVOKE crt_atof,offset value3
fadd st(0),st(1)             

INVOKE crt_atof,offset value4
fadd st(0),st(1)   
             
INVOKE crt_atof,offset value5
fadd st(0),st(1)

fstp FPU_calc         ;Store real number and pop ST(0)

There are three important mnemonics related to adding ST0 and ST1:
  fadd st(0), st(1)   ; adds st1 to st0, st1 remains valid   
  faddp st(1), st(0)   ; adds st1 to st0, st1 becomes empty
  fadd         ; identical to faddp st(1), st(0)

The version you used does not empty ST(1). So when you call crt_atof eight times, the FPU is full, and that will produce a bad result. You need to use the simple fadd instead. The best option: launch Olly and have a look.

@Ray: If the FPU is full, crt_atof produces a NAN. At least one ffree ST(7) is needed.
In contrast, MovVal ST(0), Chr$("123.456") will handle the ffree, but ST 5..7 will be trashed (which could be a problem under completely unrealistic assumptions; and before you kill me for that: a simple MsgBox also trashes two FPU regs, while many API calls trash several xmm regs without asking you for your permission 8))
Title: Re: A question on FPU
Post by: clamicun on February 09, 2016, 11:24:19 AM
Maybe you should have a look at the FPU tutorial at http://www.ray.masmcode.com/fpu.html...
That is where learned the bit I know.

I do use  the proc 5 times (maximum) - there are 5 vars "value" only
and everytime the proc starts with

FINIT  ; It is good programming practice to take the precaution of initializing FPU before starting any computation
FFREE st(0)

Is that ok. ?
Title: Re: A question on FPU
Post by: jj2007 on February 09, 2016, 11:32:31 AM
Quote from: clamicun on February 09, 2016, 11:24:19 AMFINIT  ; It is good programming practice to take the precaution of initializing FPU before starting any computation
FFREE st(0)

Is that ok. ?

MB's Init does the finit once (it is a slow instruction). It does not hurt to do it again, but avoid using finit in a tight loop.
Re ffree st(0): Read Ray's tute, the "revolver" part. What you really need is to free the last register, i.e. ffree st(7), in order to allow st(0) to load a new value. Example, assuming a "full" fpu:
FpuFill  ; for testing, fill the FPU with 1001 ... 1008
ffree st(7)  ; discard the last value
fldz  ; load zero
ffree st(7)  ; discard the last value
fldpi  ; load PI
ffree st(7)  ; discard the last value
fld1  ; load 1
You have now
ST(0)=1
ST(1)=PI
ST(2)=0

Really, the best idea is to launch Olly and watch what happens. MB has the FpuFill (http://www.webalice.it/jj2006/MasmBasicQuickReference.htm#Mb1190) macro, can be very handy for testing.
Title: Re: A question on FPU
Post by: raymond on February 10, 2016, 04:36:57 AM
QuoteFINIT  ; It is good programming practice to take the precaution of initializing FPU before starting any computation
FFREE st(0)

Is that ok. ?

For the first line comment, the answer may be yes or no according to the description of the FINIT instruction in the tutorial.

As for the second line, also from the tutorial (Chap, 1),

QuoteWhen the FPU is initialized, all the compartments are empty

It is thus useless to free "any" FPU register immediately following the finit instruction. The result of the FFREE instruction is to empty the specified register.
Title: Re: A question on FPU
Post by: dedndave on February 10, 2016, 05:58:32 AM
to execute FINIT prior to any calculation is craziness
FINIT is quite slow - i generally execute it once at the beginning of a program
then, i take care to keep track of register usage
Title: Re: A question on FPU
Post by: MichaelW on February 15, 2016, 02:27:02 AM
Quote from: raymond on February 09, 2016, 10:03:07 AM
I don't know how the crt_atof works...

The older Microsoft PSDKs,  this one  (https://www.microsoft.com/en-us/download/details.aspx?id=6510), for example, included a large part of the source for the CRTL, and if I remember correctly, this included the source for the atof function and a "worker" function that it calls to do the actual conversion.
Title: Re: A question on FPU
Post by: RuiLoureiro on May 01, 2018, 09:04:01 AM
Quote from: clamicun on February 08, 2016, 08:32:53 AM
;this does it - why ... I still am on problems with "FPU"

include \masm32\include\masm32rt.inc
include Convert8DR.inc   ;You have to download the zipFile to make it working

;=========
show_string MACRO arg1,arg2
LOCAL ws_msg
LOCAL stringlabel

.data
ws_msg    TCHAR 60 dup(?),0
stringlabel TCHAR '%s',0

.code
    pushad
    INVOKE wsprintf,addr ws_msg,addr stringlabel,reparg(arg2)
    INVOKE MessageBox,0,addr arg1,addr ws_msg,0
    popad
ENDM
;==========

.data
startvalue db "0.00",0
endvalue   db 20 dup(?),0

value1 db "1.11",0
value2 db "2.22",0
value3 db "3.33",0
value4 db "4.44",0
value5 db "5.55",0

FPU_calc    REAL8 ?
;-----------------

.code
start:

call Calculate


INVOKE ExitProcess,0

;============
Calculate proc

FINIT  ; It is good programming practice to take the precaution of initializing FPU before starting any computation
; FFREE st(0)      <<<<<<<< remove this it does nothing after finit

show_string startvalue,"start value"

INVOKE crt_atof,offset startvalue
INVOKE crt_atof,offset value1
fadd st(0),st(1)                 ; add ST(0)+ST(1)

INVOKE crt_atof,offset value2
fadd st(0),st(1)                 

INVOKE crt_atof,offset value3
fadd st(0),st(1)             

INVOKE crt_atof,offset value4
fadd st(0),st(1)   
             
INVOKE crt_atof,offset value5
fadd st(0),st(1)

fstp FPU_calc         ;Store real number and pop ST(0)

;So the result is in st(0)
;I use 'Convert8DR.inc' written by RuiLoureiro,- it works well, but I do not understand his macros... 
;Is there an "easier" way to convert the content of 'FPU_calc REAL8 ?' into a string - MasmBasics ??
 
INVOKE  ConvertReal8DR, offset FPU_calc, offset endvalue

show_string endvalue,"result add"

ret
Calculate endp
;============

end start
Hi all,
         I saw this just now. Please correct the bug that i show in red
         Good luck :t
Quote

        _finish:    mov      eax, edi
                    mov      edi, pString
                    sub      eax, edi

        _exit0:     ;mov      dword ptr [edi-4], eax            <<<<<< THIS is OK
                    mov      byte ptr [edi+eax], 0
                                             
                    xor      eax, eax
                    clc
        _exit:      mov      edx, edi
                    ;                   
                    pop      edi
                    pop      esi
                    pop      ebx
                    ret
;-----------------------------------------------------------
    _isinfinityS:   sub      edi, 1
                    cmp      byte ptr [edi], '-'
                    je       short @F
                    mov      byte ptr [edi], '+'     
            @@:     mov      dword ptr [edi+1], "IFNI"
                    mov      dword ptr [edi+5], "YTIN"
                    mov      eax, 9
                    jmp      _exit0
                   
                    ;--------------------------
                    ; value to be converted = 0
                    ;--------------------------
        _iszeroS:   sub      edi, 1
                    mov      word ptr [edi], 3020h
                    mov      eax, 2
                    jmp      _exit0
                   
                    ; --------------------
                    ; is indefinite or NAN
                    ; --------------------
        _erro1S:    mov      eax, 1
                    ; -------------------
                    ; Remove sign / space
                    ; -------------------
        _errorS:    sub      edi, 1
                    mov      dword ptr [edi+0], "ORRE"
                    mov      byte ptr [edi+4], "R"
                    ;
                    ;mov      dword ptr [edi-4], 5              <<<<<<  REMOVE THIS ALSO
                    mov      byte ptr [edi+5], 0
                    stc
                    jmp      _exit
                                       
                    ; -----------------
                    ; invalid operation
                    ; -----------------
        _erro2S:    fclex
                    mov      eax, 2
                    jmp      _errorS
ConvertReal8DR      endp