MasmBasic Quick Reference

extracted from \Masm32\MasmBasic\MbGuide.rtf 27.11.2016 - see original RTF file for correct formatting (e.g. correct Unicode - here it may appear as ?????) 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

signed ErrLines For_ Fn mcs xmov jinvoke @LibUsed$() AsmUsed$ Switch_ Try Catch Finally TryRTE Init Exit CL$() wCL$ If_ If? Div_ IsTrue CodeSize DlgDefine DlgControl DlgShow SetGlobals Enum Enum$ Choose Data Read Dll Declare deb Err$() SetErrLine Sound Asc Cvi Cvl Odd Abs Min Max PopCount Bsr64 Rand FolderOpen$ FileOpen$ FileSave$ wF Open wOpen Close Seek Lof Loc Input Input$ wInput$ Prompt$ Rename WritePipe Launch SetLaunchTimeout Launch$ LaunchEndPos ExitCode Exist LastFileSize LastFileName$ LastFileDosName$ IsFolder Kill 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 UnzipFile FileRead$ LastFileSize NoTag$ FileWrite Replace$ ParentData$ CopyData$ SendData SendControlKey SendWordCommands xlsConnect xlsOpen xlsCommand xlsSysRead$ xlsRead$ xlsWrite xlsClose xlsDisconnect xlsHotlink ddeConnect ddeCommand ddeRequest$ ddeDisconnect Win$ wWin$ SetWin$ wSetWin$ AddWin$ SetSel$ MakeBrush MakeFont PickFont ImgPaint ToolTips WinByTitle App16 Clip$ wClip$ SetClip$ wSetClip$ SetHtmlClip$ MsgMonitor GuiImage GuiImageCallback SaveImageToFile GuiImageSpeed GuiImageFrame GuiControl SetListbox SetCombobox GuiParas GuiMenu GuiColor GuiText GuiCls GuiLine MakePath SetLegend SetDoc$ GetDoc$ SetTitle$ SetStatus$ New$ Alloc16 Free16 MemSet MemState Res$ wRes$ Mlg$ MsgTable$ String$ Space$ Inkey wInkey Print wPrint Print wPrint CurDir$ PrintLine PrintBuffer SetCpUtf8 SetCpAnsi SetCpUpperLower$ ConvertCp$ ConsoleColor Locate( At CColor crtbuf CreateInvoke CRT gsl SetPoly3 GetPoly3 Dim Erase VarPtr ArrayFill wArrayFill ArraySet StringToArray Split$ Join$ Filter$ ArrayMerge Swap Let wLet Cat$ wCat$ Insert Delete GuidFromString GuidsEqual CoInvoke Ole$ BSTR wData wChr$ Chr$ Len wLen u MbCopy Bmove MsgBox wMsgBox Alert( Clr Clr$ ClearLocals StackBuffer Instr_ Rinstr wInstr InstrOr Rinstr wRinstr Count wCount LineCount Extract$ StringsDiffer FilesDiffer Left$ wLeft$ Mid$ wMid$ Right$ wRight$ Lower$ wLower$ Upper$ wUpper$ Trim$ Ltrim$ Rtrim$ Qtrim$ Mirror$ Date$ wDate$ Time$ wTime$ IsoWeek GetTZ$ fDate$() fTime$ wfDate$ wfTime$ TimeSF Timer NanoTimer Align64 AlignX Recall Csv2Tab wRecall Store QSortDesc QSortMode ArraySort ArrayMinMax ArrayMean ArrayRead ArrayStore ArrayLoadMap ArrayPlot RgbCol CgaCol SysCol ArraySearch Str$ wStr$ SetFloat Float Sinus Cosinus FpuSave FpuRestore FpuPush FpuFill FpuSet Exp10 Exp2 ExpE ExpXY Percent DefNum Hex$ Bin$ Qcmp Ocmp Fsign Fcmp Val MovVal Sqrt SetField GetField GetHash SetFlags Flags MouseX MouseY MouseK Delay void voidTrue voidFalse GetRegVal SetReg64 SetRegVal GetRegKeyArray GetRegArray GetProcessArray GetDevicesArray Masm32

; The smallprint: this library is provided "as is", and the usual disclaimers apply.
; ---------------- This help file refers to MasmBasic version 27 November 2016 -----------------------
; It is assumed that you installed the library from
; To start a new project, go to ^ File/New Masm source ^ and click on one of the links, e.g. console.
; 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\
; -------------------------------------------------------------------------------------------------------------------------------
include \masm32\MasmBasic\
; select Init below, then hit F6 to assemble, link & run the Hello World example:
    MsgBox 0, "Wow, it works!!!!", "Hi", MB_OK            ; It worked? Use a template, or try 90+ snippets
    Print CrLf$, "OK - press any key"                        ; It didn't? Give me feedback here.
; --------------------------------------------- same as 64-bit code: ---------------------------------------------------
include \masm32\MasmBasic\Res\
; delete the
x, select Init, then hit F6 to build & run the Hello World example as 64-bit code:
    Init                                                ; OxPT_64 1            ; delete the x for 64 bit assembly
    PrintLine "More examples under menu File/New Masm source: Dual 32/64 bit console/GUI templates"
    MsgBox 0, Chr$("Built with ", @AsmUsed$(1), " in ", jbit$, "-bit format"), "Wow, it works:", MB_OK
Practical hints for using MasmBasic
- You cannot use MasmBasic with the old ML.exe that comes along with Masm32 (version 6.14 doesn't know about SSE2...).
    Instead, you should use HJWasm. When you hit F6 to assemble & link a MasmBasic source, RichMasm will invite you to
    install HJWasm from the web; if that fails for some reason (e.g. antivirus software), download HJWasm32 manually
    (32bit Binary Package), then copy HJWasm32.exe from the archive 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 HJWasm32.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.
- To find commands, use the find box in the upper right corner of the RichMasm editor. The string .x lists all MasmBasic
    commands, while .xMbF lists commands starting with F. You can use wildcards, e.g. .Mxb*file will find commands that contain
    the word file. Note also the little Case box above the find box.
- You should associate the *.asc (Assembler Source Code) file extension with \Masm32\MasmBasic\RichMasm.exe
- RichMasm is a Unicode editor -    try a multilingual example
- 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.
- You can define own shortcuts: select the string #box invoke MessageBox, 0, Str$(eax), Chr$("Title"), MB_OK, then
    right-click and choose Save shortcut. Afterwards, typing box expands the shortcut.
- 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
    itself, but (WARNING) Windows in its 64-bit versions has the bad habit to trash xmm0 ... xmm5. MasmBasic preserves all
    xmm regs, but check carefully what happens if you use invoke MessageBox, ... instead of MsgBox 0, "text", "Hi", MB_OK !!
- 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
                        MsgBox 0, Str$(eax), "Surprise, surprise: -1 is bigger than 99!", MB_OK
    The problem is that Masm reads -1 as 4294967295, i.e. 2^32-1, or 0FFFFFFFFh. To avoid this, MasmBasic
    provides the signed 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
                        .if Len(My$)==2            ; code will be inserted before the .if and after the .else, and produce the expected result
                                    .if eax==3            ; hint: Len returns eax, so instead of calling Len once more, you may continue to test eax

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

            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)                        ; if the counter is an integer
                        .Break .if n==50
            Next n

            mov ebx, 200
            xor ecx, ecx
            For_ n=5 To ebx-1
                        inc ecx
                        .Break .if ecx==123
            For_ ct=10 To 0 Step -1                        ; .Step may be negative
                        Print Str$(ct), " "                        ; print a countdown
            For_ fct=1.5 To 2.0 Step 0.1                        ; counter is a float, and therefore needs to be                                   
                        Print Str$(fct), Spc2$                        ; previously defined in .data? or with SetGlobals
            For_ each x$ in My$()                        ; loop through elements of an array
                        Print Str$(ForNextCounter), Tb$, x$                        ; print the counter and each element
Rem            - if begin>end (or beginStep), the loop will not be started; use MbForReject=0 to change this
            - the optional Step variable must be an immediate integer (or real, if the counter is defined as REAL4/8/10)
            - in case you have too many For_ ... Next loops, MbForMax can be increased
            - For_ each ... can be used for string, numeric and structure arrays; for some types, edi is being used; with usedeb=1,
                a warning will be shown. Note that For_ each esi in somearray() is valid code, too.
            - For_ n=0 To eax-1 is valid code; you can nest several For_ loops, but you must use different counters.
Key            for_

            Dim floats() As REAL8
            Print Str$("%i items converted", Fn(StringToArray "12 34 56", floats()))
Rem            use Fn() as "function format" for commands that do not "return" anything

mcs            multiple commands
            mcs mov eax, 100 : add eax, 30 : sub eax, 7: deb 4, "sum:", eax : Inkey "-- press any key --"            ; OK
            mcs mov eax, 100 : add eax, 30 : sub eax, 7: <Print Str$("result: eax=%i\n", eax)>            ; avoid, or test carefully
            mcs For_ ecx=0 To 19: <Print Str$("%i ", ecx+1)>: Next                        ; a MasmBasic loop
Rem            - mcs allows to put several commands on one line, separated by a colon as in some Basic dialects
            - macros that depend on previous commands, like e.g. Print Str$(...) should start in a new line, or be put
                ; due to a rare incompatibility, ML and HJWasm/AsmC may produce different results

xmov            extended mov
            xmov MyR8, 123456.7890123456            ; assign a double directly
            xmov MyR4, MyR8            ; mem to mem, different operand size
            xmov someSQ, someSD
            xmov someSD, someSQ
            xmov ct, -5
            xmov MyR4, 32767
Rem            - use if in doubt about the best way to shove a value into a memory location (check the disassembly)
            - works with MasmBasic and 64-/32-bit JBasic

            jinvoke MyTest, &v0, *v1, addr v2, offset v3, &v4, v5
Rem            - jinvoke is a macro that works like invoke but assembles with 32- and 64-bit code alike
            - see menu File/New Masm source/Dual 32/64 bit console/GUI templates

@LibUsed$(), AsmUsed$()
            include \Masm32\MasmBasic\Res\
            % echo @AsmUsed$(0)            ; no quotes
            PrintLine Chr$("This code was assembled with ", @AsmUsed$(), " in ", jbit$, "-bit format using the ", @LibUsed$(), " library")
Rem            - output for the example: This code was assembled with HJWasm32 in 64-bit format using the JBasic library
            - with no args, i.e. (), quoted strings are returned; use e.g. @AsmUsed$(0) to return the plain string without quotes

Switch_, Case_, Default_, Endsw_
            The MasmBasic Switch macro (note the understroke) is more powerful than its C or GfaBasic equivalents, since
            it can handle even variables or registers in the "cases". Under the hood it creates, depending on the range covered
            and the number of cases, either an if/elseif chain or a jump table that is for long lists of cases (more than 4) much
            faster than the Masm32 macro. Below an example for MasmBasic Switch_
            include \masm32\MasmBasic\
            SetGlobals int somevar=5
            Init                                    ; ## Switch with jump table ##
            m2m ecx, -5
            PrintLine "----------------------------- testing the new MasmBasic Switch_ macro -----------------------------"
                        Print Str$(ecx), Tb$
                        m2m edx, -127            ; don't trigger the edx case...
                        If_ ecx==11 Then mov edx, ecx            ; ... except for testing the Case_ edx at position 11
                        Switch_ ecx
                        Case_ somevar
                                    PrintLine Str$("Case var=%i", somevar)
                        Case_ edx            ; this case triggered if ecx==edx; takes preference over 'immediate'
                                    PrintLine "Case edx ###"            ; cases but must come before lt or gt cases
                        Case_ lt -2
                                    PrintLine "Case less than -2"
                        Case_ -2
                                    PrintLine "Case -2"
                        Case_ 0
                                    PrintLine "Case NULL"
                        Case_ 10, 12
                                    PrintLine "Case 10 or 12"
                        Case_ 18
                                    PrintLine "Case 18"
                        Case_ 14 .. 16 : PrintLine "Case 14 .. 16 (one-liner)"            ; OK if only one instruction is needed
                                    PrintLine "---"            ; no matching case found
                        inc ecx
            .Until signed ecx>20
Rem            - Switch_ trashes edx but not eax (unless Case_ does it)
            - do not forget the understroke (Switch_, Case_, Default_, Endsw_), otherwise you get the Masm32 switch macro
            - Inside a case, the stack is 4 bytes off; if you really need values on the stack, use e.g. mov eax, stack[4] to compensate this.
            - You can force Endsw_ chain or Endsw_ table; with few cases that are short and far apart, e.g. case -1000, case 1, case 1000,
                Endsw_ table creates a huge jump table with many default entries; the chain mode will be slower but more size-efficient.
Key            Swi

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

include \masm32\MasmBasic\            ; 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)
            cmp edx, $            ; simple error check
            .if Zero?
                        PrintLine "Bad index!!"

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, then 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. If the include line and Init are
                        present, do not copy the content; instead, select the Init and hit F6, as in the example below.
Init            This is perhaps the smallest possible complete Masm application:
            include \masm32\MasmBasic\
            Init                                    ; <<<<<<< select and hit F6
            Inkey "Hello World"            ; print text to console and wait for a key
            EndOfCode                        ; combines Exit and 'end start'
Rem            - the Init macro inserts at least the following two lines:
            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 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
            - after using strings or arrays, you should 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
            - 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"
Rem            - use for single-line if statements
            - 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; )

