MasmBasic Quick Reference

extracted from \Masm32\MasmBasic\MbGuide.rtf 17.12.2014 - see original RTF file for correct formatting and latest additions

Download the library from the new Masm32 forum (you need the Masm32 SDK to use it) - see also Masm32 Tips, Tricks and Traps

ErrLines Try Catch Finally TryRTE Init Exit CL$() wCL$ If_ IsTrue CodeSize DlgDefine DlgControl DlgShow SetGlobals Enum Dll Declare deb Err$() SetErrLine Asc Cvi Cvl Odd Abs Min Max PopCount Rand FolderOpen$ FileOpen$ FileSave$ wF Open wOpen Close Seek Lof Loc Locate( Input Input$ wInput$ Rename WritePipe Launch see SetLaunchTimeout Launch$ LaunchEndPos ExitCode Exist LastFileSize LastFileName$ LastFileDosName$ IsFolder Kill Touch GetFiles AddFiles Files$ GetFolders AddFolders GfCallback SortFiles GfSize GfDate$ GfTime$ GfAgeHours GfAgeMinutes GfAgeMs GfLastWrite GfGetInfo GfSetInfo Age CurDir$ ExpandEnv$ wExpandEnv$ ExeFromWin$ MakeDir ZipFiles UnZipFiles UnzipFile UnzipInit UnzipFile FileRead$ LastFileSize NoTag$ FileWrite Replace$ ParentData$ CopyData$ SendData SendControlKey SendWordCommands xlsConnect ddeConnect ddeCommand ddeRequest$ ddeDisconnect Win$ wWin$ SetWin$ wSetWin$ AddWin$ MakeFont PickFont ImgPaint ToolTips WinByTitle App16 Clip$ wClip$ SetClip$ wSetClip$ SetHtmlClip$ MsgMonitor New$ Res$ wRes$ MsgTable$ String$ Space$ Inkey wInkey Print wPrint Print wPrint CurDir$ PrintLine PrintBuffer SetCpUtf8 SetCpAnsi Cls ConsoleColor crtbuf CreateInvoke gsl SetPoly3 GetPoly3 Dim Erase ArrayFill wArrayFill ArraySet StringToArray ArrayMerge Swap Let wLet Cat$ wCat$ Insert Delete GuidFromString GuidsEqual CoInvoke Ole$ BSTR wData wChr$ Chr$ Len wLen MbCopy Bmove MsgBox wMsgBox Clr Clr$ ClearLocals StackBuffer Instr_ Rinstr wInstr wRinstr Count wCount Extract$ StringsDiffer FilesDiffer Left$ wLeft$ Mid$ wMid$ Right$ wRight$ Lower$ wLower$ Upper$ wUpper$ Trim$ Ltrim$ Rtrim$ Qtrim$ Mirror$ Date$ wDate$ Time$ wTime$ fDate$() fTime$ wfDate$ wfTime$ Timer NanoTimer Recall Csv2Tab wRecall Store QSortDesc QSortMode ArraySort ArrayMinMax ArrayMean ArrayRead ArrayStore ArrayLoadMap ArrayPlot ArraySearch VarPtr For_ Str$ wStr$ FpuSave FpuRestore FpuPush FpuFill FpuSet Exp10 Exp2 ExpE ExpXY Percent DefNum Hex$ Bin$ Qcmp Ocmp Fsign Fcmp Val MovVal Sqrt SetField GetField SetFlags Flags MouseX MouseY MouseK Delay void voidTrue voidFalse GetRegVal SetReg64 SetRegVal GetRegKeyArray GetRegArray Masm32

; The smallprint: this library is provided "as is", and the usual disclaimers apply.
; ---------------- This help file refers to MasmBasic version 17 December 2014 -----------------
; It is assumed that you installed the library from http://masm32.com/board/index.php?topic=94.0
; To start a new project, go to ^ ^ File/New Masm source and select the MasmBasic link on top.
; Once you see the file in front of you, hit F6 to assemble, link and run the example.
; For help on MasmBasic keywords, hover some seconds over e.g. Init until the mouse cursor turns
; into ? (i.e. a question mark), then right-click to see detailed examples. If you left-click quickly into
; the green area, you may select code for your own use. Click outside the green area to leave it.
; the following line substitutes the standard Masm32 include \masm32\include\masm32rt.inc:
; ------------------------------------------------------------------------------------------------------------
include \masm32\MasmBasic\MasmBasic.inc
    ; hit F6 to assemble, link & run the Hello World example
    Init
    ; to test examples, replace the white line with your test code and press F6
    MsgBox 0, "Wow, it works!!!!", "Hi", MB_OK            ; It worked? Try one more, or try 50+ snippets
    Print CrLf$, "OK - press any key"                        ; It choked? Read below about JWasm.
    Exit
end start                                                ; RichMasm is a Unicode editor -    try a multilingual example
--------------------------------------------------------------------------------------------------------------
Practical hints for using MasmBasic
- You cannot use the library with the old ML.exe that comes along with Masm32 (version 6.14 doesn't know about SSE2...).
    Instead, you should use JWasm (http://sourceforge.net/projects/jwasm/files/JWasm Windows binary, use JWasm211bw.zip).
    - if you decide to use RichMasm (MasmBasic's preferred IDE), then it is sufficient to copy JWasm.exe to \Masm32\bin
    - if you prefer another editor, like qEditor, you can either modify the build batch files, or a) rename \Masm32\bin\ml.exe
        to \Masm32\bin\ml_old.exe, b) then save JWasm.exe as ml.exe
- Use the RichMasm editor: It helps if you can type opi (i.e. opi) and you get Open "I", #1, ...; for large sources, its
    search function is outstanding. For example, select Key and hit F3 to see a list of all shortcuts for MasmBasic keywords.
    Other features you may like are the bookmarks to the right, and the history: Press Alt left arrow and Alt right arrow to
    see where you recently edited your source. Press Alt K (K like keys) to toggle bookmarks with a list of keyboard shortcuts.
- You should associate the *.asc (Assembler Source Code) file extension with \Masm32\MasmBasic\RichMasm.exe
- RichMasm uses the RichEdit DLL; crashes are very rare but may happen. Save your work frequently, keep backups,
    reflect where to click if you see a "You may have had a crash" message, or try the menu File/Last good version - it opens
    the last version that assembled without errors. You will appreciate this function ;-)
- Colours are purely illustrative. RichMasm tries to colour MasmBasic commands in blue if you use the keyword shortcuts,
    e.g. sto (space) becomes Store "MyFile.txt", L$(), ct, but colours have no influence on how Masm reads your code.
- Register preservation:    When calling Windows APIs, e.g. invoke MessageBox, 0, Str$(eax), Chr$("Title"), MB_OK,
    the registers esi edi ebx ebp esp will be preserved by Windows; eax will return a value, and ecx and edx will most probably
    contain garbage. Beginners stumble sooner or later over this problem    when they use ecx for a loop and see a crash.
    MasmBasic behaves like Windows, except that it does preserve ecx.    Therefore, use the values returned in eax, never rely
    on edx after any macro or invoke except if edx returns something, and rely on ecx only if you have not used any invokes to
    Windows APIs in your loop. Note that standard Masm32 macros like print, str$() etc do invoke Windows APIs and therefore
    do trash ecx. Use Print and Str$() instead.
- Preservation of xmm registers: All xmm registers are preserved, with the exception of three compare functions:
    Fcmp, StringsDiffer and FilesDiffer, which use xmm0... xmm2. The regs xmm3..xmm7 will never be trashed by MasmBasic.
- Another popular error: Masm uses by default unsigned comparisons. This example demonstrates the effect:
            mov eax, -1                        ; definitely a lot smaller than 99, right?
            .if eax<99
                        MsgBox 0, Str$(eax), "-1 is less than 99:", MB_OK
            .else
                        MsgBox 0, Str$(eax), "Surprise, surprise: -1 is bigger than 99!", MB_OK
            .endif
    The problem is that Masm reads -1 as 4294967295, i.e. 2^32-1, or 0FFFFFFFFh. To avoid this, MasmBasic
    provides the Mbsigned equate: .if signed eax<99 will produce the behaviour you expect.
- You may see cryptic error messages; for example, Let esi=Trim$(FileRead$("Test A.txt")) will fail with "forced error".
    Often, reducing the nesting level, i.e. splitting in Let esi=FileRead$(...), then Let esi=Trim$(esi) helps.
- MasmBasic uses the Masm MACRO engine. As with all libraries using macros (e.g. print, str$, len... in Masm32), be aware
    that macro expansion takes place before the current line. Normally, this is no problem, but in rare cases incorrect code may
    be produced. One such case is .if Len(My$)==1 ... .elseif Len(My$)==2 - this does generate code for the second statement,
    but this code will never be executed. To avoid this scenario, use for example:
            .if Len(My$)==1
                        ...
            .else
                        .if Len(My$)==2            ; code will be inserted before the .if and after the .else, and produce the expected result
                                    ...
                        .else
                                    .if eax==3            ; hint: Len returns eax, so instead of calling Len once more, you may continue to test eax
                                                ...
                                    .endif
                        .endif
            .endif

Error messages
In your debug version, use ErrLines to see the line where a runtime error occurs; if you
still get only question marks in error messages, use SetErrLine in the suspect zone.




Try/Catch/Finally
MasmBasic supports Structured Exception Handling (SEH).
See \masm32\MasmBasic\Res\Templates.asc for a detailed example.




TryRTE
include \masm32\MasmBasic\MasmBasic.inc            ; download
            Init            ; select init and hit F6 to build this
            ErrLines            ; add extra code to get source code line where error occurred
            Dim My$(3)
            TryRTE BadElement, con            ; optional: con means errors to console, key means con+(wait for a keypress); default is box
            Let My$(5)="This won't work"            ; the index is too high (incrementing gradually to 4, 5, 6 would be OK)
BadElement:
            cmp edx, $            ; simple error check
            .if Zero?
                        PrintLine "Bad index!!"
            .endif
            Inkey "bye"
            Exit
end start

Rem            MasmBasic can throw a variety of runtime errors, such as "file not found" etc.; note that after
            showing them, MB will normally call ExitProcess. This can lead to tricky situations if you use
            MB in a dll. To prevent killing your main process, use TryRTE as shown above.




Error            A2121: Symbol not defined : Use_mov_z$_Left$
Cause            You tried to use mov esi, Left$("Test", 3)
Fix            Use Let esi=Left$("Test", 3), or use z$ as follows:
            MsgBox 0, z$(Right$("Hello Masm32", 6)), "z$:", MB_OK

Error            A2148: invalid symbol type
Cause            You tried to store an array that was not yet defined in the line where you call Store
Fix            Put the Store into a proc, and move that proc below the line that defines the array, e.g. below the Recall


Basic instructions
RichMasm:            To test the examples with white background,
            copy them into the nop after start,
            make sure required variables are present
            (e.g. MyReal10 REAL10 123.456 in the .data section),           
            then press F6 to see how the example works.
Init            This is perhaps the smallest possible complete Masm application:
            include \masm32\MasmBasic\MasmBasic.inc
            Init
            Inkey "Hello World"
            Exit
            end start
Rem            - the Init macro inserts the following two lines:
            .code
            start:            call MbBufferInit
            - using Init is optional; it declares the code section, the start label, and preloads some strings (CrLf$, Tb$ etc.);
                however, the check for these preloaded strings will be performed in the first Print or Let statement, too.
            - use Init tc [, con/key/box] to install a Structured Exception Handler with Try/Catch (\MasmBasic\details).



Exit            The correct way to leave a MasmBasic application
            Exit
            Exit eax
            Exit 123
            Exit debug                        ; checks if all Dims and string allocations have had matching deallocations on exit
            Exit debug, 2000            ; same but waits two seconds
           
Exit debug, box            ; same but displays a box instead
Rem            releases memory allocated for arrays or strings, then invokes ExitProcess
            Warning:
            After using strings or arrays, you must use Exit to quit an application.
            Do not confuse MasmBasic Exit with the lowercase Masm32 library exit.
Key            exit



CL$(), wCL$()
            MsgBox 0, CL$(), "Hi", MB_OK                        ; display all args in the command line (the normal MsgBox trashes ecx - this one doesn't!)
            wMsgBox 0, wCL$(), "Hi", MB_OK                        ; same as wide (Unicode) version
            MsgBox 0, Cat$("Arg1: "+Tb$+CL$(1)+CrLf$+"Arg2:"+Tb$+CL$(2)), "Args with Cat$:", MB_OK
            Let esi="Arg1: "+Tb$+CL$(1)+CrLf$+"Arg2:"+Tb$+CL$(2)
            MsgBox 0, esi, "Args with Let:", MB_OK
            Print "Testing the MasmBasic CL$() function:"
            xor ebx, ebx
@@:            ; this snippet prints all args on screen:
            mov ecx, CL$(ebx)                        ; do not put this line directly to the right of the @@ label (more)
            jecxz @F
            Print Str$("\narg #%i = [", ebx), ecx, "]"
            inc ebx
            jmp @B
@@:            Inkey Str$("\n%i valid args found", ebx-1)            ; instead, you can also use CL$(?) to get the number of arguments
Rem
            - returns pointer in eax, or zero if there is no argument
            - if used with Print, Let or Cat$(), and there is no arg, a question mark will be inserted
            - if no arg is supplied, i.e. CL$(), then one arg is assumed, even if spaces are present
                (useful e.g. to open C:\Documents and Settings\user\Documents\My File.txt);
                in this case, the commandline must not exceed 512 characters
            - CL$(0) yields the path of the executable
            - if you need args permanently, use Let MyArg$=CL$(1)



If_ ... .Then            One-line if with one or more statements
            ; Warning: this macro is powerful but read the Rem section carefully
            If_ eax Then inc ecx            ; If_ condition Then action
            If_ not eax Then dec ecx
            If_ NOT eax==1 && ecx>eax Then Print "Hello": dec ecx
Rem            - use for single-line if statements
            - multiple statements can be separated by colons (attention "quoted: text" will fail)
            - If_ !eax Then is not possible (unless you use four exclamation marks...); instead, use
                If_ not eax Then ...
            - If_ and Then are case-sensitive, not is not
            - Warning: function macros (e.g. Val(...)) are evaluated from left to right before entering the If_ macro,
                so eax and edx are definitely trashed before e.g. If_ eax==123 - caution, it will fail miserably!! To prevent
                this behaviour, you can block premature expansion with an exclamation mark before the function:
                    If_ eax==1 Then Print !Str$("eax=%i", eax; )



           
IsTrue                                    ; test condition with floats
            include \masm32\MasmBasic\MasmBasic.inc
            SetGlobals x1:REAL8, x2:REAL10
            Init            ; << select Ixnit and hit F6 to test this snippet
            SetGlobals            ; the two loops differ only regarding their precision
            fld FP8(123.456)            ; put 123.456...
            fstp x1            ; into double x1
            MovVal x2, "123.457"
            PrintLine "x1", Tb$, Tb$, "x2"
            .Repeat
                        fld x1            ; load x1
                        fadd FP8(0.0005)            ; add 0.0005
                        fstp x1            ; save it
                        PrintLine Str$(x1), Tb$, Str$(x2)            ; print it
            .Until IsTrue(x1 gt x2, top)            ; loop until x1 is greater than 123.457
            PrintLine Str$("x1 gt x2: x1=%If", x1), Str$(", x2=%If", x2), " (top precision)", CrLf$
            MovVal x1, "123.456"            ; put 123.456... into double x1
            PrintLine "x1", Tb$, Tb$, "x2"
            .Repeat
                        fld x1            ; load x1
                        fadd FP8(0.0005)            ; add 0.0005
                        fstp x1            ; save it
                        PrintLine Str$(x1), Tb$, Str$(x2)            ; print it
            .Until IsTrue(x1 gt x2)            ; loop until x1 is greater than 123.457
            PrintLine Str$("x1 gt x2: x1=%If", x1), Str$(", x2=%If", x2), " (default precision)", CrLf$
            Exit
                        end start
Rem            - returns Zero? or Sign?, depending on the condition
            - almost any numerical argument is allowed, including immediate floats and integers - see Fcmp(); the second
                argument (i.e. top, medium, low precision) is optional; avoid using eq with top as in IsTrue(x1 eq x2, top)!
            - use with .if ... and .Repeat ... .Until but not with .elseif or .While; RichMasm will warn you but other IDEs won't.



CodeSize
            include \masm32\MasmBasic\MasmBasic.inc
            Init            ; <<< select init and hit F6 to test this snippet
            call MyTest
            CodeSize MyTest
            Exit
            MyTest_s:
            MyTest proc
                        nops 99
                        ret
            MyTest endp
            MyTest_endp:
            end start
Rem            - shows the size of code in bytes between name_s: and name_endp:
            - does not trash any registers




DlgDefine, DlgControl, DlgShow
            include \masm32\MasmBasic\MasmBasic.inc
            Init
            DlgDefine "Please enter your data, tab for next line:", 0, 0, 150, -4, , 14
            DlgControl dcEdit,            "First name", WS_BORDER or WS_TABSTOP, 1, -2                        ; first control gets the focus
            DlgControl dcEdit,            "Family name", WS_BORDER or WS_TABSTOP, 1, -4                                ; x, y only, the macro will assign width, height and ID
            DlgControl dcStatic,            "Type your first name:", SS_LEFT, 1, -1, 70.0                                            ; 70 means 70% - the buttons need space
            DlgControl dcButton,            "OK", BS_DEFPUSHBUTTON or WS_TABSTOP, 71.0, -1, 12.0, , IDOK                    ; x=71%, y, width=14%, height, ID
            DlgControl dcButton,            "Quit", BS_PUSHBUTTON or WS_TABSTOP, 84.0, -1, 16.0, , IDCANCEL
            DlgControl dcStatic,            wCat$("Type your family name: (it's "+wTime$+" now)"), SS_LEFT, 1, -3
            ; DlgHandler MyHandler                    ; optional - PM me for details
            DlgShow
            .if eax==IDOK            ;    click to download JWasm
                        wMsgBox 0, wCat$(Dlg$(0)+wCrLf$+Dlg$(1)), "Please confirm:", MB_OKCANCEL
            .endif
            Exit
                        end start
Rem            - see advanced dialog \MasmBasic\examples in \masm32\MasmBasic\Res\Templates.asc
            - the dialog closes for IDOK, IDCANCEL and any other control whose ID is in the range 100...120; the ID will be in eax
            - you can use registers or variables for the strings, but make sure they are Unicode (e.g. wChr$(ecx) or wRes$(123) are fine)
            - esi cannot be used between DlgDefine and DlgShow, but ecx can be used and will not be trashed



SetGlobals            ; declares global variables relative to ebx, syntax as in LOCAL
.data?
            whatever            dd ?
            SetGlobals hMain, hStatic, hEdit
            SetGlobals MyDw=123, TheFactor:REAL4=123.456, FileName$="Test.txt"            ; variables can be initialised
            SetGlobals hMenu, hM1, hM2, rc:RECT
            SetGlobals msg:MSG, wc:WNDCLASSEX, exeBuffer[MAX_PATH]:BYTE, gBuffer[1024]:BYTE
            .code
            ; no args: set ebx to the right offset, and initialise vars; must be used in Init part and all callbacks (WndProc, SubEdit, ...)
            SetGlobals
Rem            - variables return [ebx+x]


, where x=-128 ... +128 and higher
            - for a range of 256 bytes, SetGlobals produces very size-efficient code
            - as with LOCAL variables, put large buffers at the end
            - WORD, DWORD, REAL4, REAL8 variables and strings with '$' ending can be initialised



Enum                        ; create a list of IDs
            Enum            IdMenuNew, IdMenuSave, IdMenuCopy, IdTimer
            Enum            20:IdEdit, IdButton1, 30:IdButton2, IdStatic, IdFind, IdFindStatic
Rem            - default start is 10, but (as shown above) new start values can be specified with nn:



Dll & Declare
include
\masm32\MasmBasic\MasmBasic.inc                        ; include this library
.data
MyLongLong            LONGLONG 12345678901234567890            ; the standard Masm32 crt lib is not enough to handle this
            Init                                                            ; initialise the app
            Dll "shimgvw"                        ; load the shell image view dll aka Windows Picture and Fax Viewer Library
            Declare void ImageView_Fullscreen, 4                        ; ImageView_Fullscreen expects 4 dwords but returns nothing useful
            ImageView_Fullscreen(0, 0, wCL$(1), SW_SHOW)            ; we need the wide version of the commandline arg
           
Err$(1, "ImgView")                        ; there is no retval - you may test here for errors
            Dll "NtDll"
            Declare RtlRandom, 1                        ; one arg, _Inout_    PULONG Seed
            Declare RtlRandomEx, 1                        ; same but improved function
            Dll "ntoskrnl.exe"
            Declare RtlRandomExNtos, 1 Alias "RtlRandomEx"            ; same but native API - will crash in user mode
            Dll "msvcr100"
            Declare void myprintf, C:? Alias "printf"            ; don't return anything, C calling convention, vararg (note: vxoid followed by one space)
            printf(cfm$("MyLongLong is %llX aka %llu\n"), MyLongLong, MyLongLong)            ; MyLL as hex and decimal figure
            ; Print "MyLongLong is ", Hex$(MyLongLong), Str$(" =    %u\n", MyLongLong)            ; standard MasmBasic syntax, for comparison
            DllRTE=0
            Dll "%ProgramFiles%\FreeArc\Addons\InnoSetup\unarc"                                                ; use environment variables for multilingual apps
            Declare FreeArcExtract, C:?            ; C calling convention, variable number of arguments
            .if eax
                        Print Str$("\nResult=%i", FreeArcExtract(0, "l", "--", "testfile.arc"))                        ; pCallback, listing, no more options, archive name
            .else
                        Print "unarc.dll not present?"
                .endif
            Exit                                    ; do a clean exit, inter alia FreeLibrary
end start

Rem            - Dll performs LoadLibrary, Declare initialises the macro with GetProcAddress, Exit frees the libraries
            - to perform runtime checks if certain functions are available to your application, you may use the flags DllRTE=? and DecRTE=?
                with values 1 meaning throw a runtime error if Dll or Declare not found, 0 to return zero in eax in case of error (default: 1)
            - use Declare void SomeFunction, ... if you don't need the return value
            - the number of arguments is independent of the size of arguments; pass e.g. a RECT stucture counts as one argument
            - use Declare SomeFunction, C:3 for C calling convention with three args
            - use Declare SomeFunction, C:? for C calling convention with a variable number of arguments
            - the Alias keyword allows to declare a self-defined name; use in case of "already defined" errors
            - use Declare #123=SomeFunction, 2 to access a function by ordinal number; then mov ecx, SomeFunction(arg1, arg2)
            - if the function cannot be found, try the decorated name:
                        Declare _SomeFunction@12, C:3            ; three args, C calling convention
                        then use e.g. mov ecx, SomeFunction(arg1, arg2, arg3)
            - do not use Dll & Declare for the static libraries of the Masm32 package (user32, kernel32, ..., see Masm32rt.inc)
            - up to 9 libraries can be loaded




deb            Probably the most important function for you...
            deb 1, "The first loop", ecx, $esi, $edi, MyReal10, ST, ST(5)            ; show a MsgBox ($esi means show ptr esi as a string)
            deb 2, "Second loop:", al, ecx, $esi, $My$, $MyArray$(n)
            deb 3, "Third loop:", al, ecx, $esi, xmm0, xmm1, ST, ST(5)            ; xmm in lowercase, FPU regs in uppercase
            deb 4, "#4 will show in the console:", xmm0, f:xmm0                        ; display xmm0 as integer (default) and float with f: prefix
            deb 5, "#5 will be written to DebLog.txt:", ebx, $My$, $MyArray$(n)
            usedeb=0                                    ; disable debugging completely (no code inserted - very handy...)
            deb 1, "This box will never pop up", eax
            usedeb=16                                    ; force hex display
            deb 4, "Hexadecimal:", eax, xmm1, ST(3)            ; limited to 32 bits, i.e. low dword of xmm regs, FPU as int 32
            usedeb=2                                    ; force binary display
            deb 4, "Binary:", eax, xmm1, ST(3)                        ; limited to 32 bits
            usedeb=1                                    ; decimal display (default)
            deb 4, "Multiple:", eax, x:eax, b:eax                        ; override usedeb: show arguments in decimal, hexadecimal and binary format

Rem            - the debug macro preserves ordinary reg32, xmm and all FPU registers, and the flags
            - it can show xmm and FPU registers ST(0)...ST(5), but it can not display ST(6) and ST(7)
            - numerical global and local variables can be displayed, but do not use different numerical arrays in the
                same deb line, as numerical arrays use edx, which will be set by the last one and used by all others
            - global and local variables as well as arrays can be shown as strings by using e.g. $ptr
            - the string content of registers can be shown by using $eax, $ecx, $esi etc.
            - displaying macro results as strings is possible, as e.g. in $Win$(hWnd), but multiple expansion may occur;
                check if this affects results of your code, and if yes, use a register instead: mov ecx, Win$(..) -> deb... $ecx
            - cancelling deb 1, ... does not cancel deb 2, ..., so you can test several loops in one go
            - deb 1, ... deb 3, ... are being displayed as MsgBox, while deb 4 writes to console, and deb 5 writes to file:
                deb 5, "ToFile", eax, $esi, $edi saves contents (without showing them) to DebLog.txt
                Remember that you can interrupt console output (deb 4) by pressing Ctrl C
            - if FPU regs display incorrectly as 0.0, use Init, or try a void Str$(0) earlier in your code to initialise Str$()
            - xmm display precision is REAL8, FPU registers display with REAL10 precision, i.e. 18 digits
            - structures can be used: deb 1, "The bottom value of element 20:", MyRectStruc(20, bottom)
            - with structures created e.g. with Dim MyStruc(123) As RECT, in rare cases you need % and <brackets>:
           
    % deb 1, "String in highest element of MyStruc.ms1:", $<MyStruc(MyStruc(?), ms1)>
                Note that with early Masm versions you cannot display xmm registers at the same time
            - the chg: keyword allows to monitor which WM_ messages have changed a global variable or register; example
                (you can insert the code above from the AutoCode menu - place the cursor at the end of the WndProc line):
                        WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
                        LOCAL rc:RECT, whatever
                        inc msgCount                        ; increment the default counter
                        deb 4, "# msg #", chg:msgCount                        ; this console deb will only be shown if the variable behind chg: has changed
                        SWITCH uMsg
                Note that deb picks messages to discard from an equate which you may change if needed:
                NoDebMsg equ
Key            deb



Err$()
            Err$(1)                                    ; call GetLastError, and show a MessageBox with a formatted string if there was an error
            Err$(0)                                   
; call GetLastError, and show a MessageBox with even if there was no error
            Err$(1, "some text")            ; show a MessageBox titled "some text" (1: only if there was an error)
            PrintLine "Result=", Err$()            ; print the formatted string
           
void Err$()                        ; no arg: returns <eax> plus the zero flag
           
.if !Zero?                        ; for use with .if, .Break etc
                       
MsgBox 0, eax, "Hey coder, there is an error:", MB_OK
            .else
                        deb 4, "Loop C", $eax            ; will write The operation completed successfully to the console
            .endif
Rem            the blank argument version returns a pointer to the formatted error desription in eax




SetErrLine
            Dim
My$(99)
            SetErrLine
            Print My$(98)            ; sooner or later, MasmBasic...
            SetErrLine
            Print My$(99)            ; ... will show you a nice little ...
            SetErrLine
            Print My$(100)            ; ... runtime error message ;-)
Rem            - inserts mov MbErrLine, @Line+1 into your code - a fat 10-byte instruction
            - see also Init SEH above; use mov MbFlags[4], 1 to see errors in console
            - no code will be inserted for MbUseErrLine=0 (case-sensitive)
            - if you want to see error lines in runtime error messages, put MbUseErrLine=1 early in your code
            - many macros (string arrays, Input etc) set error lines automatically for MbUseErrLine=1

Asc
            movzx ecx, byte ptr Asc(My$)
            mov dl, Asc(esi)
Rem            returns BYTE in al




Cvi
            movzx ecx, word ptr Cvi(My$)
            mov dx, Cvi(esi)
Rem            returns WORD in ax




Cvl
            mov ecx, Cvl(My$)
            mov edx, Cvl(esi)
Rem            returns DWORD in eax




Odd
            .if Odd(123)...
            .if Odd(eax)...
            .if Odd(ax)...
            .if Odd(al)...
            .if Odd(MyDword)...
Rem            returns !Zero?



Abs
            PrintLine Str$(Abs(123))
            PrintLine Str$(Abs(-123))
            mov eax, -12345
            PrintLine Str$(Abs(eax)+1000)
Rem            returns positive DWORD in eax



Key            -


Min, Max
            mov eax, Max(12, eax)            ; return value is in edx
            mov eax, Min(12, eax-2)            ; valid syntax
           
.if Min(eax, MyDword)>99
                        MsgBox 0, Str$("Too high: %i", Min(eax, MyDword)), "Sorry", MB_OK
            .endif
            For_ n=0 To Min(9, eax-1)            ; GetFiles returns #files in eax: use eax-1 with null-based index
                        PrintLine Files$(n)
            Next

Rem            returns DWORD in edx, not eax (since eax is return value of many functions)
Key            min(, max(




PopCount
            PopCount(MySource, ecx)                                    ; standalone: set count to ecx
            mov ebx, 88888888h
            Print Str$("In ebx, %i bits are set", PopCount(ebx))            ; for use with Str$() etc
            void PopCount(ebx)                                    ; returns bitcount in eax
            mov MyVar, PopCount(MySource)
Rem            runs in about 7 cycles on a Celeron M; requires Init or at least one Str$(0) before




Rand
            Rand()                                   
; empty args = initialise using rdtsc
           
Rand(0, 100, xmm1)            ; 0....+100, dest can be local/global REAL8 var or xmm
            Rand(0, ebx, MyReal8)            ; 0....ebx, dest can be local/global REAL8 var or xmm
            Rand(0, 1)                        ; 0....+1, left on FPU in ST(0)
           
Rand(-5.5, 9.3)            ; -5.3...+9.3, left on FPU in ST(0)
           
Print Str$("-5.5, 9.3: ST(0)=\t%If\n", ST(0))
            void Rand(123)            ; leaves dword in eax
            mov ecx, Rand(123)            ; range 0...123
            mov ecx, Rand(MyDword)            ; range passed as dword variable
            mov ebx, 1000            ; we want 1000 elements (we'll get 1001: 0...1000)
            Dim MyArray(ebx) As REAL4            ; could be also REAL8, QWORD, DWORD, WORD, BYTE
            Dim MyDW(ebx) As DWORD
            .Repeat
                        Rand(-888.888, 999.999, MyArray(ebx))            ; put random number into Real4 array
                        mov MyDW(ebx), Rand(1000)
                        dec ebx
            .Until Sign?
Rem            - high-speed RNG generates pseudo random sequence with excellent randomness
            - for testing, do not initialise; for release version, use Rand() before the innermost loop
            - credits to Alex Bagayev for his AxRand algo



FolderOpen$
include \masm32\MasmBasic\MasmBasic.inc            ; select Init and hit F6 to test the examples
    Init                                        ; FolderOpen$(prompt [, title] [, path] [, BIF flags])
    Let esi=FolderOpen$()            ; no args = use Windows defaults, start with current folder
    Print "Selected: [", esi, "]", CrLf$            ; if the user cancelled, esi will be an empty string
    PrintLine "Selected: ", FolderOpen$("Pick a folder:", "Masm32 is great")            ; a prompt and a nice title, start with current folder
    PrintLine "Selected: ", FolderOpen$("Pick a folder:",, "\Masm32\Examples")            ; just a prompt, default title, specific folder
    PrintLine "Selected: ", FolderOpen$("9Pick a big folder:")                        ; a big prompt (font 9), default title, current folder
    ; prompt, title, path with environment variables, flags
    Let esi=FolderOpen$("What is the edit box for?","Locate Office:", "%ProgramFiles%\Microsoft Office", BIF_USENEWUI)
    PrintLine esi
    PrintLine "Selected folder=[", FolderOpen$("Where are your tools?",, "D:\Masm32\bin"), "]"            ; fully qualified initial path
;    if you need to check whether the user cancelled, use void and check the zero flag:
    void FolderOpen$("5Pick the bin folder, please:","Getting help", "\Masm32\bin")            ; use current drive plus initial path
    .if !Zero?
            MsgBox 0, Launch$(Cat$(eax+"\link.exe /Lib")), "Library manager help:", MB_OK
    .else
            PrintLine "Nothing selected: [", eax, "]"            ; Zero? set = user cancelled, eax points to a Null$
    .endif
    Exit
end start

FileOpen$, FileSave$, wFileOpen$, wFileSave$
            .if FileSave$(offset MyFilter, "Test save:")            ; needs MyFilter db "RTF", 0, "*.asc", 0, [...etc, 0], 0
                        Let My$=FileSave$()                        ; assign to a string by using empty brackets
                        PrintLine "File openend: [", My$, "]"                ; do whatever you need the filename for
            .else
                        PrintLine "You cancelled"                        ; see Win32 docu of OPENFILENAME, lpstrFilter
            .endif
            ; you may specify the filter using the following syntax, i.e. separate by "|":
            .if FileOpen$("Rich source=*.asc|Poor sauce=*.asm|Resource=*.rc")
                        PrintLine "You opened    [", FileOpen$(), "]"            ; empty brackets means result only
            .else
                        PrintLine "You cancelled"
            .endif
            ; this example uses two more options: a default file and an extra OFN_xx flag, and returns an array of selected files
            .if FileOpen$(offset txFilter, "Test open", "MyCurrentFile.txt", ofnFlagsO or OFN_ALLOWMULTISELECT)
                        push edx                        ; # of files selected is in edx
                        mov esi, FileOpen$()                        ; empty brackets = return pointer to the buffer
                        .if edx==1
                                    PrintLine "One file selected: [", esi, "]"
                        .else
                                    PrintLine "Folder: [", esi, "]"
                        .endif
                        .While 1
                                    lodsb                        ; same as mov al, [esi], then inc esi
                                    .if !al
                                                .Break .if !byte ptr [esi]                        ; two zero bytes means end of file array
                                                PrintLine "File:", Tb$, esi
                                    .endif
                        .Endw
                        pop edx
                        Print Str$("%i files selected\n", edx)
                        sub esi, FileOpen$()
                        Print Str$("%i bytes of ", esi), Str$("%i available bytes used\n", MbBufSize/4-1000)
            .else
                        PrintLine "You cancelled"
            .endif
           
Rem            - if used with arguments, returns !Zero? for testing
            - full syntax: FileOpen$("filter" [, "title"] [, "current"] [, flags] [, sortcolumn])
            - if used without arguments, returns a pointer to the filename
            - uses two predefined OFN_xx flags called ofnFlagsO and ofnFlagsS that you can modify if really needed
            - uses a predefined ofnSortFlags in format 2*column+1, where 1 means down and 0 means sorted up; default is 2*3+1
            - see Win32 docu of OPENFILENAME, in particular lpstrFilter and OFN_xx flags
            - for example, you may use and MbOfn.nFilterIndex, 0 to clear users' choices
            - you may force a different parent window with m2m MbOfn.hwndOwner, MyWindowHandle before .if FileOpen$()
            - start folder is where the executable resides; force a different folder with, for example,
                FileOpenSetFolder "\masm32\m32lib" (Unicode: wFileOpenSetFolder "\masm32\examples\unicode_extended\UnicodeTest_GUI")
Key            fo$, fs$



Open, wOpen
            Open "I", #1, "MyFile.txt"                        ; open for Input
            Input #1, offset MyBuffer, Lof(#1)
            Close #1
            Open "O", #1, "MyOtherFile.txt"                        ; open for Output
            Print #1, "Test: ", offset MyBuffer
            Close
            Open "U", #1, "MyOtherFile.txt"                        ; open for Update
            Print #1:4, offset MyBuffer                        ; write 4 bytes
            Close
            Open "A", #ecx, "MyOtherFile.txt"                        ; open for Append (you can use #register instead of an immediate)
            Print #ecx, CrLf$, "oops, forgot the end!"
            Close
            wOpen "O", #1, wRes$(MyFileID)                        ; use a Unicode resource string as filename - could be Arabic, Chinese, ...
            wPrint #1, wChr$("Hello there")                        ; writes once a Unicode BOM, then Unicode "Hello there"
            Close
Rem            Open returns the handle in eax - you may check for INVALID_HANDLE_VALUE
Keys            opi, opo




Close
            Close #3                       
; close file #3
            Close                                   
; close all open files
Rem            returns DWORD in eax
Key            clo1, clo




Seek
            Open "O", #1, "TestShort.txt"                        ; open file for output
            Print #1, "This is a pretty long string"
            Close
            Open "U", #1, "TestShort.txt"                        ; open file for updating
            Print Str$("The file has %i bytes\n", Lof(#1))
            mov ecx, 5                        ; just for fun
            Seek #1, ecx+7                        ; abspos 5+7=12
            Seek #1, +ecx                        ; relseek: 12+5=17, file pointer on long
            Print Str$("The file pointer is now at byte %i\n", Loc(#1))
            Print #1, "short STRING"                        ; result: This is a pretty short STRING
            Seek #1, -6                        ; relseek: move file pointer 6 bytes back to STRING
            Print #1, "string"                        ; result: This is a pretty short string
            Print Str$("Now the file has %i bytes\n", Lof(#1))
            Close
            PrintLine
"The result: [", FileRead$("TestShort.txt"), "]"
Rem            Seek returns in eax the Win API SetFilePointer return value
            Note that:
            - you can use one + or - operator, as in Seek #1, eax+20 or Seek #2, Lof(#2)-200 (but not with QWORD offsets)
            - if + or - are the first char, however, it means "relative to current pointer": Seek #1, -ecx
            - for huge files, you can use Seek #1, MyQword and Seek #1, +MyQword or Seek #1, -MyQword (but not MyQ+eax etc)
            Remember that e.g. Let esi=Input$(#1, n) advances the pointer, too




Lof
            mov ecx, Lof
(#1)
            Print Str$
("File #1 has %i bytes\n", ecx)
Rem            - returns length of file in eax; see example under Seek above
            - in addition, the high dword is returned in edx, therefore this is valid syntax:
                Print Str$("The file has %i bytes\n", edx::Lof(#1))
            - Lof() doesn't throw runtime errors, but you can use .if !Sign? for error checking



Loc
            Print Str$
("The file pointer is now at byte %i\n", Loc(#1))
Rem            - returns current file pointer in eax (and high dword in edx); see example under Seek above
            - if needed, use .if !Sign? for error checking



Locate()
           
mov ebx, Locate(y)                        ; get current Y position; same for Locate(x)
            Locate(0, ebx)                        ; place the console cursor in column 0, line ebx
Rem            SetConsoleCursorPosition retval in eax




Input
            Input #1, offset MyBuffer, Lof(#1)
Rem            - returns bytes read in eax, zero for failure
            - edx returns the start of the buffer
            - in contrast to the WinAPI ReadFile, Input zero-delimits the read bytes, so that you
                can use the string directly; you can suppress this by adding one more argument
            - Input reads data from a file; for reading a line from the console, see Input$ below



Input$, wInput$
            Let esi="["+Input$("Type something and hit Enter: ")+"]"            ; input from console
            Print "You typed ", esi
            wLet esi="["+wInput$("Type something and hit Enter: ")+"]"
            wMsgBox 0, esi, "This is Unicode:", MB_OK
            Print "You typed [", Input$("Type something and hit Enter: "), "]"
            Open "O", #1, "YourData.txt"
            PrintLine #1, Input$("Hobbies:\t",            "Assembler, ")            ; the prompt can contain a tab escape,
            PrintLine #1, Input$("Profession:\t",            "Programmer")            ; and you can suggest a prefilled string
            Close #1
            Open "I", #2, "YourData.txt"
            Seek #2, Lof(#2)-10                        ; set file pointer to EOF-10
            Print "The end of your data: ", Input$(#2, 10)            ; input last 10 bytes from file
            Close           
Rem            returns temporary pointer in eax; to get a permanent string, use Let




Rename
            Rename "MbGuide.rtf",    "MbGuide.asc" [, flags]
Rem            - result in eax; uses MoveFileEx, flags e.g. MOVEFILE_REPLACE_EXISTING
            - use MOVEFILE_COPY_ALLOWED if dest is on different drive



WritePipe
            Launch "cmd.exe /C time", SW_RESTORE, cb:hEdit            ; launch an app that requires console input; show its output in the edit control
            WritePipe "20:40:50"                                    ; set the time
            ; you may add a 0 as second argument if you don't want a CrLf sequence to be appended:
            WritePipe esi, 0            ; write zero-delimited string in esi, do not append CrLf
Rem            - will show an error message if you try writing to a pipe that was already closed
            - by default, data is written to a handle obtained by Launch ... cb:handle_to_edit; however, you may try writing to multiple pipes
                (e.g. for a chat type app) by storing & exchanging the global launch structure variable ls.lsPipeWrite



Launch
            Let My$="Notepad.exe"                        ; cmd, show, timeout in ms, flags
            Launch My$                        ; defaults: SW_NORMAL, 0, 0
            Launch "Notepad.exe MyNewFile.txt"
            Launch "Notepad.exe MyNewFile.txt", SW_MINIMIZE
            Launch "Notepad.exe MyNewFile.txt", SW_MAXIMIZE, 2000
            Launch "Notepad.exe MyNewFile.txt", SW_MAXIMIZE, 2000, CREATE_NEW_CONSOLE
            ; you can specificy a handle to an edit control that receives console output:
            Launch "SendStringsToConsole.exe", SW_MAXIMIZE, cb:hEdit
            ; while the launched app is active, you may send strings: for example, you can launch the commandline interpreter
            Launch "cmd.exe /C time", SW_RESTORE, cb:hEdit            ; and see in the edit control a request to enter the new time
            WritePipe "20:40:50"                                    ; set the new time and confirm with Return, i.e. CrLf
            ; using the keyword passdata, Launch can pass a block of memory to the child process - see .ParentData$:
            Launch "MyApp.exe /some /options", passdata, pointer_or_quoted_string [, numbytes]
            Launch "MyApp.exe /some /options", passdata, Chr$("Hello Jochen", 13, 10, "...this is great")
Rem            - returns CreateProcess pinfo.hProcess in eax, and the GetExitCodeProcess error code in edx
            - if you specify a callback edit control instead of the timeout, e.g. with cb:hEdit, you may need to send EM_LIMITTEXT
                to this control
            - if you use cb:hEdit or you specify a timeout of 1 (one millisecond), the process handle will not be closed; you
                can then poll the process status e.g. in a WM_TIMER handler: .if ExitCode()!=STILL_ACTIVE ... do cleanup etc
            - the default timeout for a "normal" Launch is ten seconds; if you mostly need asynchronous launches,
                you can modify this value with e.g. SetLaunchTimeout 1 (in ms)
            - in contrast, the passdata variant returns after 100 ms; typically, the receiving app needs 5-10 ms to grab the buffer;
                if this timeout is too long or too short, use e.g.
                        Externdef MbLaMs:DWORD
                        mov MbLaMs, 200
                before calling Launch
            - when passing a Chr$() or any other zero-terminated buffer, the length para is not needed



Launch$
            Init
            ; the line below launches Arc.exe with option v and returns what SdtOut produces:
            Let esi=Launch$(ExpandEnv$(Chr$(34, "%ProgramFiles%\FreeArc\bin\Arc.exe", 34, " v Lib32.arc")))            ; see FreeArc
            StringToArray esi, FreeArc$()            ; translate linear output to an array
            ; shorter: StringToArray Launch$(ExpandEnv$(Chr$(34, "%ProgramFiles%\FreeArc\bin\Arc.exe", 34, " v Lib32.arc"))), FreeArcListing$()
            For_ ebx=0 To eax-1
                        PrintLine Str$(ebx), Tb$, FreeArc$(ebx)
            Next
            ; this line appends the current date and time, retrieved via the commandline interpreter, to an edit control:
                        AddWin$ hEdit=CrLf$+"["+Trim$(Launch$("cmd.exe /C date /T"))+", "+Trim$(Launch$("cmd.exe /C time /T"))+"]"
                        ; a bad example with a user-defined timeout of 2000 ms - don't launch console processes expecting user input:
                        MsgBox 0, Launch$("cmd.exe /C time", SW_RESTORE, 2000), "Current time:", MB_OK            ; will ask for user input and hang
Rem            - default timeout is five seconds, use args as in Launch above
            - returns string in eax (La$? for failure), and the child process' ExitProcess argument as ExitCode()



LaunchEndPos
            mov esi, Win$(hEdit)
            mov eax, LaunchEndPos()            ; get the position inside the edit control where the last pipe read ended
            add eax, esi
            MsgBox 0, eax, "User typed this:", MB_OK
Rem            for use with Launch ... cb:hEdit



ExitCode()
            Let My$=Launch$("GetInfo.exe")            ; imagine a little proggie that writes something useful to console and ...
            .if ExitCode($)==IDYES            ; ... finishes with a Yes/No/Cancel MsgBox plus invoke ExitProcess, eax
                        ... do something with My$ ...
            .endif
Rem            - returns a global variable with the para passed with ExitProcess, i.e. the DOS-style errorlevel
            - arguments:
                        ExitCode() without arguments returns the exit code of the last "standard" Launch with timeout
                        ExitCode($) returns the outcome of the last Let My$=Launch$("...") attempt
                        ExitCode(cb) returns the current status of the last Launch "MyConsoleApp", SW_SHOW, cb:handle
            - for Launch with timeout (i.e. not the Launch$() or Launch ... cb:handle variants), this value is also returned in edx




Exist
            .if Exist("\masm32\include\winextra.inc")
                        MsgBox 0, "winextra.inc is present, yeah!", "Hi", MB_OK
            .endif
            mov esi, Chr$("NoTest.txt")
            .if !Exist(esi)
                        MsgBox 0, esi, "No such file:", MB_OK
            .endif
Rem            - returns <!Zero?> - do not use Exist("...")==0, use .if !Exist("...") instead
            - after Exist(), you can use LastFileSize, LastFileName$, LastFileDosName$
                to get more info on the found file, e.g. with Print or debug:
                .if Exist("\masm32\include\sh*")            ; v v with deb, $ in front means "show as string" v v
                        Print "Match found: ", LastFileName$, Str$(" with size %i bytes\n", LastFileSize)
                        deb 1, "Last file:", LastFileSize, $LastFileName$, $LastFileDosName$, $MbExeFolder$, $CurDir$()
                .endif
                - if the file does not exist, eax and LastFileSize contain -1, otherwise both return the 32-bit size
Keys            ex(, exist(



IsFolder
            .if IsFolder("\masm32\MasmBasic\")            ; with or without trailing backslash
                        Print "It's a directory"
            .endif
Rem            returns <!Zero?> like Exist(); under the hood is GetFileAttributes




Kill
            Kill "Myfile.dat"
Rem            returns eax




Touch
            Touch "Rec_Test.dat"            ; set the file's timestamp to current time
            Touch My$(99)            ; same if filename is in a string array
            Open "U", #7, esi            ; open file for updating
            Touch #7                        ; set the file's timestamp to current time
            Close 7                                    ; close it
            .if Exist("MyFile.exe")
                        Touch "MyFile.ini", GfLastWrite(-1)            ; synchronise timestamp of two files
            .endif
            ; if usedeb=1 (default!!), Touch will throw a runtime error if the file doesn't exist or is not accessible
            usedeb=1
            Touch "Test.dat"            ; make sure you are not in the middle of something important
            TouchFcs=1            ; you may also change the file creation stamp (default: 0, last write only)
            Touch "Test.dat"            ; make sure you are not in the middle of something important

Rem            - returns SetFileTime result in eax
            - second arg may be a FILETIME in xmm0, e.g. as returned by GfLastWrite():
                Touch Files$(ecx), GfLastWrite(ecx)                        ; example: restore timestamp to a modified Files$(ecx)
            - Warnings:
                - Open "O", #1, ... Touch #1 will destroy your file
                - Open "I", #1, ... Touch #1 will not change the timestamp
                - Touch will display an error message if it encounters an error, then invoke ExitProcess. Disable with usedeb=0




GetFiles, AddFiles
            GetFiles creates the Files$() array, containing full paths of all files found:
            GetFiles filter [, startpattern] [, endpattern or lines matching] [, case sensitivity and full mode]
            GetFiles *.inc                        ; fill the MbFiles$() array with *.inc files of the current directory
            AddFiles Help\*.hlp|*.chm                        ; add to the Files$() array hlp or chm files from the Help subfolder
            mov ebx, eax                        ; eax=# of files found
            Print Str$("\nFound %i files matching *.inc:", ebx)
            For_ n=0 To ebx-1
                        Print CrLf$, Files$(n)
            Next

            ; --- search a folder for all files containing the text "Winsock", and return found lines in Files$(1/3/5 etc): ---
            GetFiles \Masm32\include\*.inc, "Winsock", 1, 1                        ; 1 = return first match only, 1 = case-insensitive
            GetFiles \Masm32\include\*.inc, "Winsock", 3, 0                        ; 3 = return up to three matches, 0 = case-sensitive

            ; --- search a folder for a block of text delimited by "rect" and "ends", and return it in Files$(1): ---
            GetFiles \Masm32\include\*.inc, "rect struct", "ends", 1
            .if eax
                        Let Files$(0)=Mid$(Files$(0), 5)                                    ; cut off the "it's a file" flag (SpTbSpSp)
                        Print "The text was found in ", Files$(0), ":", CrLf$
                        Print Files$(1), CrLf$                                    ; RECT STRUCT ... ENDS
                        Print Files$(2)                                    ; SMALLRECT STRUCT ... ENDS
            .endif
Rem            - returns # of files both in eax and MbGetFileCount
            - you may use the following switches:
                GfNoPaths=            0/1            ; 0=include the path specified by user (default); 1=use file names only
                GfNoRecurse=            0/1            ; 0=include subfolders (default); 1=search only in current folder
            - the long version returns in Files$(0) the full path of the file where the block of text was found.
                The text may have been found in more than one file. Each new file is marked by
                space tab space space in the first 4 characters.
            - if endpattern is omitted, CrLf will be used, and the first matching line only will be returned;
                in this case only, you may used Odd(n) for getting the matching lines in Files$(n)
            - if endpattern=1...126, GetFiles returns up to endpattern matches (1: same as omitted)
            - if endpattern=-1, all matching lines will be returned in Files$()
            - if endpattern=0, GetFiles looks for matches but returns only filenames in Files$()
            - you may use GetFiles WM_DROPFILES in the respective handler; the array will contain both files and folders
            - case & mode (bitwise flag):
                0=case-sensitive, +1=insensitive, +2=intellisense (Name=name),
                +4=full word search, +8=include start of line in text block search
Key            gf



GetFolders, AddFolders
            GetFolders                            ; fill the Files$() array with folders and
                                                                    subfolders starting with the current directory
            AddFolders \Masm32\include\*                ; add to Files$() folders & subfolders of the specified directory
Rem            - returns # of found folders in eax and MbGetFileCount
            - can be combined with GetFiles/AddFiles



GfCallback                        define a callback function to monitor progress in GetFiles or GetFolders
            include \masm32\MasmBasic\MasmBasic.inc
            Init            ; <<< select init and hit F6 to test this snippet
            Let esi=ExpandEnv$("%WINDIR%\")            ; usually C:\Windows
            PrintLine "Searching ", esi
            GfCallback cbGetFiles            ; define a callback function
            GetFolders esi
            Print Str$("\n%i folders found\n", eax)
            Exit
cbGetFiles:
            test edx, 1023            ; file or folder counter
            .if Zero?
                        Print "*"            ; console mode progress bar ;-)
            .endif
            ret
            end start
Rem            The callback function gets invoked every time a file or folder is found and receives the following data:
            - ebx:            pointer to the WIN32_FIND_DATAW structure used for FindFirstFileEx
            - edx:            current file or folder counter
            - edi:            path used in FindFirstFileEx
            You can do whatever you need in this callback, and you don't have to preserve any registers




SortFiles
            GetFiles \masm32\*.asm            ; create the Files$() array
            SortFiles                        ; default: sort the Files$() array by date, most recent files first
            For_ ebx=0 To eax-1            ; print the results
                        PrintLine Str$(GfSize(ebx)), Tb$, GfDate$(ebx), Spc2$, GfTime$(ebx), Tb$, Files$(ebx)
            Next
            SortFiles date, asc            ; sort Files$() array by date, oldest files first
            SortFiles size            ; sort Files$() array by size, biggest files first
            SortFiles size, asc            ; same but smallest files first
            SortFiles name, asc            ; sort alphabetically
Rem            - returns elements sorted in eax
            - after a sort by name, Files$() cannot be resorted by date or size




GfSize, GfDate$, GfTime$, GfAgeHours, GfAgeMinutes, GfAgeMs, GfLastWrite, GfGetInfo
            GetFiles *.asm            ; get all sources in the current folder and its subfolders
            xchg eax, ecx            ; save #files
            For_ ebx=0 To ecx-1
                        .if GfAgeHours(ebx)<=24                        ; pick your latest code
                                    mov esi, Cat$(Files$(ebx)+Space$(20))            ; we simulate LSET
                                    Print Str$("\n#%i ", ebx+1), GfDate$(ebx), ", ", GfTime$(ebx), Spc2$, Str$("%i bytes", edx::GfSize(ebx))            ; edx::eax for QUADWORD size
                        .endif
            Next
Rem            - all numerical arrays associated with GetFiles return eax, except for GfLastWrite, which returns:
                a) the ftLastWriteTime FILETIME member of the WIN32_FIND_DATA structure in the lower qword of xmm0
                b) the size in the upper qword of xmm0
            - GfSize returns the low quad in eax, the high quad in edx
            - GfLastWrite returns size and last write time in xmm0, for any index in Files$() and -1 for the last Exist(). In contrast,
                GfGetInfo(x) returns size and last x time in xmm0 for the last Exist(), where x can be created, modified, accessed
            - Gf....(-1I or Gf...(), i.e. index -1 or no arg: all functions return last write data for the last Exist()
            - index -2: GfDate$(-2) and GfTime$(-2) return creation time data for the last Exist()
            - you can copy file time and size with e.g. GfSetInfo 7, GfLastWrite(-1)    ; Files$(7) receives time and size of last Exist()
            - GfAge...() is calculated from the moment when GetFiles was launched, except for index=-1, which returns age
                relative to the time of the last Exist() call
            - for any other SYSTEMTIME structure, use Age(pSystime, unit)



Age
            .if Age(esi, h)<7*24                                    ; h=hours (valid units: d/h/m/s/ms/s)
                        Print "The item in the SYSTEMTIME structure is younger than seven days"
            .endif
Rem            - returns in eax the age in days, hours, minutes, seconds, milliseconds or nanoseconds/100 units
            - if you get unexpected values, try PrintLine Str$(", %i s\t", edx::Age(pMySystime, s)), as 32 bits
                might not be enough
            - you can use e.g. PrintLine Str$(", %2f days, ", Age(pMySystime, h)/24) to get fractions of units



CurDir$
            Let esi=CurDir$()+"MyFile.txt"                        ; e.g. D:\masm32\MasmBasic\Res\MyFile.txt
            Let esi=CurDir$(0)+"\MyFile.txt"                        ; same output, the (0) means "do not append a backslash"
Rem            - for use with Let & Print, returns DWORD in eax
            - in general identical to MbExeFolder$, but may be different for apps invoked by shortcuts that set the folder




ExpandEnv$, wExpandEnv$
            PrintLine "Architecture is ", ExpandEnv$("%PROCESSOR_ARCHITECTURE%, the OS is %OS%, and the CPU is"), CrLf$,\
            ExpandEnv$("%PROCESSOR_IDENTIFIER%")
            PrintLine "This is the full path: [", ExpandEnv$("%ProgramFiles%\Microsoft Office\OFFICE11\WINWORD.EXE"), "]"
            Let esi="This is the full path: ["+ExpandEnv$("%ProgramFiles%\Microsoft Office\OFFICE11\WINWORD.EXE")+"]"
            PrintLine esi
            MsgBox 0, Cat$("This is the full path: ["+ExpandEnv$("%ProgramFiles%\Microsoft Office\OFFICE11\WINWORD.EXE")+"]"), "Hi", MB_OK
            ; The Unicode version is preceded by a w, as usual:
            wPrint wChr$("Program Files path in Unicode: ["), wExpandEnv$("%ProgramFiles%", 1), wChr$("]"), wCrLf$
            wPrint wChr$("Program Files path in Unicode: ["), wExpandEnv$("%ProgramFiles%"), wChr$("]"), wCrLf$
            ; Both ANSI and Unicode versions allow an optional flag "...", 1 that forces the macro to yield the full name:
            Program Files path in Unicode: [C:\Programmi]            ; output with full name flag set (here the Italian version)
            Program Files path in Unicode: [C:\PROGRA~1]            ; output without the flag

Rem            returns ptr to temporary buffer in eax; for use with Launch, Print, Let and Cat$




ExeFromWin$
            Let esi="MbGuide.rtf"
            Print esi, " was launched by ", ExeFromWin$(WinByTitle(esi))
Rem            returns ptr to temporary buffer in eax, PID (or zero signalling an error) in edx




MakeDir
            MakeDir "A new folder"
            .if Zero?
                        PrintLine "[", eax, "] successfully created"
            .else
                        PrintLine "Could not create '", eax, "', sorry"
            .endif
            mov esi, Chr$("\Masm32\A new folder\")            ; this path ends with a backslash
            MakeDir esi
            jne BadFolder                        ; handle an error
            PrintLine "[", eax, "] created"                        ; [A new folder\] created
Rem            - zero flag set (i.e. .if Zero?) signals success
            - returns always pointer to folder in eax
            - path may or may not finish with a backslash
            - absolute and relative paths allowed
            - will (among others) trigger an error if a file (i.e. not a folder) exists with the same name



ZipFiles                                                ; requires a FreeArc installation
            GetFiles
\masm32\m32lib\*.asm                        ; fill the Files$() array with the desired files
            ZipFiles "The Masm32 lib"                        ; the minimum: just the name of the *.arc archive
            ; with archive name, file count, show, Launch console mode:
            ZipFiles "The_Masm32_lib", 10, SW_MINIMIZE, CREATE_NEW_CONSOLE            ; use first 10 files and a minimised new console
            ZipFiles "The_Masm32_lib", 0, SW_MINIMIZE, CREATE_NEW_CONSOLE            ; use all files in Files$() and a minimised new console
Rem            - requires FreeArc in its default location (e.g. C:\Program Files\FreeArc\bin\Arc.exe)
            - creates archive in *.arc format
            - return value: see Launch
            - use MbZipLog = 1 to see the command lines in ZipLog.txt



UnZipFiles                        ; requires a FreeArc installation
            UnZipFiles
"The_Masm32_lib"                        ; unzip The_Masm32_lib.arc, restoring the tree on the current drive
            ; with archive name, a new destination folder, show, Launch console mode:
            UnZipFiles "The_Masm32_lib", "\masm32\ZipTest", SW_MINIMIZE, CREATE_NEW_CONSOLE    ; unzip the
            tree to new folder
           
UnZipFiles "The_Masm32_lib", '"C:\Program Files\FreeArc\bin"'            ; spaces in dest folder need double quotes
Rem            - requires FreeArc in its default location (e.g. C:\Program Files\FreeArc\bin\Arc.exe)
            - decompresses archive in *.arc format
            - return value: see Launch
            - use MbZipLog = 1 to see the command lines in ZipLog.txt



UnzipFile                        ; extract a file from a zip archive to a buffer
include \masm32\MasmBasic\MasmBasic.inc
    Init
    UnzipInit "test.zip"            ; expects a filename, returns a comment (if present); edx has #records
    .if Sign?
            Print eax            ; print an error message
    .else
            push eax            ; UnzipInit returns a comment or an empty string
            For_ ecx=0 To edx-1            ; #files returned in edx
                        mov esi, Files$(ecx)
                        .if Rinstr(esi, "/")            ; zipfiles use forward slashes
                                    xchg eax, esi            ; we don't display the full path
                                    inc esi
                        .endif
                        PrintLine Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, esi
                        .if Instr_(esi, ".asm", 1)            ; assembler source, plain text?
                                    PrintLine "##First 60 chars: [", Left$(UnzipFile(ecx), 60), "]"
                        .endif
            Next
            pop eax
            Print "Zipfile comment: [", eax, "]"
            UnzipExit
    .endif
    Exit
end start
Rem            - decompresses files in zip archive to a buffer returned by UnzipFile(ecx)
            - you may save the file as FileWrite Cat$("\SomeFolder\"+Files$(ecx)), UnzipFile(ecx)
Key            uzi



FileRead$
            Let esi=FileRead$("MyFile.dat")            ; creates a string on the heap and reads in the specified file
            ; read a file from the Internet, strip all HTML tags, scripts and styles, and display it on the screen:
            Inkey NoTag$(FileRead$("http://www.masm32.com/board/index.php?action=unread;all"))
            Let A$=New$(100, HEAP_ZERO_MEMORY)
            Bmove Chr$(13, 10, "These are xxxxxxxxx"), A$, 12
            Open "O", #1, "A_string.txt"
            Print #1, "some bits and pieces"
            Close
            Let edi=New$(100, HEAP_ZERO_MEMORY)
            Bmove Chr$(" read from a file", 13, 10), edi, 17
            Let esi=A$+FileRead$("A_string.txt")+edi+Str$(": %i bytes", Lof("A_string.txt"))
            Print esi                                    ; Output: These are some bits and pieces read from a file: 20 bytes
Rem            - returns a pointer to heap memory in the reg32 or dword variable after Let
            - directly after FileRead$(), you can get the numbers of bytes read using mov eax, LastFileSize
Key            fr$(




NoTag$
            Let esi=NoTag$(FileRead$("http://www.masm32.com/board/index.php?action=unread;all"))
Rem            strips HTML tags, scripts and styles; don't expect miracles - reducing a perfectly styled webpage
            to pure text will not look pretty, but it's handy to filter webpages by text content




FileWrite
            FileWrite "MyFile.txt", "MyString"           
; opens a file and writes a string to file
            FileWrite "MyFile.txt", "MyString", 2           
; same but writes only first 2 bytes
            FileWrite "MyFile.txt", "MyString", xmm0           
; sets the timestamp in xmm0
Rem            returns Close retval in eax
Key            fw




Replace$
            Open "O", #1, Chr$("\masm32\MasmBasic\WinIncs.htm")
            ; This example joins two major include files, adds html structures and highlights some elements
            Let ebx=FileRead$("\masm32\include\Windows.inc")+FileRead$("\masm32\include\WinExtra.inc")
            ; The '<' and '>' chars will be misinterpreted as html tags and must therefore be removed
            Let ebx=Replace$(ebx, offset txBracketLeft, "<")                        ; src, search, repl [, case] [, count]
            Let ebx=Replace$(ebx, offset txBracketRight, ">")                        ; > becomes 'greater than'
            Let ebx="Windows.inc and WinExtra.inc"+ebx+" "
            Let ebx=Replace$(ebx, CrLf$, offset txBrCrLf)                                    ; HTML needs

            Let ebx=Replace$(ebx, "struct", "Struct", 1+4)            ; ignore case, whole word
            Let ebx=Replace$(ebx, "union", "Union", 1+4)
            Let ebx=Replace$(ebx, "qword", "qword", 1+4)
            Print #1, Replace$(ebx, "PROTO", "Proto", 1+4)                                    ; MixedCase looks nicer ;-)
            Close
Rem            - must be used with Let
            - Replace$ uses the same case flags as Instr_
            - the optional count parameter allows to limit substitution; 0 or omitted means replace all
Key            rep$(



ParentData$
            mov My$, ParentData$()            ; the parent process may have passed data through Launch xx, passdata...
Rem            - returns DWORD in eax
            - console apps can receive data
            - receiving Windows apps should use the SendData/CopyData$ combi





CopyData$
            SetWin$ hEdit="Just received:"+CrLf$+CopyData$+CrLf$+"--- end of data ---"                        ; use directly to set a window content
            Let My$="Just received:"+CrLf$+CopyData$+CrLf$+"--- end of data ---"                        ; store away for later use
Rem            - for receiving data from other apps; for use inside a WM_COPYDATA message handler, for example:
                        SWITCH uMsg
                        CASE WM_COPYDATA
                        Let My$=CopyData$+CrLf$+"received "+Time$
            - for sending data, use SendData
           
- for examples, see File/New Masm source, MB client & server



SendData
            SendData
"MyClient", Win$(hEdit)
            SendData "MyClient", "How are you?"
Rem            - returns DWORD in eax: 0=no client found
            - can also be used from a Win32 console application



SendControlKey
            SendControlKey hWin, VK_V            ; paste something to Notepad etc
Rem            receiving window must have keyboard focus




SendWordCommands
            SendWordCommands
INIT            ; prepare a DDE session; Word must be running
            .if eax
                        SendWordCommands "[FileNewDefault:InsertFile D:\Masm32\include\Windows.inc]"
                        SendWordCommands Chr$("[InsertFile ", 34, "D:\Masm32\include\WinExtra.inc", 34, "]")
                        Let esi=FileRead$("\masm32\MasmBasic\OpenDocInWord.bas")
                        SendWordCommands esi            ; send the contents of the BAS file
                        SendWordCommands "[MsgBox Cute]"
            .else
                        MsgBox 0, "MS Word doesn't answer", "Sorry", MB_OK
            .endif
            SendWordCommands
EXIT            ; finish DDE session
Rem            - swc init returns the DdeConnect retval in eax
            - DDE uses ancient WordBasic; get the help file here ; to make it appear in
                RichMasm's Help menu, save it as \masm32\MasmBasic\Help\WrdBasic.hlp
            - a sequence is included in [squared brackets]
            - multiple commands can be separated by a colon (:)
            - for strings, use either the escape sequence    (Alt 248, 92, 34) or Chr$("xx", 34, "xx") - see above
            - you can also use Let or Cat$ to send commands to MS Word
            - it is your responsibility to respect legality when using this feature



xlsConnect, MbxlsOpen, MbxlsCommand, MbxlsSysRead$(), xlsRC$()
MbxlsRead$(), MbxlsWrite, MbxlsClose, MbxlsDisconnect, MbxlsHotlink
            These macros provide an easy-to-use DDE interface to MS Excel. Click below to see
            detailed examples: Work with data, Excel and Unicode, and Xls viewer with hotlinks.
            ; A typical conversation would look like this:
            xlsConnect                        ; no args=Excel, System
            .if !Zero?                        ; non-zero means success
                        xlsOpen "MyFile.xls"                        ; we open a file
                        .if !Zero?                        ; non-zero signals success
                                    xlsCommand "[app.activate()]"                ; optional: activate Excel
                                    xlsConnect "MyDataSheet"                        ; tell Excel to which sheet you want to talk
                                    .if !Zero?                        ; non-zero signals success
                                                Print "Current selection=[", xlsRead$(), "]", CrLf$                                                ; you may print to the console...
                                                SetWin$ hEdit='R1C1:R9C5=['+xlsRead$("R1C1:R9C5")+']'+CrLf$                        ; ... or set window content
                                                AddWin$ hEdit="Extract #2=["+xlsRead$(xlsRC$(1, 1, 9, 5))+"]"+CrLf$            ; and add bits & pieces
                                                AddWin$ hEdit="Current selection=["+xlsRead$()+"]"+CrLf$                                    ; no args means current selection
                                                xlsWrite "R1C1", Cat$("This sheet was modified on "+Date$+", "+Time$)            ; writing is allowed, too
                                    .endif
                                    xlsClose 0                        ; close the file without saving (0=don't save, 1=save, no arg: ask)
                        .endif
                        xlsDisconnect                        ; say bye to Excel
            .endif
Rem            all macros return !Zero? for success


; in case of failure, macros return Zero? plus an error string in eax



ddeConnect, ddeCommand, ddeRequest$(), ddeDisconnect
    ; These macros provide the DDE interface for non-Excel apps, such as browsers:
    include \masm32\MasmBasic\MasmBasic.inc            ; download
    Init                                    ; select Init and hit F6 to test this code
    ddeConnect "Firefox|WWW_GetWindowInfo"            ; server|topic: connect e.g. to FF with the WWW_GetWindowInfo topic
    .if !Zero?
            PrintLine "URL=", ddeRequest$("URL")            ; request the current URL
            ddeDisconnect                        ; say bye to window info
            ddeConnect "Firefox|WWW_OpenURL"            ; connect to the open URL topic
            .if !Zero?
                        Inkey "Open Google?"
                        .if eax=="y"
                                    ddeCommand "www.google.com"            ; open a frequently used page
                                    ddeDisconnect
                        .endif
            .endif
    .endif
    Exit
end start
Rem            see xls macros



Win$, wWin$
            MsgBox 0, Win$(hWnd), "The title of the main window:", MB_OK
            wMsgBox 0, wWin$(hWnd), "The title of the main window in Unicode:", MB_OK
            .if WinByTitle("bignumsdk.inc - Notepad")
                        xchg eax, ecx                        ; move handle to a safe register
                        wPrint "Content=", wWin$(ecx, 15)                        ; ecx is the parent, 15 is the ID of the Notepad edit control
                        wInkey wCrLf$, "Title=", wWin$(ecx)            ; delete the w if you prefer ANSI
Rem            - returns pointer in eax
            - optional second argument is the ID of a child window; see also App16()
Key            win$(



SetWin$, wSetWin$
            SetWin$ hWnd="A new title for my app"
            wSetWin$ hEdit=wRes$(1)+wCrLf$+wCrLf$+wRes$(2)
Rem            for main windows and controls; you can use string concatenation as in Let resp wLet



AddWin$
            AddWin$ hEdit=CrLf$+"[one line more]"            ; append some text to an edit control
            ; this line appends the current date and time to an edit control:
            AddWin$ hEdit=CrLf$+"["+Trim$(Launch$("cmd.exe /C date /T"))+", "+Trim$(Launch$("cmd.exe /C time /T"))+"]"
Rem            for plain edit controls only



MakeFont
            MakeFont hHandFont, Height:40, Underline:TRUE, "Lucida Handwriting"
            MakeFont hVertFont, Height:32, Escapement:900
Rem            - in order to facilitate creating font variants, MakeFont keeps settings between calls
            - place in WM_CREATE handler



PickFont
            PickFont addr hFont            ; use the font dialog to change the font whose handle is in hFont
            PickFont addr hFont, hEdit            ; apply to edit control (if user didn't cancel)
Rem            returns font handle in eax



ImgPaint
            GetFiles *.jpg                        ; load all jpg files in this folder and below
           
AddFiles *.png                        ; add all png files
            ImgPaintInfo 0, Files$(ImgCounter)                        ; load image file in slot 0 and return image dimensions - width in eax, height in edx
            ImgPaint hStatic, 0, Files$(ImgCounter)                        ; use in the WM_PAINT handler to fill static control with Globe.ico; take slot 0 (of 30)
            ImgPaintClr 2                        ; unload image file in slot 2
Rem            - ImgPaintInfo can be used to adjust the dimensions of the control that takes the image
            - ImgPaint should be called from the WM_PAINT handler
            - ImgPaint accepts most common image formats, e.g. bmp, gif, png and jpg
            - ImgPaintClr frees memory, if really needed. The Exit macro frees all loaded images automatically
            - 30 slots are available, i.e. you can fill 30 static controls with images; should be enough for a card game ;-)




ToolTips
            ToolTips
TTS_BALLOON                                    ; optional: use a specific style
            ; sm edi, TTM_SETTITLE, TTI_INFO, chr$("Hello")                        ; optional: set a title
            ToolTips hEdit, "Type something"                                    ; immediate text for tooltip
            ToolTips
hButton1, Res$(100)                                    ; use resource string from rc file
            ToolTips
hButton2, wRes$(101)                                    ; in case you want Russian or Chinese...
            ToolTips end                                   
; not optional: close the tooltips definition
            --- below the WM_CREATE handler, you can change the text e.g. using multilingual resources as follows: ---
            SetLanguage proc uses ebx
                .if eax>=IdMenuEN && eax<=IdMenuCH                        ; e.g. eax set by a menu event
                        sub eax, IdMenuEN
                        imul ebx, eax, 400                                    ; string table uses ID 1, 2,3/401, 402, 403/801, 802, 803 etc
                        ToolTips hEdit, wRes$(ebx+1)                                    ; type here in various languages
                        ToolTips hButton1, wRes$(ebx+2)                                    ; "click on this button"
                        ToolTips hButton2, wRes$(ebx+2)                                    ; same
                .endif
                ret
            SetLanguage endp

Rem            - you can created them in a loop, but make sure you don't use esi for own purposes; on the other hand, you can
                send messages directly before ToolTips end:
                invoke SendMessage, [esi-4], TTM_SETMAXTIPWIDTH, 0, 200                        ; forces use of Cr, Lf or CrLf
                sm [esi-4], TTM_SETTITLE, TTI_INFO, Chr$("I am a tooltip:")                        ; sm stands for invoke SendMessage,
            - must be placed at the very end of the WM_CREATE handler, i.e. when all controls have been created




WinByTitle
            Print Chr$(13, 10, "Open browser window: ")
            ; optional bit mask: 0+1 for case-insensitive search, +4 for any position in title; e.g. 5 finds also - moZilla
            .if WinByTitle("- Mozilla", 4)
                        Print Win$(eax), 13, 10
            .else
                        Print "none", 13, 10
            .endif
Rem            returns handle in eax




App16
            .if App16(hWin)
                        Print "This is a legacy 16-bit app"
            .endif
Rem            useful to check if you can read text from child windows of other apps (you can't read from a 16-bit app)




Clip$, wClip$
            Print "[", Clip$(), "]"                                                ; displays the content of the clipboard
            MsgBox 0, Clip$(), "Text on the clipboard:", MB_OK                        ; same as MessageBox
            MsgBox 0,\
            Cat$("The text on the clipboard:"+CrLf$+String$(33,"-")+CrLf$+Clip$(20)+CrLf$+String$(0,0)),\
            "MasmBasic:", MB_OK                                                ; displays a MessageBox with two horizontal delimiters
           
MsgBox 0, Clip$(40), "The text on the clipboard, truncated to 40 chars:", MB_OK
            invoke lstrcpy, offset my40charbuffer, Clip$(40-1)                                    ; yep, don't forget space for the zero delimiter
            .Repeat
                        void Clip$(30)                                                ; this loop waits for changes on the clipboard
                        .if !Zero?
                                    PrintLine "New clipboard content=[", eax, "]"                        ; non-zero means new valid content available
                        .endif
                        invoke Sleep, 1                                                ; note: "new" is defined as (len(string)+first dword) different
            .Until signed rv(GetKeyState, VK_ESCAPE)<0
            Let esi=HtmlClip$()                                                ; for use e.g. with Thunderbird and Firefox
Rem            - returns ptr in eax,    to Null$ if clipboard is empty
            - see also SetClip$ below
            - you can truncate the content for security reasons, e.g. for use in a MessageBox



SetClip$, wSetClip$, SetHtmlClip$
            SetClip$ "Today is the "+Date$                                    ; replace curent clipboard with a text, format CF_TEXT
            SetClip "\Masm32\MyPhoto.bmp", CF_BITMAP                        ; put a bitmap on the clipboard6r8A0z0zraphael.bmp>
            SetHtmlClip$ "This is bold"                                    ; for use e.g. with Thunderbird
            wSetClip$ "Today is the "+wDate$+", "+wTime$                        ; the Unicode version
            SetClip #start            ; ---- set multiple clipboard formats ----
            SetHtmlClip$ "Text in HTML format"            ; use e.g. for Thunderbird and Excel
            SetClip 100, CF_BITMAP            ; 100 = ID of bitmap resource
            SetClip$ offset txTest, CF_RTF            ; Rich Text Format
            SetClip$ wRes$(123)            ; use resource string #123, "This is a sub-title" in Russian (Unicode)
            SetClip$ "This is ANSI text"
            SetClip #end            ; ---- end of multiple clipboard formats ----
Rem            returns SetClipboardData retval in eax, copied bytes in edx if successful




MsgMonitor
            WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
            LOCAL rc:RECT, hM1:HWND, hM2:HWND
                        MsgMonitor
                        SWITCH uMsg
                        ...
Rem            - writes all messages to Messages.txt, in format
            - you may add PrintLine #7, "whatever" to certain messages
            - do not use file #7 when using the monitor

New$
            Let A$=New$(100)            ; 100 zero-initialised bytes
Rem            - no return value
            - you can use also Let Files$(0)=New$(100) or Let Files$(0)=String$(n, 0)
            - you may free this memory with Clr A$, but it will also be freed automatically with Exit
Key            new$(



Res$, wRes$
            Let esi=wRes$(401)                        ; get the resource string with ID 401 from the *.rc string table
            invoke MessageBoxW, 0, esi, wRes$(402), MB_OK
            Open "O", #1, "TestUnicode.txt"
            wPrint #1, wChr$("Unicode, oh yeah!", 13, 10)
            wPrint #1, wRes$(401), wTb$, wRes$(402), wCrLf$
            Close
            Inkey "bye"
Rem            - returns DWORD in edx
            - if the resource string does not exist, a "No such string" runtime error will be triggered unless
                you set MbUseErrLine = 0. In this case, missing strings will be shown as R? (Ansi and Unicode).
            - you can embed resources in your *.asc file by using two Rsrc bookmarks
                (case-sensitive) below end Start, for example:
Rsrc
32512 ICON "\\masm32\\MasmBasic\\icons\\Globe.ico"            ; Asm, House, Keys, Globe, Hammer, Setup, Disc, Eye, ...
1 24 "\\Masm32\\MasmBasic\\Res\\XpManifest.xml"            ; adding a manifest may help to avoid AV problems
STRINGTABLE
BEGIN
            401,            "??????? ?? ??? ??????"            ; "Click on this button" in Russian
            402,            "????? ??????????"            ; "Welcome" in Russian
END
Rsrc




MsgTable$
            ; returns Unicode string describing common errors. for example "no such path" in Kernel32:
            wPrint MsgTable$(3, "kernel32.dll"), wCrLf$                        ; can't find the path
            wPrint MsgTable$(80000431h, "user32.dll"), wCrLf$            ; attempt xx failed
            wPrint MsgTable$(3, "ntdll.dll"), wCrLf$                                    ; STATUS_WAIT_3
            wInkey MsgTable$(6BAh, "kernel32")                                    ; RPC server not available




String$
            Let Files$(n)=String$(32767, 0)                                    ; assigns a zero-initialised buffer to Files$(n)
            mov eax, String$(32768-1, "A")                                    ; assigns a buffer initialised with A to eax
            Print String$(15, "+x")                                    ; prints +x+x+x+x+x+x+x+x+x+x+x+x+x+x+x
            Print String$(0, 0), CrLf$                                    ; String$(0, x) means repeat last result
Rem            - returns DWORD ptr eax; max. allowed size is 32767 bytes
            - the buffer remains valid until a new call to String$, except for String$(0,0) which just returns the last result
            - two or more String$ in the same Print or Let line will print only the last version
Key            string$(



Space$
            Let My$=Space$(20)
            Print "Twenty spaces: [", Space$(20),"]"
            ; make sure that FamilyName$ gets printed in column 21 (except if FirstName$ is longer):
            PrintLine Space$(20, FirstName$(ecx)), " ", FamilyName$(ecx)
Rem            returns pointer in eax



Inkey, wInkey
            ; Inkey behaves exactly like Print but waits for a keystroke; it returns the code of the pressed char in eax:
            Inkey "One more loop (y/n)?"
            .Break .if eax=="n"
            Inkey Str$("\nThis file has %2f kBytes\nhit any key to get outta here", Lof("MbGuide.rtf")/1024)
            wInkey wRes$(ID_1_Chinese)
Rem            - returns DWORD in eax
            - in contrast to the Masm32 inkey, which returns scan codes, Inkey and wInkey return either a char (a, A) or, if the
                key does not have a char associated, the virtual key code (VK_F1, VK_LEFT) that a Windows app would return.
            - the virtual key code is always returned in edx



Print, wPrint
Print
#n, wPrint #n
            Print "Test", CrLf$, Chr$("the library", 13, 10)
            Print #1, esi, " is ", My$
            wPrint wRes$(123)                        ; Unicode defined in a resource stringtable
            Print #1:25, "There are many chars here but I print only a few"
            Print #1:100, FileRead$("\masm32\include\Windows.inc")            ; print the first 100 chars of that file
Rem            - predefined strings do not bloat the exe (the same applies to Let):
                (w)CrLf$                        same as an offset to db 13, 10, 0
                (w)Tb$                        9 aka tab - see also \n and \t in Str$()
                Spc1$                        32
                Spc2$                        32, 32
                Spc4$                        four spaces
                MbExeFolder$            the folder of the current executable, e.g. D:\Masm32\
                CurDir$()                the current directory, e.g. D:\Masm32\
                CurDir$(0)            same but no trailing backslash, e.g. D:\Masm32
            - Unicode can be printed as e.g. wPrint #1, wCrLf$, wChr$("This is Unicode") or by defining a
                string like this: wData MyWide$, "This is Unicode", 0 , followed by wPrint #1, offset MyWide$
            - Unicode printing to the console (e.g. Russian, Chinese) is possible but do not assume
                it will work on other systems; specific language packs need to be installed.
                You may test if the following line works on your system if you assemble with Ctrl F6 and click Yes:
                uPrint "??????? ?? ??? ??????"            ; "Click on this button" in Russian
            - Important: the first occurrence of wPrint in your code triggers (hidden, but you can see it in Olly):
                        invoke SetConsoleOutputCP, CP_UTF8            ; codepage 65001, Unicode
                        ConsoleColor cGray, cBlack                        ; grey on black
           
    Without these two API calls, Unicode strings will not display properly. Note that the
                console colors are not automatically set if there is an earlier ConsoleColor statement.
                You can try different colours by using for example, before the wPrint, wPrintColor = cYellow
                (see full list under ConsoleColor below). Strangely enough, Chinese and Arabic fonts may work
                with some colours but not with others - Windows mysteries... ;-)
            - Print is powerful and versatile, but it may fail with a HeapAlloc runtime error for huge buffers; in
                these cases, use PrintBuffer (see below)
           
Key            pri, pri1




PrintLine
            PrintLine "Test", Chr$(" the library")            ; appends a CrLf$
            PrintLine                        ; no arg prints just a CrLf$ (same as Print without arguments)
Rem            see Print above




PrintBuffer
            PrintBuffer #1:eax, esi            ; #file:bytes, pBuffer            ; corresponds to Print #1:eax, esi syntax
            PrintBuffer #1, esi, eax            ; #file, pBuffer, bytes            ; same code generated
Rem            - returns # of bytes written in edx, Win API WriteFile return code in eax
            - use this macro instead of e.g. Print #1:eax, esi to avoid memory problems
Key            pb1



SetCpUtf8, SetCpAnsi
            SetCpUtf8                        ; set codepage to UTF8
            SetCpAnsi                        ; force ANSI codepage
Rem            internal macros for switching between normal Ansi and Unicode Print



Cls
            Cls            ; clear the screen
Rem            console apps only, of course



ConsoleColor
            ConsoleColor cBlue, cWhite            ; blue text on white background
            ConsoleColor cDarkRed            ; dark red text on black
            ConsoleColor eax            ; any colour that suits SetConsoleTextAttribute
Rem            available colors (see also http://support.microsoft.com/kb/319883)
            cBlack, cWhite, cGray, cBlue, cGreen, cCyan, cRed, cMagenta, cYellow
            cDarkGray, cDarkBlue, cDarkGreen, cDarkCyan, cDarkRed, cDarkMagenta, cDarkYellow



crtbuf
            crtbuf ThisExe$, MAX_PATH                                    ; create a buffer in the uninitialised data section
            invoke GetModuleFileName, 0, ThisExe$, MAX_PATH            ; use it...
            crtbuf pBuffer, 1000000, 16                                    ; create a 1 Mio bytes buffer in .data?, align 16 for use with SSE2
Rem            uses label and ORG $+size, therefore even big values will not make ml.exe freeze




CreateInvoke
            Init
;            whateverproc = any proc or result of GetProcAddress etc
            mov CreateInvoke(whatever1, 3*dword, REAL8), whateverproc
;            mov CreateInvoke(whatever2, vararg), whateverproc            ; Error A2094: Vararg requires C calling convention
            mov CreateInvokeC(whatever3, dword, REAL8, dword, REAL4), whateverproc
            mov CreateInvokeC(whatever4, vararg), whateverproc

;            example how to use the created invoke:
            invoke whatever3, 123, FP8(123.456), 456, FP4(456.789)
            Inkey "OK"
            Exit
whateverproc proc    thedd1, thereal8:REAL8, thedd2, thereal4:REAL4
            PrintLine Str$("arg2=%f", thereal8)
            PrintLine Str$("arg3=%f", thedd2)
            PrintLine Str$("arg4=%f", thereal4)
            ret
whateverproc endp
end start
Rem            make sure the proc specifies the non-dword args correctly




gsl            (GNU Scientific Library interface)
include \masm32\MasmBasic\MasmBasic.inc            ; download
; libgsl.dll download: open Binaries zip, and extract libgsl.dll and libgslcblas.dll to \masm32\MasmBasic\GnuScLib\DLL
; define the gsl function(s) you want to use with the syntax of the
GNU Scientific Library Reference
gslvar            int gsl_rng_default()
gsl            double gsl_stats_mean (const double data[], size_t stride, size_t n)
gsl            double gsl_stats_variance (const double data[], size_t stride, size_t n)
gsl            double gsl_stats_sd (const double data[], size_t stride, size_t n)

.data
MyData            REAL8 5.0, 6.0, 3.2, 1.8, 9.0                        ; define a double precision array
MyReal8            REAL8 ?                                    ; for saving results with fstp
            Init
            mov ecx, esp                        ; we check if the stack remains balanced
            gsl_INIT
            gsl_stats_mean (offset MyData, 1, 5)                        ; ptr, stride 1, 5 elements
            PrintLine Str$("Mean        \t%7f", ST(0))                        ; print ST directly from the FPU with 7 digits precision
            fstp MyReal8                        ; save result (or cleanup the FPU with fstp st)
            gsl_stats_variance (offset MyData, 1, 5)
            PrintLine Str$("Variance\t%7f", ST(0))
            fstp st
            gsl_stats_sd (offset MyData, 1, 5)
            PrintLine Str$("Standard Dev\t%7f", ST(0))
            fstp st
            mov MyDword, gsl_rng_default()                        ; you can access gsl global variables defined with gslvar
            gsl_EXIT
            sub ecx, esp
            Inkey CrLf$, "ok, stack diff ", Str$(ecx)                        ; will be zero
            Exit
end start
            Output:
            Mean                                    5.000000
            Variance                        7.620000
            Standard Dev            2.760435
Rem            Note that MasmBasic is copyrighted and therefore cannot be distributed together with
            code that uses GNU components
. Make sure you understand the legal implications



SetPoly3, GetPoly3
            Dim My3Pts(5) As DWORD                        ; create an array with 3 XY pairs, i.e. 6 elements (0 .. 5)
            ArraySet My3Pts() = 1, 100, 2, 300, 4, 150            ; assign XY values (ArraySet can be handy but any other array works, too)
            SetPoly3 My3Pts()                        ; create coefficients for a 3-point polynomial, i.e. Y=a0+a1*X+a2*X2
            Dim AllPts(11) As REAL4                        ; create a destination array with 12 elements
            Print "N", Tb$, "X", Tb$, "Y", Tb$, "Y(ecx)"            ; the last column uses the "direct" variant GetPoly3(X)
            GetPoly3(AllPts())                        ; fill starting from X=0, create coefficients for Y=a0+a1*X+a2*X2
            add eax, eax
            push eax
            xor ecx, ecx
            .Repeat                        ; print N, X, Y=f(x), Y=f(2*X)
                        Print Str$("\n%i\t", ecx/2), Str$("%2f\t", AllPts(ecx)), Str$("%3f\t", AllPts(ecx+1)), Str$("%3f", GetPoly3(ecx))
                        fstp st                        ; pop the return value from the FPU
                        add ecx, 2
            .Until ecx>=stack
            pop eax
Rem            - GetPoly3() returns #XY pairs in eax
            - GetPoly3(array()) sets the whole destination array
            - GetPoly3(X) returns a single value in ST(0)




Dim
            Dim My$(11)            ; create a string array with n+1 elements, i.e. 0...11
            mov ebx, 10
            Let My$(ebx)="This is array element 10"
            MsgBox 0, My$(ebx), "Dim an array:", MB_OK
            ; accessing the n+1th element triggers autoexpansion:
            PrintLine "This is element 12 [", My$(12), Str$("] of %i elements", My$(?))            ; ... 12 [] of 4096 elements
            Dim My$(new:100)                        ; ReDim needs the new: keyword before the first dimension counter
            Let My$(50)="This is array element 50"
            MsgBox 0, My$(50), "Re-Dim an array:", MB_OK
            Dim My$(new:123, 200)
            Let My$(100, 200)="This is array element 100, 200"
            MsgBox 0, My$(100, 200), "Re-Dim as two-dimensional array:", MB_OK
            Dim wc(3) As WNDCLASSEX                        ; an array of structures
            mov wc(0, size), SIZEOF WNDCLASSEX            ; use mov or m2m as appropriate
            Dim rc(3) As RECT
            For_ n=0 To 3
                    m2m rc(n, left), n                        ; n is a global variable, so you need m2m
            Next

            Dim MyBytes(999) As BYTE                        ; a 1000-byte array
            Dim MyWords(3, ebx) As WORD                        ; a two-dimensional WORD array
Rem            - see Let, Erase, Swap, Recall and Store
            - no Erase() needed before a redimension (string arrays only)
            - My$(?) returns the number of elements; do not use for GetFiles/AddFiles
            - string and fixed size arrays may have one or two dimensions; the second dimension count must be
                greater than zero and must not exceed 255, i.e. Dim My$(99999, 255) is ok, Dim My$(2, 256) is not.
                Note that Dim My$(12345678, 255) is formally ok but will trigger a HeapAlloc runtime error
                because you need a really big computer to allocate 12345679*256*8=25,283,950,592 bytes ;-)
            - with two-dimensional string arrays, Insert and Delete may    show buggy behaviour; test yourself
Key            Dim



Erase
            Erase
My$()                        ; free a string array; Dim My$(new:ct) is possible
            Erase MyReal8Array()                        ; free a numeric array; at
            Erase
MyStructureArray()                        ; present, no redim possible
Rem            no return value; will throw a runtime error if HeapFree fails
Key            erase




ArrayFill, wArrayFill
            Dim
MyWC(9) As WNDCLASSEX
            ArrayFill MyWC(), wc                        ; fill array with values from one .data section structure
            Dim My$(ct)
            ArrayFill My$(), "Wow, strings from "+Date$
Rem            see Let, Erase, Swap, Recall and Store



ArraySet
            Dim My3Pts(2) As DWORD                        ; create an array with 3 elements (0 .. 2)
            ArraySet My3Pts() = 12, 34, 56                        ; assign three values
            Dim MyR8(3) As REAL8                        ; create an array with 4 elements (0 .. 3)
            ArraySet MyR8() = 1.0, 2.0e3, 2.0, 4,0e3            ; same for REAL4 or REAL8
            ArraySet My$() = "abc", "def", "ghij", "klmn"            ; same with strings: no Dim before, empty brackets
            Let My$(My$(?))="Only strings support"            ; My$(?) is the current number
            Let My$(My$(?))="dynamic auto expansion"            ; of elements in the string array
            For_ ecx=0 To My$(?)-1
                        PrintLine Str$(ecx), Tb$, My$(ecx)
            Next

Rem            - numerical arrays: allowed sizes are DWORD, REAL4, REAL8; no autoexpand, no boundary
                check, #elements must be sufficient, no return value, does not trash any registers
            - strings must be initialised with empty brackets, #elements in eax; autoexpanded if needed



StringToArray
            StringToArray Win$(hEdit), My$()                        ; converts content of the edit control to a MasmBasic string array
            Let esi="A string"+Tb$+"in two columns"+CrLf$+"another"+Tb$+"string"            ; tab-delimited text
            StringToArray esi, My$(), tab                        ; convert to a two-dimensional array
            StringToArray Clip$(), L$()                        ; converts content of the clipboard to an array
Rem            - returns #elements in eax
            - internally, StringToArray uses Recall
            - there is no need to Dim the string array before



ArrayMerge
            include \masm32\MasmBasic\MasmBasic.inc
            Init                        ; << select Ixnit and hit F6 to test this snippet
            Dim A$()                        ; first array
            Dim B$()                        ; second array
            For_ ecx=0 To 9                        ; ten loops
                        Let A$(ecx)=Str$("String A %i", ecx)                        ; fill with more or less...
                        Let B$(ecx)=Str$("String B %i", ecx)            ; ... meaningful stuff
            Next

            ArrayMerge A$(), B$()                        ; A$(): 10xA, then 10xB; array B$() gets erased
            For_ ecx=0 To 19                        ; twenty loops
                        PrintLine Str$("Element %_i\t", ecx), A$(ecx)
            Next

            Exit debug                        ; debug = check integrity of allocations on exit
            end start
Rem            - returns nothing
            - destination array cannot be a Recall array
            - destination array cannot be Files$()
            - use Delete My$(n), all in case you want to truncate the destination before merging



Swap
            Swap L$(), Files$()
            Swap MyA$, MyB$
Rem            - exchanges either two arrays or two strings
            - mixing arrays and strings will fail and trigger an error message
Key            swap



Let, wLet
            Let Test$="A little test"+CrLf$+"with two lines"
            Let My$(123)=MyOther$(ebx)
            Let My$(123)=Str$("A little test: %i", ebx*3+100)
            Let esi="Another "+"example"            ; using a register is possible but see remarks
            wLet MyUnicode$="A test with Unicode: "+FileRead$("Unicode.txt")
            invoke MessageBoxW, 0, MyUnicode$, wChr$("Title"), MB_OK
            wMsgBox 0, wCat$("The file content:"+wCrLf$+FileRead$("Unicode.txt")), "Unicode is easy:", MB_YESNO
            wLet esi=wChr$("A Unicode Str$: ")+wStr$("%i little bugs", 123)+wChr$(" are waiting for you")
            invoke MessageBoxW, 0, esi, wChr$("Title"), MB_OK
Rem            - no return value
            - examples for using the Let Left$(...)=... syntax:
                Dim My$(123)            ; we create an array
                Let My$(123)="A small test"            ; we use an array element (but single strings work, too)
                Let Mid$(My$(123), 5, 3)="oke"            ; we replace small with smoke            ->A smoke test
                Let Left$(My$(123), 7)="A funny"            ; first 7 chars are A funny            ->A funny test
                Let Mid$(My$(123), 3)="futile attempt to crack the buffer"    ; result            ->A futile att
                Let Right$(My$(123), 3)="act"            ; we replace the last 3 chars            ->A futile act
            - caution when using registers:
                Let edi=eax is OK, but Let My$(123)=eax will cause trouble because eax (and edx) are trashed by the My$() macro.
                Let esi="abc" works, but Let checks whether esi is in the table of heap pointers; if yes, the previous string will be
                HeapFree'd, which is the desired behaviour allowing multiple Let esi="abc"+esi+"def" lines; however, if esi was set
                to some other memory location before the Let, then there will be an attempt to free the other one.    To avoid such
                problems, either stick to global (or zeroed local) variables (Let My$=...), or insert an xor esi, esi before Let esi=...
Key            let




Cat$, wCat$
            MsgBox 0, Cat$("The content of the editbox is"+CrLf$+Win$(hEdit)), "Info", MB_OK
Rem            returns ptr in eax and MbCat$
            - Cat$ is meant to replace Let My$=... in MessageBox or similar situations
            - every use of Cat$ overwrites content in the dedicated Cat$ buffer; mov myptr, Cat$() is
                therefore pretty useless. If you want permanent strings, use Let




Insert
            Insert My$(5)            ; move My$(5) up to My$(6)
            Let My$(5)="This is the new string"
            Insert My$(5), 10            ; move My$(5) up to My$(15), insert 10 empty strings
            Dim MyRc() As RECT            ; Insert and Delete work with autoexpanding numerical arrays
            Insert MyRc(ecx+1)            ; triggers runtime error is ecx+1 is beyond the bounds
Rem            - no return value; does not change any registers (not even eax)
            - with 2-dimensional arrays, Insert My$(1, 2) etc is possible but check carefully the result,
                especially if you need to use Insert more than once
Key            ins



Delete
            Delete My$(5)            ; move My$(6) down to replace My$(5)
            Delete My$(5), 2            ; move My$(7) down, delete also My$(5+6)
            mov ecx, 3                        ; regs are allowed but not eax or edx
            Delete My$(5), ecx            ; move My$(8) down to replace My$(5), delete also My$(6+7)
            Dim MyDw() As DWORD            ; Insert and Delete work with autoexpanding numerical arrays
            Delete MyDw(99)            ; triggers runtime error is ecx+1 is beyond the bounds
Rem            - no return value, all regs unchanged
            - to delete an array element without moving the other elements, use Clr$ My$(5)
            - deleting the last element, e.g. with Delete My$(My$(?)), triggers autoexpansion of the array,
                i.e. the new array will have (nold+128) and 4095 elements
            - with 2-dimensional arrays, Delete My$(1, 2) etc is possible but check carefully the result,
                especially if you need to use Delete more than once
            - to delete a file, use Kill "MyFile.dat"
Key            del



GuidFromString
            .data
            CLSID_InternetExplorer            GuidFromString("0002DF01-0000-0000-C000-000000000046")            ; with quotes
            IID_IWebBrowser2            GuidFromString(D30C1661-CDAF-11D0-8A3E-00C04FC9E26E)                        ; plain
            IID_IWebBrowser2a            GuidFromString({D30C1661-CDAF-11D0-8A3E-00C04FC9E26E})            ; registry format
Rem            use in .data, .const or .code section, as replacement for MyVar GUID



GuidsEqual
            ; compare two GUIDs for equality - for use with OLE
            .if GuidsEqual(offset IID_IUnknown, IID_IUnknown)            ; offset, direct
                        PrintLine "EQ"
            .else
                        PrintLine "NE"
            .endif

            .if GuidsEqual(IID_IOleObject, IID_IUnknown)                        ; 2*direct
                        PrintLine "EQ"
            .else
                        PrintLine "NE"
            .endif

            mov eax, offset IID_IOleClientSite                                    ; one offset in reg32
            .if GuidsEqual(eax, IID_IOleObject)
                        PrintLine "EQ"
            .else
                        PrintLine "NE"
            .endif
Rem            returns Zero?, trashes xmm0 but no other register




CoInvoke
            mov edi, offset WebInterface
            lea edx, vEmpty
            CoInvoke [edi], IWebBrowserVtbl.Navigate, Ole$("www.google.com"), edx, edx, edx, edx
Rem            for use with COM



Ole$ aka BSTR
            mov ecx, Chr$("Just a test")
            For_ esi=0 To 4
                        wPrint wStr$("%i [", esi+1), Ole$(ecx), "]", wCrLf$
            Next
            wPrint "[", Ole$("another test"), "]", wCrLf$
            wInkey wChr$(13, 10, "["), Ole$(ecx), " again]"
Rem            returns a BSTR in eax; the string is not permanent, since it uses MasmBasic's fat circular buffer. Advantage: There is
            no need to free it. However, avoid using it in a loop that might eventually overwrite the string's location:
                ; mov edi, Ole$("will never get overwritten")            ; immediate string would be safe because it uses the data section
                mov edi, Chr$("trashed after ca. 1500 loops")            ; the ANSI source string
                mov edi, Ole$(edi)                        ; translates an ANSI string to a BSTR
                For_ esi=0 To 1505                        ; Str$ uses the circular buffer, and will eventually return to edi
                        wPrint wStr$("Testing the limits of MasmBasic's circular buffer: %i [", esi+1), edi, "] ", wCrLf$
                Next
                wPrint "Now what happened to edi? Here it is: [",    edi, "] ", wCrLf$




wData
wChr$
Chr$
            mov ebx, "A"
            Print CrLf$, Chr$("The alphabet from a loop: ", ebx)
            .Repeat
                            inc ebx
                        Print Chr$(ebx+32)
            .Until ebx>="Z"
            mov ebx, 60
            Print Chr$(13, 10, "This is ", ebx, "great", ebx+2)            ; <great>
            w
Print #1, wChr$(13, 10, "This is Unicode"")                        ; to files or the console (see wPrint for details)
            mov eax, Chr$("This is an ANSI string")                        ; you can use a pointer to an ANSI string...
            wPrint wChr$("Not true: "), wChr$(eax)                                    ; ... to print a wide string with wChr$(reg32)
            wData MyWide$, "This is Unicode", 0                                    ; use in code section, access as mov eax, offset MyWide$
Rem            returns pointer in eax
Key            c$(




Len, wLen
            mov ebx, Len(My$)
            mov eax, Len("123")                        ; Len returns 3 in eax, so Olly will show mov eax, eax
            void Len("123")                        ; put the result into eax (void avoids the mov eax, eax)
            mov eax, wLen(MyUnicode$)                        ; wideLen for Unicode strings
            void wLen(wChr$("123"))                        ; returns 3 chars in eax
Rem            returns length in eax; bytes for ANSI, chars for Unicode strings




MbCopy, Bmove
            invoke MbCopy, ptrDest, ptrSource, ctBytes
            Bmove ptrSource, ptrDest [, ctBytes]
Rem            - returns a pointer to the end of the destination in eax (for fast string concatenation)
            - Bmove is identical but uses like Masm32 szCopy the inverted src to dest logic; ctBytes can be omitted
            - for ctBytes=-1, MbCopy calculates the length of the source; it thus does the same as lstrcpy
                but is over twice as fast; a 1k copy needs 1080 cycles on a Celeron M (lstrcpy 2200, szCopy 2100)
            - for ctBytes=-2, MbCopy calculates the length of the source but does not include the zero delimiter when
                copying; use this for cat$ situations but note that the last element must have ctBytes=-1.




MsgBox, wMsgBox
            MsgBox 0, "MasmBasic is cute", "Welcome:", MB_YESNOCANCEL
            Dim My$(9)                                    ; create a string array
            ArrayFill My$(), "Ciao"                        ; set all strings to Ciao
            Let My$(9)="Good morning"                        ; make an exception for #99 ;-)
            MsgBox 0, Cat$(My$(3)), My$(9), MB_OK
            wArrayFill My$(), "Ciao as Unicode"                        ; reset all strings to Unicode Ciao
            wLet My$(9)="Good morning as Unicode:"            ; different exception...
            wMsgBox 0, wCat$(My$(3)), My$(9), MB_OK
            wMsgBox 0, wCat$("Now:"+wCrLf$+wDate$+", "+wTime$), "Unicode is easy:", MB_OK
Rem            returns result in eax; ecx is preserved
Key            mb, wmb



Clr
            Clr MyVar1            ; clears local and global dword variables
            Clr MyVar1, MyVar2            ; multiple arguments allowed
            Clr eax, MyVar1, MyVar2            ; the first one may be a register
Rem            if a register is specified as first of several arguments, code size will be smaller, and the register will be zeroed, too
Key            clr



Clr$
            Clr$ My$(123)            ; clear an array element
            Clr$ My$                        ; clear a string (free heap mem, assign Null$)
Rem            no return value
            Delete removes an array element, or assigns zero to a string (and frees heap mem, too)




ClearLocals
            MyTest proc uses edi esi ebx arg1:DWORD, arg2:RECT
            LOCAL v1, v2, rc:RECT, buffer[100]:BYTE
                ClearLocals            ; first line after the LOCALs
                MsgBox 0, Str$("The value of v1: %i", v1), "Test clv:", MB_OK
                ret
            MyTest endp
Rem            very fast and compact (5 bytes per call), leaves all registers intact           
Key            clv



StackBuffer
            MyTest proc uses edi esi ebx arg1:DWORD, arg2:RECT
            LOCAL rc:RECT, sbuf1, sbuf2, sbuf3, whatever[100]:BYTE
            ; optional: ClearLocals            ; first line after the LOCALs
                mov sbuf1, StackBuffer(100000)            ; allocate two fat buffers, and make sure
                mov sbuf2, StackBuffer(4000h)            ; they are 16-byte aligned for use with SSE2
                invoke GetFileSize, hFile, 0            ; you may use a register or any other variable to specify the buffer size
                mov sbuf3, StackBuffer(eax, nz)            ; option nz means "no zeroing" - much faster (the buffer end is zeroed anyway)
                PrintLine "Start buffer 1:", Tb$, Hex$(sbuf1)
                PrintLine "Start buffer 2:", Tb$, Hex$(sbuf2)
                StackBuffer()            ; release all buffers (sb without args = free the buffer)
                ret
            MyTest endp
Rem            - buffer size is limited by start address of stack; normally, you can use close to one MB
            - the start address is aligned to 64 bytes for use with SIMD instructions



            - you can use StackBuffer anywhere (not only at proc start & end), but make sure esp is unchanged
            - StackBuffer zero-inits the buffer, unless option nz is specified (much faster)
            - with option nz, only the end of the buffer (+/- 2 bytes, one DWORD) is zeroed
            - can be combined with ClearLocals
            - StackBuffer does the stack probing for you; up to about half a megabyte, it is significantly faster than HeapAlloc




Instr_, Rinstr, wInstr
            Print "The current drive is ", Left$(ThisExe$, Instr_(ThisExe$, "\")-1)
            mov pos, Instr_(1, L$(n), "equ", 1+4)            ; 1=start pos, 1=case-insensitive + 4=full word
Rem            - returns relative pos in edx, absolute in eax
            - five syntax variants allowed:
                A: has startpos:            Instr_(1, "Test", "Te", 2)            ; 4 args
                B: no startpos:            Instr_("Test", "Te", 2)            ; 3 args, last one immediate = mode
                C: has startpos:            Instr_(1, "Test", "Te")            ; 3 args, last one not immediate
                D: no startpos:            Instr_("Test", "Te")                        ; 2 args
                E: fast & case-sensitive:            Instr_(1, "Test", "Te", FAST)            ; 4 args, last one is uppercase FAST
            - case & mode (bitwise flag):
                0=case-sensitive, +1=insensitive, +2=intellisense (Name=name),
                +4=full word search, +8=include start of line in text block search
                Note: full word search returns failure for chars above ASCII 64 to the left or right
                Example: Nostructfoundhere, this123struct456isfound, this@struct, too
            - wRinstr is available but only as wRinstr(src, pattern) with a one-byte pattern (e.g. "\")

Key            instr(



Count, wCount
            Let esi=FileRead$("\Masm32\include\Windows.inc")                            ; Create a buffer for Windows.inc
            Print Str$("%i chars read\n", Len(esi))
            Print Str$(" Count equ=\t%i\n\n", Count(esi, Chr$("equ"), 1))                ; Count equ (1=case-insensitive)
            .if Exist("\Masm32\include\WindowsWide.inc")                            ; to test wCount, open the ANSI version in an editor and save it as UNICODE
                        wLet esi=FileRead$("\Masm32\include\WindowsWide.inc")             ; Let or wLet are the same for a plain FileRead$()
                        Print Str$("%i chars read, including BOM", wLen(esi)), Str$(", filesize=%i\n", LastFileSize)
                        Print Str$("wCount equ=\t%i\n", wCount(esi, wChr$("equ"), 1))
            .endif
Rem            - counts occurrences of a pattern
            - returns counter in edx
            - options as in Instr_/wInstr




Extract$            extracts a substring based on left and right matches
            ; simple example:
            Let esi='This is a link to that can be extracted'
            Print "The URL for ", Extract$(esi, Chr$(34, 62), "
"), " is "            ; 34, 62 = "
            Inkey Extract$(esi, "http://", Chr$(34), xsIncL)                                    ; you could use '"' (single/double/single quote) instead of Chr$(34)
            ; result: The URL for Google is http://www.google.com

            ; syntax: Extract$(pSrc, "left match" [, "right match"] [, xsFlags] [, maxlines*)] [, startIndex])
            ; right match: if omitted, end of line is assumed
            ; xsFlags: if omitted, search for left match is case-sensitive, left and right matches are excluded
            ; maxlines: default is 1, i.e. right match must be in same line; for extracting structures etc, put a reasonable value, e.g. 100
            ; startIndex: default is 1, i.e. beginning of string; if xsLoop is set, search for the left match restarts where the last right match
            was found; see also the options for Instr_ - Extract$ uses Instr_ for the left match
            xsCaseI            ; case-insensitive search for left match (right: always case-sensitive)
            xsIs                    ; intellisense; search is case-insensitive for 1st char only, i.e. Hello = hello
            xsI1c            ; ignore 1st char in left match, e.g. ?:\Masm32\...
            xsFullW            ; full word (left match only)
            xsIncL            ; include left pattern (e.g. http://)
            xsIncR            ; include right pattern
            xsExcL            ; exclude left pattern, e.g. {url= ... }
            xsExcR            ; exclude right pattern
            xsTrimL            ; trim left side, i.e. strip everything <=ASCII 32 (spaces, tabs, CrLf$...)
            xsTrimR            ; trim right side (after excluding right match if xsExcL is set)
            xsTrim=xsTrimL or xsTrimR            ; trim both sides
            xsTrim39            ; trim everything <=ASCII 39 aka single quote
            xsLineL            ; include line of the left match
            xsLineR            ; include rest of line after the right match (must include right match...)
            xsScan            ; scan line sequentially for e.g. spaces; left match must equal right match
            xsLoop            ; let Instr_ start behind the last position, for use in loops
            *) if the right pattern contains a linefeed (LF, Ascii 10), the maxlines counter will never stop the pattern search
            ----------------------------------------------------------
            The last flag, xsLoop, is used in the following demo, a Windows console application that extracts
            all structures from the two main Masm32 include files. Do not use the result for work, as there are
            problems with nested structures (e.g. unions ending with ends) and some structures ending with
            lowercase ends.
           
include \masm32\MasmBasic\MasmBasic.inc            ; download
            Init
            ; First, let's get some useful source string:
            Let ecx=FileRead$("\Masm32\include\Windows.inc")+FileRead$("\Masm32\include\WinExtra.inc")
            Open "O", #1, "Structures.txt"            ; open a file for output
            xor ebx, ebx            ; reset counter
            .While 1
                        inc ebx
                        .Break .if !Extract$(ecx, "STRUCT", 'ENDS', xsFullW or xsLineL or xsIncR or xsLoop, 100)
                        Print #1, eax, CrLf$
            .Endw
            Close #1            ; file #1 closed
            Inkey Str$("%i structures found\n", ebx)
            Exit
end start

Rem            - returns pointer in eax, and len of result in edx
            - can be used with Print and Let even if no match found, i.e. eax=0; in this case, Extract$ will print as ?
Key            ex$




StringsDiffer
            .if StringsDiffer("Hello", "Hallo")
                        MsgBox 0, "The two strings are different", "Hi", MB_OK
            .endif
            test eax, StringsDiffer("Hallo", "HALLO", 1)
            .if Zero?
                        MsgBox 0, "The two strings are equal", "Hi", MB_OK
            .endif
Rem            - returns byte difference as DWORD in eax, and relative position in edx
            - you may get a "please split" error message, e.g. with
                .if StringsDiffer(Str$(esi), Str$(edi))
                The reason for the error is that Str$() uses eax as return value - in both
                cases, therefore they would always seem "equal" (eax==eax). To avoid this,
                split the line by using a global var or a free register (not edx or eax) as follows:
                mov ecx, Str$(esi)
                .if StringsDiffer(ecx, Str$(edi))




FilesDiffer
            .if FilesDiffer("MyFileA.txt", "MyFileB.txt")
                        MsgBox 0, "The two files are different", "Hi", MB_OK
            .endif
            Let esi="MyFileA.txt"
            test eax, FilesDiffer(esi, offset MyFileB)
            .if Zero?
                        MsgBox 0, "The two files are equal", "Hi", MB_OK
            .else
                        MsgBox 0, "The two files are different", "Hi", MB_OK
            .endif
Rem            - returns DWORD in eax
            - you may get a "please split" error message, e.g. with
                .if FilesDiffer(Files$(esi), Files$(edi))
                The reason for the error is that Files$() uses eax as return value - in both
                cases, therefore they would always seem "equal" (eax==eax). To avoid this,
                split the line by using a global var or a free register (not edx or eax) as follows:
                mov ecx, Files$(esi)
                .if FilesDiffer(ecx, Files$(edi))




Left$, wLeft$
            Let My$(123)=Left$("A little test", 8)
            Let Left$(My$(123), 5)="A small "            ; valid also for Mid$, Right$
            mov eax, z$(Left$("Test", 3))
Rem            for use with Let and Print; if you need the mov version, use z$()
Key            left$(



Mid$, wMid$
            Let My$(123)=Mid$("A little test", 3, 6)
            mov eax, z$(Mid$("Test", 2))
Rem            for use with Let and Print; if you need the mov version, use z$()
Key            mid$(



Right$, wRight$
            Let My$(123)=Right$("A little test", 4)
            mov eax, z$(Right$("Test", 3))
Rem            for use with Let and Print; if you need the mov version, use z$()
Key            right$(



Lower$, wLower$
Upper$, wUpper$
            Print Lower$("This is a test"), CrLf$, Upper$("This is a test")
            mov esi, z$(Upper$(eax))
Rem            - returns pointer to converted copy of string in eax
            - for use with Let and Print; if you need the mov version, use z$()
            - example for Unicode message boxes:
                wLet esi="Hello, What Is The Purpose Of This String?"
                wMsgBox 0, wCat$(wLower$(wLeft$(esi, 5))), "hello", MB_OK
                wMsgBox 0, wCat$(wUpper$(wMid$(esi, 8, 4))), "WHAT", MB_OK
                wMsgBox 0, wCat$(wLower$(wRight$(esi, 7))), "string?", MB_OK
            - attention Left$(Lower$(...)) will throw a "not allowed" error; use Lower$(Left$(...))
Key            lower$(, upper$(



Trim$
Ltrim$
Rtrim$
Qtrim$
            Print CrLf$,        "                |                Trim a string                |",13, 10
            Print "LTRIM$: |", Ltrim$("                Trim a string                "), "|",CrLf$
            Print "RTRIM$: |", Rtrim$("                Trim a string                "), "|",CrLf$
            Print "TRIM$:    |",    Trim$("                Trim a string                "), "|",CrLf$, CrLf$
            Let My$=Trim$("    Test ")
            Let My$=Trim$(Chr$(9, "    Test ", 13, 10))
            Let My$=Qtrim$(Chr$(9, '    "quoted"    ', 13, 10))            ; removes "double quotes"
            Let My$=Qtrim$(CL$())                        ; same for complete quoted commandline
Rem            - for use with Let and Print
           
- removes trailing spaces, tabs and other characters below Ascii 32
            - old Gfa Ztrim$ syntax possible but has no effect
Key            trim$(



Mirror$
            cmp eax, Mirror$("Masm")                        ; easier than cmp eax, "msaM"
            PrintLine Mirror$("Masm32 is great")                        ; uses an entry in the .data section
            mov edx, Mirror$("Test")
            Let esi=Mirror$("Masm32 is great")
            PrintLine esi                        ; shows            taerg si 23msaM
            Let esi=Mirror$(esi)
            PrintLine esi                        ; back to            Masm32 is great
Rem            - with immediate args, Mirror$ returns an immediate string, and does not by itself create any .data entry
            - Mirror$(esi) returns a pointer to a temporary buffer that can be used with Let and Print




Date$, wDate$, CrtDate$
            Print "Today is the ", Date$
            Print "This file was created on ", CrtDate$
Rem            - by default, returns in eax a string in format dd.MM.yyyy, e.g. 31.12.2013
            - except for CrtDate$, you can change this format e.g. with
                MbDateDmy equ "MMMM dd, yyyy" to get August 02, 2013 (see MSDN GetDateFormat for details)
            - with MbDateDmy equ esi (or any other reg32) you can programmatically specify a different format




Time$, wTime$, CrtTime$
            Print "Now it is ", Time$
Rem            - by default, returns in eax a string in format HH:mm:ss, e.g. 13:45:56
            - except for CrtTime$, you can change this format e.g. with
                MbTimeHms equ "hh:mm tt" to get e.g. 03:45 PM (see MSDN GetTimeFormat for details)
            - with MbTimeHms equ esi (or any other reg32) you can programmatically specify a different format




fDate$(), fTime$(), wfDate$(), wfTime$()
            ; formatted date and time; takes a pointer to a SYSTEMTIME structure and an optional format string
            Print "HKCU\Software\...\Explorer entries changed last week:"
            GetRegKeyArray "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer", Explorer$(), LastMod()
            For_ ecx=0 To eax-1
                        .if Age(LastMod(ecx), h)<=7*24
                                    Print Str$("\nKey %i\t", ecx), fDate$(LastMod(ecx), "dd MMMM yyyy"), ", ", fTime$(LastMod(ecx))
                                    Print Tb$, Explorer$(ecx)
                        .endif
            Next
            ; if the registry value is of type REG_QWORD, it might be a FILETIME; you can display it as follows:
            void GetRegVal("HKLM\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0", "LastInstallTime", 0)
            ; if this key and its value exist, then the REG_QWORD (a FILETIME) will be returned in xmm0:
            PrintLine "LastInstallTime: [", fDate$(xmm0, "dd MMMM yyyy"), ", ", fTime$(xmm0), "]"
            wPrint "LastInstallTime: [", wfDate$(xmm0, "dd MMMM yyyy"), ", ", wfTime$(xmm0), "]"           
Rem            for use with SYSTEMTIME structures or arrays, e.g. those filled by the second arg of GetRegKeyArray()



Timer
            push Timer                ; put current mlliseconds on stack           
            Delay 500                ; make a little pause of 500 ms
            sub Timer, [esp]            ; subtract from eax the value on stack (eax is Timer's retval)
            pop edx                                    ; correct the stack
            Print Str$("\nTime elapsed: %i\n", eax)
Rem            returns DWORD in eax; uses GetTickCount but preserves ecx



NanoTimer
            NanoTimer()            ; start timing without arguments
            Delay 500                        ; simulate a loop...
            Print Str$("Time elapsed: %4f seconds\n", NanoTimer(s))                        ; s, ms or s
            NanoTimer()
            Delay 15
            Print "The Delay 15 took ", NanoTimer$()            ; returns e.g. 15 ms, i.e. a string with a unit adapted to the result
Rem            returns DWORD in eax; uses QPC, the effective resolution is about 0.3 microseconds



Recall                                    ; recall an array from file
            Recall "\masm32\include\winextra.inc", MyRec$()            ; translate file input to an array of strings
            mov lc, Min(eax, 20)
            Print Str$("%i lines found", lc)
            For_ n=0 To lc-1
                        Print Str$("\nRec %i\t", n)
                        Print Left$(MyRec$(n), 50)
            Next

            Recall "MyFile.csv", MyRec$(), csv                        ; loads a spreadsheet in comma separated values format
            Csv2Tab                                    ; optional: replace commas with tabs (needed for QSort by column)
            Recall "MyFile.txt", MyRec$(), tab                        ; loads a spreadsheet in tab-delimited format
            Dim MyFS(99) As FLOATSTRUC                        ; we define an array with 100 strucs 40 bytes = 4000 bytes
            Open "I", #1, "StrucStore.dat"
            Recall #1, MyFS()                        ; load all previously stored values (example)
            Recall #1, MyWc                        ; fill a WNDCLASSEX from file
            Close
Rem            - returns lines in eax (null if an error occurred), and the number of total bytes read in edx
            - see Store below for saving arrays
            - note the tab and csv versions do not allow direct string concatenation with Print or Let:
                PrintLine "fails: A2=", MyCellArray$(1,0), Tb$, "A3=", MyCellArray$(2, 0)            ; fails miserably showing twice the same cell
                Print "works: A2=", MyCellArray$(1,0), Tb$, "A3="            ; workaround: separate the two ...
                PrintLine MyCellArray$(2, 0)                                    ; ... cells by printing over two lines
            - there is no wRecall, but if Unicode is detected, Recall converts strings to UTF8; use Print to display them
            - with the spreadsheet variants, single cells can be accessed via Let My$=MyRec$(row, column);
                for usage, see the spreadsheet demo
            - Gfa syntax (Recall #3, Url$(), -1, MyLineCounter) works, but -1 will be ignored, and #3 will be closed
Key            Rec



Store
            Store "MyWin1.inc", MyRec$()                        ; store the whole array excluding trailing empty strings
            Store "MyWin1.inc", MyRec$(), 1000                        ; store the first 1000 strings of the array
            StoreUtf8 "MyWin1.inc", MyRec$(), 1000            ; store the first 1000 strings of the array

            Open "O", #1, MyFileName$
            Store #1, MyWc                        ; store a WNDCLASSEX from the .data? or .data segment
            Store #1, MyRc()                        ; store an array of RECT structures (->Dim)
            Store #1, MyRec$(), 20                        ; store the first 20 strings
            Store #1, MyOtherRec$()                        ; add the complete array not including trailing empty strings
            Close #1
Rem            - will trigger runtime error if file cannot be opened
            - if a file name is being used, the file will be closed after writing the strings
            - for #n, the file remains open for writing, allowing e.g. to write several arrays to the same file
Key            sto



QSort, QSortDesc
            Recall "\Masm32\include\Windows.inc", L$()            ; load wininc into a string array
            mov ecx, eax                        ; save the linecount
            shr eax, 2                                    ; just for fun, we sort only ...
            QSort L$(), eax                        ; ... one quarter of the strings
            Open "O", #1, "SortedAscending.txt"
            xor esi, esi
            For_ n=0 To ecx-1
                        .if Len(L$(n)) && esi<5000                        ; we skip the empty strings, and write the first 5000
                                    Print #1, L$(n), CrLf$
                                    inc esi
                        .endif
            Next

            Print Str$("%i lines written to SortedAscending.txt\n", esi)
            QSortDesc L$(), 0, sls                        ; now sort descending, all, skip leading spaces
            Store "SortedDescending.txt", L$()                        ; and write them back to file
            ; spreadsheet mode - you can sort tab-delimited files by column:
            Recall "MyDataBase.tab", db$(), tab                        ; tab = tell Recall it's a tab-delimited file, Excel style
            xchg eax, ecx                        ; keep #rows read in ecx
            QSort db$(), 0, 3    [, 0]                        ; sort db$(), all rows, by column 3; [optional 0: do not skip the header row]
            QSort db$(), 20:30, 5                        ; starting with row 20, sort 30 rows, by column 3
            QSort db$(), 50:, 4                        ; sort all rows after row 50 by column 4
Rem            - returns #elements sorted
            - by default, comparisons are case-insensitive; to make sorts case-sensitive, use
                QSortMode casemode, skipmode
                where casemode can be 0 (=case-sensitive), 1 or cis, and skipmode can be 0 (=don't skip), 1 or sls. Examples:
                QSortMode 0, sls            ; case-sensitive, skip leading spaces
                QSortMode cis, 0            ; case-insensitive, don't skip leading spaces
            - when sorting by column, the header row is skipped by default; use 0 after the column to override this
            - csv files can be sorted by column only if you use Csv2Tab directly below Recall .., csv
            - sorting by column and skip leading spaces (sls) mode cannot be combined
Key            qst




ArraySort
            mov ebx, 1000                        ; we want 1000+1 elements (arrays are zero-based, 0...1000=1001)
            Dim MyR4(ebx) As REAL4                        ; numerical arrays only; for strings, use QSort
            Dim MyDW(ebx) As DWORD
            Dim MyR8(ebx) As REAL8
            Dim MyQW(ebx) As QWORD
            Dim KeyArr(ebx) As DWORD
            .Repeat
                        Rand(-888.888, 999.999)                        ; fill the Real4 array with random numbers between -888 and +999
                        fstp MyR4(ebx)
                        mov MyDW(ebx), Rand(1000)                        ; same for the dword array, numbers between 0 and 1000
                        mov KeyArr(ebx), ebx                        ; we may need to know the original position before sorting
                        dec ebx
            .Until Sign?
            lea edi, MyDW(0)                        ; get the start address of the dword array
            ; ArraySort edi                        ; illegal - arrays are not zero terminated, so we must know the count
            ArraySort edi:Elements                        ; edi is pointer to a dword array; Elements is # of dwords
            lea edi, MyR4(0)                                                                        ; get the start address of the Real4 array
            ArraySort REAL4 ptr edi:Elements                        ; same but with single floats array (and we must declare them)
            ArraySort MyR4()                        ; sort a MasmBasic Real4 array ascending
            ArraySort MyR4(+)                        ; same, the + is optional
            ArraySort MyR4(+:123)                        ; same but first 123 elements only
           
mov ecx, 123
            ArraySort MyR4(+:ecx)                        ; same but using a register (or any other dword variable)
            ArraySort MyR4(-)                        ; sort a MasmBasic Real4 array descending
            ArraySort MyR4(-:123)                        ; same but first 123 elements only
            lea esi, KeyArr(0)                        ; load start address of an array containing keys (e.g. original position)
            ArraySort MyR4(-), esi                        ; sort Real4 array descending, keep key values with Real4 values
            ArraySort MyR4(+), KeyArr(), fill                        ; use key array directly, fill with original unsorted order (0, 1, 2, ... n)
Rem            - returns #of sorted elements in eax
            - use for signed DWORD, signed QWORD, REAL4 and REAL8 arrays; for strings, see QSort
            - ArraySort sorts ascending if no - is found in the first argument. In case you need to determine the order based on
                a runtime parameter, you need to use the invoke syntax as follows:
                        MbArrSort PROTO :DWORD, :DWORD, :DWORD, :DWORD
                        invoke MbArrSort, ptr to first element, #elements, ptr to key array, mode
                        with mode=size (4, 8) or (32 and real) or (64 and ascending) or (1 and MinMaxOnly) or (2 and fill the key)
            - uses a very fast algo inspired by Marwin's site, often faster than QuickSort (and much faster than the crt qsort)
            - Real4 and Dword use the same algo; the only difference is that the Real4 variant applies an extra
                pass to invert the order of negative elements. Speedwise there is no measurable difference



ArrayMinMax
            Dim
MyR4(9) As REAL4
            ArrayMinMax MyR4()                        ; min in eax, max in edx - both as "pushable" REAL4
            push edx
            push eax
           
ffree st(7)
           
fld REAL4 ptr [esp]
            Print Str$("The Real Min=\t%Df\n", ST(0))
            pop edx
            ffree st(7)
           
fld REAL4 ptr [esp]
            Print Str$("The Real Max=\t %Df\n", ST(0))
            pop edx
            fstp st
            fstp st
            ArrayMinMax MyR4(XY)                        ; uppercase XY: the array consists of x,y coordinates
            deb 1, "MinMax for X:", ST(0), ST(1)                        ; y minmax on FPU, y minmax returned as shown above
            Dim MyDw(9) As DWORD
            ArrayMinMax MyDw()                        ; uses ArraySort syntax
            push edx
            Print Str$("The dw Min=\t%i\n", eax)
            pop edx
            Print Str$("The dw Max=\t %i\n", edx)
Rem            - returns minimum in eax, maximum in edx for DWORD arrays and REAL4 arrays
            - returns minimum in xmm0, maximum in xmm1 for REAL8 array
            - with XY, x and y minmax values are returned separately: the x values will be on the FPU



ArrayMean
                                    ArrayMean MyR8()                        ; all array elements are added up, then the sum is divided by the count
                                    Print Str$("Resall=\t%f\n", ST(0))            ; the resulting mean is left on the FPU
                                    fstp st
                                    ArrayMean MyR8(XY)                        ; same as before but we assume the array consists of x,y pairs
                                    Print Str$("ResY=\t%f\n", ST(0))            ; the mean of the Y elements is returned in ST(0)
                                    fstp st
                                    Print Str$("ResX=\t%f\n", ST(0))                        ; the mean of the X elements is returned in ST(1)
                                    fstp st
Rem            returns mean(s) on the FPU and total number of elements in eax




ArrayRead
            Dim rc4() As REAL4
            ArrayRead rc4(), "MyReal4.dat"           
Rem            returns #elements in eax



ArrayStore
            ArrayStore #1, rc4()
Rem            writes array to disk; same as Store #1, rc4()




ArrayLoadMap
            ArrayLoadMap 0, "Europe"
Rem            requires map in *.map/*.dmi format; PM the author for details




ArrayPlot
            CASE WM_CREATE
                        Dim Sinus() As REAL8            ; example: in the CREATE handler,
                        xor ecx, ecx            ; we fill an array with sinus values
                        push 180
                        fild stack
                        push ecx
                        fldpi
                        fdivr
                        fstp REAL4 PTR stack[4]
                        .Repeat
                                    mov stack, ecx
                                    fild stack
                                    fmul REAL4 PTR stack[4]
                                    fsin
                                    fstp Sinus(ecx)
                                    inc ecx
                        .Until ecx>500            ; a few hundred are enough
                        pop ecx
                        pop eax
            CASE WM_PAINT            ; in the PAINT handler, we plot it onto a static control
                        ArrayPlot hStatic, RgbCol(200, 255, 240)            ; init with window (or control) handle and background colour
                        ArrayPlot Sinus()
                        ArrayPlot exit, "Sinus"            ; finish with a title

Rem            no return value
Key            apl




ArraySearch
            ; mov eax, ArraySearch(pSrc, sizeof Src, pattern [, size])                                    ; src, len(src), pattern, byte/word/dword
            Print Str$("Len %i\n", ArraySearch(esi, sizeof Src, 0, BYTE))                        ; look for a nullbyte
            mov eax, dword ptr bins                                                            ; where bins is db "al", 0, 0
            Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, eax, WORD))                        ; word given in eax
            Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, 6c61h, WORD))                        ; immediate word
            Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, "al", WORD))                        ; same but as string
            Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, MyPattern, word))            ; taken from mem, i.e. "al" is MyPattern dd "la"
            Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, "algo", DWORD))            ; dword as string
            Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, 'algo'))                                    ; same, dword assumed
            Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, "olgo", DWORD))            ; this one won't be found
Rem            - returns offset into first occurrence of pattern: if src is "This is my algo", then
                pattern algo as dword will not be found (eax=-1), but for "This was my algo", pos 12 would be returned
                pattern my as word will be found at pos 8
                pattern i as byte will be found at pos 2
            - ArraySearch uses SSE2 and is fast: it searches a 400 byte string more than four times
                as fast as repne scasd and 5 times as fast as BinSearch
            - by specifying zero as a byte pattern, you can use ArraySearch as a Len() substitute;
                however, MasmBasic Len() is twice as fast
            - do not confuse with Masm32 BinSearch, which finds patterns at "odd" positions, too, i.e.
                in the example above, invoke BinSearch, 0, chr$("This is my algo"), 16, chr$("al"), 3 would
                return a valid position 11



VarPtr
            Dim MyR8(5, 6) As REAL8
            mov eax, VarPtr(MyR8(3, 3))            ; get the address of element 3, 3
            fld REAL8 ptr [eax]
Rem            - returns DWORD in edx
            - for string arrays use mov eax, My$(123) instead



For_
            mov ebx, 2000                        ; note it is not necessary to define the
            For_ n=0 To ebx-1000                        ; counter (here: n ) in the data section
                        Print Str$("\nLoop %i", n)
                        .Break .if n==50
            Next n


            mov ebx, 200
            xor ecx, ecx
            For_ n=5 To ebx-1
                        inc ecx
                        .Break .if ecx==123
            Next
Rem            - if begin>end, the loop will not be started; use MbForReject=0 to change this
            - in case you have too many For_ ... Next loops, MbForMax can be increased
            - For_ n=0 To eax-1 is valid code; you can nest several For_ loops, but you must use different counters.
Key            for_



Str$, wStr$
            Print Str$(ebx)                        ; simple
            Print Str$(MyReal10)                        ; works for most kinds of arguments
            Print Str$(ebx*MyReal8+123.0e45)                        ; multiply two arguments, add an immediate float
            MsgBox 0, Str$("Profits increased by %3f per cent", 103.45/100-1*100), "Three digits precision:", MB_OK
            Print Str$("The number PI is %Hf", PI)                        ; precision H = 17 digits (I=18 works fine, J may yield rubbish)
            Print Str$("The number 19 is %__i", 19)            ; integers only: two understrokes to get one leading space with a two-digit number
            Print Str$("The number 9 is %000i", 9)            ; integers only: 3 zeros to get four leading zeros with a one-digit number
            mov eax, Val(Str$(12*34-8))                        ; slow and not very elegant, but it works
            Print "Real4: ", Str$(MyR4)                        ; Real4 variable
            Print "qWord: ", Str$(MyQword)                        ; print a qword; see also edx::eax in remarks
            mov eax, 31416                        ; you can mix xmm registers with FPU and ordinary registers and
            movd xmm0, eax                        ; directly print the result
            fldpi                                                ; load 3.14159 onto the FPU
            mov ecx, 123                        ; just for fun;    \n is CrLf, \t is tab in Str$()
            Print Str$("\nXmm0=\t%f", xmm0/ST(0)*ecx)            ; output: [newline] Xmm0=    1230003.0
            fstp st                                                ; cleanup the FPU
Rem            - returns address to buffer; default precision is 7 digits
            - if a string is given as input, %i (integer) or %f (float) mark its insertion point, and %9f its precision
            - Warning: Str$() takes up to 5 numeric arguments. However, they are processed
                in order of appearance, i.e. Str$("Expected: 5+6*7-8*9=-25, result=%i", 5+6*7-8*9)
                yields 621: 5+6=11, *7=77, -8=69, *9=621; workaround: do multiplications first.
            - Str$ trashes the FPU registers ST(6) and ST(7); in case you have valuable data on the FPU and do not want to
                trash it, use a FpuSave/Str$/FpuRestore sequence
            - you can also print qwords moved into the edx::eax pair as follows:
                mov eax, 123456789            ; some code that generates an edx::eax pair
                mov ecx, eax
                mov edx, 1000000000
                mul edx
                Print Str$(edx::eax+ecx)
           
Key            s$(



FpuSave, FpuRestore
            FpuSave 3            ; save three FPU regs, i.e. store ST(5) ... ST(7) on the stack
            ... do FPU calculations - stack pointer must be the same afterwards ...
            FpuRestore            ; get ST(5) ... ST(7) back, correct the stack
            FpuSave            ; no args means save 2 regs: ST(6) and ST(7)
            Print Str$("A test with eax=%i", eax)            ; Str$ trashes ST(6) and ST(7)
            FpuRestore            ; get ST(6) and ST(7) back, correct the stack
Rem            returns nothing, trashes nothing, but is e.g. for 4 saved FPU regs about 10 times faster than fsave/frstor



FpuPush
            FpuPush 123            ; pushes 123 onto ST(0)
            FpuPush FP8(123.456)            ; pushes a REAL8 value onto ST(0)
MyQ            dq 1234567890123456789
            movlps xmm1, MyQ
            FpuPush xmm1            ; pushes the QWORD in xmm1 into ST(0)
            movlps xmm0, MyR8
            FpuPush f:xmm0            ; pushes the DOUBLE in xmm1 into ST(0)
            Inkey Str$("\nOn FPU:\t%If", ST(0))
            FpuPush f:xmm1
Rem            does not trash any reg32; valid args as in Str$()



FpuFill
            FpuFill            ; no args means push 1001...1008 on the FPU
            FpuFill 5            ; push 1001...1005
            deb 4, "FPU 5:", ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7)
Rem            for testing purposes; does not trash any reg32



FpuSet
            FpuSet MbNear64            ; set full precision, rounding near
            FpuSet                                    ; no arg = set back to previous state
            FpuSet MbDown24, exall            ; set low precision, rounding down, force all exceptions except precision
Rem            - trashes only edx; uses fldcw & fnstcw with equates composed of rounding mode and precision:
                Mb[Round][Prec] with Round=Near, Trunc, Up or Down and Prec=24, 53 or 64
            - Windows initialises the FPU to MbNear53, but the Init macro sets MbNear64 (i.e. the finit default - full precision)
            - exall as second arg sets FPU exception flags to zero; since the precision flag would trigger exceptions
                all the time, exall leaves it set, so that only division by zero and similar errors are being caught
            - if you need a different set of exception flags, just use them as second arg, e.g. FpuSet MbNear64, 11010y



Exp10, Exp2, ExpE and ExpXY
            ; four exponential functions are available, for 10^y, 2^y, e^y and x^y
            Exp10(0.5, MyDest10)            ; calculates 10^0.5 and saves it to a REAL10 variable called MyDest10
            Print Str$("Res=%Jf\n", MyDest10)
            ExpXY(4, 0.5, MyDest8)            ; calculates 4^0.5 and saves it to a REAL8 variable called MyDest8
            Print Str$("Res=%Jf\n", MyDest8)            ; 4^0.5 = 2

            Print Str$("Exp10    \t%Jf\n", Exp10(0.5))            ; if no destination is specified as last arg, the result is returned in ST(0)...
            fstp st            ; ... and you should pop it when no longer needed
            Print Str$("Exp2        \t%Jf\n", Exp2(0.5))            ; 2^0.5 = 1.414
            fstp st
            Print Str$("ExpE        \t%Jf\n", ExpE(3))            ; 2.718^3 = 20.085
            fstp st
Rem            - returns result in ST(0) if no destination specified
            - non-zero edx signals infinity error
            - beware of precision problems; FpuSet MbNear64 is recommended, although the rounding mode has no
                influence when 64bit precision is set




Percent, .f4Percent, .f8Percent, .fpuPercent
include \masm32\MasmBasic\MasmBasic.inc
.data
srcr10            REAL10 12345.6789
pcr10            REAL10 12.3
srcdd            dd 123

            Init
            Print Str$("f4pc(srcr10, 33.33)=\t%If\n", f4Percent(srcr10, 33.33))                        ; f4: returns REAL4
            Print Str$("f8pc(srcr10, 33.33)=\t%If\n", f8Percent(srcr10, 33.33))                        ; f8: returns REAL8 e.g. for use with xmm regs
            Print Str$("fpupc(srcr10, pcr10)=\t%If\n", fpuPercent(srcr10, pcr10))            ; fpu: leaves result in ST(0)
            Print Str$("fpc(12345.678, ebx)=%f\n", f8Percent(12345.678, ebx))
            Print Str$("fpc(12345.6, ebx)=%f\n", f8Percent(12345.6, ebx))
            Print Str$("pc(12345.0, ebx)=%f\n", Percent(12345.0, ebx))                        ; with immediate real source and reg32 percentage
            Print Str$("pc(srcdd, ebx)=%f\n", Percent(srcdd, ebx))                                    ; with DWORD source and reg32 percentage
            Print Str$("pc(12345, 20)=%f\n", Percent(12345, 20))                                    ; with immediate integer source and percentage
            movq xmm0, f8Percent(srcr10, 33.33)
            Print Str$("xmm0=%f\n", f:xmm0)                        ; use the f: prefix to force interpretation as float
            push 0AB54A98Ch                        ; 12345678901234567890
            push 0EB1F0AD2h                        ; pushed as two dwords
            movlps xmm1, qword ptr [esp]
            add esp, QWORD
            Print Str$("xmm0=%u\n", xmm1)                        ; use %u in the format string to force interpretation as unsigned
            movd xmm0, Percent(srcr10, 33.33)
            Print Str$("xmm0=%f\n", xmm0)
            Inkey "ok"
            Exit
end start
Rem            source can be almost anything, same for percentage



DefNum
            DefNum 3
            Print Str$("PI=%f", PI)            ; print PI at 3 digits precision
            Print Str$("PI=%4f", PI)            ; print PI at 4 digits precision, i.e. override DefNum
            DefNum -1
            Print Str$("PI=%f", PI)            ; print PI at max digits precision (i.e. 18)
            DefNum 19
            Print Str$("PI=%f", PI)            ; tickle out one more digit (same as Str$("PI=%Jf", PI), may yield rubbish)
Rem            sets default precision; override with Str$("%nf", number), where n=12...ABCDEFGHI as shown above




Hex$
            Print Hex$(eax)            ; reg32, 12345678
            Print Hex$(cx)            ; reg16, 1234
            Print Hex$(dl)            ; reg8, 12 - same for dh, ah etc
            Print Hex$(123)            ; immediate
            Print Hex$(MyDword)            ; global and local variables
            Print Hex$(MyWord)
            Print Hex$(MyByte)
            Print Hex$(MyQword)            ; QWORD will be displayed with one space as 12345678 90123456
            Print Hex$(xmm1)            ; if QWORD is not enough, get e.g. 11AA22BB 33CC44DD 55AA66BB 77CC88DD
            Print Hex$(MyReal8)            ; same as QWORD
            Print Hex$(MyR4(ecx))            ; numerical arrays can be used, too
            Let H$="Hex="+Hex$(1a2b3c4dh)+"h"            ; in case you need the trailing h or a leading 0x, use Let or Cat$()
Rem            returns DWORD in edx




Bin$
            Print Bin$(123)
            Let esi=Bin$(-1)+"y"
            MsgBox 0, Bin$(ebx, f), "The Bin$", MB_OK
Rem            returns DWORD in edx - use only once in Let
            option f adds a formatted bit counter



Qcmp, Ocmp
.data            ; for testing
qSmall            qWORD 7700000000000001h
qBig            qWORD 7700000000000003h
oSmall            OWORD 77000000000000000000000000000001h
oBig            OWORD 77000000000000000000000000000003h            ; OWORD for JWasm and higher ML.exe versions
; oBig            qWORD 00000000000000003h, 7700000000000000h            ; 2 QWORDS for ML 6.15
.code
            Qcmp qBig, qSmall            ; compare two global variables
            mov ecx, offset oBig            ; use a pointer (not edx, please) ...
            Ocmp ecx, oSmall            ; ... for one (or both) of them
            oqDeb=1            ; if this flag is set, Qcmp or Ocmp will print e.g.
                                    "ecx GREATER xmm0" or "MyOWORD LESSER xmm2" to console
            movups xmm0, OWORD PTR oSmall            ; ML 6.15 accepts OWORD PTR as used here, but not in .data
            Ocmp ecx, xmm0            ; a pointer and an XMM reg (xmm0...xmm2 will be trashed)
            deb 4, "Result", flags            ; CzSo, i.e. Carry? and Sign? set
Rem            - returns flags as in a normal cmp eax, edx comparison (control for overflow!)
            - will trash eax and edx, xmm0 and xmm1; do not use edx as input pointer
            - you cannot use both ecx and ebx as input pointers (an error will be thrown)



Fsign
            .if Fsign(MyRealVar)
                        MsgBox 0, "MyRealVar is negative", "Fsign:", MB_OK
            .else
                        MsgBox 0, "MyRealVar is not negative", "Fsign:", MB_OK
            .endif

Rem            returns Sign?, and works with all sizes (Real4, Real8, Real10)




Fcmp
            MyPI_hi            REAL4            3.14160
            ...
            Fcmp MyPI_hi, PI, medium            ; PI is what you think it is
            .if FcmpLess
                        Print Str$("MyPI_hi at %f is lower than the real PI\n", MyPI_hi)
            .elseif Zero?
                        Print Str$("MyPI_hi at %f is exact\n", MyPI_hi)
            .else
                        Print Str$("MyPI_hi at %f is higher than the real PI\n", MyPI_hi)
            .endif
Rem            - returns Zero? and Sign? flags (and only these are valid): Sign? means "first arg below second arg"
            - you may use FcmpGreater and FcmpLess (aka !Sign? and Sign?)
            - single arg, e.g. Fcmp xmm1, tests for zero
            - see also QCmp and Ocmp for comparing QWORDs and OWORDs
            - almost any number formats can be compared, including xmm registers etc




Val
            mov eax, Val(Chr$("123"))            ; eax=123, edx=3
            mov eax, Val("123")            ; eax=123, edx=3
            mov esi, Chr$(9, "    12345")            ; eax=12345, edx=9 (5 plus a tab and three spaces)
            mov ebx, Val(esi)
            mov ebx, Val("12345h")            ; hex
            mov ebx, Val("0x12345")            ; hex, C notation
            mov ebx, Val("$12345")            ; hex, leading $ notation
            mov ebx, Val("10101b")
            Dim MyArray(1000) As DWORD
            mov MyArray(n), Val("12345678")            ; assign to a dword array member
            mov ebx, Val("$12345")
            .if signed edx>0            ; signed is an equate for sdword ptr
                        .if dh==1            ; dh set means it was a Bin$; use movzx edx, dl if you need the real # of chars used
                                    MsgBox 0, Str$("We found a Bin$: %i", ebx), "Val:", MB_OK            ; 10101b or 10101y
                        .elseif dh==2
                                    MsgBox 0, Str$("We found a Hex$: %i", ebx), "Val:", MB_OK
                        .else
                                    MsgBox 0, Str$("We found a decimal: %i", ebx), "Val:", MB_OK
                        .endif
            .else
                        MsgBox 0, "Sorry, the format was no good", "Val:", MB_OK
            .endif

Rem            - Val returns value in eax, and the number of used characters in dl (i.e. the lowbyte of edx)
            - Val accepts a variety of formats, see examples above. Make sure, though, that you do not feed more than 32 bits to a
                dword; for example, Val("1234567890") is ok but Val("9876543210") needs a QWORD destination, see MovVal below
            - for floats, use MovVal, see next entry
            - non-zero dh signals a Bin$ (dh=1) or Hex$ (dh=2), edx==-127 signals an error, i.e. not a number format
            - you can do simple calculations by combining Val and Str$:
                mov eax, Val(Str$(12*34-8))            ; slow and not very elegant, but it works
Key            val(




MovVal
            MovVal MyDword, Chr$("123.4567")                        ; assign a dword
            MovVal MyDword, esi                        ; variable from
            MovVal MyDword, offset MyString                        ; a string
            Print Str$("MyDword=\t%f\n", MyDword)            ; and print it
            MovVal MyR4, Input$("Type a number and hit Enter:    ")            ; get a number from the console
            Print Str$("MyR4=\t%f\n", MyR4)
            MovVal f:xmm0, Left$(Chr$("123.4567"), 6)            ; to use xmm regs in float mode, use the
            Print Str$("MyXmm=\t%f\n", f:xmm0)                        ; f: prefix both for MovVal and Str$
            MovVal xmm0, Left$(Chr$("123.4567"), 6)            ; same xmm reg but in integer mode
            Print Str$("MyXmm=\t%f\n", xmm0)
            MovVal MyQword, "123.4567"                        ; qwords
            Print Str$("MyQword=\t%f\n", MyQword)
            MovVal MyR4, Chr$("123.4567")                        ; REAL4
            Print Str$("MyR4=\t%f\n", MyR4)
            MovVal ST(0), "3.141592653589793238"                        ; to FPU (as REAL10)
            Print Str$("PI is %Jf", ST(0))                        ; should be followed by fstp st or other processing
            push eax                        ; create a DWORD slot
            MovVal stack, "12345678"                        ; looks elegant but mov ecx, Val(...) is shorter
            pop ecx                        ; pop a DWORD
            Print Str$("Popped from stack: %i\n", ecx)
            MovVal eax, Chr$("-1234")                        ; same result as Val(..)
            MovVal MyR10, Chr$(9, "    -123.45678901234567890e-123 cute, insn't it?")            ; edx=30 (incl. tab + 2 spaces)
Rem            returns # of used chars in dl - see Val() above




Sqrt
            Print "All tests for a value of 32", CrLf$
            mov edx, 32
            Print Str$("Square root reg32 \t%If\n", Sqrt(edx))
            MovVal xmm0, "32"
            MovVal f:xmm1, "32"
            Print Str$("Square root xmm0 \t%If\n", Sqrt(xmm0))            ; xmm0 in integer mode
            Print Str$("Square root xmm1 \t%If\n", Sqrt(f:xmm1))            ; xmm1 in float mode
            Print Str$("Square root Word \t%If\n", Sqrt(V32W))                        ; Word variable
            Print Str$("Square root DWord \t%If\n", Sqrt(V32DW))            ; Dword
            Print Str$("Square root QWord \t%If\n", Sqrt(V32QW))            ; Qword
            Print Str$("Square root Real4 \t%If\n", Sqrt(V32R4))            ; Real4
            Print Str$("Square root Real8 \t%If\n", Sqrt(V32R8))            ; Real8
            Print Str$("Square root Real10    \t%If\n", Sqrt(V32R10))            ; Real10

Rem            returns a Real10 variable (named MbDebugR10) for use with Str$()




SetField
            MyRecord RECORD SlotHigh:4, SlotMid:7, SlotLow:7, SlotRest:32-4-2*7            ; the order is high to low
            .data?
            MyRec            MyRecord <>            ; define an empty 32-bit record with 4 fields
            .code
            SetField MyRec.SlotHigh, 15            ; set all 4 bits
            SetField MyRec.SlotMid, eax            ; 7 bits available
            SetField MyRec.SlotLow, 10, clear            ; clear all 7 bits, then set value 10
Rem            - argument must be immediate or DWORD (register or variable)
            - important: if there is any third argument, the existing field bits will be cleared with and MyRec, not mask field;
                then, bits will set with or MyRec, eax. The and costs 10 bytes (!) for a global variable, and can easily be
                avoided by clearing the entire record (and MyRec, 0) before setting individual fields
            - for immediate arguments, an error will be thrown if the value exceeds the available range




GetField
            Print Str$("The content of MyRec.SlotHigh is %i", GetField(MyRec.SlotHigh))
            mov MyVar32, GetField(MyRec.SlotLow)
Rem            returns field value in eax




SetFlags
            SetFlags
3, 1            ; Set flag #3
            SetFlags
3, 0            ; Clear flag #3
            FileIsDirty = 31            ; define your own name (0...31)
            SetFlags
FileIsDirty, 1
Rem            - use with Flags(), see below
            - returns previous flag value in Carry?




Flags
            .If Flags(3)
                        MsgBox 0, "Flag 3 was set", "Hi", MB_OK
            .else
                        MsgBox 0, "Flag 3 was not set", "Hi", MB_OK
            .endif

Rem            returns current flag value in Carry?




MouseX, MouseY, MouseK
            .if MouseK==1 && MouseY>600 && MouseX<9
                        MsgBox 0, "You clicked in the lower left corner", "Hi", MB_OK
            .endif
Rem            returns result in eax



Delay
            Delay 1000            ; wait a second...
Rem            uses Sleep but preserves ecx




void, voidTrue, voidFalse
            void Len("a test")                                                            ; use Len but avoid a mov eax, eax
            voidTrue Len(ecx)                                                            ; same but print "zero eax in line xx" if Len is zero and usedeb is on
            voidFalse rv(SendMessage, hEdit, WM_GETTEXTLENGTH, 0, 0)            ; prints "non-zero eax in line xx" to console if usedeb!=0 and eax!=0
Rem            returns DWORD in eax




GetRegVal
            PrintLine GetRegVal("HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment","Path", "no path found"), CrLf$
            EnvVars equ "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"            ; long paths: use an equate
            Print GetRegVal(EnvVars,"PROCESSOR_IDENTIFIER", "unknown"), CrLf$                                    ; get to know your CPU...
            print GetRegVal(EnvVars, "PROCESSOR_IDENTIFIER", "unknown"), 13, 10                                    ; same with Masm32 print macro
            PrintLine GetRegVal("HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment","PROCESSOR_IDENTIFIER", "unknown"), CrLf$                                    ; same with full path
            PrintLine GetRegVal("HKCR\.doc", 0, "documents"), " are files with ending *.doc"
            PrintLine GetRegVal("HKEY_CLASSES_ROOT\.bax", 0, "unknown"), " are files with ending *.bax"
            ; this line would fail with a run-time error because the key does not exist and there is no default value given:
            ; PrintLine GetRegVal("HKEY_CLASSES_ROOT\.bax", 0), " are files with ending *.bax"
            PrintLine "Your default console font is ", GetRegVal("HKCU\Console","FaceName", "Arial")
            ; you can use the MbRegValRTE equate to determine the behaviour in case of errors:
            ; by default, you see a box "Line n: Get/SetRegVal failed" if you gave no default and the key or value does not exist
           
MbRegValRTE = 1                        ; default mode: bark if there is a problem
            void GetRegVal("HKCR\.dox", 0)                        ; no default, MbRegValRTE = 1: box "registry access failed in line xx"
            void GetRegVal("HKCR\.dox", 0, "dox files")            ; default, MbRegValRTE = 1: default value returned
            PrintLine eax, " are files with ending *.dox"
            MbRegValRTE = 0                        ; silent mode - no Run-Time Error in case of problems
            void GetRegVal("HKCR\.dox", 0)                        ; no default, MbRegValRTE = 0: edx=0, pointer to empty string in eax
            .if edx
                        PrintLine eax, " are files with ending *.dox"
            .else
                        PrintLine "dox files unknown here"
            .endif
            ; REG_QWORD values and REG_BINARY are returned in xmm0; you need void plus fDate$(xmm0) to see them:
            void GetRegVal("HKLM\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0", "LastInstallTime", 0)
            PrintLine "LastInstallTime: [", fDate$(xmm0, "dd MMMM yyyy"), ", ", fTime$(xmm0, "HH:mm"), "]"
Rem            - returns in eax either a pointer to a string, or a DWORD, and in edx the REG_xx type or failure (0)
            - in case you expect REG_NONE (=0), check [eax] for the string Rg? indicating failure
            - for types REG_QWORD (i.e. FILETIMEs) and REG_BINARY, eax is a pointer to the data, while
                the first 16 bytes are returned in xmm0, e.g. for use with fDate$() and fTime$()
            - remember that Print and Let trash eax and edx. Use void GetRegVal to test for edx, then Let xx=eax
            - returned values can be up to 128 kBytes long
            - if you provide a default value, GetRegVal returns it even if the key and/or the value are not present
            - if a default was given, edx is always 1, without default string or dword edx=0 means failure
            - error codes for RegOpenKeyEx are in rvRegKey, those for RegQueryValueEx are in rvRegQuery
            - if you can't see certain keys on 64-bit systems, try SetReg64
Key            grv(




SetRegVal
            SetRegVal
"HKCU\TheKey","TheValueName", "NewString"
            SetRegVal "HKEY_CURRENT_USER\Console\JJ", "NewValue", 12345                        ; creates new REG_DWORD
            SetRegVal "HKCU\Console\JJ", "NewValue", 54321                                                ; changes existing value
            SetRegVal "HKEY_CURRENT_USER\Console\JJ", 0, "New default value"            ; 0: sets (Default) to a string
            ; do not replace an existing REG_SZ with a dword - this will produce an exception:
            ; SetRegVal "HKEY_CURRENT_USER\Console\JJ", 0, 123
            ; existing REG_DWORD receives a DWORD string pointer - this works but is meaningless and bug-prone:
            ; SetRegVal "HKEY_CURRENT_USER\Console\JJ", "NewValue", "A string"
            ; tries to create a new key, will fail with a run-time error:
            ; SetRegVal "HKEY_CURRENT_USER\Console\NoSuchKey", "NewValue", 123
Rem            - returns in eax the original new value, in edx success (1) or failure (0); see also remarks for GetRegVal
            - you are not allowed to create a new key, but you can create a new REG_SZ or REG_DWORD value
            - with MbRegValRTE = 1, attempts to create a new key will trigger a run-time error
            - with MbRegValRTE = 0, attempts to create a new key will fail, and edx will be zero



GetRegKeyArray, GetRegArray
            PrintLine "Keys in ... \CurrentVersion\Explorer:"
            ; key name, names array, optional: last modification array; the latter is a SYSTEM_TIME array;    you can
            ; get the time elapsed since the last modification with Age(LastMod(index), x), with x=d, h, m, s, ms or s
            GetRegKeyArray "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer", My$(), LastMod()
            For_ ecx=0 To eax-1
                        .if Age(LastMod(ecx), h)<=7*24                                    ; h=hours (valid units: d/h/m/s/ms/s)
                                    Print fDate$(LastMod(ecx), "dd MMMM yyyy"),\                ; the dd MM... format is optional
                                    ", ", fTime$(LastMod(ecx)), Tb$, My$(ecx), CrLf$            ; default time format is e.g. HH:mm:ss
                        .endif
            Next
            PrintLine "My environment variables:"
            GetRegArray "HKCU\Environment", MyEnv$(), MyData$()            ; the optional second array takes the values
            For_ ecx=0 To eax-1
                        PrintLine MyEnv$(ecx), Tb$, MyData$(ecx)            ; print names and values
            Next
Rem            returns #strings in eax; see also two detailed examples in MbSnippets.asc




Masm32 macros:
print            use Print
fopen            use Open
fread            use Input #
fclose            use Close
inkey            use Inkey
mtxt            use Mirror$
szRev            use Mirror$
str$            use Str$
hex$            use Hex$
cat$            use Cat$
exit            use Exit
len            use Len



DosBasic help            ; similar to 32-bit MasmBasic but not identical
Init            Initialise DosBasic; optional: desired stack, e.g. Init 2000h
Exit            DOS equivalent to ExitProcess; optional: exit code (default: 0)
Chr$            Print Chr$("Line 1", 13, 10, "Line 2")
CL$            Print CL$(), " is the commandline", 13, 10
Instr_            Print Str$(Instr_("A test", "test", 3)), 9, " is 3", 13, 10            ; startpos n is optional
m2m            m2m dest, source                                    ; ok for DWORD & WORD destinations
Inkey            Inkey "Hello"
Val32            Val32 pStr/    ValX pStr
Val            Val pStr/    ValX pStr
ValX            ValX pStr/    push si
Str$            Print Str$(eax) or Print Str$(cx) or Print Str$(al) etc
Hex$            Print Hex$(eax) or Hex$(ax) or Hex$(al) or Hex$(123) etc
Psp            mov ax, Psp(0)
Exist            .if Exist("MyFile.txt")
Open            Open "O", #1, "Test.txt"; modes are Output, Input, Update and Append
Seek            Seek #1, ecx            ; takes immediate, reg32, reg16, var16, var32
Close            Close #n
Print            Print "Test", 13, 10
FileRead            FileRead dest, fname
Make$            Make$ TheName, Bytes
Time$            Print Time$()
Input$            mov si, Input$("Type something and hit Enter: ")

RichMasm options
OPT_Susy            Console            ; RichMasm option, forces a console
OPT_DelTmp            0
OPT_arg1            "This is the first argument passed through the commandline"
OPT_arg2            "This is the second argument passed through the commandline"