News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

fpu function needed

Started by bsdsource, January 07, 2014, 01:16:04 PM

Previous topic - Next topic

bsdsource

I was wondering if someone would be interested in posting a procedure to convert kilometers to miles. I was messing around with fpu a bit, and to tell you the truth I have no desire to learn it. I wanted to make a simple km to miles program so I can use it at work. Would be great if the output was converted to a string.

1 kilometer is equivalent to 0.6214 miles

dedndave

it's fairly simple, really
you can use REAL4, REAL8, or REAL10 numbers
i'd say REAL4 has enough precision for what you want, but it may not have enough range
REAL8 might be a better choice

use FLD to load values into the FPU
use FMUL to multiply
use FSTP to store and pop the result
that leaves one item on the FPU stack (the conversion constant)
you can use FFREE to get rid of it

so - you don't have to learn the entire FPU - just those 4 instructions

Ray has an excellent tutorial that will explain each of them

http://www.ray.masmcode.com/

you can also use the forum search tool to find examples

bsdsource

Thanks dedndave. I didn't notice before that raymond had a fuction to convert floating point numbers to strings, and vice versa. One of the problems I had encountered was how to get the number from the top of the stack and convert it to a string. Anyways, I now how the following working code:

.486
.model flat, stdcall
option casemap :none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
include \masm32\include\Fpu.inc
 
  includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\Fpu.lib

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

.data
        miles                dq      0.6214
        km                    db     "99.5",0
        result               dd      0
        kilometers        dq      0

.code

start:
invoke StrToFloat,addr km,addr kilometers

fld     miles
fmul    kilometers

invoke FpuFLtoA,0,1,addr result,SRC1_FPU + SRC2_DIMM
invoke MessageBox, 0,addr result,0,MB_OK

    invoke ExitProcess, 0

end start

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

jj2007

Short version:

include \masm32\MasmBasic\MasmBasic.inc        ; download
        Init
        Inkey Str$("%3f miles", Val(Input$("Kilometres? ", "123"))*0.614)
        Exit
end start


With more detail:

include \masm32\MasmBasic\MasmBasic.inc
.data
km2miles        REAL4 0.614
tmpvar          REAL4 ?
        Init
        Let esi=Input$("Gimme kilometres: ", "1000")
        MovVal tmpvar, esi    ; convert string to number
        fld tmpvar
        fmul km2miles
        fstp tmpvar
        Inkey Str$("The result is %3f miles", tmpvar)
        Exit
end start

dedndave

if you choose not to use MasmBasic, you can shorten your preamble
        .486
        .model flat, stdcall
        option casemap :none

        include \masm32\include\windows.inc
        include \masm32\include\user32.inc
        include \masm32\include\kernel32.inc
        include \masm32\include\masm32.inc
        include \masm32\include\Fpu.inc

        includelib \masm32\lib\user32.lib
        includelib \masm32\lib\kernel32.lib
        includelib \masm32\lib\masm32.lib
        includelib \masm32\lib\Fpu.lib


        include     \masm32\include\masm32rt.inc
        include     \masm32\include\Fpu.inc
        includelib  \masm32\lib\Fpu.lib

bsdsource

#5
Appreciate all the help. I've attached my final program just in case someone was looking for a simple kilometer <---> miles converter.


    include           \masm32\include\masm32rt.inc
    include           \masm32\include\debug.inc
    include           \masm32\include\fpu.inc
   
    includelib        \masm32\lib\debug.lib
    includelib        \masm32\lib\fpu.lib

    WinMain            PROTO :DWORD,:DWORD,:DWORD,:DWORD
    EditBox            PROTO :DWORD,:DWORD,:DWORD,:DWORD
    TopXY              PROTO :DWORD,:DWORD
    km2miles           PROTO :DWORD
    miles2km           PROTO :DWORD

.const
     MY_ICON           =  5
     BTNCLEAR          =  3001
     BTNEXIT           =  3002

