MasmBasic Quick Reference

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

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

signed ErrLines For_ Fn mcs xmov jinvoke @LibUsed$() @AsmUsed$ @Upper$ @Lower$ @MbRet MbWinVersion ComCtl32$ EndOfCode wComCtl32$ Switch_ Case_ Default_ Endsw_ Try Catch Finally TryRTE Init Exit CL$() wCL$ If_ If? Div_ IsTrue CodePage$ CodeSize DlgDefine DlgControl DlgShow SetGlobals Enum Enum$ Choose Data Read Dll Declare HeapStrings deb fdeb ifdeb Err$() SetErrLine Sound Asc Cvi Cvl Odd Abs ModZero Min Max PopCount Bsr64 Rand FolderOpen$ FileOpen$ FileSave$ wF Open wOpen Close Seek Lof Loc Eof Input LineInput Input$ wInput$ Prompt$ Rename WritePipe ClosePipe Launch ShEx SetLaunchTimeout Launch$ LaunchEndPos ExitCode Exist LastFileSize LastFileName$ LastFileDosName$ IsFolder IsRichEdit Kill Touch GetFiles AddFiles Files$ GetFolders AddFolders GfCallback SortFiles GfSize GfDate$ GfTime$ GfAgeHours GfAgeMinutes GfAgeMs GfLastWrite GfGetInfo GfSetInfo Age CurDir$ SetMru AddMru MruText$ ExpandEnv$ wExpandEnv$ ExeFromWin$ ExeFromExt$ GetFileProps FilePropName$ MakeDir ZipFiles UnZipFiles UnzipFile UnzipFile FileRead$ LastFileSize Download WM_DOWNLOADFINISHED 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$ wAddWin$ SetSel$ Sel$ wSel$ MakeBrush MakeFont PickFont ImgPaint ToolTips WinByTitle App16 Clip$ wClip$ . SetClip$ wSetClip$ SetHtmlClip$ MsgMonitor GuiImage GuiImageCallback SaveImageToFile GuiImageSpeed GuiImageFrame GuiControl GuiGroup Link$ EndOfEvents GuiEnd SetListbox SetCombobox Event GuiParas GuiMenu GuiColor GuiText GuiCls MakePath MakePen SetLegend SetDoc$ GetDoc$ SetTitle$ SetStatus$ New$ Resize Alloc16 Free16 MemSet MemState Res$ wRes$ wRec$ ResFile$ Lg$ Geo$ MsgTable$ String$ Space$ SpaceToTab$ Inkey wInkey Print wPrint Print wPrint CurDir$ PrintLine wPrintLine PrintBuffer PrintRtf PrintCpu Cpu$ SetCpUtf8 SetCpAnsi SetCpUpperLower$ ConvertCp$ ConsoleColor Locate( At CColor crtbuf CreateInvoke CRT QuadMath Quad Quad$ gsl SetPoly3 GetPoly3 Dim Erase VarPtr ArrayFill wArrayFill ArrayStripDuplicates ArraySet StringToArray Split$ Join$ Filter$ ArrayMerge Swap Let wLet Cat$ wCat$ Lset Insert Delete GuidFromString GuidsEqual CoInvoke Ole$ BSTR wData wChr$ Utf8$ Chr$ Len wLen u MbCopy Bmove SafeCopy MbCopyz MsgBox wMsgBox Alert( Clr Clr$ Cls Cls ClearLastError ClearFileCache ClearLocals _Local StackBuffer Instr_ Rinstr wInstr InstrOr Rinstr wRinstr Count wCount LineCount Extract$ StringsDiffer wStringsDiffer FilesDiffer u Left$ wLeft$ Mid$ wMid$ Right$ wRight$ Lower$ wLower$ Upper$ wUpper$ Trim$ Ltrim$ Rtrim$ Qtrim$ Ntrim$ Mirror$ Date$ wDate$ Time$ wTime$ IsoWeek GetTZ$ fDate$() fTime$ wfDate$ wfTime$ TimeSF Timer NanoTimer Align64 AlignX Recall Csv2Tab wRecall Store QSortDesc Scramble QSortMode ArraySort ArrayMinMax ArrayMean ArrayRead ArrayStore ArrayIndex ArraySearch ArrayLoadMap PaintMap MapColours SetFolder ArrayMapRegion CanvasZoom ArrayPlot ArrayPlotValues SetAxisX SetAxisY RgbCol CgaCol SysCol Red Green Blue Str$ wStr$ Quad$ SetFloat SetInt Float MulQQ Floor Ceil Sinus Cosinus rSinus rCosinus FpuSave FpuRestore FpuPush FpuFill FpuSet Log2 LogE Log10 Exp10 Exp2 ExpE ExpXY Percent DefNum Hex$ HexDump$ Bin$ Qcmp Ocmp Oword16 Fsign Fcmp Val MovVal Sqrt SetField GetField GetHash SetFlags Flags MouseX MouseY MouseK Delay void voidTrue voidFalse GetRegVal SetReg64 SetRegVal GetRegKeyArray GetRegArray SetReg64 GetProcessArray FindProcess GetDevicesArray PushText Masm32

; The smallprint: this library is provided "as is", and the usual disclaimers apply.
; ---------------- This help file refers to MasmBasic version 11 May 2019 ---------------
; 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\
; ------------------------------------------------------------------------------------------------------------------
; Hit F6 to assemble, link & run the Hello World example:
include \masm32\MasmBasic\
    PrintLine "Welcome to assembly, friend from ", Geo$(), "..."
    MsgBox 0, "Wow, it works!!!!", "Hi", MB_OK     ; It worked? Use a template, or try 90+ snippets
    Print "OK - press any key"                    ; It didn't? Give me feedback here.
; ------------------------------ same as dual 32-/64-bit assembly code: -----------------------------
; select Init below, then hit F6 to build & run this example:
include \masm32\MasmBasic\Res\
    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 ... .exlseif Len(My$)==2 - this does indeed 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
                For_ each edi in Files$() : PrintLine edi                            ; one-liner: for..each only, needs a register   
                For_ each ecx in Files$() : <PrintLine Str$("file #%i\t", ForNextCounter), ecx>                ; use for complex statements
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$(), @Upper$(), @Lower$()
                include \Masm32\MasmBasic\Res\
                % echo @AsmUsed$(0)                        ; no quotes
                tmp$ CATSTR , @Upper$(Norwegian)                ; for use in macros
                % echo tmp$
            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

                SomeAlgo proc uses esi arg1, arg2
                push ecx
            @MbRet                                                    ; shout foul if the stack is not balanced
                SomeAlgo endp
Rem        - use instead of ret; active for usedeb=1 or useMbRet=1, otherwise only a ret will be generated
                - if the stack is not balanced, an error message will be written to the console
                - do NOT use at the end of callback functions

                include \masm32\MasmBasic\
                Init                                        ; select and hit F6
                Print Str$("This is Windows version %i", MbWinVersion()), Str$(".%i", ecx)
                Print Str$(", build %i\n", MbWinVersion(build))
                Print Str$("The long story: Windows version %i", MbWinVersion()), Str$(".%i ", ecx),\
                GetRegVal("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ReleaseID", "(no version #)"),\
                Str$(", build %i\nrunning on ", MbWinVersion(build)), Cpu$()
Rem        - returns major version in eax, minor version in ecx, and build in dx
                - HKLM ... works on Win10, but older Windows versions do not have the ReleaseID value
                - for structures that require bytes, use mov xy.byte, MbWinVersion(b)

ComCtl32$        test your manifest - returns the DLL version
                include \masm32\MasmBasic\                ; download
                Init                                        ; select and hit F6
            Print "CC: ", ComCtl32$()
                MsgBox 0, ComCtl32$("This program uses common controls version %3f"), "Hi", MB_OK
            EndOfCode XP                        ; EndOfCode with the optional XP adds a manifest (polink only)
Rem        - returns 5.82 (no or bad manifest) or 6.?? for XP and higher
                - use wComCtl32$ for GuiControl
                - you may use a format string containing %3f

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 .. endif 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, stack4] 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 (or, if there is no Init, select the whole white zone and hit F6).

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 <brackets> around the function:
                        If_ eax==5 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")
                invoke ShowWindow, hEdit1, If?(rv(IsWindowVisible, hEdit1), SW_HIDE, SW_SHOW)    ; toggle visibility for edit control #1
                invoke ShowWindow, hEdit2, If?()                                                ; repeat the action for edit control #2
                .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
                xor ecx, ecx
                Print Str$("zero flag set: %i\txor ecx, ecx\n", If?(Zero?, 111, 222))                    ; you may use the Zero? ...
                or ecx, -1
                Print Str$("zero flag set: %i\tor ecx, -1\n", If?(Zero?, 111, 222))
                Print Str$("carry set: %i\tstc\n", If?(Carry?, 111, 222))                                            ; ... or Carry? flag
                Print Str$("carry set: %i\tclc\n", If?(Carry?, 111, 222))
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
                - !Zero? and !Carry? are not allowed; swap arguments instead
                - when used with no arguments, i.e. If?(), the last result will be used - see ShowWindow example

            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
            PrintLine CodePage$(?)    ; returns DOS cp, e.g. 65001 (UTF-8)
            PrintLine CodePage$(w)    ; e.g. Windows cp, e.g. 1252    (ANSI - Latin I)
            PrintLine CodePage$(1251)            ; 1251    (ANSI - Cyrillic)

                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
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            ; using a start ID
                Enum                                        40:Button*10                                        ; create Button0=40, ..., Button9=50
                Enum                                        #MYSUBLANG_ENGLISH_, 1:US, UK, AUS, CAN, NZ, EIRE            ; use a prefix
                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:
                - multiple IDs can be specified as Enum Button*10, MyEdits*3 etc
                - 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)v)    ; trailing v: 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$()
                For_ ecx=0 To my$(?)-1
                                Print my$(ecx), "."
                Read eax, ecx
                deb 4, "Read again", eax, ecx
                $Data No, quotes, in, this, string, array, 333, 444                        ; use .$Data for a series of strings without quotes
            Read my$()                            ; you can re-use an array, here for the $Data items
                For_ ecx=0 To my$(?)-1
                                Print my$(ecx), "."
                $Data "But, if you really, really need commas, 'quotes' are needed for $Data"
                Read ecx
                PrintLine CrLf$, ecx