If?            ; ternary operator
            Syntax: mov eax, If?(comparison, result_true, result_false)
            mov ecx, 123
            mov edi, ecx
            Print Str$("ecx eq 123: %i\n", If?(ecx eq edi, 111, 222))                                                            ; if ecx equals edi, take 111, otherwise 222
            Print Str$("ecx ne 123: %i\n", If?(ecx ne 123, 111, 222))
            PrintLine "ecx above 123: ", If?(ecx ab 123, "above", "not above")                                    ; unsigned comparison, string output
            PrintLine "ecx below 123: ", If?(ecx bl 123, "below", "not below")
            PrintLine "ecx ge 123: ", If?(ecx ge 123, "greater or equal", "not greater or equal")            ; signed comparison, string output
            PrintLine "ecx gt 123: ", If?(ecx gt 123, "greater", "not greater")
            .if FileSave$("Plain Asm=*.asm|MasmBasic=*.asc||Rich text=*.rtf|All files=*.*")                        ; saves quite some bytes with complex functions
                        FileWrite FileSave$(), stream:hMyEdit, If?(InstrOr(FileSave$(), ".rtf" or ".asc", 1), SF_RTF, SF_TEXT)            ; choose the right format
Rem            - the comparison follows the macro syntax, with some deviations (in red) for technical reasons:
                - eq=equal, ne=not equal
                - ab=above, ae=above or equal            (unsigned)
                - bl=below, be=below or equal
                - gt=greater, ge=greater or equal            (signed)
                - lt=less than, le=less or equal
            - if one of the options is "quoted text", strings will be returned, otherwise integers

            include \masm32\MasmBasic\
            SetGlobals R4:REAL4=100.0, R8:REAL8=1000.0, R10:REAL10=10000.0
            Init            ; select and hit F6
            mov eax, 1000
            Print Str$("Imm real\t%i\n", Div_(22.222222222222222222))
            mov eax, 12300000
            Print Str$("Imm real\t%i\n", Div_(R4))
            mov eax, 12300000
            Print Str$("Imm real\t%i\n", Div_(R8))
            mov eax, 12300000
            Print Str$("Imm real\t%i\n", Div_(R10))
            mov ecx, 1230000
            Print Str$("Imm real\t%i\n", Div_(ecx/R10))
            Print Str$("Imm real\t%i\n", Div_(R10/R4))
Rem            returns result in eax

IsTrue                                    ; test condition with floats
            include \masm32\MasmBasic\
            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"
                        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"
                        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$
                        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.

            include \masm32\MasmBasic\
            Init            ; <<< select init and hit F6 to test this snippet
            call MyTest
            CodeSize MyTest
            MyTest proc
                        nops 99
            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\
            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
            .if eax==IDOK
                        wMsgBox 0, wCat$(Dlg$(0)+wCrLf$+Dlg$(1)), "Please confirm:", MB_OKCANCEL
                        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
            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 int MyInt=111, MyInt2, float MyR4a=444.44, MyR4b, double MyR8a=888.88            ; C-style
            SetGlobals OWORD xm0, xm1, xm2    ; use with xmm registers - if this is the first SG statement, all xm? are aligned 16
            SetGlobals msg:MSG, wc:WNDCLASSEX, exeBuffer[MAX_PATH]:BYTE, gBuffer[1024]:BYTE
            ; no args: set ebx to the right offset, and initialise vars; must be used in callbacks (WndProc, SubEdit, ...)
Rem            - variables return [ebx+x]

, where x=-128 ... +128 and higher
            - if SetGlobals declarations are present, the Init macro sets ebx to the .data? block reserved for the variables
            - 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 or constants
            Enum            IdMenuNew, IdMenuSave, IdMenuCopy, IdTimer
            Enum            20:IdEdit, IdButton1, 30:IdButton2, IdStatic, IdFind, IdFindStatic
            Enum            #MYSUBLANG_ENGLISH_, 1:US, UK, AUS, CAN, NZ, EIRE
            Print Str$("NZ=%i\n", MYSUBLANG_ENGLISH_NZ)            ; 5; see SUB_LANG... in
Rem            - default start is 10, but (as shown above) new start values can be specified with nn:
            - with #, a prefix can be specified