.data
     Caption           db "Converter",0
     SingleInstance    db "Only Instance Allowed",0
     ClassName         db " ",0
     LabelClass        db "Static",0
     EditClass         db "Edit",0
     BtnClass          db "Button",0
     FontType          db "arial",0
     LabelKilometer    db "Kilometers",0
     LabelMiles        db "Miles",0
     LabelClear        db "Clear",0
     LabelExit         db "Exit",0
     zero              db "0",0
     miles             dq  0.6214
     kilometers        dq  1.60934

.data?
     hWnd              HANDLE ?
     hInstance         HANDLE ?
     hMutex            HANDLE ?
     hEditbox1         HANDLE ?
     hEditbox2         HANDLE ?
     hFont1            HANDLE ?
     hFont2            HANDLE ?
     hBtnClear         HANDLE ?
     hBtnExit          HANDLE ?
     OldWndProc        dd     ?

.code
  start:   
      invoke CreateMutex,0,FALSE,addr SingleInstance
      mov hMutex,eax
      invoke GetLastError
         .if eax == ERROR_ALREADY_EXISTS
            invoke ExitProcess, 0
         .endif

     invoke GetModuleHandle, NULL
     mov hInstance, eax
     
     invoke WinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT
     invoke ExitProcess,eax
     invoke InitCommonControls

; ########################################################################
WinMain proc hInst:DWORD, hPrevIns:DWORD, CmdLine:DWORD, CmdShow:DWORD
     LOCAL  wc   :WNDCLASSEX
     LOCAL  msg  :MSG
     LOCAL  Wwd  :DWORD
     LOCAL  Wht  :DWORD
     LOCAL  Wtx  :DWORD
     LOCAL  Wty  :DWORD
   
     mov    wc.cbSize,SIZEOF WNDCLASSEX
     mov    wc.style,CS_HREDRAW + CS_VREDRAW
     mov    wc.lpfnWndProc,OFFSET WndProc
     mov    wc.cbClsExtra,NULL
     mov    wc.cbWndExtra,NULL
     mov    eax,hInstance
     mov    wc.hInstance,eax
     invoke LoadIcon,hInstance,MY_ICON
     mov    wc.hIcon,eax
     mov    wc.hIconSm,eax
     invoke LoadCursor,NULL,IDC_ARROW
     mov    wc.hCursor,eax
     mov    wc.hbrBackground,COLOR_BTNFACE+1
     mov    wc.lpszMenuName,NULL
     mov    wc.lpszClassName,OFFSET ClassName
     invoke RegisterClassEx, addr wc

     mov    Wwd, 375
     mov    Wht, 175
       
     invoke GetSystemMetrics,SM_CXSCREEN
     invoke TopXY,Wwd,eax
     mov    Wtx, eax
   
     invoke GetSystemMetrics,SM_CYSCREEN
     invoke TopXY,Wht,eax
     mov    Wty, eax
   
     invoke CreateWindowEx, WS_EX_STATICEDGE,
            addr ClassName,
            addr Caption,
            WS_OVERLAPPED + WS_SYSMENU + WS_MINIMIZEBOX  + WS_CAPTION,
            Wtx,Wty,Wwd,Wht,
            NULL,
            NULL,
            hInst,
            NULL
           
     mov    hWnd,eax
     invoke ShowWindow,hWnd,SW_SHOWDEFAULT
   
     mov ebx, hWnd
     lea esi, msg
     xor edi, edi
  @@:   
     invoke GetMessage,esi,edi,edi,edi
     cmp eax, edi
     je @F
     invoke IsDialogMessage, ebx, esi
     cmp eax, edi
     jne @B
     invoke TranslateMessage, esi
     invoke DispatchMessage,  esi
     jmp @B
  @@: 
    ret
WinMain endp

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

WndProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