Rem        - this is for fans of good ol' BASIC; in general, Recall does a better job
                - 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 then 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
HeapStrings (for debugging)
            include \masm32\MasmBasic\
            Init                                                        ; select Init and hit F6
                For_ ecx=0 To 99                                ; 0 To 100 would produce an error
                                Let esi=Str$("This is string #%i", ecx)
                                lodsb                                    ; forbidden, increments esi!

                HeapStrings                                        ; display all strings
Rem        By default, you can create 100 different strings (if you need more, use string arrays). These strings can be reassigned.
                This works because Let x$="..." checks if x$ is already assigned; if yes, it will be freed. When using registers (Let esi="..."),
                however, you need to be careful not to change the pointer - this would create a fresh string without freeing the previous one.
                If you suspect such a problem because you got an error message, use HeapStrings before the error occurs to check which
                of the strings misbehave
deb, fdeb, ifdeb                            Never was debugging easier...
                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
                ifdeb push eax: push edx                ; this line is active only for usedeb=1; see mcs for multiple commands
            fdeb 1, "But this one will", eax                                ; forced deb overrides usedeb=0
            ifdeb pop edx: pop eax                    ; note the order
            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 (caution with macros - test it...)
                - it can show xmm and FPU registers ST(0)...ST(7)
            - numerical global and local variables can be displayed, but do not use different numerical arrays in the
                    same deb line (numerical arrays use edx, which will be set by the last element 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 the messages to discard from an equate; you can adapt it to your needs:
                    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 "880/200/150"        ; plays frequency 880Hz for 200 ms, followed by 150 ms of silence
            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

                For_ ecx=0 To 19
                                PrintLine Str$(ecx), Tb$, Str$(ModZero(ecx, 3))
Rem        returns 1 if the modulo is zero

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

            Rand()                                                                    ; no args = initialise using rdtsc
            Rand(seed:abcd)                ; use "abcd" as seed (4 letters)
            Rand(seed:0FCDFDEABh)    ; use any binary seed (5+ letters or numbers)
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?
                Rand(pt:pTable())            ; set probability table: uses a DWORD array (->example)
                mov somevar, Rand(pt)    ; return index weighted by probabilities
                Rand(-tests/2, tests/2+1, SomeInteger(ecx), unique)        ; min, max, destination, unique: never get the same int32 value twice
Rem        - high-speed RNG generates pseudo random sequence with excellent randomness
                - for testing, do not initialise; for the release version, use Rand() before the innermost loop (seed is in MbRndSeed[4])
                - 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$

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 sources=*.asc|All sauces=*.as?;*.inc|Resources=*.rc")
                                PrintLine "You opened    [", FileOpen$(), "]"        ; empty bracket: return the result
                                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

            Open "I", #1, "TypeTest.udt"        ; file with user-defined type data
                                Recall #1, MyUDT
                                deb 4, "Test", MyUDT.mydouble, MyUDT.myfloat, MyUDT.mydword
                .Until Eof(#1)                    ; no more data
Rem        returns the sign flag

                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

                include \masm32\MasmBasic\
            Init                                                        ; select Init and hit F6
                Open "I", #1, "\Masm32\include\"
                Dim My$()
                xor ecx, ecx
                                LineInput My$(ecx)
                                .Break .if !edx                ; Eof(#1)
                                PrintLine Str$("Line %i\t", ecx), My$(ecx)
                                inc ecx
                .Until ecx>20
Rem        - provided for compatibility with older Basic dialects; use Recall, it's faster, simpler and safer
                - for text files with a max string len of ca. 8k chars
                - eax returns ptr to the string, edx flags Eof()
                - works only with file #1

Input$, wInput$
            Let esi="["+Input$("Type something and hit Enter: ")+"]"                ; input from console
                Print "You typed ", esi
                Let esi=Input$("Your hobby: ", "Asm", flush max 20)                        ; you may use flush as third para to flush the
            wLet esi="["+wInput$("Type something and hit Enter: ")+"]"            ; input queue, and/or max nn to limit the input
                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:[colHobbies:\t",        "Assembler, ")                                    ; the prompt can contain a tab escape,
                PrintLine #1, Input$("Profession:[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
                - you may use ClosePipe to interrupt a task
                - 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"
                ; if you prefer to open the doc via ShellExecute, you can use ShEx instead:
                ShEx "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 [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

GuiParas equ "controls demo", w180, h100            ; select guiparas and hit F6 to build this code
include \masm32\MasmBasic\Res\MbGui.asm
                GuiControl Edit1, "RichEdit", w500
                GuiControl Edit2, "Edit", x500, w500
                .if IsRichEdit(hEdit1)
                                SetWin$ hEdit1="This is a RichEdit control"
                                SetWin$ hEdit1="This is a poor edit control"
                .if IsRichEdit(hEdit2)
                                SetWin$ hEdit2="This is a RichEdit control"
                                SetWin$ hEdit2="This is a poor edit control"
Rem        - returns DWORD in eax
Key        -

                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)
                Touch Files$(ecx), TimeSF("01.02.2013 12:34:56")            ; TimeSF returns FILETIME value in xmm0
            - 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
                    GfNoUtf8=                                        0/1        ; 0=write a UTF8 BOM when storing Files$() (default); 1=don't
            - 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)
cbGetFiles:     test ecx, 1023    ; file or folder counter
                                If_ Zero? Then Print "*"                ; console mode progress bar ;-)
                                .if InstrOr(edi, ".exe" or ".dll", 1)
                                        or ecx, -1    ; combined with the inc ecx below, this sets Zero?
                                inc ecx
                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
                        ecx                                current file or folder counter
                        edi                                path used in FindFirstFileEx
                - to exclude a file from Files$(), set the zero flag before the ret; recommended, as shown above:
                    use or ecx, -1 if the exclusion condition is met, plus an inc ecx before the ret
                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
            include \masm32\MasmBasic\
            Init                                        ; <<< select init and hit F6 to test this snippet
            GetFiles *.as?                    ; get all asm or asc sources in the current folder and its subfolders
            SortFiles                                                            ; sort by date, latest first
            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$(40))    ; we simulate LSET
                                                                Print Str$("\n#%i ", ebx+1), Left$(esi, 40), 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
                                Ini$("\Masm32\MasmBasic\Res\RichMasm.ini")            ; load the *.ini file
                                PrintLine Ini$("SizeNormal")
                                PrintLine Ini$("COLBG")
                                PrintLine Ini$("FaceFix")