Enum$                                    ; return numeric constant as text
                                    Case WM_CREATE
                                                Enum 20:IdEdit, IdButton1, 30:IdButton2, IdStatic, IdFind, IdFindStatic                        ; create a list of IDs
                                                ; use with CreateWindowEx
                                    Case WM_COMMAND
                                                ; ID is loword(wParam), print it as Enum$(ID, list of numeric constants)
                                                PrintLine "command for control ", Enum$(word ptr wParam, IdEdit, IdButton1, IdButton2, IdStatic, IdFind, IdFindStatic)
                                                mov ecx, Enum$(uMsg, WM_CREATE, WM_PAINT, WM_SIZE, WM_SIZING, WM_COMMAND)
                                                .if byte ptr [ecx]!="?"            ; if no matching entry is found, Enum$() returns a question mark
                                                            PrintLine "Message: ", ecx            ; output e.g. Message: WM_PAINT
Rem                                    for debugging, returns string for use with Print or Let

                                    include \masm32\MasmBasic\            ; download
                                    tx123            db "This is tx123", 0
                                    Init            ; select init and hit F6 to test this snippet
                                    mov ecx, Chr$("ecx is a string")
                                    .While 1
                                                            Inkey "Gimme a number: "
                                                            .Break .if eax==VK_ESCAPE
                                                            .if Choose(eax-"0", 100, 101, 102, "abc", 124, 125, 12345.67, 12345678.90123456789, offset tx123)<=ChooseString
                                                                        .if eax==ChooseReal
                                                                                    Print Str$("You chose the real number %Jf\n", ST(0))
                                                                                    fstp st
                                                                        .elseif eax==ChooseError
                                                                                    PrintLine "No such entry"
                                                                                    Print Str$("You chose the integer %i\n", eax)
                                                                        PrintLine "You chose the string [", eax, "]"
Rem            returns in eax either an integer below 65536 (e.g. 100, 101, ... above), or a string (eax>ChooseString, i.e. >65536), ChooseReal or ChooseError

Data and Read
                                    include \masm32\MasmBasic\
                                    Data 123, "Hello World, how are you?", 'single quotes are allowed', My$, sq$, 456
                                    SetGlobals My$, sq$, c1$, c2$, MyByte, MyDword, MyR4:REAL4, MyR8:REAL8, Last$, MyQ:QWORD
                                    Data 789, 111, 1234567890123456789, 1234567890.123456789, 12345.6789, 12345.6789
                                    Init                        ; select Init and hit F6 to test this example
                                    Data 33333, "Last string item"                        ; Data statements can go almost everywhere...
                                    Read ecx, My$, sq$, c1$, c2$, eax            ; once My$+sq$ are read, c1$ and c2$ can be copied
                                    Read edx
                                    Read esi           
                                    Data 1234567890123456789                        ; ... but values must be defined before they are read
                                    Data "This", "is", "an", "entire", "string", "array", 111, 222
                                    Read xmm0, f:xmm1, MyR8, MyR4, edi, Last$, MyQ
                                    deb 4, "Read variables:", ecx, eax, $My$, $sq$, $c1$, $c2$, edx, esi, xmm0, f:xmm1, MyR8, MyR4, edi, MyQ, $Last$
                                    Read my$()
                                    Read eax, ecx
                                    deb 4, "Read again", eax, ecx
                                    $Data No, quotes, in, this, string, array, 333, 444
                                    For_ ecx=0 To my$(?)-1
                                                Print my$(ecx), "."
                                    Read my$()            ; you can re-use an array, here for the $Data items
                                    For_ ecx=0 To my$(?)-1
                                                Print my$(ecx), " "

Rem            - the Data statements can be in code or data sections
            - with string arrays, Read stops when it encounters a non-quoted item, e.g. 111 as shown above
            - use the variant $Data for non-quoted strings, but note that 333 and 444 are treated as strings

Dll & Declare
\masm32\MasmBasic\                        ; include this library
MyLongLong            LONGLONG 12345678901234567890            ; the standard Masm32 crt lib is not enough to handle this
            Init                                                            ; initialise the app
            Dll "msvcrt"                        ; good ol' CRT
            Declare double sin, C:1                        ; the crt sinus function returns a double aka REAL8 in FPU register ST(0)
            Print Str$("Sinus(3)= %Jf from CRT\n", sin(3.0))           
            fstp st                        ; since doubles are returned via the FPU, ST(0) must be popped
            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
            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
                        Print "unarc.dll not present?"
            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
            - 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$:60, $MyArray$(n)            ; show another MsgBox, limit the display of My$ to 60 bytes
            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.; with e.g. $esi:50, you can limit the amount
                of bytes displayed. Note that console output is limited to 53200 bytes under Win XP, 62600 under Windows 7.
            - 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$(1)                                    ; call GetLastError, and show a MessageBox with a formatted string if there was an error
; 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
                        deb 4, "Loop C", $eax            ; will write The operation completed successfully to the console
Rem            the blank argument version returns a pointer to the formatted error desription in eax

            Print My$(98)            ; sooner or later, MasmBasic...
            Print My$(99)            ; ... will show you a nice little ...
            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


            Sound "880:200"            ; plays frequency 880Hz for 200 ms; allowed separators are space, tab, / and :
            Sound 111            ; plays a wav resource with ID 111 (in rc file: 111 WAVE "hello.wav")
            Sound "hello.wav"            ; plays a file directly (wav only)
            Sound "hello.mp3"            ; plays a file in its associated player
            Sound "大桥在混乱的水.mp3"            ; Unicode names are allowed
            Sound Utf8$(wCL$())            ; even if passed via the commandline
Rem            - The simple "frequency:duration" syntax works on Windows 7 and higher but not on Windows Vista;
                a minimum duration of about 90...100 ms is required
            - PlaySound/Beep/ShellExecute results can be checked with Err$()

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

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

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

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

            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
            For_ n=0 To Min(9, eax-1)            ; GetFiles returns #files in eax: use eax-1 with null-based index
                        PrintLine Files$(n)

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

            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

            movlps xmm0, q3
            Print Str$("Bsr64 of xmm0 \t%i\n", Bsr64(xmm0))
            Print Str$("Bsr64 of q1 \t%i\n", Bsr64(q1))
            For_ ecx=0 To 2
                        PrintLine Str$("Bsr64 of MyQ(%i)\t", ecx), Str$(Bsr64(MyQ(ecx)))

Rem            returns position of msb for 64-bit values; without arguments, edx::eax is assumed

; 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
                        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

include \masm32\MasmBasic\            ; 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
            PrintLine "Nothing selected: [", eax, "]"            ; Zero? set = user cancelled, eax points to a Null$
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
                        PrintLine "You cancelled"                        ; see Win32 docu of OPENFILENAME, lpstrFilter
            ; 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
                        PrintLine "You cancelled"
            ; 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, "]"
                                    PrintLine "Folder: [", esi, "]"
                        .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
                        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)
                        PrintLine "You cancelled"
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
            Open "U", #1, "MyOtherFile.txt"                        ; open for Update
            Print #1:4, offset MyBuffer                        ; write 4 bytes
            Open "A", #ecx, "MyOtherFile.txt"                        ; open for Append (you can use #register instead of an immediate)
            Print #ecx, CrLf$, "oops, forgot the end!"
            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"
Rem            Open returns the handle in eax - you may check for INVALID_HANDLE_VALUE
Keys            opi, opo

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

            Open "O", #1, "TestShort.txt"                        ; open file for output
            Print #1, "This is a pretty long string"
            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))
"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

            mov ecx, Lof
            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

            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

            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
            Let esi=Input$("Your hobby: ", "Asm", flush)                                    ; you may use flush as third para to flush the input queue
            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
Rem            returns temporary pointer in eax; to get a permanent string, use Let

            Let esi="Returned: ["+Prompt$("Title", "proposal")+"]"            ; title, suggestion, hFont, x, y, width, height
Rem            provides an edit control; for console apps only, 160k max

            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

            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

            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), up to a value of 65535, i.e. 65 seconds
            - 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

            ; 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)
            ; 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()

            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

            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$ ...
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

            .if Exist("\masm32\include\")
                        MsgBox 0, " is present, yeah!", "Hi", MB_OK
            mov esi, Chr$("NoTest.txt")
            .if !Exist(esi)
                        MsgBox 0, esi, "No such file:", MB_OK
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$()
                - if the file does not exist, eax and LastFileSize contain -1, otherwise both return the 32-bit size
Keys            ex(, exist(

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

            Kill "Myfile.dat"
Rem            returns eax

            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
            ; if usedeb=1 (default!!), Touch will throw a runtime error if the file doesn't exist or is not accessible
            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)

            ; --- 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
Rem            - returns # of files both in eax and MbGetFileCount
            - the Files$() array is filled with UTF8-encoded strings; you may have to use the SetCpUtf8 macro once to display non-English filenames   
            - 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
            - similarly, GetFiles CL (Unicode: GetFiles wCL) transfers a list of files in the commandline to the
                Files$() array; SortFiles works as expected, also the Gf*** functions; if no arguments are present, eax will be zero
            - 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\
            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)
            test edx, 1023            ; file or folder counter
            .if Zero?
                        Print "*"            ; console mode progress bar ;-)
            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

            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)
            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
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)

            .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"
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

            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$,\
            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$

            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 "A new folder"
            .if Zero?
                        PrintLine "[", eax, "] successfully created"
                        PrintLine "Could not create '", eax, "', sorry"
            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
