News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

MasmBasic

Started by jj2007, May 23, 2012, 10:16:07 PM

Previous topic - Next topic

Gunther

Jochen,

the cleanup of the FPU stack is always necessary. That has to do with the stack roll over.

Gunther
You have to know the facts before you can distort them.

jj2007

Quote from: Gunther on December 27, 2014, 01:14:45 PMthe cleanup of the FPU stack is always necessary.

That is rarely necessary. In fact, the cleanup instruction fstp st appears only 14 times in the MB source.

QuoteThat has to do with the stack roll over.
While I know what you mean when writing "stack roll over", it is not a term used when dealing with the FPU. You can check that in Raymond's excellent tutorial.

Btw if you want to make a little tutorial for the noobs, you should do it properly: Open a FPU thread in the Campus.

Gunther

Quote from: jj2007 on December 27, 2014, 06:34:40 PM
That is rarely necessary. In fact, the cleanup instruction fstp st appears only 14 times in the MB source.

I think it's a good style to give back the entire processor in the same state how did you get it. That reduces furthermore error sources. But it's only my point of view, no offense.

Gunther
You have to know the facts before you can distort them.

jj2007

Update 29 December: Str$() can now print REAL10 numbers with up to 19 digits precision, if a) the format string contains %Jf and b) the number starts with less than 923... (which is the case for 92.3% of all numbers ;)).

deb uses this format for all REAL10 variables.

include \masm32\MasmBasic\MasmBasic.inc
.data
qwjj      QWORD 7fffffffffffffffh  ; aka +9223372036854775807
qwj2      qword -9223372036854775807
      SetGlobals numericvar1:REAL10=1.8888888888888888888888888888e9
      SetGlobals numericvar2:REAL10=9.2222222222234567895e9
      SetGlobals numericvar3:REAL10=9.2333333333333333335e9
      SetGlobals numericvar4:REAL10=1.2345678901234567890e9
      SetGlobals numericvar5:REAL10=9.2345678901234567890e9
      Init
      fldpi
      deb 4, "Values", numericvar1, numericvar2, numericvar3, PI, ST(0)
      fstp st
      mov f2sOlly, 123
      Print Str$("qword limit\t+%i\n", qwjj)      ; 4294967295 or 2147483647
      Print Str$("qword limit\t%i\n", qwj2)      ; 4294967295 or 2147483647
      fldpi
      Print Str$("PI=\t\t%Jf\n", ST(0))
      fstp st
      Print Str$("PI=\t\t%Jf\n", PI)
      Print Str$("numericvar1=\t%Jf\n", numericvar1)
      Print Str$("numericvar2=\t%Jf\n", numericvar2)
      Print Str$("numericvar3=\t%Jf\n\n", numericvar3)
      Print Str$("numericvar4=\t%Jf (number starts with 123, 19 digits)\n", numericvar4)
      Print Str$("numericvar5=\t%Jf  (number starts with 923, 18 digits)\n", numericvar5)
      Exit
end start


Output:
Values
numericvar1     1888888888.888888889
numericvar2     9222222222.223456788
numericvar3     9233333333.33333333
PI              3.141592653589793238
ST(0)           3.141592653589793238
qword limit     +9223372036854775807
qword limit     -9223372036854775807
PI=             3.141592653589793238
PI=             3.141592653589793238
numericvar1=    1888888888.888888889
numericvar2=    9222222222.223456788
numericvar3=    9233333333.33333333

numericvar4=    1234567890.123456789 (number starts with 123, 19 digits)
numericvar5=    9234567890.12345678  (number starts with 923, 18 digits)

jj2007

Version 2 January (download): The restriction to not assign eax to an array element is removed. This is now legal code:

include \masm32\MasmBasic\MasmBasic.inc  ; download
  Init
  Dim My$()
  invoke GetCommandLine                  ; CL$() is easier to use, but for this demo we take the API
  Let My$(1)=eax                         ; you can now assign eax to an array element
  Inkey "Commandline=[", My$(1), "]"
  Exit
end start