Rem                        searches a *.ini file for the given string, in mode case-insensitive & full word

SetMru, AddMru, MruText$
                SetMru "EditorDemo.ini"                 ; load text file with most recently used files (in WM_CREATE handler, after menu creation)
                Event Menu                                            ; (similar for a manual WM_COMMAND handler)
                            Switch_ MenuID
                                Case_ 2                ; "save as" menu
                                                            .if FileSave$("Assembler=*.asc;asm|Includes=*.inc|Resources=*.rc|All files=*.*")
                                                                                Let CurrentFile$=FileSave$()                        ; change name of current document
                                                                            FileWrite CurrentFile$, Win$(hMyEdit, xxl)                            ; the xxl means "use a big buffer"
                                                                            AddMru CurrentFile$
                                Case_ MruFirst .. MruLast                            ; get a file path from Most Recently Used menu
                                                            Let CurrentFile$=MruText$()                        ; load a document from the menu
                Event Close
                                StoreUtf8 Cat$(MbExeFolder$+"\EditorDemo.ini"), Mru$(), 8            ; on exit, write up to 8 strings to the ini file               
Rem        see the "editor with toolbar" template for a full example

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$

ExeFromWin$, ExeFromExt$
            include \masm32\MasmBasic\
                Let esi="MbGuide.rtf"
                PrintLine esi, " was launched by ", ExeFromWin$(WinByTitle(esi))
                PrintLine "files with the ending 'rtf' get launched by [", ExeFromExt$("html"), "]"
                PrintLine 'ExeFromWin$("Recent"):', Tb$, ExeFromWin$("Recent Unread", 8)
                PrintLine 'ExeFromExt$("html"):', Tb$, ExeFromExt$("html")
Rem        returns ptr to temporary buffer in eax; ExeFromWin$ returns in edx the PID, or zero signalling an error

GetFileProps                                    ; get an array of file properties
            include \masm32\MasmBasic\
                GetFileProps                                        ; no args = current file
            For_ ecx=0 To FileProp$(?)-1
                                Print Str$("%_i    ", ecx), FilePropName$(ecx)
                                PrintLine At(30) FileProp$(ecx)
                GetFileProps "\Masm32\qEditor.exe"
                Inkey "Description of qEditor.exe: [", FileProp$(FileDescription), "]"