.if uMsg == WM_CREATE

     invoke CreateFont,24,0,0,0,300,0,0,0,0,0,0,0,0,addr FontType     
     mov hFont1,eax
     
     invoke CreateFont,48,0,0,0,300,0,0,0,0,0,0,0,0,addr FontType     
     mov hFont2,eax
     
     invoke CreateWindowEx,
     0,
     addr LabelClass,
     addr LabelKilometer,
     WS_CHILD + WS_VISIBLE,
     45,5,130,24,
     hWin,
     NULL,
     hInstance,
     NULL
     invoke SendMessage,eax,WM_SETFONT,hFont1,TRUE


     invoke CreateWindowEx,
     0,
     addr LabelClass,
     addr LabelMiles,
     WS_CHILD + WS_VISIBLE,
     250,5,250,24,
     hWin,
     NULL,
     hInstance,
     NULL
     invoke SendMessage,eax,WM_SETFONT,hFont1,TRUE


     invoke CreateWindowEx,
     WS_EX_STATICEDGE,
     addr EditClass,
     NULL,
     WS_CHILD + WS_VISIBLE + WS_TABSTOP + ES_CENTER,
     5,35,175,50,
     hWin,
     NULL,
     hInstance,
     NULL
     mov hEditbox1,eax
     invoke SendMessage,hEditbox1,WM_SETFONT,hFont2,TRUE
     invoke SendMessage,hEditbox1,EM_SETLIMITTEXT,5,0
     invoke SetFocus,hEditbox1
     
      invoke SetWindowLong,hEditbox1,GWL_WNDPROC,addr EditBox
      mov OldWndProc,eax


     invoke CreateWindowEx,
     WS_EX_STATICEDGE,
     addr EditClass,
     NULL,
     WS_CHILD + WS_VISIBLE + WS_TABSTOP + ES_CENTER,
     188,35,175,50,
     hWin,
     NULL,
     hInstance,
     NULL
     mov hEditbox2,eax
     invoke SendMessage,hEditbox2,WM_SETFONT,hFont2,TRUE
     invoke SendMessage,hEditbox2,EM_SETLIMITTEXT,5,0
     
      invoke SetWindowLong,hEditbox2,GWL_WNDPROC,addr EditBox
      mov OldWndProc,eax


     invoke CreateWindowEx,
     WS_EX_STATICEDGE,
     addr BtnClass,
     addr LabelClear,
     WS_CHILD + WS_VISIBLE + WS_TABSTOP,
     5,95,177,50,
     hWin,
     BTNCLEAR,
     hInstance,
     NULL
     mov hBtnClear,eax
     invoke SendMessage,hBtnClear,WM_SETFONT,hFont1,TRUE


     invoke CreateWindowEx,
     WS_EX_STATICEDGE,
     addr BtnClass,
     addr LabelExit,
     WS_CHILD + WS_VISIBLE + WS_TABSTOP,
     188,95,177,50,
     hWin,
     BTNEXIT,
     hInstance,
     NULL
     mov hBtnExit,eax
     invoke SendMessage,hBtnExit,WM_SETFONT,hFont1,TRUE


.elseif uMsg == WM_COMMAND
    mov eax,wParam
    shr eax, 16
        .if ax == EN_UPDATE
        invoke GetFocus
                .if eax == hEditbox1
                    mov eax,lParam
                        .if eax == hEditbox1
                            invoke km2miles,eax
                        .endif
                .elseif eax == hEditbox2
                    mov eax,lParam
                        .if eax == hEditbox2
                            invoke miles2km,eax
                        .endif
                .endif
        .endif

    mov eax,wParam
        .if ax == BTNCLEAR
            invoke SetWindowText,hEditbox1,0
            invoke SetWindowText,hEditbox2,0
            invoke SetFocus,hEditbox1
        .elseif ax == BTNEXIT
            invoke DestroyWindow,hWin
        .endif

.elseif uMsg == WM_DESTROY
        invoke PostQuitMessage,NULL
.else
     invoke DefWindowProc,hWin,uMsg,wParam,lParam
     ret
.endif
     xor eax,eax
     ret

WndProc endp
;######################################################################################################################
km2miles proc handle:DWORD
local txt    :QWORD
local km     :QWORD
local result :QWORD
local final  :QWORD

invoke   RtlZeroMemory,addr txt,sizeof txt
invoke   RtlZeroMemory,addr km,sizeof km
invoke   RtlZeroMemory,addr result,sizeof result

    invoke SendMessage,handle,WM_GETTEXT,sizeof txt,addr txt ;get string from editbox1

    invoke atodw,addr txt                                     ;convert txt to decimal
        .if eax == 0                                          ;now check if decimal is 0
        invoke SetWindowText,hEditbox2,0                  ;if 0 don't process the rest
        ret                                               ;and get out of here
        .endif