OPT_Arg1      Hello Forum                ; RichMasm's way to supply arguments for testing


Output: Commandline=[C:\somepath\some.exe Hello Forum]

jj2007

#215
see updated post below

jj2007

By default, REAL10 numbers are now printed with 19 digits precision. The last digit might be one too high or too low.
The range of printable numbers is now 1.0e+-309, i.e. roughly the range of ordinary doubles.
Speedwise Str$() has improved, it is now about a factor 15 faster than the gcc ssprintf() function provided here by adeyblue (thanks!!). Attached a testbed using Str$(), MovVal and the gcc counterparts ssprintf() and sscanf().

include \masm32\MasmBasic\MasmBasic.inc      ; download version 26 January 2015
  SetGlobals MyR10:REAL10, Add01:REAL10=0.111111111111111111111
  Init
  xor ecx, ecx
  Print "digits", Tb$, "  1234567890123456789"
  .Repeat
      inc ecx
      push ecx
      fld Add01
      fimul stack      ; load ct*0.111
      pop eax
      fstp MyR10      ; store back to memory
      .if ecx<10 || ecx>=84 && ecx<99 || ecx>=994
            Print Str$("\n#%i\t", ecx), Str$(MyR10)
      .elseif ecx==10 || ecx==99
            Print      ; insert a blank line
      .endif
  .Until ecx>=1000
  Exit
end start


Output:
digits    1234567890123456789
#1      0.1111111111111111111
#2      0.2222222222222222222
#3      0.3333333333333333333
#4      0.4444444444444444444
#5      0.5555555555555555555
#6      0.6666666666666666666
#7      0.7777777777777777777
#8      0.8888888888888888888
#9      1.000000000000000000

#84     9.333333333333333333
#85     9.444444444444444444
#86     9.555555555555555555
#87     9.666666666666666666
#88     9.777777777777777777
#89     9.888888888888888888
#90     10.00000000000000000
#91     10.11111111111111111
#92     10.22222222222222222
#93     10.33333333333333333
#94     10.44444444444444444
#95     10.55555555555555555
#96     10.66666666666666667
#97     10.77777777777777778
#98     10.88888888888888889

#994    110.4444444444444444
#995    110.5555555555555555
#996    110.6666666666666667
#997    110.7777777777777778
#998    110.8888888888888889
#999    111.0000000000000000
#1000   111.1111111111111111

jj2007

Update 2 February 2015 - download here:

1. Join$() converts a string array to one string:

  include \masm32\MasmBasic\MasmBasic.inc
  Init     
  Dim My$()
  Let My$(0)="Masm32"
  Let My$(1)="is"
  Let My$(2)="great"
  PrintLine "[", Join$(My$()), "]"   ; with no second arg, output is [Masm32\nis\ngreat], i.e. items separated by 13, 10
  Print "[", Join$(My$(), " "), "]"  ; shows [Masm32 is great] with " " as second arg
  Exit
  EndOfCode


For Split$() and Filter$(), see the guide during installation.

2. Bugfix for Extract$():
   When using the flag xsLineR, one byte from the right match string was duplicated. It's fixed. Usage example (this extracts structures from a C header file; you need Visual Studio installed):

include \masm32\MasmBasic\MasmBasic.inc      ; download
  Init
  My$ equ esi         ; two equates saying we'll use a register for the string to be searched
  Struct$ equ edi     ; and for the strings returned, in this case C/C++ structures
  SetReg64            ; the registry wants to cheat 32-bit programs, we cheat back ;-)
  Let My$=GetRegVal("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\2E22551E7BF434E44AAD692811011B7C", "66FC8F6438BA7A83992B5AEB05E74E27", "NoSuchFile.bla")

  .if Exist(My$)      ; should be something like C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\srv.h
      Let My$=FileRead$(My$)
      Open "O", #1, "Structures.txt"      ; open a file for output
      xor ebx, ebx      ; reset counter
      .While 1
            ; flags: full word, include beginning of first line and end of last line, use in a loop; 100 lines max (enough for a big structure)
            mov Struct$, Extract$(My$, "struct", '}', xsFullW or xsLineL or xsLineR or xsIncR or xsLoop, 100)
            .Break .if !Struct$
            Print Struct$, CrLf$, CrLf$      ; print to console
            Print #1, Struct$, CrLf$, CrLf$      ; and to file
            inc ebx
      .Endw
      Close #1
      Print Str$("%i structures found\n", ebx)
  .endif
  Exit