Rem        - GetFileProps creates the FileProp$() array
                - to read it, you may use a counter or one of the predefined version info names, e.g. Comments, FileDescription
                - when using a counter, FilePropName$(ecx) shows the predefined names

            MakeDir "A new folder"
                .if Zero?
                                PrintLine "[", eax, "] successfully created"
                                PrintLine "Could not create '", eax, "', sorry"
                mov esi, Chr$("\Masm32\A new\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 or URL, 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
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
            PrintLine FileRead$("NoSuchFile.txt")    ; shows a MsgBox and terminates program
            PrintLine FileRead$("NoSuchFile.txt", err:#)        ; prints the # character, program continues
            Let esi=FileRead$(77)                    ; assigns content of RCDATA resource with ID 77 to esi (allowed IDs are 1 ... 126)
            ; read a file from the Internet, strip all HTML tags, scripts and styles, and display it on the screen:
                Inkey NoTag$(FileRead$(";all"))
                ; 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
            - for asynchronous downloads see Download
                - directly after FileRead$(), you can get the numbers of bytes read using mov eax, LastFileSize
Key        fr$(

                Download "" ; [, msg:hWnd] [, cb:callback]
                Download "", cb:MyCallback
                SetWin$ TBarControl(2)=Str$("%i bytes\ndownloaded", edx)                ; currrent state in edx
                Delay 1
                invoke GetKeyState, VK_SHIFT
                test ah, ah                                        ; stop download if Sign? flag set
Rem        - for use with Event Download (triggers a WM_DOWNLOADFINISHED message)
                - if msg: is not specified, hWnd is assumed
                - optional callback function: no paras, bytes read in edx, cancel if the sign flag is set
                - wParam points to content, lParam holds length in bytes
                - if content starts with #D-, an error occurred; #D-U indicates no URL (offline?), #D-R means reading failed

                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
                If_ Not Exist("build.bat") Then FileWrite "build.bat", res:99    ; extract a file from RCDATA resource #99
            FileWrite "MyFile.txt", "MyString", xmm0                ; sets the timestamp in xmm0
Rem        returns Close retval in eax and bytes written in edx
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
                - the sending program can use e.g.
                    Let esi=FileRead$("\Masm32\include\")
                    Launch "receiving.exe", passdata, Left$(esi, 400)        ; note the console's printing limits around 32k
                - 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" Then 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
                - length limit is 159,999 bytes; if that is not enough, use e.g. FileWrite "test.txt", stream:hRichEdit, SF_TEXT or SF_UNICODE
                - for getting Unicode from RichEdit controls, use wSetWin$ hUcEdit=wRec$(Win$(hRichEdit))
                - 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 applications
            SetWin$ ecx, 15="If ecx is Notepad's handle, then we can set its edit control because its ID is 15"
            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 their controls; you can use string concatenation as in Let resp wLet

AddWin$, wAddWin$
                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
Sel$, wSel$                                            ; select the white zone and hit F6
GuiParas equ wSel$ demo, w240, h120
GuiMenu equ <@Edit, Selection>
include \masm32\MasmBasic\Res\MbGui.asm
    GuiControl MyRich, "richedit", "С Sel$ макрокоманды может показать любой раздел."
    invoke UpdateWindow, hMyRich
Event Menu
    .if MenuID==0                                ; first menu item clicked
                wMsgBox 0, wSel$(hMyRich, 35, 47), "Hi", MB_OK
                SetWin$ hMyRich="Same with English: Click the menu again to show the range 35...47"
Rem        works with richedit controls only; if start and end are omitted, the current selection will be returned

                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$, .uClip$, ClipboardChanged
                Print "[", Clip$(), "]"                                ; displays the content of the clipboard
            MsgBox 0, Clip$(), "Text on the clipboard:", MB_OK                            ; the 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 clipboard
            SetHtmlClip$ "This is bold"            ; for use e.g. with Thunderbird
                wSetClip$ "Today is the "+wDate$+", "+wTime$        ; the Unicode version
            wSetClip$ wRec$("Добро пожаловать")                            ; if pasting yields ???, hit Ctrl F6 and confirm
                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

            GuiParas equ "demo"        ; select GuiParas and hit F6
                include \masm32\MasmBasic\Res\MbGui.asm
            Event Paint                        ; can be Event CanvasPaint if you have a GuiControl xyz, "canvas"
                            ; 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
                                if 0                                        ; you may select the white zone and hit F6, but these v v v need to be provided
                                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#edi                                ; paint to hdc, filename in edi (for example)
                                GuiImage Files$(ecx), 238, 7, 70, 70                        ; you can dynamically load images
Rem                        - images can be animated GIFs
                                - with usedeb=1, errors are printed to console

GuiImageCallback, SaveImageToFile, GuiImageSpeed, GuiImageFrame
                            For use with GuiImage:
                                GuiImageSpeed 50 [, 2000]            ; sets playing speed to 50% [leave a 2000 ms pause after the last frame]
                            GuiImageFrame    ; force display of a specific frame in an animated GIF image
                                SaveImageToFile                ; for use in GuiImageCallback; attention, only first frame gets saved - the others are lost
Rem                        - inside the callback, giImage (=GpImage) and giGraphics (=GpGraphics) are available; plus GuiWidth, GuiHeight etc
                                - click here for an example using the callback and saving functions

                                ; to test this snippet, select the white zone from GuiParas to GuiEnd, then hit F6
                            GuiParas equ "Test window", x650, y20, w300, h200
                            GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
                                include \masm32\MasmBasic\Res\MbGui.asm
                                GuiControl MyEdit, "richedit", "Go to the menu and open a file", y0+20, h1000-20            ; correct y and h for SysLink control
                                    GuiControl VisitMoscow, "syslink", h0+20, "Get info on Moscow: Развлечения, история, ..."    ; Unicode is ok
                                GuiControl MySbar, "statusbar"
                                    SetWin$ hMySbar="uses ComCtl32 version "+ComCtl32$()                    ; returns the dll version used, e.g. 5.82 (XP) or 6.16 (W7)
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 button, canvas, edit, listbox, richedit, scintilla, static, statusbar, syslink,
                                    toolbar (template), progressbar, trackbar, date, time; click here for a full example including GuiGroup
                                - the syslink control has tooltips, but only the first link will be used; opening with Link$() works correctly
                                - 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

EndOfEvents, GuiEnd
                ; to test this snippet, select the white zone from GuiParas to GuiEnd, then hit F6
                            GuiParas equ "See Windows messages", x650, y20, w200, h200
                                                            GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
                                include \masm32\MasmBasic\Res\MbGui.asm
                                ; ... add e.g. GuiControl, or your own WM_CREATE stuff ...
                            Event Menu
                                                                deb 1, "You clicked", MenuID
                            Event Message
                                                                inc msgCount
                                                                .if uMsg==WM_MOUSEMOVE
                                                                                call myproc
                                                                                deb 4, "msg", chg:msgCount
                                myproc proc
                                                                Print Str$(msgCount), Cr$
                                myproc endp
Rem                        A MasmBasic Gui program has a very simple structure, as shown above:
                                - GuiParas and GuiMenu declarations
                                - the include line
                                - any WM_CREATE code
                                - Event sections: Menu, Key, Size, Paint, Timer, Command, Notify, or Message for any other uMsg
                                - EndOfEvents, followed by user procedures
                                - GuiEnd, followed by comments or RichMasm options

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

                ; to test this snippet, select GuiParas and hit F6
            GuiParas equ "Event-based programming is easy", x750, y20, w250, h120, b RgbCol(255, 255, 160)
            GuiMenu equ @File, &Open, &Save, -, &Download, E&xit, @Edit, Undo, Copy, Paste    ; define a menu (paras & menu before the include line)
                include \masm32\MasmBasic\Res\MbGui.asm
                GuiControl MyEdit, "edit"
                ; ... implicit Create event here: any code needed once during window creation, e.g. GuiControl, or CreateWindowEx, if you prefer it manually ...
            Event Message                    ; generic event, use e.g. Switch_ uMsg to handle specific messages
                                inc msgCount
                                NoDebMsg CATSTR NoDebMsg, <, WM_ENTERIDLE, WM_MOUSEMOVE>                ; exclude some messages
                                deb 4, "msg", chg:msgCount            ; simple message monitoring (needs console)
            Event Menu                            ; WM_COMMAND, WM_NOTIFY
                                .if MenuID==2
                                                                Download ""
                                .elseif MenuID<=7            ; MenuID is zero for the first entry, 1 for the second etc
                                                                MsgBox 0, Str$("You clicked menu #%i", MenuID), "Hi", MB_OK
            Event Key                            ; WM_KEYDOWN
                                Print Str$("You hit key %i\n", VKey), "Hi", MB_OK            ; returns VKey from main window, -VKey from edit controls
                Event Data                            ; WM_COPYDATA
                                MsgBox 0, CopyData$, "Received:", MB_OK
                Event Download                    ; WM_DOWNLOADFINISHED
                                SetWin$ hMyEdit=NoTag$(wParam_)                ; lParam holds length, not needed for a textfile
            Event DropFiles                ; loads the Files$() array; use wRec$() to convert Utf8 file names to true Unicode
                            wMsgBox 0, wCat$(wStr$("%i files dropped, first one is\n[", Files$(?))+wRec$(Files$(0))+"], last one is "+CrLf$+"["+wRec$(Files$(Files$(?)-1))+"]"), "Event Dropfiles:", MB_OK or MB_TOPMOST
                                StoreUtf8 "~tmpDropped.txt", Files$()    ; write dropped filenames to a text file in Utf8 format
Rem        a MasmBasic Gui application provides a simple interface to the standard Windows message loop

GuiParas, GuiMenu, GuiColor, GuiText, .GuiTextBox, GuiCls, GuiRefresh, GuiLine, .GuiCircle, .GuiEllipse
                ; to test this snippet, double-click on GuiParas and hit F6
            GuiParas equ "Hello jj2007", x650, y20, w200, h222, cblack, bLiteGrey    ; xpos, ypos, width, height, color, bgcolor (see RgbCol)
            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
                                GuiRefresh <GuiControl ID> updates a specific GuiControl
                                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
                                GuiControl ID, "type" [, "text or file"] [, x, y, w, h]                ; type is e.g. edit, richedit, button, ...
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, ClosedCurve, Ellipse, Pie, Polygon, PolyLine and Rect, see
                                    Graphics Paths in GDI+ for details. The shape 'Text' is not implemented, use GuiTextBox instead

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 huge allocation,
                mov ecx, 900000000                            ; precisely because concatenation of the old and the new string is allowed
                Let My$(0)=New$(ecx)                        ; 900 MB may work on many machines, but do thorough testing!
Rem        - returns pointer to zero-initialised memory in eax
                - you can also use Let Some$=String$(n, 0)
                - Resize Some$:nBytes performs a HeapReAlloc (+ or -) on Some$; added nBytes will be zeroed
                - 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
                MemState(dump)                                                                    ; prints sums and averages for all monitoring points
            MemState(reset)                                                                ; resets sums and counters
            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, reset and always (=print even if there was no change); default is del
                - 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$, wRec$, ResFile$
                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$
                Recall "TestUnicode.txt", rc$()                ; file is Unicode
                For_ ecx=0 To eax-1
                                wPrintLine wRec$(rc$(ecx))            ; Recall translates Unicode text files to UTF-8; use wRec$() to get Unicode back
                Print "A file in resources defined as 77 RCDATA "HelloWorld.txt:", ResFile$(77)                ; file content returned in eax
                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

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

                Print "I live in ", Geo$()
Rem        your computer knows roughly where you live ;-)[
                ; 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

            include \masm32\MasmBasic\
                PrintLine "123        56 78        90"
                Inkey SpaceToTab$("123        56 78        90")
Rem        use to convert a fixed size database to tab-delimited

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

Print, wPrint

Print #n, wPrint #n
            Print "Test", CrLf$, Chr$("the library", 13, 10)
                Print #1, esi, " is ", My$
                wPrint wRes$(123)                            ; Unicode defined in a resource stringtable
                Print #1:25, "There are many chars here but I print only a few"
                Print #1:100, FileRead$("\masm32\include\")    ; 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, wPrintLine
            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
PrintRtf                                        ; send the contents of a RichEdit control to a printer
            GuiParas equ "Test window", x650, y20, w300, h200
                include \masm32\MasmBasic\Res\MbGui.asm                                ; select the white zone and hit F6 to test this
            GuiControl MyEdit, "RichEdit"                    ; create a control
                SetWin$ hMyEdit=String$(20, cfm$("select some text and hit Ctrl P to print this\n"))
                Event Key
                    If_ VKey==VK_P || VKey==-VK_P Then <MsgBox 0, Str$("%i pages printed", PrintRtf(hMyEdit, 2)), "PrintRtf:", MB_OK>
Rem        - returns #pages printed
                - requires a handle to a RichEdit control
                - if text is selected, then only the selection will be printed
                - optional second arg: 0=use default printer, 1=use once the printer dialog, 2=use it always, 4+x=use 'no setup' dialog
                - optional: margins in mm, e.g. void PrintRtf(hMyEdit, 0, 20, 20, 15, 20)            ; left, top, right, bottom
                - printer settings like orientation etc are kept while the program runs

PrintCpu, Cpu$
                            PrintCpu                                ; print full info to the console
                                PrintCpu 0                                            ; print basic info
                            PrintLine "[", Cpu$(), "]"                            ; basic info, for use in Gui apps
Rem                        useful for benchmarking different CPUs

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$()
                - note that Recall returns UTF8 when opening a Unicode text file
                - for UTF-8 -> Unicode, use wChr$()
                - for Unicode -> UTF-8, use Utf8$()
                - limit is 40k; use Recall and a loop for bigger files

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
            Print At(20) "Hello"                        ; one argument: print in column 20, current row
            Print At(ct+1, Locate(y)+1) CColor(cBlack, cYellow) "Hello"        ; one column right of some counter, next line, black on yellow:
                Print At(5, 5) CColor(cBlack, cYellow) "Attention, no commas after At() and CColor() !!"
                Print At(#esi) esi            ; esi points to ABCD sometext, where A=col, B=row, C=foreground, D=background colour
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)v) to display them
                - if one para is called buffer as shown above, it will be allocated and freed automatically

QuadMath, Quad, Quad$
include \masm32\MasmBasic\
                SetGlobals REAL16 quadNumA, quadNumB, quadResult
                Init quad
                MovVal quadNumA, "2.141592653589793238462643383279502884"            ; string to flt128
                movups quadNumB, Quad(FP4(1.0))                                ; REALx to flt128
                movups quadResult, QuadMath(__addtf3, quadNumA, quadNumB)
                Inkey "PI=", Quad$(quadResult, "%#*.32Qg")                            ; Quad$ instead of Str$()
Rem        - GCC QuadMath DLLs needed
                - over 100 REAL16 functions
                - click here for an example how to use the GCC QuadMath library

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)v)    ; a trailing v 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)                ; preallocate 100000 elements, i.e. 0...100000 (slightly faster)
            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)
                - 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 ;-)
                - My$(?) returns the number of elements; My$(123, ?) returns #columns for two-dimensional arrays; use My$(?, ?) to
                    get the max #columns, testing only the first 127 elements; the line index with the highest column count is in edx
                - the max #elements is limited by HeapAlloc, e.g. 88 Mio REAL8, 170 Mio REAL4 or DWORD elements on Win7-64 with 4MB RAM
                - 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
            include \masm32\MasmBasic\
                Init                                                        ; < < select Init and hit F6 to test this snippet
            ArrayFill Years() As WORD, 2000, 5, 2    ; create a WORD sized array and fill it; start value is 2000, 5 elements, increment 2
                For_ ecx=0 To eax-1
                                PrintLine Str$("Year #%_u\t", ecx), Str$(Years(ecx))

                Dim rent(11) As DWORD    ; create a fixed-size DWORD array with 0...11=12 elements
            ArrayFill rent(), 1000    ; fill it with the value 1000
                Dim MyRect(2) As RECT    ; create an array with 0...2=3 elements
                rect RECT <12, 34, 56, 78>
                ArrayFill MyRect(), rect                ; fill array with values from one .data section structure
                For_ ecx=0 To eax-1
                                Print Str$("rect %i: ", ecx), Str$("left=%i", MyRect(ecx, left)), Str$(", right=%i\n", MyRect(ecx, right))
                Dim My$(2)                                            ; fixed size string array
                ArrayFill My$(), "String created "+Date$+"!!"
                xchg eax, ecx
                                Let My$(ecx)=Str$("String #%i added", ecx)
                                inc ecx
                .Until ecx>=5
                For_ ecx=0 To My$(?)-1
                                PrintLine Str$("String #%i\t", ecx), My$(ecx)