invoke StrToFloat,addr txt,addr km                       ;convert string to floating point
fld     miles                                             ;put miles on the fpu stack
fmul    km                                                ;multiply km by miles and store the result on fpu stack
invoke FpuFLtoA,0,1,addr result,SRC1_FPU + SRC2_DIMM     ;convert floating point result on the stack to string
invoke  szLtrim,addr result,addr final                    ;trim spaces from string. sometimes they appear for some reason.
invoke SetWindowText,hEditbox2,addr final                ;set string converted to miles from km to editbox2
fstp st                                                   ;pop the stack otherwise crash
ret
km2miles endp
;######################################################################################################################
miles2km proc handle:DWORD
local txt    :QWORD
local mile   :QWORD
local result :QWORD
LOCAL final  :QWORD

invoke   RtlZeroMemory,addr txt,sizeof txt
invoke   RtlZeroMemory,addr mile,sizeof mile
invoke   RtlZeroMemory,addr result,sizeof result

    invoke SendMessage,handle,WM_GETTEXT,sizeof txt,addr txt ;get string from editbox1

    invoke atodw,addr txt                                     ;convert txt to decimal
        .if eax == 0                                          ;now check if decimal is 0
        invoke SetWindowText,hEditbox1,0                  ;if 0 don't process the rest
        ret                                               ;and get out of here
        .endif

invoke StrToFloat,addr txt,addr mile                     ;convert string to floating point
fld     kilometers                                        ;put kilometers on the fpu stack
fmul    mile                                              ;multiply mile by kilometers and store the result on fpu stack
invoke FpuFLtoA,0,1,addr result,SRC1_FPU + SRC2_DIMM     ;convert floating point result on the stack to string
invoke  szLtrim,addr result,addr final                    ;trim spaces from string. sometimes they appear for some reason.
invoke SetWindowText,hEditbox1,addr final                ;set string converted to kilometers from miles to editbox1
fstp st                                                   ;pop the stack otherwise crash
ret
miles2km endp
;######################################################################################################################
EditBox proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    .if uMsg == WM_CHAR
    mov eax,wParam
            .if eax != 8 && eax != 46 && eax != 48 && eax != 49 && eax != 50 && eax != 51 && eax != 52 && eax != 53 && eax != 54 && eax != 55 && eax != 56 && eax != 57
                xor eax,eax
                ret
            .endif
    .endif
   
    invoke CallWindowProc,OldWndProc,hWin,uMsg,wParam,lParam
    ret
   
EditBox endp
;######################################################################################################################
TopXY proc wDim:DWORD, sDim:DWORD
      shr sDim, 1
      shr wDim, 1
      mov eax, wDim
      sub sDim, eax
      mov eax,sDim
      ret
TopXY endp
;######################################################################################################################
end start


note: modified code & attachment

jj2007

Very nice work :t

You are using fucomp to pop ST(0)? Here, it doesn't matter, but for speed critical apps, fstp st is definitely faster. Unless you need the comparison, of course.

Intel(R) Celeron(R) M CPU        420  @ 1.60GHz (SSE3)

354     cycles for 100 * fstp st
458     cycles for 100 * fucomp valid
18749   cycles for 100 * fucomp invalid
670     cycles for 100 * fucompp

bsdsource

QuoteYou are using fucomp to pop ST(0)? Here, it doesn't matter, but for speed critical apps, fstp st is definitely faster. Unless you need the comparison, of course.

jj2007 I wasn't really sure what was best to pop ST(0) off the stack. Was looking at different instructions, and decided just to use fucomp. I will be sure to use fstp st next time around. Looks like I had picked a slow poke.

jj2007

Quote from: azdps on January 08, 2014, 10:26:12 AMLooks like I had picked a slow poke.

It matters only if you need a Million loops. But it can be a bit slow if (see above) the comparison is invalid, i.e. if you compare a valid ST(0) to an empty ST(1).