end start


Output:
typedef struct srv_datetime
{       // Format for SRVDATETIME
    long dtdays;            // number of days since 1/1/1900
    unsigned long dttime;   // number 300th second since mid
} DBDATETIME;

typedef struct srv_money
{               // Format for SRVMONEY
    long mnyhigh;
    unsigned long mnylow;
} DBMONEY;

typedef struct dbdatetime4
{       // Format for SRVDATETIM4
    unsigned short numdays; // number of days since 1/1/1900
    unsigned short nummins; // number of minutes sicne midnight
} DBDATETIM4;

typedef struct dbnumeric
{       // Format for SRVNUMERIC,SRVNUMERICN,SRVDECIMAL,SRVDECIMALN
        BYTE precision;
        BYTE scale;
        BYTE sign;
        BYTE val[MAXNUMERICLEN];
} DBNUMERIC;

4 structures found

jj2007

As noted on other occasions, the 64-bit versions of Windows have the bad habit to destroy registers xmm0 ... xmm5 in API calls, nota bene: in 32-bit code on a 64-bit OS. Since 32-bit code on 32-bit Windows does not trash them, and many people may have relied on this benign but undocumented behaviour, there is a nice potential for bugs that come out of the blue when running the same application the first time on a 64-bit OS.

For performance reasons, MasmBasic does not preserve the xmm regs for each and every API call used, but its own algos do preserve the xmm regs.

This worked fine in general, but I ran into a strange case that needed fixing:

include \masm32\MasmBasic\MasmBasic.inc      ; download the fixed version of 6 February 2015
  Init
  Dim MyDouble() As REAL8      ; same for QWORD

  MovVal f:xmm0, "1234.5678"      ; fill low quad of xmm0 with float
  movlps MyDouble(0), xmm0
  deb 4, "Element 0", f:xmm0, MyDouble(0)

  MovVal f:xmm0, "1234.5678"
  movlps MyDouble(1), xmm0      ; <<<<<<< this instruction misbehaved
  deb 4, "Element 1", f:xmm0, MyDouble(1)

  MovVal f:xmm0, "1234.5678"
  movlps MyDouble(2), xmm0
  deb 4, "Element 2", f:xmm0, MyDouble(2)
  Exit
end start

Old buggy version:
Element 0
f:xmm0          1234.567800000000
MyDouble(0)     1234.567800000000

Element 1
f:xmm0          0
MyDouble(1)     0.0

Element 2
f:xmm0          1234.567800000000
MyDouble(2)     1234.567800000000


The reason for this unexpected behaviour was that the simple instruction movlps MyDouble(1), xmm0 has a nice feature under the hood: MyDouble() is a dynamic array. When an index hits the boundary, new memory is being requested via HeapReAlloc. And that request trashed the xmm regs - all six of them. I'd like to trash Windows, but Linux doesn't seem any better, so the solution was to preserve the six regs around the HeapReAlloc call. It works correctly from version 6 Feb 2015 onwards.

jj2007

Update 10 Feb (download): Since I ran often into problems with Windows' bad habit to trash xmm0 ... xmm5 in its 64-bit versions, I decided to address this Microsoft 'feature' more systematically.

Inter alia, ordinary message boxes and setting the timestamp for a FileWrite did not work in 32-bit code on Win64. This is now fixed, and the simple unzipper below will work as expected:

include \masm32\MasmBasic\MasmBasic.inc
  Init
  Let esi=CL$()
  .if !Instr_(esi, ".zip", 1)
      MsgBox 0, esi, "Not a zip archive?", MB_OK
      Exit
  .endif
  UnzipInit esi            ; expects a filename, returns a comment (if present); edx returns #records
  .if Sign?
      Print eax      ; print an error message
  .else
      For_ ecx=0 To edx-1      ; #files returned in edx
            Let esi=Files$(ecx)
            .if Rinstr(esi, "/")      ; zipfiles use forward slashes
                  xchg eax, esi      ; we don't display the full path, and we
                  inc esi      ; don't use folder names for extraction!!
            .endif
            PrintLine Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, esi
            Inkey "Extract? (yes/no/quit) ", Cr$
            .Break .if eax=="q" || eax==VK_ESCAPE
            .if eax=="y"
                  FileWrite esi, UnzipFile(ecx), GfLastWrite(ecx)      ; setting the timestamp works now on Win7-64, too
            .endif
      Next
      UnzipExit
  .endif
  Exit
end start

jj2007

Update 16 Feb 2015 (download):

- MovVal MyFloat(ct), My$(ct) is legal code now
- For_ ... Next has Step now (can be negative, too)
- In addition to At(col, row), Print can now take a CColor(foreground, background) arg

include \masm32\MasmBasic\MasmBasic.inc
  Init
  Dim My$()
  Dim MyFloat() As REAL8
  For_ ct=0 To 1000
      Let My$(ct)=Str$(ct)
      MovVal MyFloat(ct), My$(ct)
  Next
  For_ ct=100 To 1000 Step 100
        mov ecx, ct
        and ecx, 15
      Print At(35, Locate(y)+2) CColor(ecx, cWhite) Str$(ct), Str$("\tMyFloat(ct) = %Jf", MyFloat(ct))
  Next
  Exit
end start

jj2007

Update 24 February (download):

This snippet creates a windows application with a menu and monitors all WM_xx messages to the console.

GuiParas equ "Hello jj2007", x650, y20, w200, h200, cblack, b00FFFFD0h
GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm
Event Message
  inc msgCount
  deb 4, "msg", chg:msgCount      ; , wParam, lParam      ; see deb
GuiEnd


More a proof of concept than a serious exercise, but it works. Attached a more detailed example, using create, key, menu and paint "events".

jj2007

Just for fun:

include \masm32\MasmBasic\MasmBasic.inc      ; download
  SetGlobals fct:REAL10, fStart:REAL8=2.0, fEnd:REAL8=1.5
  Init
  For_ fct=1.0 To 2.0 Step 0.25
      Print Str$("\n%Jf\t", fct)
  Next
  Print CrLf$, "..."
  For_ fct=fStart To fEnd Step -0.1
      Print Str$("\n%Jf\t", fct)
  Next
  Exit
end start


Output:
1.000000000000000000
1.250000000000000000
1.500000000000000000
1.750000000000000000
2.000000000000000000
...
2.000000000000000000
1.900000000000000000
1.800000000000000000
1.700000000000000000
1.600000000000000000
1.500000000000000000

rrr314159

Do u realize the last 12 posts in this thread are yours? Figured I'd break your streak :)

But also it's interesting you're using float instead of integer in the for-next loop, because I've just done the similar thing with timing macros. I'll soon post a "faster" 32-bit algo in the laboratory - this time using MichaelW's timers, everything "by the book", but with "massively parallel" use of xmm registers. I'd gotten used to returning 64-bits from the rdtsc call and couldn't stand being confined to 32-bits again (1 second vs. 100 years) so wrote a 32-bit version of timers using REAL8 to hold the rdtsc result. Works fine, it's fast enough, this way you can add, average print out etc the timing figures easily.

Anyway, c u later!
I am NaN ;)

jj2007

Thanks for the heads up ;-)

I posted a float example because that's new - integers always worked.

Re rdtsc, printing a qword is not a big deal:

include \masm32\MasmBasic\MasmBasic.inc      ; download
  Init
  rdtsc
  Print Str$("TSC=%i\n", edx::eax)
  invoke Sleep, 1
  rdtsc
  Print Str$("TSC=%i\n", edx::eax)
  Exit
end start


The problems with rdtsc are elsewhere. M$ suggests QueryPerformanceCounter instead, which is under the hood of NanoTimer().