\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
"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\            ; (no FreeArc installation needed here)
    UnzipInit ""            ; .UnzipInit expects a filename, returns a comment (if present); edx has #records
    .if Sign?
            Print eax            ; print an error message
            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
                        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), "]"            ; see snippets for an example with UnzipFile(index, pathtowrite)
            pop eax
            Print "Zipfile comment: [", eax, "]"            ; before UnzipExit
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)
            - even better: UnzipFile(ecx, "D:\SomePath\")            ; needs full path, i.e. drive letter and backslash at the end
Key            uzi

            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$(";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"
            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
            ; translate two files and one webpage into a string array:
            StringToArray FileRead$("\Masm32\include\")+FileRead$("")+FileRead$("\Masm32\include\"), x$()
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$(

            Let esi=NoTag$(FileRead$(";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 "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.rtf", stream:hRichEdit            ; save contents of the RichEdit control
            FileWrite "MyFile.uctxt", stream:hRichEdit, SF_TEXT or SF_UNICODE            ; same but specify a format
            FileWrite "MyFile.txt", "MyString", xmm0            ; sets the timestamp in xmm0
Rem            returns Close retval in eax
Key            fw

            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\")+FileRead$("\masm32\include\")
            ; 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=" and"+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 ;-)
Rem            - must be used with Let, Print or Inkey
            - Replace$ uses the same case flags as Instr_
            - the optional count parameter allows to limit substitution; 0 or omitted means replace all
Key            rep$(

            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

            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
            - on IPC, see also MSDN: Interprocess Communications
            - for examples, see File/New Masm source, MB client & server

"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 hWin, VK_V            ; paste something to Notepad etc
Rem            receiving window must have keyboard focus

                SendWordCommands            ; prepare a DDE session; Word must be running
            .if eax
                        ; adjust J: to your needs; use single quotes outside to allow double quotes inside:
                        SendWordCommands '[FileNewDefault:InsertFile "J:\Masm32\include\"]'
                        SendWordCommands Chr$("[InsertFile ", 34, "J:\Masm32\include\", 34, "]")
                        Let esi=FileRead$("\masm32\MasmBasic\Res\OpenDocInWord.bas")
                        SendWordCommands Replace$(esi, "#:\", Left$(MbExeFolder$, 3))            ; send the contents of the BAS file
                        SendWordCommands '[MsgBox "Cute"]'
                        MsgBox 0, "MS Word doesn't answer", "Sorry", MB_OK
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, xlsOpen, xlsCommand, xlsSysRead$(), xlsRC$()
xlsRead$(), xlsWrite, xlsClose, xlsDisconnect, xlsHotlink
            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
                                    xlsClose 0                        ; close the file without saving (0=don't save, 1=save, no arg: ask)
                        xlsDisconnect                        ; say bye to Excel
Rem            - all macros return !Zero? for success

; in case of failure, macros return Zero? plus an error string in eax
            - Excel macros are language sensitive: instead of xlsRead$("R1C1"), you may have to use L1C1 (French: ligne),
                Z1S1 (German: Zeile1:Spalte1) or F1C1 (Spanish: fila); try this multilingual page in case of problems

ddeConnect, ddeCommand, ddeRequest$(), ddeDisconnect
    ; These macros provide the DDE interface for non-Excel apps, such as browsers:
    include \masm32\MasmBasic\            ; 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 a web page?"
                        .if eax=="y"
                                    ddeCommand ""            ; open a frequently used page
    EndOfCode            ; combines invoke ExitProcess & 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(" - 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"
            SetWin$ rv(GetConsoleWindow)="Hello Masm Forum"            ; for console applicationss
            wSetWin$ hEdit=wRes$(1)+wCrLf$+wCrLf$+wRes$(2)            ; Unicode from resources
            wSetWin$ hEdit=uChr$("Добро пожаловать")                        ; Unicode directly with UTF-8 build (Ctrl F6 in RichMasm)
            Let esi="Добро пожаловать"                                    ; assign string as UTF-8
            wSetWin$ hEdit=uChr$(esi)                                    ; translate to wide format using uChr$()
Rem            for main windows and controls; you can use string concatenation as in Let resp wLet

            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

            SetSel$ hRichEdit="доброе утро"
            SetSel$ hRichEdit=Cat$("File=["+Files$(0)+"]")
Rem            - works with richedit controls only
            - direct assembly of UTF8 text (e.g. Russian as shown above) is possible in RichMasm by using Ctrl F6 for the first build
            - default mode is "replace selected text"
            - users may modify the internal SETTEXTEX structure with, for example, SetSel$ cp:CP_UTF8, flags:ST_DEFAULT

            MakeBrush hRed, RgbCol(255, 0, 0)                                    ; use with Gdi32
            MakeBrush hBlue, 0FF8080h                                                ; note the inverse BGR notation in hex format
            MakeBrush hRed, RgbCol(200, 255, 0, 0)                                    ; use with GdiPlus; "200" is the alpha
            MakeBrush brushes(ecx), SysCol(ecx+1), gdip                        ; last arg must be gdip if used with gdi+
Rem            - place in WM_CREATE handler; no need to define hBrush
            - for use with gdi+ (->MakePath, GuiFill, GuiDraw), use either RgbCol() with four parameters, or (e.g. in loops)
                SysCol(index) with an additional gdip as shown above; using Gdi32 brushes with gdi+ causes exceptions

            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 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

            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 ;-)

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
hButton1, Res$(100)                                    ; use resource string from rc file
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
            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

            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
                        Print "none", 13, 10
Rem            returns handle in eax

            .if App16(hWin)
                        Print "This is a legacy 16-bit app"
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
                        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
                        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

            WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
            LOCAL rc:RECT, hM1:HWND, hM2:HWND
                        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

            include \masm32\MasmBasic\Res\MbGui.asm
            Event Paint
                        ; args are imgsrc$ or resource ID, optional: no args=upper left corner, original size, x=fit: use full canvas, or x, y [, w, h]
                        GuiImage "\Masm32\examples\exampl04\car\car.jpg", fit                                    ; use full canvas (background image)
                        GuiImage "\Masm32\examples\exampl04\car\car.jpg", 50, 400, 160, 900            ; use specific x, y, w, h
                        GuiImage "\Masm32\examples\exampl04\car\car.jpg", 50, 200                        ; use x, y, original w, h
                        GuiImage "\Masm32\MasmBasic\Res\FileOpen.png", 100, 7, 40, 40            ; use specific x, y, w, h
                        GuiImage "\Masm32\MasmBasic\Res\MasmLogo.png"                                    ; upper left corner, original size
                        GuiImage ";attach=6;type=avatar", 7, 7
                        GuiImage "Это тест.jpg", fit                                    ; Unicode is allowed
                        GuiImage wCL$(), 315, 7                                    ; also via the commandline
                        GuiImage 125, 84, 7, 70, 70                                    ; 125 is a RC_DATA resource ID (IDs must be 48...127)
                        GuiImage hdc#imgCt+48                                    ; paint to hdc, imgCt=0...79 (i.e. ID 48...127)
                        GuiImage Files$(ecx), 238, 7, 70, 70                        ; you can dynamically load images
Rem                        - images can be animated GIFs
                        - with usedeb=1, load errors are printed to console
GuiImageCallback, SaveImageToFile, GuiImageSpeed, GuiImageFrame
                        For use with GuiImage:
                        GuiImageSpeed 50            ; sets playing speed to 50%
                        GuiImageFrame            ; force display of a specific frame in an animated GIF image
Rem                        click here for an example using the callback and saving functions

                        GuiParas equ "MasmBasic is easy", x650, y20, w300, h200
                        GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
                        include \masm32\MasmBasic\Res\MbGui.asm
                            GuiControl MyEdit, "richedit", h=1000-35, text "Go to the menu and open a file"
                            GuiControl MySbar, "statusbar", y=1000-35, text "Everything is fine"
Rem                        - allows to create a full-fledged GUI application in a few lines; see Event below for a list of standard events
                        - controls implemented are edit, richedit, button, static, statusbar, toolbar, listbox, scintilla
                        - dimensions can be set as [x/y/w/h]=[per mille value]+fixed offset
                        - resizing is automatic
                        To create a toolbar GuiControl:
                        - provide per-button gif files in a folder
                        - build CreateTbInfo
                        - create a link to the executable and move this link into the folder with the gif files
                        - drag one of the gif files over the link and follow the instructions (PM the author in case of problems)
Key                        guic

SetListbox, SetCombobox
            include \masm32\MasmBasic\Res\MbGui.asm
            GuiControl MyLb, "listbox", y 60, h 940
            GuiControl MyEd, "static", text "Double-click to open a file - Дважды щелкните, чтобы открыть файл", h 50
            GetFiles *.as?|*.rc            ; asc, asm, rc
            SortFiles            ; latest files on top
            SetListbox Files$()            ; Fill the listbox with UTF-8 encoded file names
            Event Command
            .if NotifyCode==LBN_DBLCLK
                        uShEx LbSel$            ; open selected file with ShellExecuteW (u=assume UTF-8 for LbSel$)
Rem            - fills a listbox or combobox with the elements of a string array, e.g. Files$()
            - if you need to reset the listbox, use e.g.            SetListbox other$(), clear            to force a LB_RESETCONTENT message
            - SetListBox is speed-optimised; use e.g.            SetListbox Files$(), short            to optimise for code size (55 bytes less)
            - use the LBN_DBLCLK, LBN_SELCHANGE or CBN_SELCHANGE notify codes of Event Command to perform specific actions
            - you can pass a callback function to SetListBox which decides via the sign flag if a string (in esi) should be added or not:
                SetListbox x$(), cb:MyCb
                MyCb:            ; a simple filter function
                dec Instr_(esi, s1$)            ; does esi have a match for string 1?
                .if !Sign?
                        dec Instr_(esi, s2$)            ; if yes, match for string 2?
                retn            ; return the sign flag
            - current selection is returned as LbSel$ or CbSel$, UTF-8 encoded

GuiParas, GuiMenu, GuiColor, GuiText, .GuiTextBox, GuiCls, GuiLine, .GuiCircle, .GuiEllipse, .Event
            ; no include \masm32\MasmBasic\ here
            GuiParas equ "Hello jj2007", x650, y20, w200, h222, cblack, b00FFFFD0h            ; xpos, ypos, width, height, color, bgcolor
            GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
            include \masm32\MasmBasic\Res\MbGui.asm            ; select Init and hit F6 to test this snippet
            Event Menu
            MsgBox 0, Str$("You clicked menu #%i", MenuID), "Hi", MB_OK                        ; MenuID is zero for the first entry, 1 for the second etc
            Event Message
            inc msgCount
            deb 4, "msg", chg:msgCount                                    ; , wParam, lParam            ; see deb
            Event Paint
            GuiTextBox 99.9-90, 10, 80, 50, "This is a simple text box", bcol RgbCol(255, 255, 255)
            For_ ct=0 To 9
                        imul ecx, ct, 25
                        mov ecx, RgbCol(ecx, 0, 0)
                        GuiText ct*8+7, ct*16+7, Str$("Line %i ", ct+1), fcol ecx
            GuiEnd                                    ; replaces end start here
            other Gui macros (see extended example here):
                        GuiColor foreground, background
                        GuiCls clears the canvas
                        GuiTextBox text, left, top, right, bottom [, style]
                        GuiText x, y, text [, style]
                        GuiLine x0, y0 [, x1, y1]                                    ; 4 args: from ... to, 2 args: ... to
                        GuiEllipse x, y, radiusX, radiusY
                        GuiCircle x, y, radius
                        GuiSetFill brush

Rem            A quick-and-dirty way to write a Windows application in a few lines - click here for a full example.
            Supported Events are: Message (for all messages), Create, Menu, Key, Size and Paint

MakePath                                    ; for use with gdi+ in GUI applications
                        MakePath MyBezier, Bezier(150:0, 0:90, 0:120, 300:300)            ; 4/7/10 etc * x:y
                        MakePath MyCircle, Circle(333)                                    ; radius
                        MakePath MyEllipse, Ellipse(500:300)                        ; x, y, width, height
                        MakePen hPen, RgbCol(100, 0, 0, 255), width 3            ; blue, half transparent
                        MakeBrush hBrush, RgbCol(100, 255, 255, 0)
                        Event Paint
                        GuiDraw MyCircle, hPen, 100, 200                        ; draw outline at x=100, y=200
                        GuiFill MyEllipse, hBrush, 10.0, 20.0            ; fill ellipse at x=10%, y=20% of client area
Rem                        - MakePath creates a Gdi+ path object, for use with GuiDraw or GuiFill ; see here for a full example
                        - Gdi+ has the bad habit to trash the FPU; if you use it, preserve your data between Guixx calls
                        - use MakePen and MakeBrush for outline and interior, and make sure you specified 4 args in RgbCol(...)
                        - see here for a pie chart example using SetLegend
                        - available shapes are Arc, Bezier, Circle, Ellipse, Pie, Polygon, Rect

SetDoc$, GetDoc$, SetTitle$, SetStatus$            ; for use with Gui apps
            .if FileSave$("Assembler=*.asm|MasmBasic=*.asc|All files=*.*")
                        FileWrite FileSave$(), stream:hMyEdit, If?(InstrOr(FileSave$(), ".rtf" or ".asc", 1), SF_RTF, SF_TEXT)
                        SetDoc$ FileSave$()            ; assign new document name and set window title accordingly
            SetStatus$ GetDoc$()            ; set doc name without path to first column of status bar
            SetStatus$ GetDoc$(full)            ; same but will full path
            SetStatus$ "Добрый Вечер", 1            ; just a test - Unicode is no problem
            SetStatus$ GetDoc$(full), 2            ; set doc name with full path to third column of status bar
            Switch MenuID
            Case 3 .. 12            ; the Masm32 Case macro accepts a range
                        SetMenuLanguage MenuID-3            ; change menu language on the fly
                        SetTitle$            ; use current document name and menu language
Rem            check here for an example

            Let A$=New$(100)                        ; 100 zero-initialised bytes
            mov ecx, 500                        ; reg32 except eax+edx are allowed to specify size
            Let My$(0)=New$(ecx)                        ; destination can be an array element
            Let My$(0)=My$(0)+New$(1000)+My$(0)+"#bye"            ; and concatenation is legal code
            Clr$ My$(0)                                    ; it is recommended to clear the old string before a new fat allocation,
            mov ecx, 900000000                        ; precisely because concatenation of the old and the new string is allowed,
            Let My$(0)=New$(ecx)                        ; this may work on most Windows version, but do thorough testing!
Rem            - returns pointer to zero-initialised memory in eax
            - you can also use Let Some$=String$(n, 0)
            - you may free this memory with Clr Some$, but it will also be freed automatically with Exit
Key            new$(

Alloc16, Free16
include \masm32\MasmBasic\            ; download
            Dim PtrSSE() As DWORD
            For_ ct=0 To A16Max-1            ; 100 aligned pointers
                        Alloc16 Rand(10000)
                        mov PtrSSE(ct), eax
                        Print Hex$(al), " "
            For_ ct=0 To A16Max-1
                        Free16 PtrSSE(ct)
Rem            for use with SSE instructions that require 16-bit alignment

            MemSet offset somebuffer, 0, 1000                                    ; dest. pattern, #bytes
            MemSet offset somebuffer, "x", 1000                                    ; 1000 * x
            MemSet offset somebuffer, Mirror$("abcd"), 1000            ; 250 * abcd, slightly faster
            mov eax, Chr$("Masm32 is great ")                                    ; string must have (at least) 16 bytes
            movups xmm0, oword ptr [eax]
            mov edx, offset somestring
            MemSet edx, xmm0, 99
Rem            returns end of buffer in edx

MemState                        ; detect leaks
            MemState("leaked kBytes: %i")                                    ; prints to console the leaked bytes using Str$("...") format
            MemState("leaked %i kB", abs del)                                    ; prints both the current absolute value and the leaked bytes
            MemState("leaked kBytes: %i\n", always)                                    ; prints both the current absolute value and the leaked bytes
            PrintLine "PagefileUsage: 0x", Hex$(MemState(PagefileUsage))            ; print a member of PROCESS_MEMORY_COUNTERS
Rem            - use in loops to detect memory leaks
            - standalone format prints only if a change was detected; use e.g. msTrigger=8192 to change the threshold (default is 4k)
            - function format returns, standalone format leaves all registers intact
            - second arg may be a combi of absolute, delta and always (=print even if there was no change); default is dif
            - with msUse=0, no code will be generated (the function format, e.g. MemState(PagefileUsage), returns -127)
            - default unit is kBytes; use msUnit=10 for kBytes, msUnit=20 for MBytes and msUnit=0 for bytes

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$
            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:
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
            401,            "Нажмите на эту кнопку"            ; "Click on this button" in Russian
            402,            "Добро пожаловать"            ; "Welcome" in Russian

Mlg$            multilingual strings
            include \masm32\MasmBasic\
            Init            ; *** select Init and hit F6 ***
            Mlg$(File:"\Masm32\MasmBasic\Res\Tb4Res\")            ; file:"path" loads the multilingual matrix
            For_ ct=0 To 5
                        Mlg$(Language: Mid$("ENESBRITDEFR", ct*2+1, 2))            ; example: Mlg$(Lang:IT) sets Italian
                        PrintLine Mlg$(LgTx), ":", CrLf$, Tb$, Mlg$(_ASM), CrLf$, Tb$, Mlg$("_BEL")
                        PrintLine Tb$, Mlg$("_USA"), CrLf$, Tb$, Mlg$(_AUS), CrLf$, Tb$, Mlg$(_AUT)
Rem            for use with multilingual GUIs; matrices should be edited as spreadsheets, and must be saved in tab format

            ; 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

            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
            - see also MemSet
Key            string$(

            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
#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\")            ; 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 "Test", Chr$(" the library")            ; appends a CrLf$
            PrintLine                        ; no arg prints just a CrLf$ (same as Print without arguments)
Rem            see Print above

            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, SetCpUpperLower$
            SetCpUtf8                        ; set codepage to UTF8
            SetCpAnsi                        ; force ANSI codepage
            SetCpUpperLower$ CP_UTF8            ; assume source for e.g. Let esi=Upper$(esi) is UTF-8 encoded; decode to same codepage
            SetCpUpperLower$ CP_UTF8, 1252            ; encode as UTF-8, decode as standard Windows codepage
            SetCpUpperLower$            ; reset to user's systemwide codepage setting (usually CP 1252)
Rem            - internal macros for switching between normal Ansi and Unicode Print
            - SetCpUpperLower$ source [, dest] may be needed if text comes from a file, and does not have the same encoding
            - in RichMasm, by pressing Ctrl F6 the user can decide to build the project entirely in UTF-8; codepages will be set accordingly

            Let esi=ConvertCp$(esi, CP_UTF8, 1252)                                    ; convert esi from UTF-8 to standard Windows
            SetCpAnsi                                                            ; set codepage 1252 for printing
            PrintLine "Test: [", ConvertCp$(esi, CP_UTF8, 1252), "]"                        ; print convert esi from UTF-8 to standard Windows
Rem            - may be handy e.g. with UTF-8 encoded Files$()
            - for UTF-8 -> Unicode, use wChr$()
            - for Unicode -> UTF-8, use Utf8$()

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

            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
            cBlack, cWhite, cGray, cBlue, cGreen, cCyan, cRed, cMagenta, cYellow
            cDarkGray, cDarkBlue, cDarkGreen, cDarkCyan, cDarkRed, cDarkMagenta, cDarkYellow

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

At, CColor
            ; one column right of some counter, next line, blacl on yellow:
            Print At(ct+1, Locate(y)+1) CColor(cBlack, cYellow) "Hello"
            Print At(5, 5) CColor(cBlack, cYellow) "Attention, no commas after At() and CColor() !!"
Rem            for use with Print only; separate with blanks, not commas, from the first Print argument

            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

;            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"
            whateverproc proc    thedd1, thereal8:REAL8, thedd2, thereal4:REAL4
                        PrintLine Str$("arg2=%f", thereal8)
                        PrintLine Str$("arg3=%f", thedd2)
                        PrintLine Str$("arg4=%f", thereal4)
            whateverproc endp
            end start
Rem            make sure the proc specifies the non-dword args correctly

include \masm32\MasmBasic\
            SetGlobals timeinfo:SYSTEMTIME, f$="Now it's %I:%M %p"
            Init                                    ; select init and hit F6 to test this snippet
            Inkey "strftime(): ** ", CRT(strftime, buffer, 80, f$, addr timeinfo), " **"
            EndOfCode                        ; for parameters, see e.g. C++ strftime()
            Output: strftime(): ** Now it's 12:00 AM **
Rem            - allows to use C RunTime functions inter alia with Let and Print, in a format close to C
            - note that some C functions return numbers in ST(0); use void ..., then Print Str$(ST(0)#) to display them
            - if one para is called buffer as shown above, it will be allocated and freed automatically

gsl            (GNU Scientific Library interface)
include \masm32\MasmBasic\
; 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)

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                                                            ; select init and hit F6 to test this snippet
            SetFloat MyReal8=gsl_stats_mean(offset MyData, 1, 5)            ; ptr, stride 1, 5 elements
            PrintLine Str$("Mean        \t%7f", MyReal8)            ; print with 7 digits precision
            PrintLine Str$("Variance\t%7f", gsl_stats_variance (&MyData, 1, 5))
            fstp st                                                ; just print, then cleanup the FPU
            PrintLine Str$("Standard Dev\t%7f", gsl_stats_sd (addr MyData, 1, 5)#)            ; a trailing # tells Str$() to fstp st
            mov eax, gsl_rng_default()                        ; you can access gsl global variables defined with gslvar
            Mean                                    5.000000
            Variance                        7.620000
            Standard Dev            2.760435
Rem            - if the GSL dll files are not found, an attempt will be made to get them from the oscats site; in case
                of problems, check if your antivirus software interferes with the download, and if yes, act accordingly
            - 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 My$()                        ; create a string array (expand the number of elements as necessary)
            Dim My$(100000)            ; create a string array, preallocate 100000 elements, i.e. 0...100000
            Dim My$(tab)            ; create a two-dimensional string array, e.g. for a table (expand as necessary, use Tb$ as delimiter)
            Dim My$(csv)            ; same but using comma-separated values for storing to a csv file
            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

            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

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

            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

ArrayFill, wArrayFill
            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

            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)

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

            ; convert string to a string array:
            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
            ; convert string to a numerical array:
            include \masm32\MasmBasic\
            SetGlobals a1$="123 27.5    28.49    -56.78 '20h' 0x40 11111b/123456789"            ; string with a wild mix of number formats
            Init            ; < < select Init and hit F6 to test this snippet
            Dim MyDw() As DWORD
            For_ ecx=0 To Fn(StringToArray a1$, MyDw())-1            ; strings to dwords conversion
                        PrintLine Str$("MyDw(%i)=", ecx), Str$(MyDw(ecx))

Rem            - returns #elements in eax; in For_ ... Next loops, you may use Fn(...)-1 as shown above
            - numeric arrays must be declared (BYTE ... QWORD, REAL4/8/10), but there is no need to Dim a string array before

            include \masm32\MasmBasic\
            Init            ; < < select Init and hit F6 to test this snippet
            For_ ct=0 To Split$("Masm32 is great", " ", My$())-1
                        PrintLine "[", My$(ct), "]"
Rem            converts a string to an array using the specified delimiter; under the hood, StringToArray is being used

                        ; converts a string array to one string
            include \masm32\MasmBasic\
            Dim My$()
            Let My$(0)="Masm32"
            Let My$(1)="is"
            Let My$(2)="great"
            PrintLine "[", Join$(My$()), "]"            ; with no second arg, output is [Masm32\nis\ngreat], i.e. items separated by 13, 10
            Print "[", Join$(My$(), " "), "]"            ; shows [Masm32 is great] with " " as second arg
            Join$(MyArray$(), Dest$)            ; convert a string array to one linear Dest$ separated by CrLf$
Rem            - returns ptr in eax when used in function form, i.e. mov var, Join$(array$(), "fillstring")
            - standalone, i.e. Join$(array$(), dest$) can be slightly faster than Let dest$=Join$(array$())

            include \masm32\MasmBasic\
                Init                        ; select Init and hit F6 to run this snippet
                Recall "\Masm32\include\", L$()
                Print Str$("%i lines loaded, now filtering for STRUCT:", eax)
                For_ ecx=0 To Filter$(L$(), "STRUCT", 1, 5)-1            ; 1=keep if match, 1=case-insensitive+4=full word
                        Print Str$("\n%i\t", ecx), L$(ecx)                        ; lists all strings that contain STRUCT as full word
            end start
Rem            - returns remaining #strings in eax
            - syntax: mov ecx, Filter$(array$(), match$ [, include] [instr mode]
            - include: 0 (exclude matching strings) or 1 (include, default)
            - instr mode: see Instr_(); 1=case-insensitive, 4=full word
            - in general, Extract$() offers more and better options

            include \masm32\MasmBasic\
            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

            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)

            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 L$(), Files$()
            Swap MyA$, MyB$
Rem            - exchanges two arrays or two variables of the same type
            - allowed numeric types: DWORD, QWORD, REAL4, REAL8, REAL10
            - mixing arrays and strings will fail and trigger an error message
            - to Swap two strings of the same array, use Swap #x$(ct), #x$(ct2) (works only with JWasm!)
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            - returns pointer to resulting string in eax
            - 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 esi=eax works if eax is a valid pointer, 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 xor esi, esi before Let esi=... other code ... followed by Clr$ 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 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 D2$(tab)            ; create a two-dimensional, tab-delimited string array
            Insert D2$(0, 1), 3            ; insert 3 cells at row 0, column 1
            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)
Key            ins

            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

            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

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

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

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

            mov edi, offset WebInterface
            lea edx, vEmpty
            CoInvoke [edi], IWebBrowserVtbl.Navigate, Ole$(""), 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$
            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$
                wPrint "Now what happened to edi? Here it is: [",    edi, "] ", wCrLf$

            mov ebx, "A"
            Print CrLf$, Chr$("The alphabet from a loop: ", ebx)
                            inc ebx
                        Print Chr$(ebx+32)
            .Until ebx>="Z"
            For_ ct=0 To 9
                        Rand("a", "z", c1)            ; create random text
                        Rand("a", "z", c2)
                        Rand("a", "z", c3)
                        Rand("a", "z", c4)
                        Rand("a", "z", c5)
                        Let some$(ct)=Str$(ct)+Tb$+Chr$("_", c1, c2, c3, c4, c5, "_")
                        PrintLine some$(ct)
            mov ebx, 60
            Print Chr$(13, 10, "This is ", ebx, "great", ebx+2)            ; <great>
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, uLen
            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
            Let esi="Добро пожаловатьäöü"                        ; assign a UTF-8 string
            uPrint "[Добро пожаловатьäöü]"                        ; or print it directly
            Print Str$(" is %i characters long (expected: 19)\n", uLen(esi))
Rem            returns length in eax; bytes for ANSI, chars for Unicode; for UTF-8 encoded strings, Len returns bytes, uLen returns chars

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, Alert()
            MsgBox 0, "MasmBasic is cute", "Welcome:", MB_YESNOCANCEL
            .if Alert("text", "title", MB_YESNO)==IDYES
                        ... do something ...                        ; same as MsgBox but returns a value
            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            MsgBox returns result in eax; ecx is preserved
Key            mb, wmb, alert

            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$ 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)

            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
            MyTest endp
Rem            very fast and compact (5 bytes per call), leaves all registers intact
Key            clv

            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)
            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, InstrOr
            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
            - if no match is found, zero is returned in edx, and eax points to the start of the 'haystack'; same if 'needle' is empty
            - six 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: test several patterns:            InstrOr("Test", "Te" or "st", 1)                        ; 3 args (startpos is 1)
                    F: extra fast mode:            Instr_(FAST, My$(ecx), "Hello", 0)            ; 4 args, no startpos, modes 0+2 only
            - case & mode (bitwise flag):
                0=case-sensitive, +1=insensitive, +2=intellisense (Name=name, i.e. case of first char ignored),
                +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
            - the FAST option is typically about twice as fast as CRT strstr, but 3..4 times as fast when used with
                string arrays (Intel Core i5 timings for counting a rare word in a file with 800 MB, 6 Mio lines):
                        232 ms            for fast Instr_
                        795 ms for "normal" Instr_
                        999 ms for Masm32 InString
                        929 ms for CRT strstr
            - using FAST, binary search in haystacks containing zeros is possible by assigning the buffer size to edx:
                    mov edx, LastFileSize            ; any info on length of buffer can be used with edx
                    Print Str$("Pos in executable: %i", Instr_(FAST, esi, "kernel32", 2 or 64)            ; 2=first char case-insensitive, 64=len in edx
            - Rinstr is about 10% slower than Instr_ but about 10% faster than Masm32 InString
            - 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\")                            ; Create a buffer for
            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\")                            ; to test wCount, open the ANSI version in an editor and save it as UNICODE
                        wLet esi=FileRead$("\Masm32\include\")             ; 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))
Rem            - counts occurrences of a pattern
            - returns counter in edx
            - options as in Instr_/wInstr

            mov ecx, LineCount(pBuffer)                        ; count the number of carriage returns (Ascii 13, 0Dh) in a zero-delimited buffer
            mov ecx, LineCount(pBuffer, 1000)            ; count CRs in the first 1000 bytes of the buffer
            mov ecx, LineCount(pBuffer, 1000, lf)            ; count linefeeds (Ascii 10, 0Ah)
            mov ecx, LineCount(pBuffer, 1000, "a")            ; count the char a
Rem            - returns DWORD in eax
            - use instead of EM_EXLINEFROMCHAR (which returns wrapped lines)

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

            ; 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
            xsWildcard            ; allow a wildcard for left pattern e.g. for finding *> in HTML pages
            *) 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\            ; download
            ; First, let's get some useful source string:
            Let ecx=FileRead$("\Masm32\include\")+FileRead$("\Masm32\include\")
            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$
            Close #1            ; file #1 closed
            Inkey Str$("%i structures found\n", ebx)
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$

            .if StringsDiffer("Hello", "Hallo")
                        MsgBox 0, "The two strings are different", "Hi", MB_OK
            test eax, StringsDiffer("Hallo", "HALLO", 1)
            .if Zero?
                        MsgBox 0, "The two strings are equal", "Hi", MB_OK
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))

            .if FilesDiffer("MyFileA.txt", "MyFileB.txt")
                        MsgBox 0, "The two files are different", "Hi", MB_OK
            Let esi="MyFileA.txt"
            test eax, FilesDiffer(esi, offset MyFileB)
            .if Zero?
                        MsgBox 0, "The two files are equal", "Hi", MB_OK
                        MsgBox 0, "The two files are different", "Hi", MB_OK
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$()
            - may not work for UTF-8 encoded strings; try getting the index via Instr_(..), uLen, etc
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$(...))
            - see also SetCpUpperLower$ for dealing with codepage problems
Key            lower$(, upper$(

            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$(

            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
            - in Print or Let lines that use e.g. Str$(), Date$ may misbehave; use fDate$(0) instead
            - 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
            - in Print or Let lines that use e.g. Str$(), Time$ may misbehave; use fTime$(0) instead
            - 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

            include \masm32\MasmBasic\
            Init                        ; select and hit F6 to test this example
            Print "Yesterday, ", fDate$(-1, "dddd dd MMMM yyyy "), fTime$(0, "HH:mm"), Str$(", we were in ISO week %i\n", IsoWeek(-1))
            Print "Today, ", fDate$(0, "dddd dd MMMM yyyy "), fTime$(0, "HH:mm"), Str$(", we are in ISO week %i\n", IsoWeek())
            Let esi="11.12.2017"
            Print esi, Str$(" will be in week %i", IsoWeek(esi))
Rem            returns ISO 8601 week; you can specify a negative or positive offset, e.g. IsoWeek(-1) for yesterday's week

            PrintLine "Time zone is UTC", GetTZ$()                        ; Time zone is UTC+1
            PrintLine "Time zone solar = ", Utf8$(GetTZ$(StandardName))            ; StandardName as Unicode string
            PrintLine "Time zone legal = ", Utf8$(GetTZ$(DaylightName))
Rem            uses GetTimeZoneInformation; values for other TIME_ZONE_INFORMATION members are returned in xmm0

fDate$(), fTime$(), wfDate$(), wfTime$()
            ; formatted date and time; takes a pointer to a SYSTEMTIME structure and an optional format string
            include \masm32\MasmBasic\
            Init                                                                        ; select and hit F6 to test this code
            PrintLine "Tomorrow is the ", fDate$(1, "dd MMM yy")                                                ; formatted date
            PrintLine "In ten minutes, i.e. at ", fTime$(10, "HH:mm"), ", I will go to bed"            ; formatted time
            Print "Today, ", fDate$(0, "dddd dd MMMM yyyy "), fTime$(0, "HH:mm"), Str$(", we are in ISO week %i\n", IsoWeek())
            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)
            ; 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()
            - immediate values from -128 to +127 are interpreted as offsets: days for fDate$(dx), minutes for fTime$(dx)

TimeSF                        ; translate date string to system and file time           
            void TimeSF("01.02.2013 12:34:56")            ; returns FILETIME value in xmm0 for further use
            Print Str$("%i days since 1st of January\n", Age(TimeSF("01.01."), d))            ; Age accepts a FILETIME in xmm0; year, hour, minutes as of 'right now'
Rem            returns FILETIME in xmm0 and pointer to SYSTEMTIME structure in eax

            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()            ; start timing without arguments
            Delay 500                        ; simulate a loop...
            Print Str$("Time elapsed: %4f seconds\n", NanoTimer(s))                        ; s, ms or µs
            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

            include \masm32\MasmBasic\
            Init            ; select Init and hit F6 to run this snippet
            nops 123
            Align64            ; align 64 is not available in MASM or JWasm
            mov eax, $            ; get current location
            Print Hex$(eax)
            xor ecx, ecx
            .Repeat            ; loop start is aligned to 64 bytes
                        inc ecx
            .Until ecx>100
Rem            may help to fit a loop into an instruction cache line; use e.g. AlignX 32 for other alignments

Recall                                    ; fill a string array with content from file or URL
            Recall "\masm32\include\", 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)

            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
Rem            - returns lines in eax (null if an error occurred), and the number of total bytes read in edx
            - autodetects Windows (CrLf), Linux (Lf) and Mac (Cr) text files
            - see Store below for saving arrays
            - instead of a filename, you can specify a URL (select init and hit F6 to test this example):
                include \masm32\MasmBasic\
                Init            ; show a data set from the World Health Organisation
                Recall "", who$(), csv
                Print Str$("%i records downloaded", eax)
                For_ ct=0 To who$(?)-1
                        Print CrLf$, who$(ct, 0)
                        Print At(33, Locate(y)) Spc2$, who$(ct, 4), Space$(40)
                        For_ ecx=8 To 14            ; columns 8...14 contain the data
                                    Print At(ecx*8-25, Locate(y)) Spc2$, who$(ct, ecx)
                end start
            - 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 "", MyRec$()                        ; store the whole array excluding trailing empty strings
            Store "", MyRec$(), 1000                        ; store the first 1000 strings of the array
            StoreUtf8 "", 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\", 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

            Print Str$("%i lines written to SortedAscending.txt\n", esi)
            QSortDesc L$(), 0                        ; now sort descending, all
            Store "SortedDescending.txt", L$()                        ; and write them back to file
            ; spreadsheet mode - you can sort tab-delimited files by column:
            Recall "", 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$(), 0, 2003h                        ; same but 2000h added: sort using Val() of column 3
            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
            - QSortMode may take, as a third parameter, a DWORD array:
                QSortMode casemode, skipmode, MyIndex()
                On return, this array contains the original index of the sorted strings. Note: when using Insert or Delete on the
                string array, do the same for the index array, otherwise a new sort will recreate it.
            - when sorting by column, the header row is skipped by default; use 0 as 3rd arg 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

            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
                        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

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 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

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

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

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

            CASE WM_CREATE
                        Dim MySinus() As REAL8            ; example: in the CREATE handler,
                        xor ecx, ecx            ; we fill an array with sinus values
                        push 180
                        fild stack
                        push ecx
                        fstp REAL4 PTR stack[4]
                                    mov stack, ecx
                                    fild stack
                                    fmul REAL4 PTR stack[4]
                                    fstp MySinus(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 MySinus()
                        ArrayPlot exit, "Sinus"                        ; finish with a title

Rem            no return value
Key            apl

RgbCol, CgaCol, SysCol
            mov eax, RgbCol(ebx, 0, 0)                        ; any mix of reg32, vars and immediates is allowed
            mov bl, 127                        ; only bl will be used
            invoke SetTextColor, esi, RgbCol(ebx, 0, 0)            ; Red Green Blue for Gdi32 functions
            invoke SetTextColor, esi, SysCol(ebx)                        ; use the system palette
            mov hPG, rv(GdipCreatePen1, RgbCol(alpha, red, green, blue), FP4(3.0), UnitWorld, addr pPen)
Rem            - Gdi functions want RGB values, i.e. three parameters
            - in contrast, GdiPlus expects ARGB, i.e. four parameters; the first one, alpha, defines
                transparency ranging from 0=fully transparent to 255=opaque
            - CgaCol(index) may be used with console colours; see ConsoleColor for identifiers
            - SysCol(index) returns a GetSystemPaletteEntries colour for use in Gui applications

            ; 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

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 %Jf", PI)                        ; precision J = 19 digits, f=float
            Print Str$("The number PI is %Je", PI)                        ; precision J = 19 digits, e=force exponential
            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 for floats, 16 for doubles, 19 for REAL10 variables and ST(0); range 1.0e+-309
            - if a string is given as input, %i (integer) or %f (float) mark its insertion point, and e.g. %9f its precision
            - floating point precision is 1...9A...J digits, with J meaning 19 digits. If the number starts with 9.23 or higher,
                digit #19 may be incorrectly rounded up or down; use DefNum 18 if this is unacceptable; at 19 digits precision, rounding
                may be incorrect, and the last digit may be one higher or lower than expected.
            - 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$(

            Dim MySinus() As REAL8
            For_ ct=-400 To 400
                        SetFloat MySinus(ct+400)=Sinus(ct)
Rem            use with functions that return values in ST(0), e.g. Float(); do not use fstp st afterwards

            include \masm32\MasmBasic\
            Init                                                ; select init and hit F6
            Dim float() As REAL4
            For_ ct=0 To 9
                        SetFloat float(ct)=Float(ct)                        ; converts integer ct to ST(0)
                        Print Str$("i=%i", ct), Str$(", f=%2f\n", float(ct))

            Inkey "hit any key"
Rem            int->float cast, returns ST(0); for use with SetFloat

Sinus, Cosinus
            include \masm32\MasmBasic\
            Init            ; select Init and hit F6 to run this snippet
            Print "x", Tb$, "sin x", Tb$, Tb$, Tb$, "error"            ;    ## compare results to fsin ##
            testStep=30            ; change as needed
            For_ ecx=-90 To 450 Step testStep
                        Print CrLf$, Str$(ecx), Tb$, Str$(Sinus(ecx)), Tb$
                        void Sinus(ecx, 1)            ; 1=fpu fsin
                        Print Str$("%3f", ST(0))
                        fstp st
            PrintLine CrLf$
            Print "x", Tb$, "cos x", Tb$, Tb$, Tb$, "error"
            For_ ecx=-90 To 450 Step testStep
                        Print CrLf$, Str$(ecx), Tb$, Str$(Cosinus(ecx)), Tb$
                        void Cosinus(ecx, 1)            ; 1=fpu fcos
                        Print Str$("%3f", ST(0))
                        fstp st
Rem            returns REAL10 value in ST(0); store it to a variable with fistp or fstp. If you use it in Str$(), it must be followed by fstp st

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 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            ; 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 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\
srcr10            REAL10 12345.6789
pcr10            REAL10 12.3
srcdd            dd 123

            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"
end start
Rem            source can be almost anything, same for percentage

            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...ABCDEFGHIJ as shown above

            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

            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
            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)

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

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

            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)
                        Print Str$("MyPI_hi at %f is higher than the real PI\n", MyPI_hi)
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

            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")
            mov ebx, Val("12:34:56", 3)            ; take the third value, e.g. 56 seconds
            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
                                    MsgBox 0, Str$("We found a decimal: %i", ebx), "Val:", MB_OK
                        MsgBox 0, "Sorry, the format was no good", "Val:", MB_OK

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 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

            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
            Print Str$("Square root left on FPU \t%If\n", Sqrt(2, ST(0)))            ; you may provide a destination as second arg,
            fstp st                                                            ; e.g. ST(0); for the latter, fstp st cleanup is needed

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

            MyRecord RECORD SlotHigh:4, SlotMid:7, SlotLow:7, SlotRest:32-4-2*7            ; the order is high to low
            MyRec            MyRecord <>            ; define an empty 32-bit record with 4 fields
            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

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

include \masm32\MasmBasic\
            Let esi=FileRead$("\Masm32\qEditor.exe")
            .if GetHash(esi, LastFileSize, sha)                                                            ; FileRead$ sets LastFileSize, also for binary files
                        PrintLine "The SHA of qEditor.exe is    ", Tb$, Hex$(xmm0), " ", Hex$(ecx)
                        PrintLine "Hashing failed: ", Err$()
            void GetHash(esi, LastFileSize)
            mov ecx, edx                                                            ; fifth word returned in edx
            Print "Fast MD5 of qEditor.exe is ", Tb$, Hex$(xmm0), CrLf$
            GetHashRev=1                                                            ; use official MD5 byte order
            .if GetHash(FileRead$("\Masm32\qEditor.exe"))
                        PrintLine "qEditor, byte order MD5 specs: ", Tb$, Hex$(xmm0)            ; MD5, len of string will be calculated
Rem            - returns success (1) or failure (0) in eax
            - four dwords are returned in xmm0
            - in case of SHA, the fifth dword will be in edx
            - results can be displayed as a Hex$(xmm0), plus Hex$(edx) for SHA. The byte order will be reversed, compared to
                online hash calculators. You may use GetHashRev=1 to follow the MD5 specs, but more code will be generated
            - with only one para, GetHash calculates the length; this is meaningful only for text files

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

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

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
Rem            returns result in eax

            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

            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"
            ; 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"
                        PrintLine "dox files unknown here"
            ; 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(

"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
            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
Rem            - returns #added strings in eax, i.e. not the total; if you use the same array repeatedly, either keep track
                of the start value, or use some$(?) to get the total #elements
            - see also two detailed examples in MbSnippets.asc

            include \masm32\MasmBasic\
            Init                        ; select init and hit F6 to test this snippet
            Print "ct", Tb$, "ID", Tb$, "path"
            For_ ecx=0 To GetProcessArray(?)-1
                        Print Str$("\n%i\t", ecx), Str$(MbProcID(ecx)), Tb$, MbProc$(ecx)
            Inkey CrLf$, "--- hit any key ---"
Rem            - creates a string array MbProc$() and an array of process IDs, MbProcID()
            - returns # strings when used with (?)
            - or use it standalone without args: GetProcessArray()

            include \masm32\MasmBasic\
            Init                        ; select init and hit F6 to test this snippet
            Print "ct", Tb$, "device"
            For_ ecx=0 To GetDevicesArray(?)-1
                        Print Str$("\n%i\t", ecx), MbDevices$(ecx)
            Inkey CrLf$, "--- hit any key ---"
Rem            - creates a string array MbDevices$()
            - returns # strings when used with (?)
            - or use it standalone without args: GetDevicesArray()

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

RichMasm options
OPT_Susy            Console
OPT_arg1            "first argument passed"
OPT_arg2            "second argument passed"
OPT_Tmp2Asm            1