P.S.: I just added a new function to MasmBasic: Instead of...

MovVal tmpvar, esi    ; convert string to number
fld tmpvar

... you can now use:

MovVal ST(0), esi    ; convert string to number, and put it into ST(0)


bsdsource

Avast gives a false positive to my converter program. This has been the case since I first programmed it. Is there a way to modify my source code to prevent an antivirus program from thinking my legit program is a possible virus? I wanted to distribute it at work to some friends but the outlook email system (or antivirus software used in conjunction with) in use believes it to be a virus as well.

dedndave

it is helpful if your program has a manifest and a version control block
is it a console app or GUI ?

can you post the source ?

bsdsource

GUI. The source and executable file are in the converter.zip a few posts above.

dedndave

ok - adding a manifest and a version control block are pretty easy, really
it's mostly a copy and paste thing

you want to add a manifest file to the resource (.rc file)
simple version....
create a text file named "converter.xml", and add it to the project folder
put this text in it
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df"/>
    </dependentAssembly>
  </dependency>
</assembly>

that is a simple manifest to tell the OS that common controls version 6 is desired (i don't think it's really required)

now, to make that work, you have to do 2 additional things
1) add a call to the beginning of your program to InitCommonControlsEx
    .DATA

icc INITCOMMONCONTROLSEX <sizeof INITCOMMONCONTROLSEX,ICC_WIN95_CLASSES>

    .CODE

Start:  INVOKE  InitCommonControlsEx,offset icc


2) you have to add a line to the resource file to add the manifest
1 24 "converter.xml"

ok - that's the manifest part
now, we want to add a version control block to the resource file
this is a template i use - i change the names and dates for each project, as required
VS_VERSION_INFO VERSIONINFO
FILEVERSION     1,0,0,0
PRODUCTVERSION  1,0,0,0
FILEOS          VOS_NT_WINDOWS32
FILETYPE        VFT_APP
{
  BLOCK "StringFileInfo"
  {
    BLOCK "040904E4"
    {
      VALUE "CompanyName",      "DednDave\000"
      VALUE "FileDescription",  "StarMap\000"
      VALUE "FileVersion",      "1.0\000"
      VALUE "LegalCopyright",   "\251 2015 David R. Sheldon\000"
    }
  }
  BLOCK "VarFileInfo"
  {
    VALUE "Translation", 0x409, 0x4E4
  }
}

in the event that your resource.h file doesn't include them...
"VS_VERSION_INFO" may be replaced with "1"
"VOS_NT_WINDOWS32" may be replaced with " 0x00040004L"
"VFT_APP" may be replaced with "1"

the first line in your resource file should be....
#include "\masm32\include\resource.h"

bsdsource

Nice it worked great. Initially it wasn't working and Avast was still seeing it as a virus but I missed putting in "1 24 "converter.xml" into my resource file. When I didn't have that in the resource file my program was detected as a virus. After I included it, it isn't being detected as a virus (false positive).

I did some further testing and I needed to include both the version control block and the manifest otherwise Avast detected my program as a virus. Excluding either one of those would cause a false positive.

Some things I didn't need:

icc INITCOMMONCONTROLSEX <sizeof INITCOMMONCONTROLSEX,ICC_WIN95_CLASSES>

INVOKE  InitCommonControlsEx,offset icc
Note: I already had invoke InitCommonControls in my source file which worked fine.

#include "\masm32\include\resource.h"

Some things I did need:

Just defined the 2 below in my resource file.

"VOS_NT_WINDOWS32" may be replaced with " 0x00040004L"

"VFT_APP" may be replaced with "1



Thank you for the help!

dedndave

icc INITCOMMONCONTROLSEX <sizeof INITCOMMONCONTROLSEX,ICC_WIN95_CLASSES>

INVOKE  InitCommonControlsEx,offset icc


those will be required if the program is to work with a manifest under XP   :t
without them, the window will not appear

#include "\masm32\include\resource.h"

standard practice to put this first in the resource file
it defines many of the standard constants, like VOS_NT_WINDOWS32, etc