Rem        - use ArrayFill array() As SIZE, start, #elements [, step] to create and fill numeric arrays (BYTE...DWORD, REAL4...REAL10)
                - strings and structures like RECT must first be Dim'ed as fixed size arrays; after the ArrayFill, they are dynamic
            - see also Let, Erase, Swap, Recall and Store

            include \masm32\MasmBasic\
                Init                                                        ; < < select Init and hit F6 to test this snippet
            Dim MyNumbers() As DWORD
                For_ ecx=0 To 99999        ; create 100,000 elements
                                mov MyNumbers(ecx), Rand(20)        ; random values 0...19
                ArraySort MyNumbers(-)    ; sort descending
                ArrayStripDuplicates MyNumbers()
                For_ ecx=0 To eax-1
                                Print Str$(ecx), Str$("\t%i\n", MyNumbers(ecx))
Rem        - removes all duplicate elements from a numerical array
                - returns new #elements in eax

                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
                StringToArray 123, L$()                ; converts resource;    use e.g. 123 RCDATA "names.txt" in the Rsrc section
                ; 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

                include \masm32\MasmBasic\
            Init                                                        ; << select Ixnit and hit F6 to test this snippet
                Let esi=Space$(50)
                Lset esi="Let's test this today, "+fDate$(0)+", "+fTime$(0)
                PrintLine "[", esi, "]"
                Lset esi="Lset a much longer string, like this one: "+String$(100, "abc")
                PrintLine "[", esi, "]"
Rem        use to set a string to a fixed length

                Insert My$(5)                    ; move My$(5) up to My$(6)
            Let My$(5)="This is the new string"
                Insert My$(6)="This is the new string #6"            ; inserts a string and assigns a value
            Insert <My$(ecx)>=Str$("String #%i", ecx)            ; needs <brackets> because Str$() returns eax
            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 if 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)
                PrintLine Utf8$(wCL$())                ; translate the Unicode commandline to Utf-8
            wPrintLine wCL$()            ; same effect in this case
                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$
                invoke TextOut, PtDC, 7, 2, Chr$("Hello"), c$Len                ; some functions need the length; use c$Len
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, SafeCopy
                invoke MbCopy, ptrDest, ptrSource, ctBytes
                invoke MbCopyz, ptrDest, ptrSource    ; zero-delimited
                Bmove ptrSource, ptrDest [, ctBytes]
                SafeCopy ptrDest, ptrSource, maxBytes
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 (e.g. on an Intel Core i5 for ct=one Million) over three times as fast
                - 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 should have 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, Clr$, Cls
                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
                Clr$ My$(123)                    ; clear an array element
            Clr$ My$                                                ; clear a string (free heap mem, assign Null$)
            Cls                                                                    ; clears the console
