MasmBasic Quick Reference

extracted from \Masm32\MasmBasic\MbGuide.rtf 15.04.2013 - 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

Some ErrLines Catch Finally TryRTE Init Exit CL$() DlgDefine DlgControl DlgShow SetGlobals Enum Dll Declare deb Err$() SetErrLine Asc Cvi Cvl Odd Abs Min Max PopCount Rand FileOpen$ FileSave$ w Open Close Seek Lof Loc Locate( Input Input$ w Rename WritePipe Launch see Launch$ ExitCode Exist LastFileSize LastFileName$ LastFileDosName$ IsFolder Kill Touch GetFiles AddFiles GetFolders AddFolders SortFiles GfSize GfDate$ GfTime$ GfAgeHours GfAgeMinutes GfAgeMs GfLastWrite CurDir$ ExpandEnv$ ExpandEnv$ ExeFromWin$ MakeDir ZipFiles UnZipFiles FileRead$ LastFileSize NoTag$ FileWrite Replace$ ParentData$ CopyData$ SendData SendControlKey SendWordCommands xlsConnect ddeConnect ddeCommand ddeRequest$ ddeDisconnect Win$ _Win$ SetWin$ AddWin$ MakeFont ImgPaint ToolTips WinByTitle App16 Clip$ SetClip$ w SetHtmlClip$ MsgMonitor New$ Res$ MsgTable$ String$ Space$ Inkey Print w Print w CurDir$ PrintLine PrintBuffer SetCpUtf8 SetCpAnsi Cls ConsoleColor crtbuf CreateInvoke gsl SetPoly3 GetPoly3 Dim Erase ArrayFill ArraySet StringToArray Swap Let Cat$ Insert Delete GuidFromString GuidsEqual CoInvoke Ole$ BSTR w w Chr$ Len w MbCopy Bmove MsgBox Clr Clr$ ClearLocalVariables PopUses Instr_ Rinstr Count Extract$ StringsDiffer FilesDiffer Left$ Mid$ Right$ Lower$ Upper$ Trim$ Ltrim$ Rtrim$ Mirror$ Date$ Time$ Timer NanoTimer Recall w Store QSortDesc ArraySort ArrayMinMax ArrayMean ArrayRead ArrayStore ArrayLoadMap ArrayPlot ArraySearch VarPtr For_ Str$ FpuSave FpuRestore FpuPush FpuFill FpuSet Exp10 Exp2 ExpE ExpXY Percent DefNum Hex$ Bin$ Fsign Fcmp Val MovVal Sqrt SetField GetField SetFlags Flags MouseX MouseY MouseK Delay void GetRegVal SetRegVal Masm32

; The smallprint: this library is provided "as is", and the usual disclaimers apply.
; This help file refers to MasmBasic version 15 April 2013
; -----------------------------------------------------------------------

include \masm32\MasmBasic\                ; this line substitutes include \masm32\include\

; It is assumed that you have installed the library from
; If you haven't already done so, go to ^ ^ File/New Masm source and select the MasmBasic link on top.
; Once you see the file in front of you, hit F6 to assemble, link and run the example. To get specific help,
; select any instruction, e.g. Open, and hit the F1 key, or hover some seconds over the keyword.

MyReal10                REAL10 123.456

My$                dd ?
                nop                    ; to test examples, replace the nop with your test code and press F6

                Print CrLf$, "OK - press any key"
end start
Some practical hints for using MasmBasic
- Use the RichMasm editor: It helps a lot if you can type opi (i.e. opi) and you get Open "I", #1,
- 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.
- 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 Masm32 macros like print, str$() etc do invoke Windows APIs and therefore do trash ecx.
- Preservation of xmm registers: All xmm registers are preserved, with the exception of three compare functions:
    Fcmp, StringsDiffer and FilesDiffer, which use xmm0... xmm2. The regs xmm3..xmm7 will never be trashed by MasmBasic.
- Another popular error: Masm uses by default unsigned comparisons. This example demonstrates the effect:
                mov eax, -1                                ; definitely a lot smaller than 99, right?
                .if eax<99
                                MsgBox 0, Str$(eax), "-1 is less than 99:", MB_OK
                                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 Mbsigned equate: .if signed eax<99 will produce the behaviour you expect.
- You may see cryptic error messages; for example, Let esi=Trim$(FileRead$("Test A.txt")) will fail with "forced error".
    Often, reducing the nesting level, i.e. splitting in Let esi=FileRead$(...), then Let esi=Trim$(esi) helps.
- MasmBasic uses the Masm MACRO engine. As with all libraries using macros (e.g. print, str$, len... in Masm32), be aware
    that macro expansion takes place before the current line. Normally, this is no problem, but in rare cases incorrect code may
    be produced. One such case is .if Len(My$)==1 ... .elseif Len(My$)==2 - this does generate code for the second statement,
    but this code will never be executed. To avoid this scenario, use for example:
                .if Len(My$)==1
                                .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
In your debug version, use ErrLines to see the line where a runtime error occurs; if you
still get only question marks in error messages, use SetErrLine in the suspect zone.

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

include \masm32\MasmBasic\                ; download
                MbErrLines                ; add extra code to get source code line where error occurred
                Dim My$(3)
                TryRTE BadElement, key                ; optional: con means errors to console, key = con+wait for a keypress; default is box
                Let My$(7)="This won't work"
                cmp edx, $                ; simple error check
                .if Zero?
                                PrintLine "Bad index!!"
                Inkey "bye"
end start

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

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

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

Basic instructions
RichMasm:                To test the examples with white background,
                copy them into the nop after start,
                make sure required variables are present
                (e.g. MyReal10 REAL10 123.456 in the .data section),               
                then press F6 to see how the example works.
Init                This is perhaps the smallest possible complete Masm application:
                include \masm32\MasmBasic\
                Inkey "Hello World"
                end start
Rem                - the Init macro inserts 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 (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 must use Exit to quit an application.
                Do not confuse MasmBasic Exit with the lowercase Masm32 library exit.
Key                exit

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

DlgDefine, DlgControl, DlgShow
                include \masm32\MasmBasic\
                DlgDefine "Please enter your data, tab for next line:", 0, 0, 150, -4, , 14
                DlgControl dcEdit,                "First name", WS_BORDER or WS_TABSTOP, 1, -2                                ; first control gets the focus
                DlgControl dcEdit,                "Family name", WS_BORDER or WS_TABSTOP, 1, -4                                        ; x, y only, the macro will assign width, height and ID
                DlgControl dcStatic,                "Type your first name:", SS_LEFT, 1, -1, 70.0                                                        ; 70 means 70% - the buttons need space
                DlgControl dcButton,                "OK", BS_DEFPUSHBUTTON or WS_TABSTOP, 71.0, -1, 12.0, , IDOK                        ; x=71%, y, width=14%, height, ID
                DlgControl dcButton,                "Quit", BS_PUSHBUTTON or WS_TABSTOP, 84.0, -1, 16.0, , IDCANCEL
                DlgControl dcStatic,                wCat$("Type your family name: (it's "+wTime$+" now)"), SS_LEFT, 1, -3
                ; DlgHandler MyHandler                        ; optional - PM me for details
                .if eax==IDOK
                                wMsgBox 0, wCat$(Dlg$(0)+wCrLf$+Dlg$(1)), "Please confirm:", MB_OKCANCEL
                                end start
Rem                - see advanced dialog examples in \masm32\RichMasm\Res\Templates.asc
                - 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 hMenu, hM1, hM2, rc:RECT
                SetGlobals msg:MSG, wc:WNDCLASSEX, exeBuffer[MAX_PATH]:BYTE, gBuffer[1024]:BYTE
                ; no args: set ebx to the right offset; must be used in Init part and all callbacks (WndProc, SubEdit, ...)
Rem                variables return [ebx+x]

Enum                                ; create a list of IDs
                Enum                IdMenuNew, IdMenuSave, IdMenuCopy, IdTimer
                Enum                20:IdEdit, IdButton1, 30:IdButton2, IdStatic, IdFind, IdFindStatic
Rem                - variables return [ebx+x]

                - default start is 10, but different start values can be specified

Dll & Declare
\masm32\MasmBasic\                                ; include this library
                                Init                                                                ; initialise the app
                                Dll "shimgvw"                                ; load the shell image view dll aka Windows Picture and Fax Viewer Library
                                Declare ImageView_Fullscreen, 4                                ; ImageView_Fullscreen expects 4 dwords
                                void ImageView_Fullscreen(0, 0, wCL$(1), SW_SHOW)                ; we need the wide version of the commandline arg
Err$(1)                                                ; there is no retval, so we have to test for errors
                                Exit                                                ; do a clean exit, inter alia FreeLibrary
end start

Rem                - Dll performs LoadLibrary, Declare uses GetProcAddress, Exit frees the libraries
                - 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
                - use Declare #123=SomeFunction, 2 to access a function by ordinal number; then mov ecx, SomeFunction(arg1, arg2)
                - if the function cannot be found, try the decorated name:
                                Declare _SomeFunction@12, C:3                ; three args, C calling convention
                                then use e.g. mov ecx, SomeFunction(arg1, arg2, arg3)
                - do not use Dll & Declare for the static libraries of the Masm32 package (user32, kernel32, ..., see
                - up to 9 libraries can be loaded

deb                Probably the most important function for you...
                deb 1, "The first loop", ecx, $esi, $edi, MyReal10, ST, ST(5)
                deb 2, "Second loop:", al, ecx, $esi, $My$, $MyArray$(n)
                deb 3, "Third loop:", al, ecx, esi$, xmm0, xmm1, ST, ST(5)                ; xmm in lowercase, FPU regs in uppercase
                deb 4, "#4 will show in the console:", ebx, $My$, $MyArray$(n)
                deb 5, "#5 will be written to DebLog.txt:", ebx, $My$, $MyArray$(n)

Rem                - the debug macro preserves ordinary reg32, xmm and all FPU registers, and the flags
                - it can show xmm and FPU registers ST(0)...ST(5), but it can not display ST(6) and ST(7)
                - 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.
                - 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 console output (deb 4) by pressing Ctrl C
                - if FPU regs display incorrectly 0.0, 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 and Jwasm 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:
                                WndProc proc uses esi ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
                                LOCAL rc:RECT
                                ; the variable whose changes are monitored is xmm7, but we can't do it directly, so we use eax
                                movd eax, xmm0
                                inc msgCount
                                deb 4, "xmm0 change for ...", chg:eax, msgCount                ; this console deb will only be shown if chg:xxx has changed
                                SWITCH uMsg
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
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

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

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

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

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

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

Key                -

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                Let My$="Notepad.exe"                                ; cmd, show, timeout in ms, flags
                Launch My$                                ; defaults: SW_NORMAL, 0, 0
                Launch "Notepad.exe MyNewFile.txt"
                Launch "Notepad.exe MyNewFile.txt", SW_MINIMIZE
                Launch "Notepad.exe MyNewFile.txt", SW_MAXIMIZE, 2000
                Launch "Notepad.exe MyNewFile.txt", SW_MAXIMIZE, 2000, CREATE_NEW_CONSOLE
                ; you can specificy a handle to an edit control that receives console output:
                Launch "SendStringsToConsole.exe", SW_MAXIMIZE, cb:hEdit
                ; while the launched app is active, you may send strings: for example, you can launch the commandline interpreter
                Launch "cmd.exe /C time", SW_RESTORE, cb:hEdit                ; and see in the edit control a request to enter the new time
                WritePipe "20:40:50"                                                ; set the new time and confirm with Return, i.e. CrLf
                ; using the keyword passdata, Launch can pass a block of memory to the child process - see .ParentData$:
                Launch "MyApp.exe /some /options", passdata, pointer_or_quoted_string [, numbytes]
                Launch "MyApp.exe /some /options", passdata, Chr$("Hello Jochen", 13, 10, "...this is great")
Rem                - returns CreateProcess pinfo.hProcess in eax, and the GetExitCodeProcess error code in edx
                - if you specify a callback edit control instead of the timeout, e.g. with cb:hEdit, you may need to send EM_LIMITTEXT
                    to this control
                - if you use cb:hEdit or you specify a timeout of 1 (one millisecond), the process handle will not be closed; you
                    can then poll the process status e.g. in a WM_TIMER handler: .if ExitCode()!=STILL_ACTIVE ... do cleanup etc
                - the 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 rather useless example with a user-defined timeout of 2000 ms:
                                MsgBox 0, Launch$("cmd.exe /C time", SW_RESTORE, 2000), "Current time:", MB_OK                ; will ask for user input but won't get any... ;-)
Rem                args as in Launch above; returns string in eax, and the para passed by the child process with ExitProcess as ExitCode()

                Let My$=Launch$("GetInfo.exe")                ; imagine a little proggie that writes something useful to console and ...
                mov eax, ExitCode($)                ; ... finishes with a Yes/No/Cancel MsgBox plus invoke ExitProcess, eax
                .if eax==IDYES
                                ... 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 in front means show as string
                                Print "Match found: ", LastFileName$, Str$(" with size %i bytes\n", LastFileSize)
                                deb 1, "Last file:", LastFileSize, $LastFileName$, $LastFileDosName$, $MbExeFolder$, $CurDir$()
Keys                ex(, exist(

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

                Kill "Myfile.dat"
Rem                returns eax

                Touch "Rec_Test.dat"                ; set the file's timestamp to current time
                Touch My$(99)                ; same if filename is in a string array
                Open "U", #7, esi                ; open file for updating
                Touch #7, 1                ; set the file's timestamp to current time
                Close 7                                                ; close it
                ; for debugging, you can check for success: throw an error if the file doesn't exist or is not accessible
                Touch "Rec_Test.dat", 1                ; make sure you are not in the middle of something important

Rem                returns SetFileTime result in eax, except with option 1
                1. Open "O", #1, ... Touch #1 will destroy your file
                2. Open "I", #1, ... Touch #1 will not change the timestamp
                3. Touch with error check will display a message, then invoke ExitProcess if it encounters an error.

GetFiles, AddFiles
                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
                - you may use the following switches:
                    GfNoPaths=                0/1                ; 0=include the path specified by user (default); 1=use file names only
                    GfNoRecurse=                0/1                ; 0=include subfolders (default); 1=search only in current folder
                - the long version returns in Files$(0) the full path of the file where the block of text was found.
                    The text may have been found in more than one file. Each new file is marked by
                    space tab space space in the first 4 characters.
                - if endpattern is omitted, CrLf will be used, and the first matching line only will be returned;
                    in this case only, you may used Odd(n) for getting the matching lines in Files$(n)
                - if endpattern=1...126, GetFiles returns up to endpattern matches (1: same as omitted)
                - if endpattern=-1, all matching lines will be returned in Files$()
                - if endpattern=0, GetFiles looks for matches but returns only filenames in Files$()
                - 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

                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
                GetFiles *.asm                ; get all sources in the current folder and its subfolders
                xchg eax, ecx                ; save #files
                For_ ebx=0 To ecx-1
                                .if GfAgeHours(ebx)<=24                                ; pick your latest code
                                                mov esi, Cat$(Files$(ebx)+Space$(20))                ; we simulate LSET
                                                Print Str$("\n#%i ", ebx+1), GfDate$(ebx), ", ", GfTime$(ebx), Spc2$, Str$("%i bytes", edx::GfSize(ebx))                ; edx::eax for QUADWORD size
Rem                - all GetFiles special arrays return a DWORD in eax, except for GfLastWrite, which returns
                    in xmm0 the ftLastWriteTime FILETIME member of the WIN32_FIND_DATA structure
                - GfSize returns the high quad in edx
                - age is calculated from the moment when GetFiles was launched

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

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

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

                Let esi="MbGuide.rtf"
                Print esi, " was launched by ", ExeFromWin$(WinByTitle(esi))
Rem                returns DWORD in eax

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

\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

"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

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

                Let esi=NoTag$(FileRead$(";all"))
Rem                strips HTML tags, scripts and styles; don't expect miracles - reducing a perfectly styled webpage
                to pure text will not look pretty, but it's handy to filter webpages by text content

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

                Open "O", #1, Chr$("\masm32\MasmBasic\WinIncs.htm")
                ; This example joins two major include files, adds html structures and highlights some elements
                Let ebx=FileRead$("\masm32\include\")+FileRead$("\masm32\include\")
                ; The '<' and '>' chars will be misinterpreted as html tags and must therefore be removed
                Let ebx=Replace$(ebx, offset txBracketLeft, "<")                                ; src, search, repl [, case] [, count]
                Let ebx=Replace$(ebx, offset txBracketRight, ">")                                ; > becomes 'greater than'
                Let ebx=""+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
                - Replace$ uses the same case flags as Instr_
                - the optional count parameter allows to limit substitution; 0 or omitted means replace all
Key                rep$(

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

                SetWin$ hEdit="Just received:"+CrLf$+CopyData$+CrLf$+"--- end of data ---"                                ; use directly to set a window content
                Let My$="Just received:"+CrLf$+CopyData$+CrLf$+"--- end of data ---"                                ; store away for later use
Rem                - for receiving data from other apps; for use inside a WM_COPYDATA message handler, for example:
                                SWITCH uMsg
                                CASE WM_COPYDATA
                                Let My$=CopyData$+CrLf$+"received "+Time$
                - for sending data, use SendData
- 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

INIT                ; prepare a DDE session; Word must be running
                .if eax
                                SendWordCommands "[FileNewDefault:InsertFile D:\Masm32\include\]"
                                SendWordCommands Chr$("[InsertFile ", 34, "D:\Masm32\include\", 34, "]")
                                Let esi=FileRead$("\masm32\RichMasm\OpenDocInWord.bas")
                                SendWordCommands esi                ; 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\RichMasm\Help\WrdBasic.hlp
                - a sequence is included in [squared brackets]
                - multiple commands can be separated by a colon (:)
                - for strings, use either the escape sequence    (Alt 248, 92, 34) or Chr$("xx", 34, "xx") - see above
                - you can also use Let or Cat$ to send commands to MS Word
                - it is your responsibility to respect legality when using this feature

xlsConnect, MbxlsOpen, MbxlsConnect, MbxlsCommand, MbxlsSysRead$(), MbxlsRead$(), MbxlsWrite, MbxlsClose, MbxlsDisconnect, MbxlsHotlink
                These macros provide an easy-to-use DDE interface to MS Excel. See detailed examples
                in \Masm32\RichMasm\Masm2Excel.asc    and \Masm32\RichMasm\Res\XlsViewer.asc

(uses hotlinks)
                ; A typical sequence 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$('R9C1:R12C5')+"]"+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

ddeConnect, ddeCommand, ddeRequest$(), ddeDisconnect
                ; These macros provide the DDE interface for non-Excel apps, such as browsers:
                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?
                                                ddeCommand ""                ; open a frequently used page
Rem                see xls macros

Win$, .wWin$
                MsgBox 0, Win$(hWnd), "The title of the main window:", MB_OK
                wMsgBox 0, wWin$(hWnd), wChr$("The title of the main window in Unicode:"), MB_OK
Rem                returns pointer in eax; _Win$ can be used for Gfa compatibility
Key                win$(

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

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

                MakeFont 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

                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                must be placed at the very end of the WM_CREATE handler, i.e. when all controls have been created

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

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

Clip$, .wClip$
                Print "[", Clip$(), "]"                                                                ; displays the content of the clipboard
                MsgBox 0, Clip$(), "Text on the clipboard:", MB_OK                                ; same as MessageBox
                MsgBox 0,\
                Cat$("The text on the clipboard:"+CrLf$+String$(33,"-")+CrLf$+Clip$(20)+CrLf$+String$(0,0)),\
                "MasmBasic:", MB_OK                                                                ; displays a MessageBox with two horizontal delimiters
MsgBox 0, Clip$(40), "The text on the clipboard, truncated to 40 chars:", MB_OK
                invoke lstrcpy, offset my40charbuffer, Clip$(40-1)                                                ; yep, don't forget space for the zero delimiter
                                void Clip$(30)                                                                ; this loop waits for changes on the clipboard
                                .if !Zero?
                                                PrintLine "New clipboard content=[", eax, "]"                                ; non-zero means new valid content available
                                invoke Sleep, 1                                                                ; note: "new" is defined as (len(string)+first dword) different
                .Until signed rv(GetKeyState, VK_ESCAPE)<0
                Let esi=HtmlClip$()                                                                ; for use e.g. with Thunderbird
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$ "Hello World"
                SetClip$ "Today is the "+Date$+", and the time is "+Time$
                SetHtmlClip$ "This is bold"                                                                ; for use e.g. with Thunderbird
                wSetClip$ "Today is the "+wDate$+", and the time is "+wTime$
                mov hBmp, rv(CreateCompatibleBitmap, hDC, width, height)
                ... BitBlt etc ...
                SetClip$ hBmp, CF_BITMAP                ; not exactly a string, but it works, too ;-)
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

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

Res$, .wRes$
                Let esi=wRes$(401)                                ; get the resource string with ID 401 from the *.rc string table
                invoke MessageBoxW, 0, esi, wRes$(402), MB_OK
                Open "O", #1, "TestUnicode.txt"
                wPrint #1, wChr$("Unicode, oh yeah!", 13, 10)
                wPrint #1, wRes$(401), wTb$, wRes$(402), wCrLf$
                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\\RichMasm\\icons\\Globe.ico"                ; Asm, House, Keys, Globe, Hammer, Setup, Disc, Eye, ...
1 24 "\\Masm32\\RichMasm\\Res\\XpManifest.xml"                ; adding a manifest may help to avoid AV problems
                401,                "Нажмите на эту кнопку"                ; "Click on this button" in Russian
                402,                "Добро пожаловать"                ; "Welcome" in Russian

                ; 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
Key                string$(

                Let My$=Space$(20)
                Print "Twenty spaces: [", Space$(20),"]"
Rem                returns pointer in eax

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

Print, wPrint
Print #n, wPrint #n
                Print "Test", CrLf$, Chr$("the library", 13, 10)
                Print #1, esi, " is ", My$
                wPrint wRes$(123)                                ; Unicode defined in a resource stringtable
                Print #1:25, "There are many chars here but I print only a few"
                Print #1:100, FileRead$("\masm32\include\")                ; 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.
                - Important: the first occurrence of wPrint in your code triggers (hidden, but you can see it in Olly):
                                invoke SetConsoleOutputCP, CP_UTF8                ; codepage 65001, Unicode
                                ConsoleColor cGray, cBlack                                ; grey on black
    Without these two API calls, Unicode strings will not display properly. Note that the
                    console colors are not automatically set if there is an earlier ConsoleColor statement.
                    You can try different colours by using for example, before the wPrint, wPrintColor = cYellow
                    (see full list under ConsoleColor below). Strangely enough, Chinese and Arabic fonts may work
                    with some colours but not with others - Windows mysteries... ;-)
                - Print is powerful and versatile, but it may fail with a HeapAlloc runtime error for huge buffers; in
                    these cases, use PrintBuffer (see below)
Key                pri, pri1

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

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

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

                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

                crtbuf ThisExe$, MAX_PATH                                                ; give me a string in the uninitialised data section
                invoke GetModuleFileName, 0, ThisExe$, MAX_PATH                ; eax returns # of bytes copied
Rem                uses label/ORG len/db ?, 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

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

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

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

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

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

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

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

                Dim My3Pts(2) As DWORD                                ; create an array with 3 elements (0 .. 2)
                ArraySet My3Pts() = 12, 34, 56                                ; assign three values
                Dim MyR8(3) As REAL8                                ; create an array with 4 elements (0 .. 3)
                ArraySet MyR8() = 1.0, 2.0e3, 2.0, 4,0e3                ; same for REAL4 or REAL8
Rem                - no return value, does not trash any registers
                - allowed sizes are DWORD, REAL4, REAL8
                - #elements must be sufficient (no autoexpand, no boundary check)

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

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

Let, .wLet
                Let Test$="A little test"+CrLf$+"with two lines"
                Let My$(123)=MyOther$(ebx)
                Let My$(123)=Str$("A little test: %i", ebx*3+100)
                Let esi="Another "+"example"                ; using a register is possible but see remarks
                wLet MyUnicode$="A test with Unicode: "+FileRead$("Unicode.txt")
                invoke MessageBoxW, 0, MyUnicode$, wChr$("Title"), MB_OK
                wMsgBox 0, wCat$("The file content:"+wCrLf$+FileRead$("Unicode.txt")), "Unicode is easy:", MB_YESNO
                                                                wLet esi=wChr$("A Unicode Str$: ")+wStr$("%i little bugs", 123)+wChr$(" are waiting for you")
                invoke MessageBoxW, 0, esi, wChr$("Title"), MB_OK
Rem                - no return value
                - the wide version wLet does not allow wLet="abc"; use wLet=wChr$("abc")
                - 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="abc" works, but Let checks whether esi is in the table of heap pointers; if yes, the previous string will be
                    HeapFree'd, which is the desired behaviour allowing multiple Let esi="abc"+esi+"def" lines; however, if esi was set
                    to some other memory location before the Let, then there will be an attempt to free the other one.    To avoid such
                    problems, either stick to global (or zeroed local) variables (Let My$=...), or insert an xor esi, esi before Let esi=...
Key                let

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

                Insert My$(5)                ; move My$(5) up to My$(6)
                Let My$(5)="This is the new string"
                Insert My$(5), 10                ; move My$(5) up to My$(15), insert 10 empty strings
Rem                - no return value; does not change any registers (not even eax)
                - with 2-dimensional arrays, Insert My$(1, 2) etc is possible but check carefully the result,
                    especially if you need to use Insert more than once
Key                ins

                Delete 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)
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"
                mov ebx, 60
                Print Chr$(13, 10, "This is ", ebx, "great", ebx+2)                ; <great>
Print #1, wChr$(13, 10, "This is Unicode"")                                ; to files or the console (see wPrint for details)
                mov eax, Chr$("This is an ANSI string")                                ; you can use a pointer to an ANSI string...
                wPrint wChr$("Not true: "), wChr$(eax)                                                ; ... to print a wide string with wChr$(reg32)
                wData MyWide$, "This is Unicode", 0                                                ; use in code section, access as mov eax, offset MyWide$
Rem                returns pointer in eax
Key                c$(

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

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

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

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

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

                MyTest proc arg1:DWORD, arg2:RECT
                LOCAL v1, v2, rc:RECT, buffer[100]:BYTE
                    ClearLocalVariables uses edi esi ebx
                    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
                - do NOT use with the standard syntax, i.e. MyTest proc uses esi edi arg1:DWORD
Key                clv

Instr_, Rinstr, .wInstr
                Print "The current drive is ", Left$(ThisExe$, Instr_(ThisExe$, "\")-1)
                mov pos, Instr_(1, L$(n), "equ", 1+4)                ; 1=start pos, 1=case-insensitive + 4=full word
Rem                - returns relative pos in edx, absolute in eax
                - four 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
                - case & mode (bitwise flag):
                    0=case-sensitive, +1=insensitive, +2=intellisense (Name=name),
                    +4=full word search, +8=include start of line in text block search
                    Note: full word search returns failure for chars above ASCII 64 to the left or right
                    Example: Nostructfoundhere, this123struct456isfound, this@struct, too

Key                instr(

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

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 39 aka single quote
                xsTrimR                ; trim right side (after excluding right match if xsExcL is set)
                xsTrim=xsTrimL or xsTrimR                ; trim both sides
                xsLineL                ; include line of the left match
                xsLineR                ; include rest of line after the right match (must include right match...)
                xsLoop                ; let Instr_ start behind the last position, for using in loops
                *) if the right pattern contains a linefeed (LF, Ascii 10), the maxlines counter will never stop the pattern search
                The last flag, xsLoop, is used in the following demo, a Windows console application that extracts
                all structures from the two main Masm32 include files. Do not use the result for work, as there are
                problems with nested structures (e.g. unions ending with ends) and some structures ending with
                lowercase ends.
include \masm32\MasmBasic\                ; download
                ; First, let's get some useful source string:
                Let ecx=FileRead$("\Masm32\include\")+FileRead$("\Masm32\include\")
                Open "O", #1, "Structures.txt"                ; open a file for output
                xor ebx, ebx                ; reset counter
                .While 1
                                inc ebx
                                .Break .if !Extract$(ecx, "STRUCT", 'ENDS', xsFullW or xsLineL or xsIncR or xsLoop, 100)
                                Print #1, eax, CrLf$
                Close #1                ; file #1 closed
                Inkey Str$("%i structures found\n", ebx)
end start

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

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

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

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

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

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

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

                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))
Rem                - for use with Let and Print
- removes trailing spaces, tabs and other characters below Ascii 32
                - old Gfa Ztrim$ syntax possible but has no effect
Key                trim$(

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

Date$, .wDate$, CrtDate$
                Print "Today is the ", Date$
                Print "This file was created on ", CrtDate$
Rem                returns ptr in eax

Time$, .wTime$, CrtTime$
                Print "Now it is ", Time$
Rem                returns ptr 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
Rem                returns DWORD in eax; uses QPC

, the effective resolution is about 0.3 microseconds
                Recall "\masm32\include\", MyRec$()                ; translate file input to an array of strings
                mov lc, Min(eax, 20)
                Print Str$("%i lines found", lc)
                For_ n=0 To lc-1
                                Print Str$("\nRec %i\t", n)
                                Print Left$(MyRec$(n), 50)

                Recall "MyFile.csv", MyRec$(), csv                                ; loads a spreadsheet in comma separated values format
                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
                - see Store below for saving arrays
                - note the tab and csv versions do not allow direct string concatenation with Print or Let:
                    PrintLine "fails: A2=", MyCellArray$(1,0), Tb$, "A3=", MyCellArray$(2, 0)                ; fails miserably showing twice the same cell
                    Print "works: A2=", MyCellArray$(1,0), Tb$, "A3="                ; workaround: separate the two ...
                    PrintLine MyCellArray$(2, 0)                                                ; ... cells by printing over two lines
                - there is no wRecall, but if Unicode is detected, Recall converts strings to UTF8; use Print to display them
                - with the spreadsheet variants, single cells can be accessed via Let My$=MyRec$(row, column);
                    for usage, see the spreadsheet demo
                - Gfa syntax (Recall #3, Url$(), -1, MyLineCounter) works, but -1 will be ignored, and #3 will be closed
Key                Rec

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

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

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

                Print Str$("%i lines written to SortedAscending.txt\n", esi)
                QSortDesc L$()                                ; now sort them all descending
                Store "SortedDescending.txt", L$()                                ; and write them back to file
Rem                eax returned by CRT qsort and has no meaning ("return value: none")
Key                qst

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

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

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

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

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

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

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

Rem                no return value
Key                apl

                ; 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

                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

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

                mov ebx, 200
                xor ecx, ecx
                For_ n=5 To ebx-1
                                inc ecx
                                .Break .if ecx==123
Rem                For_ n=0 To eax-1 is valid code; you can nest several For_ loops, but you must use different counters.
Key                for_

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

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

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

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

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

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

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

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

                Print Str$("f4pc(srcr10, 33.33)=\t%If\n", f4Percent(srcr10, 33.33))                                ; f4: returns REAL4
                Print Str$("f8pc(srcr10, 33.33)=\t%If\n", f8Percent(srcr10, 33.33))                                ; f8: returns REAL8 e.g. for use with xmm regs
                Print Str$("fpupc(srcr10, pcr10)=\t%If\n", fpuPercent(srcr10, pcr10))                ; fpu: leaves result in ST(0)
                Print Str$("fpc(12345.678, ebx)=%f\n", f8Percent(12345.678, ebx))
                Print Str$("fpc(12345.6, ebx)=%f\n", f8Percent(12345.6, ebx))
                Print Str$("pc(12345.0, ebx)=%f\n", Percent(12345.0, ebx))                                ; with immediate real source and reg32 percentage
                Print Str$("pc(srcdd, ebx)=%f\n", Percent(srcdd, ebx))                                                ; with DWORD source and reg32 percentage
                Print Str$("pc(12345, 20)=%f\n", Percent(12345, 20))                                                ; with immediate integer source and percentage
                movq xmm0, f8Percent(srcr10, 33.33)
                Print Str$("xmm0=%f\n", f:xmm0)
                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...ABCDEFGHI as shown above

                Print Hex$(eax)
                Print Hex$(cx)
                Print Hex$(dl)
                Print Hex$(MyDword)
                Print Hex$(MyWord)
                Print Hex$(MyByte)
                Print Hex$(123)
                Let A$="Hex="+Hex$(1a2b3c4dh)+"h"

Rem                returns DWORD in edx - use only once in Let

                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

                .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
                .if FcmpLesser
                                Print Str$("MyPI_hi at %f is lower than the real PI\n", MyPI_hi)
                .elseif Zero?
                                Print Str$("MyPI_hi at %f is exact\n", MyPI_hi)
                                Print Str$("MyPI_hi at %f is higher than the real PI\n", MyPI_hi)
Rem                - returns Zero? and Sign? flags: Sign? means "first arg below second arg"
                - you may use FcmpGreater and FcmpLess (aka !Sign? and Sign?)
                - single arg, e.g. Fcmp xmm1, tests for zero
                - almost any number formats can be compared, including xmm registers etc

                mov eax, Val(Chr$("123"))                ; eax=123, edx=3
                mov eax, Val("123")                ; eax=123, edx=3
                mov esi, Chr$(9, "    12345")                ; eax=12345, edx=9 (5 plus a tab and three spaces)
                mov ebx, Val(esi)
                mov ebx, Val("12345h")                ; hex
                mov ebx, Val("0x12345")                ; hex, C notation
                mov ebx, Val("$12345")                ; hex, leading $ notation
                mov ebx, Val("10101b")
                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 f:
                Print Str$("MyXmm=\t%f\n", f:xmm0)                                ; 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, Chr$("123.4567")                                ; qwords
                Print Str$("MyQword=\t%f\n", MyQword)
                MovVal MyR4, Chr$("123.4567")                                ; REAL4
                Print Str$("MyR4=\t%f\n", MyR4)
                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

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

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

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

Rem                returns current flag value in Carry?

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

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

                void Len("a test")                ; use Len but avoid a mov eax, eax
Rem                returns DWORD in eax

                PrintLine GetRegVal("HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment","Path", "no path found"), CrLf$
                EnvVars equ "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"                ; long paths: use an equate
                Print GetRegVal(EnvVars,"PROCESSOR_IDENTIFIER", "unknown"), CrLf$                                                ; get to know your CPU...
                print GetRegVal(EnvVars, "PROCESSOR_IDENTIFIER", "unknown"), 13, 10                                                ; same with Masm32 print macro
                PrintLine GetRegVal("HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment","PROCESSOR_IDENTIFIER", "unknown"), CrLf$                                                                ; same with full path
                PrintLine GetRegVal("HKCR\.doc", 0, "documents"), " are files with ending *.doc"
                PrintLine GetRegVal("HKEY_CLASSES_ROOT\.bax", 0, "unknown"), " are files with ending *.bax"
                ; this line would fail with a run-time error because the key does not exist and there is no default value given:
                ; PrintLine GetRegVal("HKEY_CLASSES_ROOT\.bax", 0), " are files with ending *.bax"
                PrintLine GetRegVal("HKCU\Console","FaceName", "Arial"), " is your default console font"                ; works fine
                ; this line would produce garbage because GetRegVal can only be first para in Let and Print combinations:
                ; PrintLine "Your default console font is ", GetRegVal("HKCU\Console","FaceName", "Arial")
                ; workaround with Cat$ (no need for cleanup with Clr$):
                PrintLine Cat$("Your default console font is "+GetRegVal("HKCU\Console","FaceName", "Arial"))
                ; workaround with a permanent string, e.g. in esi:
                Let esi=GetRegVal("HKCU\Console","FaceName", "Arial")
                PrintLine "Your default console font is ", esi, " with FontFamily ", Hex$(GetRegVal("HKCU\Console","FontFamily"))
                Clr$ esi                ; if you don't use esi in another Let esi=..., a Clr$ esi should free the heap
                ; 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"

Rem                - returns in eax either a pointer to a string, or a DWORD
                - returns in edx success (1) or failure (0)
                - keep in mind that Print and Let trash eax and edx. You can use void GetRegVal to test for edx
                - 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
Key                grv(

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

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

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