Rem        - Clr: if a register is specified as first of several arguments, code is shorter and the register will be zeroed, too
                - to "clear" an array element, use e.g. Delete My$(123)
                invoke GetTickCount
                PrintLine Err$()
Rem        for debugging; same as invoke SetLastError, 0, but ecx is safe, and no code will be generated for usedeb=0

                include \masm32\MasmBasic\
                Let esi="\Masm32\include\"
                For_ ecx=0 To 9
                                ClearFileCache esi            ; tell the OS to expel the file from the cache
                                Let edi=FileRead$(esi)
                                PrintLine Str$("%i bytes read in ", LastFileSize), NanoTimer$()
                For_ ecx=0 To 9
                                NanoTimer()        ; no caching this time
                                Let edi=FileRead$(esi)
                                PrintLine Str$("%i bytes read in ", LastFileSize), NanoTimer$()
Rem        for benchmarks of algos that involve disk I/O; returns CreateFile error code in eax

ClearLocals, _Local
                MyTest proc uses edi esi ebx arg1:DWORD, arg2:RECT, TheString
                    LOCAL v1, v2, rc:RECT, buffer[100]:BYTE            ; ordinary locals first
                _Local v3=123, v4:REAL4=123.456                ; see SetFloat for allowed syntax
                _Local x$="Hello World", y$=TheString    ; strings can be initialised, too
                    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
                - the order is important: "normal" locals first, then numeric _Local vars, finally strings
                - if you need buffers, use StackBuffer
                - you can initialise DWORD, REAL and string variables with _Local (use after the "normal" locals)
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
                - if you see an "unmatched block nesting" error, you forgot the StackBuffer() before the ret
                - 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
                )                                                            ; 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
                include \masm32\MasmBasic\
            Let esi=FileRead$("\Masm32\include\")                                ; read into a buffer
            Print Str$("Count equ=\t%i\n", Count(esi, "equ", 1))                    ; Count equ (1=case-insensitive)
            Print Str$("Count equ=\t%i", Count(esi, "equ", LastFileSize))    ; same but faster and case-sensitive
Rem    - counts occurrences of a pattern
                - with no args, the count is case-sensitive; otherwise, options as in Instr_/wInstr,
                    except for LastFileSize, which is very fast but always case-sensitive

                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...)
                xsRinstrL                            ; search first match from the right, exclude left & right patterns
                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
                - Let esi=Extract$(CL$(), "\", ".", xsRinstrL)+".inc"    ; if arg is path\somefile.txt, esi will be
                - can be used with Print and Let even if no match found, i.e. eax=0; in this case, Extract$ will print as ?
Key        ex$

StringsDiffer, wStringsDiffer
                .if StringsDiffer("Hello", "Hallo")
                                MsgBox 0, "The two strings are different", "Hi", MB_OK
                test eax, StringsDiffer("Hallo", "HALLO", 1)        ; 1=case-insensitive
                .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
                - trashes xmm0...xmm2
                - 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))
.uLeft$, uMid$, .uRight$                ; UTF-8 string extraction
            include \masm32\MasmBasic\
                SetGlobals r$="Введите текст здесь"        ; "Enter text here" in Russian
            SetGlobals c$="在這裡輸入文字"    ; "Enter text here" in Chinese
            Init                                                        ; select and hit F6
                PrintLine "[", r$, "] (original string)"
                PrintLine "[", uRight$(r$, 5), "_", uMid$(r$, 9, 5)), "_", uLeft$(r$, 7), "] (right_mid_left, fixed)"
                PrintLine "[", uRight$(r$, 5), "_", Mid$(r$, Instr_(r$, "текст"), 2*5)), "_", uLeft$(r$, 7), "] (right_mid_left, Instr)"
                wMsgBox 0, wRec$("["+uLeft$(c$, 5)+"]"), "Chinese, uLeft$(5):", MB_OK
                wMsgBox 0, wRec$("["+uRight$(c$, 3)+"]"), "Chinese, uRight$(3):", MB_OK
Rem        - use uLeft$(src, chars) if you know the #UTF-8 chars needed
                - use "normal" Left$() etc if you got the #chars from Instr_(); but note the need to calculate bytes, see 2*5 above

Left$, wLeft$
                Let My$(123)=Left$("A little test", 8)
                Let Left$(My$(123), 5)="A small "            ; valid also for Mid$, Right$
            Print Left$("test", 20, ".")        ; optional: use a padding char if the string is shorter
            mov eax, z$(Left$("Test", 3))
Rem        - for use with Let and Print; if you need the mov version, use z$()
                - with UTF-8 encoded strings, either get the index via Instr_(..), uLen, etc, or use uLeft$()
                - with tab-delimited strings, you can also obtain cells from columns 0...n (Left$) or n ... end (Mid$) using @n:
                    For_ ecx=0 To L$(?)-1
                                Print Str$("\n%i    [", ecx), Left$(L$(ecx), @2), "] [", Mid$(L$(ecx), @5), "]"

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

Trim$                                                                remove white space to the left and right

Ltrim$                                            trim left

Rtrim$                                            trim right
Qtrim$                                            trim quotes
Ntrim$                                            trim anything below a number
                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
                Let My$=Ntrim$("$+abc123+'")        ; strip chars below the number "0": !"]%&'()*+,-./
Rem        - for use with Let and Print
- removes trailing spaces, tabs and other characters below or equal Ascii 32 (Q:34, N:47)
                - 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 Init 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)
                Print fDate$(0, "dddd dd MMMM yyyy "), fTime$(0)
                Print CrLf$, fDate$(0, "dddd dd MMMM yyyy ", russian), fTime$(0, "HH:mm:ss"), Str$(", мы находимся в ИСО неделе %i", IsoWeek())
                wMsgBox 0, wCat$(wfDate$(0, "dddd dd MMMM yyyy ", hindi)+wfTime$(0)), "This is Unicode:", MB_OK
                ; 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()
                - default is user locale, but you can force a language as shown above with russian and hindi
                - see also TimeSF
                - immediates -128 to +127 are interpreted as offsets: days for fDate$(dx), minutes for fTime$(dx); fTime$(s:dx) uses seconds

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'
                Print fDate$(TimeSF("11.11.2011"), "dd MMMM yy"), ", ", fTime$(TimeSF("12:34:56.789"), "HH:mm:ss:fff")    ; 11 November 11, 12:34:56:789   
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 123, some$()                        ; converts resource;    use e.g. 123 RCDATA "names.txt" in the Rsrc section
                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 and Eof() for checking end of data
                - 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)
                - 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
                - instead of a filename, you can pass the ID of a RC_DATA resource file
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$()                ; .StoreUtf8 prepends a UTF-8 BOM
                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
                - for sorting the Files$() array, see SortFiles
                - for testing, use Scramble My$() to randomise the string array
                - 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()

            include \masm32\MasmBasic\
                SetGlobals tmp8:REAL8    ; binary search demo
                Init                                                        ; select init and hit F6
                Dim table() As REAL8        ; create an array (could also be As DWORD)
                Rand()                                    ; generate a random seed
                For_ ecx=0 To 255
                                Rand(-1000, 1000, table(ecx))    ; fill array with random values
                ArraySort table()            ; sort ascending
                For_ ecx=0 To 9
                                Rand(-500, -400, tmp8)                    ; check a narrow range for tmp8
                            Print Str$("position=%i\n", ArrayIndex(table(), tmp8))    ; should be around 70 +/- 10
Rem        - returns the position of a value in a sorted array of REAL8 or DWORD numbers
                - for use with MasmBasic arrays; with pointers to other arrays, use ArrayIndex(pArray, pattern, lengthoftable))
                - binary search, over twice as fast as repnz scasd; see also ArraySearch
                - if an exact match is found, dl will be zero; if not
            ; 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

ArrayLoadMap, PaintMap, MapColours
            GuiParas equ "Map viewer"                            ; double-click on GuiParas and hit F6 to build this example
            include \masm32\MasmBasic\Res\MbGui.asm
                GuiControl map, "canvas"                                ; define a canvas control
                GuiControl Sbar, "statusbar"                        ; status bar
                ; .CanvasMap(0, 0)                        ; canvas control 0 gets initially map 0 (default)
            SetFolder "\Masm32\MasmBasic\Res"            ; you may use SetFolder to set the map's folder
            ArrayLoadMap 0, "Europe"
                MapColours(0, "abcdefghiabcdefghiabcdefghiabcdefghiabcdefghiabcdefghi") ; set colours to map 0 (a=red, i=green)
                MapColours(0, 25, 20)                    ; give blue to Spain
                Event Message                                    ; use .MapRegion/.MapRegion$ in a .WM_MAPCLICKED handler
                If_ uMsg_==WM_MAPCLICKED && MapRegion>=0 Then SetWin$ hSbar=Str$("You clicked on region %i, ", MapRegion)+MapRegion$
                Event CanvasPaint
                    ArrayPlot RgbCol(0, 240, 255)                                ; init and set background colour
                    PaintMap RgbCol(127, 127, 127), lines=2            ; map with grey borders 2px thick
                    ArrayPlot exit, "Europe"
Rem        - requires a map in *.map/*.dmi format; the Europe map is included in MasmBasic (PM the author for details)
                - to see the numbers of individual regions, use this loop:
                    lea edi, Maps(0, numPoly)
                    For_ ecx=0 To MapName$(?)-1
                                PrintLine Str$("Region %i\t", ecx), MapName$(ecx)
                - use ArrayMapRegion in a WM_SETCURSOR handler to check if the mouse is hovering over a particular state or region
                - colours can be set for all regions, as shown above with the "abc" string, or for individual regions

            Event Key
                Switch_ VKey
                Case_ VK_I: CanvasZoom MyImg        ; type "i" to zoom the image, "m" to zoom the map
                Case_ VK_M
                                CanvasZoom MyMap, MyList, MyEdit                ; zoom the map, hide two non-canvas controls
Rem        - if more than one GuiControl , "canvas" are present, CanvasZoom can be used to zoom one of them
                - calling it repeatedly toggles the effect
                - if non-canvas controls are present, add their IDs as shown above

ArrayPlot, ArrayPlotValues, SetAxisX, SetAxisY
    GuiParas equ "Plot sinus & cosinus curves", x600, y100, w600, h500, bRgbCol(192, 255, 255)
    include \masm32\MasmBasic\Res\MbGui.asm            ; select the white zone and hit F6
    Dim MySinus() As REAL4            ; REAL4 precision is enough ...
    Dim MyCosinus() As REAL8        ; ... but REAL8 is ok, too
    For_ ecx=0 To 360
                SetFloat MySinus(ecx)=Sinus(ecx)                ; fill an array
                SetFloat MyCosinus(ecx)=Cosinus(ecx)        ; you may use ArrayRead for reading data from a file
    MakeFont hTextFont, Height:16, Weight:FW_SEMIBOLD
    MakeFont hHorzFont, Height:18, Italic:TRUE
    MakeFont hVertFont, Escapement:900, Italic:FALSE
    MakePen hPenAxis, RgbCol(255, 0, 0)
    MakePen hPenGrid, RgbCol(255, 255, 222), width 2
    SetAxisX "degrees", s 0, d 15.0/3, xmax 300, grid 1, penx hPenAxis, peng hPenGrid, font hHorzFont, format "%i"            ; s=start value, d=difference between gridlines
    SetAxisY "sinus & cosinus", s -1, d 0.2/5, grid 2, font hVertFont, format "%2f"            ; the text can be Unicode
    $Data "Plotting an array of points is very easy: All you need is the array itself, plus a Paint event that draws it."
    $Data "The axes can be set using SetAxisX and SetAxisY, taking arguments as shown in the source. Now resize this window to see how it behaves"
    Read desc$()                                                    ; read a string array from $Data
    Event Paint
        ArrayPlot MySinus(), RgbCol(255, 222, 160), 2, 00000110h, 0, -1, 1                    ; red sinus, 2px wide; use margins left top right bottom, min0, max-1, filled=1
        ArrayPlotValues "%2f", 8, -12, 15                    ; format$, dx & dy positions, step (here: 8px right, 12px up, every 15th value)
        ArrayPlot MyCosinus(), RgbCol(0, 0, 255)                    ; plot the cosinus array in blue, same margins as previous plot
        ArrayPlot exit, 0150006028# "Sinus & Cosinus"            ; finish with a title; optional: xxxxyyyyFF#: x, y pos, font size
        GuiTextBox 12, 100.0-80, 440, 66, Join$(desc$(), " "), bcol RgbCol(192, 255, 192), font hTextFont
Rem        - try resizing the window to see how the plots behave
                - array elements above #100000 will not be displayed
                - optional: use xxxxyyyyhh# text to modify title position and font, e.g. ArrayPlot exit, -120015024# "title to the left and deeper, font 24"
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, hDC, RgbCol(ebx, 0, 0)        ; Red Green Blue for Gdi32 functions
                invoke SetTextColor, hDC, SysCol(ebx)    ; use the system palette
                invoke SetTextColor, hDC, RgbCol(?)        ; use the colour picker
            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
                - predefined colours are Red, Green, Blue, GreenBlue, LiteRed, LiteGreen, LiteBlue, LiteYellow,
                    LiteGrey, LiteGreenBlue, DarkRed, DarkGreen, DarkBlue, DarkYellow, LiteGrey, Grey, DarkGrey
                - CgaCol(index) may be used with console colours; see ConsoleColor for identifiers
                - SysCol(index) returns a GetSystemPaletteEntries colour for use in Gui applications

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 PIThe number PI is %Jf", PI)        ; precision J = 19 digits, f=float
            Print Str$("The number PIThe number PI is %Je", PI)        ; precision J = 19 digits, e=force exponential
            Print Str$("The number 19The number 19 is %__i", 19)        ; integers only: two understrokes to get one leading space with a two-digit number
            Print Str$("The number 9 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                                        ; \n is CrLf, \t is tab in Str$()
            Print Str$("\nresult=\t%f", xmm0/ST(0)*ecx)         ; output: [newline] Xmm0=    1230003.0
            fstp st                                                                ; cleanup the FPU
                fldpi                                                                    ; PI again
                Print Str$("\nresult=\t%f", xmm0/ST(0)v*ecx)     ; same but cleanup done by the v after ST(0)
            invoke TextOut, APs.apMemDC, 25, ptY, Str$("%3f", fct), s$Len    ; s$Len returns chars used for Str$()
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)
                - for REAL16 variables, use Quad$(MyReal16)                   
Key        s$(

SetFloat, SetInt
            Dim MySinus() As REAL8
                For_ ct=-400 To 400
                                SetFloat MySinus(ct+400)=Sinus(ct)
                fmul FP4(100.0)
                SetInt ecx                            ; mov ecx, 314
Rem        - use SetFloat with functions that return values in ST(0), e.g. Float(); do not use fstp st afterwards
                - use SetInt to convert ST(0) to a reg32, xmmreg or a DWORD variable

                include \masm32\MasmBasic\
                SetGlobals MyR4:REAL4, MyW:WORD=-123, MyDw:DWORD=-123, MyQ:QWORD=123456789012345678
            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

                include \masm32\MasmBasic\
                MyQ1                                        dq 12345678901
                MyQ2                                        dq 11111111111
                result                                    dq ?
                Init                                                        ; select init and hit F6
                MulQQ(MyQ1, MyQ2, addr result)
                Print Str$("MyQ1*MyQ2\t%u \t(expected: 137174210009739369011 - overflow!!)\n", result)
                Print Str$("12345*Q2\t%u    \t(expected: 137166666665295, OK)\n", MulQQ(12345, MyQ2))
                movlps xmm0, MyQ2
                mov eax, 12345
                movd xmm1, eax
                Print Str$("12345*xmm0\t%u    \t(expected: 137166666665295, OK)\n", MulQQ(xmm1, xmm0))
                movlps xmm0, MyQ2
                mov eax, 12345
                Print Str$("12345*xmm0\t%u    \t(expected: 137166666665295, OK)\n", MulQQ(eax, xmm0))
                MulQQ(12345, addr MyQ2, edx::eax)            ; if you need top speed, use the edx::eax pair as destination
                Print Str$("12345*Q2\t%u    \t(expected: 137166666665295, OK)\n", edx::eax)            ; Str$() allows edx::eax as QWORD arg
Rem        - multiplies two QWORDs
                - first arg can be an immediate DWORD or a 32-bit register
                - use MulQQ(q1, q2, pDest) to transfer the result to a QWORD destination
                - xmm regs are valid args, but xmm0 and xmm1 will be trashed after the call
                - beware of overflow: the result is a QWORD, not an OWORD; the difference is irrelevant in many cases,
                    e.g. for random number generation, but do not expect miracles here!

Floor, Ceil
            include \masm32\MasmBasic\
                SetGlobals MyR4:REAL4, MyW:WORD=-123, MyDw:DWORD=-123, MyQ:QWORD=123456789012345678
            Init                                                        ; select init and hit F6
                PrintLine cfm$("number\t\tFloor(number)\tCeil(number)")
                For_ ecx=0 To 29
                                Rand(-99.9, +99.9, MyR4)
                                PrintLine Str$("%+4f ", MyR4), Str$(" \t%+5f", Floor(MyR4)v), Str$("    \t%+5f", Ceil(MyR4)v)        ; v = fstp st
                Floor(123.456, MyDw)        ; second arg is the destination (int or real variable)
            Print Str$("dw(123.456)=%i\n", MyDw)
Rem        - returns a double in ST(0)
                - source can be immediate or WORD ... QWORD, resp. REAL4 ... REAL10 variable

Sinus, Cosinus, rSinus, rCosinus
                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
                Print Str$("\nSinus(60)=\t%5f\n", Sinus(60)v)                    ; the v means "pop the fpu", i.e. fstp st
                Print Str$("Sinus(1.0472)=\t%5f\n", rSinus(1.0472)v)        ; pass the angle as a radian, here: 60*PI/180
Rem        - returns REAL10 value in ST(0); store it to a variable with fistp or fstp. If you use it in Str$(), it must either
                    be followed by fstp st, or you use Str$(Cosinus(ecx)v)
                - if your degrees are rad values like e.g. 0.5, use rSinus(0.5) or rCosinus(0.707)

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)
                FpuPush MyReal16                ; requires a GCC installation
            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:
                MbRound]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

Log2, LogE, Log10
                include \masm32\MasmBasic\
                SetGlobals ct:REAL8
            Init                                        ; select and hit F6
                For_ ct=1.5 To 10.0 Step 0.5
                                Print Str$(" Log2(%2f)", ct), Str$("\t%Jf ", Log2(ct)v), Str$("\t%Jf ", LogE(ct)v), Str$("\t%Jf\n", Log10(ct)v)
Rem        these functions return the logarithms in ST(0); use the v before the closing bracket of Str$() to pop ST
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)v)                ; 2.718^3 = 20.085 (the v replaces 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 HexDump$(pBuffer)                ; prints 512 bytes in format 00000010 ab cd ef ... text
            Print cfm$("\nGetModuleHandle(0):\n"), HexDump$(rv(GetModuleHandle, 0), 90h)
                00400000    4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ...........
                00400010    B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 .......@.......
                00400020    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
                00400030    00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ...............
                00400040    0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 .....!.L!Th
                00400050    69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno
                00400060    74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS
                00400070    6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode....$.......
                00400080    50 45 00 00 4C 01 03 00 FB BB BF 59 00 00 00 00 PE..L...Y....
                Let edi=HexDump$(esi, eax, 0)    ; assigns eax bytes to edi with offset 0
                Let edi=HexDump$(esi, 128, db)    ; uses db 0ABh, 0CDh for use in code
            Let edi=HexDump$(esi, 4096, notext)        ; assigns 4kB, no ascii text
            Let edi=HexDump$(esi, file, dq)                ; esi holds filename, translate whole file, use dq for code       
Rem        returns ptr in eax; maxlen 40,000 bytes

                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!)
                - you may assign immediates with e.g. movaps xmm0, Oword16(1234567890); the "16" means that data is aligned
                - 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)
                Fcmp st, st(1), xtra        ; compare st(0) and st(1) with maximum precision
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?)
                - the desired precision can be indicated as third argument: low, medium, high, top and
                    xtra precision; caution in loops that exit only if Zero? is set!
                - 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("12345        ; hex
            mov ebx, Val("0x12345")                ; hex, C notation
            mov ebx, Val("$12345")    ; hex, leading $ notation
            mov ebx, Val("10101; binary
                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), "12345789123456789120000000000000000000000000000000"
                Print Str$("V=%Jf\n", ST(0)v)    ; V=1.234578912345678912e+50; v means "pop ST with fstp st"
            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\
                If_ GetHash(FileRead$(<CL$()>), LastFileSize, md5) Then PrintLine "The MD5 of ", CL$(), " is ", Hex$(xmm0)
                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
                - uses CryptGetHashParamfiles; try also e.g. PrintLine Launch$(Cat$("certutil -hashfile "+CL$()+" MD5"))
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...
            PrintLine fTime$(0, "HH:mm:ss.fff"), Tb$, "started"
                Delay until fTime$(s:1, "HH:mm:ss.123")                ; use time$(current plus one second)
            PrintLine fTime$(0, "HH:mm:ss.fff "), Tb$, NanoTimer$(), " restarted"
Rem        - uses Sleep but preserves ecx
                - the until variant uses a time string to wait until a precise moment; use e.g. fTime$(1, "HH:mm") to
                    suspend running until the current minute is finished; fTime$(s:10, ...) would mean 10 seconds

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...
                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"
                ; 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; =0 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
            ; SetReg64                    ; optional: force seeing 64-bit registry values
            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
                - if regedit.exe shows more items, try SetReg64
                - 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
                If_ FindProcess("Explorer.exe") Then MsgBox 0, Str$("FindProcess returned ID %i\n", [eax.PROCESSENTRY32.th32ProcessID]), "Hi", MB_OK
Rem        - returns pointer to a PROCESSENTRY32 structure, or zero if no process found
                - accepts UTF-8 process names

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

include \masm32\MasmBasic\                ; download
                PushText "The new PushText macro provides a simple\noption to place strings on the stack"
                PrintLine esp
                PushText "Добро пожаловать", 1    ; Unicode is ok; an optional 1 hides the text
            PrintLine esp
                PushText                                                ; no args: balance the stack
Rem        pushes text as dwords on the stack

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
Resources must be put below the end label; when pressing F6, content between the two Rsrc lines will be written to [filename].rc
ID 59 is reserved for the toolbar in Note opt_icon will be ignored - what counts is the icon specified in the resource file.
You may delete these three lines once you don't need them any more.
#include "resource.h"
IDI_APPLICATION ICON        "\\Masm32\\MasmBasic\\icons\\Smiley.ico"                // Calc, Disk, Editor, Eye, Globe, MasmBasic, Smiley
01 RT_MANIFEST                "\\Masm32\\MasmBasic\\Res\\XpManifest.xml"            // works with tooltips

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