extracted from \Masm32\MasmBasic\MbGuide.rtf version 20.02.2024
- see original RTF file for correct formatting and latest additions (RichMasm menu File/MasmBasic help)
- the invitation to 'select Init and hit F6 to test this snippet' works only when opening MasmBasic help in RichMasm
Download the library and RichMasm from the Masm32 forum - (you need the Masm32 SDK to use it: see Masm32 Tips, Tricks and Traps)
MasmBasic signed ErrLines EndOfCode For_ Step Fn mcs xmov jinvoke @LibUsed$ @AsmUsed$ @Upper$ @Lower$ @MbRet Admin MbWinVersion ComCtl32$ wComCtl32$ Switch_ Case_ Default_ Endsw_ Try Catch Finally TryRTE Init Exit CL$ wCL$ If_ Then If? Div_ IsTrue CodePage$ CodeSize CheckStackBalance DlgDefine DlgControl DlgShow SetGlobals Enum Enum$ Choose Let wLet Cat$ wCat$ StringBuild Lset Read $ DefStruct Dll Declare SetDllFolder HeapStrings deb fdeb ifdeb Err$ SetErrLine Sound Say Asc Cvi Cvl Odd Abs ModZero Min Max PopCount Bsr64 Rand Rand64 Encrypt Decrypt FolderOpen$ FileOpen$ FileSave$ wFileOpen$ wFileSave$ wFileOpenSetFolder Open wOpen Close Seek Lof Loc Eof Input LineInput Input$ wInput$ Prompt$ Rename WritePipe ClosePipe Launch ShEx SetLaunchTimeout Launch$ LaunchEndPos SetLaunchEnvironment ExitCode( Exist LastFileSize LastFileName$ LastFileDosName$ IsFolder IsRichEdit Kill Touch GetFiles AddFiles Files$ GetFolders AddFolders GfCallback SortFiles GfDate$ GfTime$ GfAgeHours GfAgeMinutes GfAgeMs GfLastWrite GfSize GfGetInfo GfSetInfo Age CurDir$ GetDrives$ Ini$ SetMru AddMru MruText$ ExpandEnv$ wExpandEnv$ ExeFromWin$ ExeFromExt$ GetFileProps FilePropName$ MakeDir ArcFiles ZipFiles UnzipInit UnzipFile( FileRead$ LastFileSize Download WM_DOWNLOADFINISHED NoTag$ FileWrite Replace$ ParentData$ CopyData$ SendData SendControlKey SendWordCommands OpenAdo xlsConnect xlsOpen xlsCommand xlsSysRead$ xlsRead$ xlsWrite xlsClose xlsDisconnect xlsHotlink ddeConnect ddeCommand ddeRequest$ ddeDisconnect Win$ wWin$ SetWin$ wSetWin$ AddWin$ wAddWin$ SetSel$ Sel$ wSel$ CreateAndSelect MakeBrush MakeFont FontExist( PickFont ImgPaint ToolTips WinByTitle WinByClass App16 Clip$ wClip$ ClipboardImageWH SetClip$ wSetClip$ SetHtmlClip$ MsgMonitor GuiImage GuiImageCallback SaveImageToFile GuiImageSpeed GuiImageFrame GuiControl GuiGroup Link$ Coloured EndOfEvents GuiEnd SetListbox SetCombobox Event Event Event Event Event Event Event Event Event Event GuiParas GuiMenu GuiColor GuiText GuiTextBox GuiCls GuiLine GuiCircle GuiEllipse GuiMs GuiSetFill icon MakePath GuiDraw MakePen Legend SetDoc$ GetDoc$ SetTitle$ SetStatus$ New$ Resize Alloc16 Free16 MemSet MemState Res$ wRes$ wRec$ ResFile$ Lg$ Geo$ MsgTable$ String$ SpaceToTab$ Inkey wInkey Print wPrint Print wPrint CurDir$ PrintLine wPrintLine PrintBuffer PrintRtf PrintCpu Cpu$ SetCpUtf8 SetCpAnsi SetCpUpperLower$ ConvertCp$ ConsoleColor Locate( At CColor crtbuf xCall CreateInvoke CRT QuadMath Quad Quad$ gsl SetPoly3 GetPoly3 GetLinReg LinRegY Dim Erase VarPtr ArrayFill wArrayFill ArrayStripDuplicates ArraySet StringToArray Split$ Join$ Filter$ ArrayMerge Swap Insert Delete GuidFromString Guid$ GuidsEqual CoInvoke Ole$ BSTR wChr$ Utf8$ Chr$ RichEd$ Len wLen uLen MbCopy Bmove SafeCopy MbCopyz MsgBox wMsgBox Alert( Clr Clr$ Cls ClearLastError ClearFileCache ClearLocals _Local Locals StackBuffer StackWalk SetWatch Instr_ Rinstr wInstr InstrOr Rinstr wRinstr RinstrX wCount LineCount LineNumber Clean$ Strip$ StringsDiffer wStringsDiffer FilesDiffer uLeft$ uMid$ uRight$ Left$ wLeft$ Mid$ wMid$ Right$ wRight$ Lower$ wLower$ Upper$ wUpper$ Trim$ Ltrim$ Rtrim$ Qtrim$ Ntrim$ Mirror$ Date$ wDate$ Time$ wTime$ IsoWeek Json$ GetTZ$ fDate$ fTime$ wfDate$ wfTime$ TimeSF TimeDiff Timer NanoTimer CyCtStart WinFromID WinFromID$ Align64 AlignX Recall Csv2Tab wRecall Store StoreUtf8 _Passed$ QSortDesc Scramble QSortMode BitSort ArrayMinMax ArrayInfo ArrayRead ArrayStore ArrayFind ArrayIndex ArraySearch ArrayLoadMap PaintMap MapColours CanvasMap( SetFolder MapRegion MapRegion$ WM_MAPCLICKED ArrayMapRegion CanvasZoom ArrayPlot ArrayPlotValues SetAxisX SetAxisY RgbCol CgaCol SysCol Red Green Blue Str$ wStr$ Quad$ SetFloat SetInt AddFloat Int64 Int128 shlXmm Float MulQQ Floor Ceil Sinus Cosinus rSinus rCosinus ArcSinus ArcCosinus ArcTangens FpuSave FpuRestore FpuPush FpuFill FpuSet FastMath MbMod LogE Log10 Exp10 Exp2 ExpE ExpXY Percent f4Percent f8Percent fpuPercent DefNum Hex$ HexDump$ Bin$ IntAsWords$ Qcmp Ocmp Oword16( Fsign Fcmp Val Val? HexVal MovVal Sqrt SetField GetField GetHash SetFlags Flags MouseX MouseY MouseK Delay void voidTrue voidFalse GetRegVal SetReg64 SetRegVal GetRegKeyArray GetRegArray SetReg64 GetProcessArray FindProcess GetDevicesArray PushText TitleCase$ Masm32
; The smallprint: this library is provided "as is", and the usual disclaimers apply.
ErrLines
EndOfCode
For_ .. Next
Fn
mcs multiple commands
xmov extended mov
jinvoke
@LibUsed$(), @AsmUsed$(), @Upper$(), @Lower$()
@MbRet
Admin
MbWinVersion
ComCtl32$ test your manifest - returns the DLL version
Switch_, Case_, Default_, Endsw_
Try/Catch/Finally
TryRTE
Init This is perhaps the smallest possible complete Masm application:
Exit The correct way to leave a MasmBasic application
CL$(), wCL$() command line args
If_ ... Then One-line if with one or more statements
If? ternary operator
Div_
IsTrue ; test condition with floats
CodePage$
CodeSize & CheckStackBalance
DlgDefine, DlgControl, DlgShow
SetGlobals ; declares global variables relative to ebx, syntax as in LOCAL , where x=-128 ... +128 and higher
Enum ; create a list of IDs or constants
Enum$ ; return numeric constant as text
Choose, Choose$
Let, wLet assign text to a string
Cat$, wCat$
StringBuild fast string concatenation
Lset
Data and Read
DefStruct
Dll & Declare, SetDllFolder
HeapStrings (for debugging)
deb, fdeb, ifdeb Never was debugging easier...$db
Err$()
SetErrLine
Sound
Say
Asc
Cvi
Cvl
Odd
Abs
ModZero
Min, Max
PopCount
Bsr64
Rand , Rand64
Encrypt & Decrypt
FolderOpen$
FileOpen$, FileSave$, wFileOpen$, wFileSave$
Open, wOpen
Close
Seek
Lof
Loc
Eof
Input
LineInput
Input$, wInput$
Prompt$
Rename
WritePipe
Launch
Launch$ get string from executable
LaunchEndPos
SetLaunchEnvironment
ExitCode()
Exist
IsFolder
IsRichEdit
Kill
Touch
GetFiles, AddFiles
GetFolders, AddFolders
GfCallback define a callback function to monitor progress in GetFiles or GetFolders
SortFiles sort the Files$() array
GfDate$ , GfTime$, GfAgeHours, GfAgeMinutes, GfAgeMs, GfLastWrite , GfSize, GfGetInfo
Age
CurDir$
GetDrives$
Ini$ , SetIni$
SetMru, AddMru, MruText$
ExpandEnv$, wExpandEnv$
ExeFromWin$, ExeFromExt$
GetFileProps ; get an array of file properties
MakeDir
ArcFiles ; requires a FreeArc installation
UnArcFiles ; requires a FreeArc installation
ZipFiles
FileRead$
Download
NoTag$
FileWrite
Replace$
ParentData$
CopyData$
SendData
SendControlKey
SendWordCommands
OpenAdo Advanced Data Objects
xlsConnect, xlsOpen, xlsCommand, xlsSysRead$(), xlsRC$()
xlsRead$(), xlsWrite, xlsClose, xlsDisconnect, xlsHotlink ; in case of failure, macros return Zero? plus an error string in eax
ddeConnect, ddeCommand, ddeRequest$(), ddeDisconnect
Win$, wWin$
SetWin$, wSetWin$
AddWin$, wAddWin$
SetSel$
Sel$, wSel$ ; select the white zone and hit F6
CreateAndSelect
MakeBrush
MakeFont
PickFont
ImgPaint
ToolTips
WinByTitle
App16
Clip$, wClip$, uClip$, ClipboardChanged
SetClip$, wSetClip$, SetHtmlClip$
MsgMonitor
GuiImage
GuiImageCallback, SaveImageToFile, GuiImageSpeed, GuiImageFrame
GuiControl
Coloured rounded buttons
EndOfEvents, GuiEnd
SetListbox, SetCombobox
Event
Event Message
Event Timer
Event Command, Event Menu
Event Close
Event Data
Event Key
Event CanvasPaint
Event Paint
GuiParas, GuiMenu, GuiColor, GuiText, GuiTextBox, GuiCls, GuiRefresh, GuiLine, GuiCircle, GuiEllipse
icon in GuiParas
MakePath & GuiDraw ; for use with gdi+ in GUI applications
SetDoc$, GetDoc$, SetTitle$, SetStatus$ ; for use with Gui apps
New$
Alloc16, Free16
MemSet
MemState ; detect leaks
Res$, wRes$, wRec$, ResFile$
Lg$ multilingual strings
Geo$
MsgTable$
String$
SpaceToTab$
Inkey, wInkey
Print #n, wPrint #n
PrintLine, wPrintLine
PrintBuffer
PrintRtf ; send the contents of a RichEdit control to a printer
PrintCpu, Cpu$
SetCpUtf8, SetCpAnsi, SetCpUpperLower$
ConvertCp$
ConsoleColor
Locate()
At, CColor
crtbuf
xCall ; call a label with arguments
CreateInvoke
CRT
QuadMath, Quad, Quad$
gsl (GNU Scientific Library interface) #mat
SetPoly3, GetPoly3
GetLinReg , LinRegY linear regression
Dim
Erase
VarPtr
ArrayFill, wArrayFill
ArrayStripDuplicates
ArraySet
StringToArray
Split$
Join$
Filter$
ArrayMerge
Swap
Insert
Delete
GuidFromString
Guid$ create a GUID/UUID string
GuidsEqual
CoInvoke
Ole$ aka BSTR
Chr$
RichEd$ ; get text from RichEdit control
Len, wLen, uLen get string length
MbCopy, Bmove, SafeCopy
MsgBox, wMsgBox, Alert()
Clr, Clr$, Cls free variables and clear console
ClearLastError
ClearFileCache
ClearLocals, _Local
StackBuffer
StackWalk
Instr_, Rinstr, wInstr, InstrOr
RinstrX for use with file extensions
LineCount
LineNumber
Clean$
Strip$
Extract$ extracts a substring based on left and right matches
StringsDiffer, wStringsDiffer
FilesDiffer
uLeft$, uMid$, uRight$ ; UTF-8 string extraction
Left$, wLeft$
Mid$, wMid$
Right$, wRight$
Upper$, wUpper$
Trim$ remove white space to the left and right
Ltrim$ trim left
Rtrim$ trim right
Qtrim$ trim quotes
Ntrim$ trim anything below a number
Mirror$
Date$, wDate$, CrtDate$
Time$, wTime$, CrtTime$
IsoWeek
Json$ extract text and numbers from Json files
GetTZ$
fDate$(), fTime$(), wfDate$(), wfTime$()
TimeSF ; translate date string to system and file time
TimeDiff ; difference between two times
Timer
NanoTimer
WinFromID, WinFromID$ find a window associated with a process ID
Align64
Recall ; fill a string array with content from file or URL
Store save text array to file
_Passed$ pass a string array to a procedure
BitSort
ArrayMinMax
ArrayInfo
ArrayRead
ArrayStore
ArrayFind find matches in a string array
ArrayIndex
ArraySearch
ArrayLoadMap, PaintMap, MapColours
CanvasZoom
ArrayPlot, ArrayPlotValues, SetAxisX, SetAxisY
RgbCol, CgaCol, SysCol
Str$, wStr$
SetFloat, SetInt , AddFloat , Int64 , Int128
shlXmm
Float returns integer as float in ST(0)
MulQQ multiply two QWORDs
Floor, Ceil
Sinus, Cosinus, rSinus, rCosinus
ArcSinus, ArcCosinus, ArcTangens
FpuSave, FpuRestore
FpuPush
FpuFill
FpuSet
FastMath create a fast mathematical function
MbMod
Exp10, Exp2, ExpE and ExpXY
Percent, f4Percent, f8Percent, fpuPercent
DefNum
Hex$
HexDump$
Bin$
IntAsWords$ translate integer to English text
Qcmp, Ocmp
Fsign
Fcmp
Val, Val?, HexVal
MovVal
Sqrt
SetField
GetField
GetHash
SetFlags
Flags
MouseX, MouseY, MouseK
Delay
void, voidTrue, voidFalse
GetRegVal
SetRegVal
GetRegKeyArray, GetRegArray
GetProcessArray
FindProcess
GetDevicesArray
PushText
TitleCase$
Masm32 macros:
; --------- This help file refers to MasmBasic version 15 February 2024 --------------------------
; It is assumed that you installed the library from http://masm32.com/board/index.php?topic=94.0
; To start a new project, go to ^ File/New Masm source ^ and click on one of the links, e.g. console.
; Once you see the file in front of you, hit F6 to assemble, link and run the example.
; For help on MasmBasic keywords, hover some seconds over e.g. Init until the mouse cursor turns
; into ? (i.e. a question mark), then right-click to see detailed examples. If you left-click quickly into
; the green area, you may select code for your own use. Click outside the green area to leave it.
; the following line substitutes the standard Masm32 include \masm32\include\masm32rt.inc:
; -----------------------------------------------------------------------------------------------------------------
; Hit F6 to assemble, link & run the Hello World example:
include \masm32\MasmBasic\MasmBasic.inc
Init
PrintLine "Welcome to assembly, friend from ", Geo$(), "..."
MsgBox 0, "Wow, it works!!!!", "Hi", MB_OK ; It worked? Use a template, or try 99 snippets
PrintLine "OK - press any key" ; It didn't? Give me feedback here.
EndOfCode
; -- same as dual 32-/64-bit assembly code (click here for a Masm64 SDK example): --
; select Init below, then hit F6 to build & run this example:
include \masm32\MasmBasic\Res\JBasic.inc ; instead of JBasic, you might try the Masm64 SDK
Init ; OxPT_64 1 ; delete the x for 64 bit assembly
PrintLine "More examples under menu File/New Masm source: Dual 32/64 bit console/GUI templates"
MsgBox 0, Chr$("Built with ", @AsmUsed$(1), " in ", jbit$, "-bit format"), "Wow, it works:", MB_OK
EndOfCode
Practical hints for using MasmBasic
- You cannot use MasmBasic with the old ML.exe that comes along with Masm32 (version 6.14 doesn't know about SSE2...).
Instead, you should use UAsm. When you hit F6 to assemble & link a MasmBasic source, RichMasm will invite you to
install UAsm from the web; if that fails for some reason (e.g. antivirus software), download UAsm64 manually
(64bit Binary Package), then copy UAsm64.exe from the archive to \Masm32\bin
- if you prefer another editor, like qEditor, you can either modify the build batch files, or a) rename \Masm32\bin\ml.exe
to \Masm32\bin\ml_old.exe, b) then save UAsm64.exe as ml.exe
- Use the RichMasm editor: It helps if you can type opi (i.e. opi
search function is outstanding. For example, select Key and hit F3 to see a list of all shortcuts for MasmBasic keywords.
Other features you may like are the bookmarks to the right, and the history: Press Alt left arrow and Alt right arrow to
see where you recently edited your source. Press Alt K (K like keys) to toggle bookmarks with a list of keyboard shortcuts.
- To find commands, use the find box in the upper right corner of the RichMasm editor. The string .xMb lists all MasmBasic
commands, while .xMbF lists commands starting with F. You can use wildcards, e.g. .Mxb*file will find commands that contain
the word file. Note also the little Case box above the find box.
- You should associate the *.asc (Assembler Source Code) file extension with \Masm32\MasmBasic\RichMasm.exe
- RichMasm is a Unicode editor - try a multilingual example
- RichMasm uses the RichEdit msftedit.dll; crashes are very rare but may happen. Save your work frequently, keep backups,
reflect where to click if you see a "You may have had a crash" message, or try the menu File/Last good version - it opens
the last version that assembled without errors. You will appreciate this function ;-)
- Colours are purely illustrative. RichMasm tries to colour MasmBasic commands in blue if you use the keyword shortcuts,
e.g. sto (space) becomes Store "MyFile.txt", L$(), ct, but colours have no influence on how Masm reads your code.
- You can define your own shortcuts: select the string #box invoke MessageBox, 0, Str$(eax), Chr$("Title"), MB_OK, then
right-click and choose Save shortcut. Afterwards, typing box
- Register preservation: When calling Windows APIs, e.g. invoke MessageBox, 0, Str$(eax), Chr$("Title"), MB_OK,
the registers esi edi ebx ebp esp will be preserved by Windows; eax will return a value, and ecx and edx will most probably
contain garbage. Beginners stumble sooner or later over this problem when they use ecx for a loop and see a crash.
MasmBasic behaves like Windows, except that it does preserve ecx. Therefore, use the values returned in eax, never rely
on edx after any macro or invoke except if edx returns something, and rely on ecx only if you have not used any invokes to
Windows APIs in your loop. Note that standard Masm32 macros like print, str$() etc do invoke Windows APIs and therefore
do trash ecx. Use Print and Str$() instead.
- Preservation of xmm registers: All xmm registers are preserved, with the exception of three compare functions:
Fcmp, StringsDiffer and FilesDiffer, which use xmm0... xmm2. The regs xmm3..xmm7 will never be trashed by MasmBasic
itself, but (WARNING) Windows in its 64-bit versions has the bad habit to trash xmm0 ... xmm5. MasmBasic preserves all
xmm regs, but check carefully what happens if you use invoke MessageBox, ... instead of MsgBox 0, "text", "Hi", MB_OK !!
- Another popular error: Masm uses by default unsigned comparisons. This example demonstrates the effect:
mov eax, -1 ; definitely a lot smaller than 99, right?
.if eax<99
MsgBox 0, Str$(eax), "-1 is less than 99:", MB_OK
.else
MsgBox 0, Str$(eax), "Surprise, surprise: -1 is bigger than 99!", MB_OK
.endif
The problem is that Masm reads -1 as 4294967295, i.e. 2^32-1, or 0FFFFFFFFh. To avoid this, MasmBasic provides
the signed equate: .if signed eax<99 will produce the behaviour you expect.
- You may see cryptic error messages; for example, Let esi=Trim$(FileRead$("Test A.txt")) will fail with "forced error".
Often, reducing the nesting level, i.e. splitting in Let esi=FileRead$(...), then Let esi=Trim$(esi) helps.
- MasmBasic uses the Masm MACRO engine. As with all libraries using macros (e.g. print, str$, len... in Masm32), be aware
that 0pmacro expansion takes place before the current line. Normally, this is no problem, but in rare cases incorrect code may be
produced. One such case is .if Len(My$)==1 ... .exlseif Len(My$)==2 - this does indeed generate code for the second statement,
but this code will never be executed. To avoid this scenario, use for example:
.if Len(My$)==1
...
.else
.if Len(My$)==2 ; code will be inserted before the .if and after the .else, and produce the expected result
...
.else
.if eax==3 ; hint: Len returns eax, so instead of calling Len once more, you may continue to test eax
...
.endif
.endif
.endif
Error messages
When debugging, use ErrLines to see the line where a runtime error occurs; if you still
get only question marks in runtime error messages, use SetErrLine in the suspect zone.
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6
MsgBox 0, "Easy!!", "Hi", MB_OK
EndOfCode
Rem does some cleanup; use instead of "end start"
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals fct:REAL8
Init ; select Init and hit F6
For_ ct=0 To 3
Print Str$("\nouter loop %i\t", ct) ; nested loops are ok
For_ cta=0 To 2
Print Str$("A%i ", cta)
For_ ecx=0 To 2
Print Str$("B%i ", ecx)
Next
Next
For_ ecx=0 To 2
Print Str$("C%i ", ecx)
Next
Next
Recall "\Masm32\include\Windows.inc", My$() ; get a string array
Delete My$(10), -1 ; keep 0...9, delete the rest
mov eax, 2000 ; note it is not necessary to define the
For_ n=0 To eax-1000 ; counter (here: n ) in the data section,
Print Str$("\nLoop %i", n) ; if the counter is an integer
.Break .if n==9
Next n
Print
For_ ct=10 To 0 Step -1 ; Step may be negative
Print Str$(ct), " " ; print a countdown
Next
Print
For_ fct=1.5 To 2.0 Step 0.1 ; counter is a float, and therefore needs to be
Print Str$(fct), Spc2$ ; previously defined in .data? or with SetGlobals
Next
For_ each esi in My$() ; loop through elements of an array
PrintLine Str$(ForNextCounter), Tb$, esi ; print the counter and each element
Next
GetFiles *.ini ; fill the Files$() array
For_ each edi in Files$() : PrintLine edi ; one-liner: for..each only, needs a register
For_ each ecx in Files$() : <PrintLine Str$("file %__i\t", ForNextCounter), ecx> ; use
EndOfCode
Rem - if begin>end (or begin
- the optional Step variable must be an immediate integer (or real, if the counter is defined as REAL4/8/10)
- in case you have too many For_ ... Next loops, MbForMax can be increased
- For_ each ... can be used for string, numeric and structure arrays; for some types, edi is being used; with usedeb=1,
a warning will be shown. Note that For_ each esi in somearray() is valid code, too.
- For_ n=0 To eax-1 is valid code; you can nest several For_ loops, but you must use different counters.
Key for_
Dim floats() As REAL8
Print Str$("%i items converted", Fn(StringToArray "12 34 56", floats()))
Rem use Fn() as "function format" for commands that do not "return" anything
mcs mov eax, 100 : add eax, 30 : sub eax, 7: deb 4, "sum:", eax : Inkey "-- press any key --" ; OK
mcs mov eax, 100 : add eax, 30 : sub eax, 7: <Print Str$("result: eax=%i\n", eax)> ; avoid, or test carefully
mcs For_ ecx=0 To 19: <Print Str$("%i ", ecx+1)>: Next ; a MasmBasic loop
Rem - mcs allows to put several commands on one line, separated by a colon as in some Basic dialects
- macros that depend on previous commands, like e.g. Print Str$(...) should start in a new line, or be put
xmov MyR8, 123456.7890123456 ; assign a double directly
xmov MyR4, MyR8 ; mem to mem, different operand size
xmov someSQ, someSD
xmov someSD, someSQ
xmov ct, -5
xmov MyR4, 32767
Rem - use if in doubt about the best way to shove a value into a memory location (check the disassembly)
- works with MasmBasic and 64-/32-bit JBasic
jinvoke MyTest, &v0, *v1, addr v2, offset v3, &v4, v5
Rem - jinvoke is a macro that works like invoke but assembles with 32- and 64-bit code alike
- see menu File/New Masm source/Dual 32/64 bit console/GUI templates
include \Masm32\MasmBasic\Res\JBasic.inc ; needs uppercase opt_64 1
Init
% echo @AsmUsed$(0) ; no quotes
tmp$ CATSTR
% echo tmp$
PrintLine Chr$("This code was assembled with ", @AsmUsed$(), " in ", jbit$, "-bit format using the ", @LibUsed$(), " library")
EndOfCode
Rem - output for the example: This code was assembled with UAsm64 in 64-bit format using the JBasic library
- with no args, i.e. (), quoted strings are returned; use e.g. @AsmUsed$(0) to return the plain string without quotes
SomeAlgo proc uses esi arg1, arg2
push ecx
@MbRet ; shout foul if the stack is not balanced
SomeAlgo endp
Rem - use instead of ret; active for usedeb=1 or useMbRet=1, otherwise only a ret will be generated
- if the stack is not balanced, an error message will be written to the console
- do NOT use at the end of callback functions
If_ not Admin() Then PrintLine "This program must be run as admin"
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6
Print Str$("This is Windows version %i", MbWinVersion()), Str$(".%i", ecx)
Print Str$(", build %i\n", MbWinVersion(build))
Print Str$("The long story: Windows version %i", MbWinVersion()), Str$(".%i ", ecx),\
GetRegVal("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ReleaseID", "(no version #)"),\
Str$(", build %i\nrunning on ", MbWinVersion(build)), Cpu$()
EndOfCode
Rem - returns major version in eax, minor version in ecx, and build in dx
- HKLM ... works on Win10, but older Windows versions do not have the ReleaseID value
- for structures that require bytes, use mov xy.byte, MbWinVersion(b)
include \masm32\MasmBasic\MasmBasic.inc ; download
Init ; select and hit F6
Print "CC: ", ComCtl32$()
MsgBox 0, ComCtl32$("This program uses common controls version %3f"), "Hi", MB_OK
EndOfCode XP ;
EndOfCode with the optional XP adds a manifest (polink only)
Rem - returns 5.82 (no or bad manifest) or 6.?? for XP and higher
- use wComCtl32$ for GuiControl
- you may use a format string containing %3f
The MasmBasic Switch_ macro (note the understroke) is more powerful than its C or GfaBasic equivalents, since
it can handle even variables or registers in the "cases". Under the hood it creates, depending on the range covered
and the number of cases, either an if .. elseif .. endif chain or a jump table that is for long lists of cases (more than 4)
much faster than the Masm32 macro. Below an example for MasmBasic Switch_
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals int somevar=5
Init ; ## Switch with jump table ##
m2m ecx, -5
PrintLine "----------------------------- testing the new MasmBasic Switch_ macro -----------------------------"
.Repeat
Print Str$(ecx), Tb$
m2m edx, -127 ; don't trigger the edx case...
If_ ecx==11 Then mov edx, ecx ; ... except for testing the Case_ edx at position 11
Switch_ ecx
Case_ lt -2
PrintLine "Case less than -2"
Case_ somevar: <PrintLine Str$("Case var=%i", somevar)> ;
Case_ edx ; this case triggered if ecx==edx; takes preference over 'immediate'
PrintLine "Case edx ###" ; cases but must come before lt or gt cases
Case_ -2
PrintLine "Case -2"
Case_ 0
PrintLine "Case NULL"
Case_ 10, 12
PrintLine "Case 10 or 12"
Case_ 18
PrintLine "Case 18"
Case_ 14 .. 16 : PrintLine "Case 14 .. 16 (one-liner)" ; OK if only one instruction is needed
Default_
PrintLine "---" ; no matching case found
Endsw_
inc ecx
.Until signed ecx>20
EndOfCode
Rem - Switch_ trashes edx but not eax (unless Case_ does it)
- do not forget the understroke (Switch_, Case_, Default_, Endsw_), otherwise you get the Masm32 switch macro
- Inside a case, the stack is 4 bytes off; if you really need values on the stack, use e.g. mov eax, stack[4] to compensate this.
- You can force Endsw_ chain or Endsw_ table; with few cases that are short and far apart, e.g. case -1000, case 1, case 1000,
Endsw_ table creates a huge jump table with many default entries; the chain mode will be slower but more size-efficient.
Key Swi
MasmBasic supports Structured Exception Handling (SEH).
See \masm32\MasmBasic\Res\Templates.asc for a detailed example.
include \masm32\MasmBasic\MasmBasic.inc ; download
Init ; select init and hit F6 to build this
ErrLines ; add extra code to get source code line where error occurred
Dim My$(3)
TryRTE BadElement, con ; optional: con means errors to console, key means con+(wait for a keypress); default is box
Let My$(5)="This won't work" ; the index is too high (incrementing gradually to 4, 5, 6 would be OK)
BadElement:
cmp edx, $ ; simple error check
.if Zero?
PrintLine "Bad index!!"
.endif
EndOfCode
Rem MasmBasic can throw a variety of runtime errors, such as "file not found" etc.; note that after
showing them, MB will normally call ExitProcess. This can lead to tricky situations if you use
MB in a dll. To prevent killing your main process, use TryRTE as shown above.
Error A2121: Symbol not defined : Use_mov_z$_Left$
Cause You tried to use mov esi, Left$("Test", 3)
Fix Use Let esi=Left$("Test", 3), or use z$ as follows:
MsgBox 0, z$(Right$("Hello Masm32", 6)), "z$:", MB_OK
Error A2148: invalid symbol type
Cause You tried to store an array that was not yet defined in the line where you call Store
Fix Put the Store into a proc, and move that proc below the line that defines the array, e.g. below the Recall
*** Basic instructions ***
RichMasm: To test the examples with white background, copy them into the nop after start, then copy them into the
nop after start, make sure required variables are present (e.g. MyReal10 REAL10 123.456 in the .data
section), then press F6 to see how the example works.
If the include MasmBasic.inc line and Init are present, do not copy the content; instead, select the
Init and hit F6, as in the example below (or, if there is no Init, select the whole white zone and hit F6).
include \masm32\MasmBasic\MasmBasic.inc
Init ; <<<<<<< select and hit F6
Inkey "Hello World" ; print text to console and wait for a key
EndOfCode ; combines Exit and 'end start'
Rem - the Init macro inserts at least the following two lines:
.code
start: call MbBufferInit
- using Init is optional; it declares the code section, the start label, and preloads some strings (CrLf$, Tb$ etc.);
however, the check for these preloaded strings will be performed in the first Print or Let statement, too.
- use Init tc [, con/key/box] to install a Structured Exception Handler with Try/Catch (\MasmBasic\details).
Exit
Exit eax
Exit 123
Exit debug ; checks if all Dims and string allocations have had matching deallocations on exit
Exit debug, 2000 ; same but waits two seconds
Exit debug, box ; same but displays a box instead
Rem - releases memory allocated for arrays or strings, then invokes ExitProcess
- after using strings or arrays, you should use Exit to quit an application
- do not confuse MasmBasic Exit with the lowercase Masm32 library exit
Key exit
MsgBox 0, CL$(), "Hi", MB_OK ; display all args in the command line (the normal MsgBox trashes ecx - this one doesn't!)
wMsgBox 0, wCL$(), "Hi", MB_OK ; same as wide (Unicode) version
MsgBox 0, Cat$("Arg1: "+Tb$+CL$(1)+CrLf$+"Arg2:"+Tb$+CL$(2)), "Args with Cat$:", MB_OK
Let esi="Arg1: "+Tb$+CL$(1)+CrLf$+"Arg2:"+Tb$+CL$(2)
MsgBox 0, esi, "Args with Let:", MB_OK
Print "Testing the MasmBasic CL$() function:"
xor ebx, ebx
@@: ; this snippet prints all args on screen:
mov ecx, CL$(ebx) ; do not put this line directly to the right of the @@ label (more)
jecxz @F
Print Str$("\narg #%i = [", ebx), ecx, "]"
inc ebx
jmp @B
@@: Inkey Str$("\n%i valid args found", ebx-1) ; instead, you can also use CL$(?) to get the number of arguments
Rem - returns pointer in eax, or zero if there is no argument
- if used with Print, Let or Cat$(), and there is no arg, a question mark will be inserted
- if no arg is supplied, i.e. CL$(), then one arg is assumed, even if spaces are present
(useful e.g. to open C:\Documents and Settings\user\Documents\My File.txt);
in this case, the commandline must not exceed 512 characters
- CL$(0) yields the path of the executable
- if you need args permanently, use Let MyArg$=CL$(1)
; Warning: this macro is powerful but read the Rem section carefully
If_ eax Then inc ecx ; If_ condition Then action
If_ not eax Then dec ecx
If_ NOT eax==1 && ecx>eax Then Print "Hello"
Rem - use for single-line if statements
- If_ !eax Then is not possible (unless you use four exclamation marks...); instead, use
If_ not eax Then ...
- If_ and Then are case-sensitive, not is not
- Warning: function macros (e.g. Val(...)) are evaluated from left to right before entering the If_ macro,
so eax and edx are definitely trashed before e.g. If_ eax==123 - caution, it will fail miserably!! To prevent
this behaviour, you can block premature expansion with
If_ eax==5 Then Print <Str$("eax=%i", eax)>
Syntax: mov eax, If?(comparison, result_true, result_false)
mov ecx, 123
mov edi, ecx
Print Str$("ecx eq 123: %i\n", If?(ecx eq edi, 111, 222)) ; if ecx equals edi, take 111, otherwise 222
Print Str$("ecx ne 123: %i\n", If?(ecx ne 123, 111, 222))
PrintLine "ecx above 123: ", If?(ecx ab 123, "above", "not above") ; unsigned comparison, string output
PrintLine "ecx below 123: ", If?(ecx bl 123, "below", "not below")
PrintLine "ecx ge 123: ", If?(ecx ge 123, "greater or equal", "not greater or equal") ; signed comparison, string output
PrintLine "ecx gt 123: ", If?(ecx gt 123, "greater", "not greater")
invoke ShowWindow, hEdit1, If?(rv(IsWindowVisible, hEdit1), SW_HIDE, SW_SHOW) ; toggle visibility for edit control #1
invoke ShowWindow, hEdit2, If?() ; repeat the action for edit control #2
.if FileSave$("Plain Asm=*.asm|MasmBasic=*.asc||Rich text=*.rtf|All files=*.*") ; saves quite some bytes with complex functions
FileWrite FileSave$(), stream:hMyEdit, If?(InstrOr(FileSave$(), ".rtf" or ".asc", 1), SF_RTF, SF_TEXT) ; choose the right format
.endif
xor ecx, ecx
Print Str$("zero flag set: %i\txor ecx, ecx\n", If?(Zero?, 111, 222)) ; you may use the Zero? ...
or ecx, -1
Print Str$("zero flag set: %i\tor ecx, -1\n", If?(Zero?, 111, 222))
stc
Print Str$("carry set: %i\tstc\n", If?(Carry?, 111, 222)) ; ... or Carry? flag
clc
Print Str$("carry set: %i\tclc\n", If?(Carry?, 111, 222))
Rem - the comparison follows the macro syntax, with some deviations (in red) for technical reasons:
- eq=equal, ne=not equal
- ab=above, ae=above or equal (unsigned)
- bl=below, be=below or equal
- gt=greater, ge=greater or equal (signed)
- lt=less than, le=less or equal
- if one of the options is "quoted text", strings will be returned, otherwise integers
- !Zero? and !Carry? are not allowed; swap arguments instead
- when used with no arguments, i.e. If?(), the last result will be used - see ShowWindow example
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals R4:REAL4=100.0, R8:REAL8=1000.0, R10:REAL10=10000.0
Init ; select and hit F6
mov eax, 1000
Print Str$("Imm real\t%i\n", Div_(22.222222222222222222))
mov eax, 12300000
Print Str$("Imm real\t%i\n", Div_(R4))
mov eax, 12300000
Print Str$("Imm real\t%i\n", Div_(R8))
mov eax, 12300000
Print Str$("Imm real\t%i\n", Div_(R10))
mov ecx, 1230000
Print Str$("Imm real\t%i\n", Div_(ecx/R10))
Print Str$("Imm real\t%i\n", Div_(R10/R4))
EndOfCode
Rem returns result in eax
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals x1:REAL8, x2:REAL10
Init ; << select Ixnit and hit F6 to test this snippet
SetGlobals ; the two loops differ only regarding their precision
fld FP8(123.456) ; put 123.456...
fstp x1 ; into double x1
MovVal x2, "123.457"
PrintLine "x1", Tb$, Tb$, "x2"
.Repeat
fld x1 ; load x1
fadd FP8(0.0005) ; add 0.0005
fstp x1 ; save it
PrintLine Str$(x1), Tb$, Str$(x2) ; print it
.Until IsTrue(x1 gt x2, top) ; loop until x1 is greater than 123.457
PrintLine Str$("x1 gt x2: x1=%If", x1), Str$(", x2=%If", x2), " (top precision)", CrLf$
MovVal x1, "123.456" ; put 123.456... into double x1
PrintLine "x1", Tb$, Tb$, "x2"
.Repeat
fld x1 ; load x1
fadd FP8(0.0005) ; add 0.0005
fstp x1 ; save it
PrintLine Str$(x1), Tb$, Str$(x2) ; print it
.Until IsTrue(x1 gt x2) ; loop until x1 is greater than 123.457
PrintLine Str$("x1 gt x2: x1=%If", x1), Str$(", x2=%If", x2), " (default precision)", CrLf$
Exit
end start
Rem - returns Zero? or Sign?, depending on the condition
- almost any numerical argument is allowed, including immediate floats and integers - see Fcmp(); the second
argument (i.e. top, medium, low precision) is optional; avoid using eq with top as in IsTrue(x1 eq x2, top)!
- use with .if ... and .Repeat ... .Until but not with .elseif or .While; RichMasm will warn you but other IDEs won't.
include \masm32\MasmBasic\MasmBasic.inc
Init ; <<< select init and hit F6 to test this snippet
PrintLine CodePage$(?) ; returns DOS cp, e.g. 65001 (UTF-8)
PrintLine CodePage$(w) ; e.g. Windows cp, e.g. 1252 (ANSI - Latin I)
PrintLine CodePage$(1251) ; 1251 (ANSI - Cyrillic)
EndOfCode
include \masm32\MasmBasic\MasmBasic.inc
Init ; <<< select init and hit F6 to test this snippet
CodeSize MyTest
call MyTest
useCsb=1 ; put 0 to see a crash
Exit
MyTest_s:
MyTest proc
CheckStackBalance
nops 98
push eax
CheckStackBalance
ret
MyTest endp
MyTest_endp:
CodeSize MyTest, echo
end start
Rem - CodeSize shows the size of code in bytes between name_s: and name_endp: (
- it does not trash any registers, and the echo version does not generate code
- if the stack is not balanced, StackBalance corrects it and tells you what to look for; activate it with useCsb=1
include \masm32\MasmBasic\MasmBasic.inc
Init
DlgDefine "Please enter your data, tab for next line:", 0, 0, 150, -4, , 14
DlgControl dcEdit, "First name", WS_BORDER or WS_TABSTOP, 1, -2 ; first control gets the focus
DlgControl dcEdit, "Family name", WS_BORDER or WS_TABSTOP, 1, -4 ; x, y only, the macro will assign width, height and ID
DlgControl dcStatic, "Type your first name:", SS_LEFT, 1, -1, 70.0 ; 70 means 70% - the buttons need space
DlgControl dcButton, "OK", BS_DEFPUSHBUTTON or WS_TABSTOP, 71.0, -1, 12.0, , IDOK ; x=71%, y, width=14%, height, ID
DlgControl dcButton, "Quit", BS_PUSHBUTTON or WS_TABSTOP, 84.0, -1, 16.0, , IDCANCEL
DlgControl dcStatic, wCat$("Type your family name: (it's "+wTime$+" now)"), SS_LEFT, 1, -3
; DlgHandler MyHandler ; optional - PM me for details
DlgShow
.if eax==IDOK
wMsgBox 0, wCat$(Dlg$(0)+wCrLf$+Dlg$(1)), "Please confirm:", MB_OKCANCEL
.endif
EndOfCode
Rem - see advanced dialog \MasmBasic\examples in \masm32\MasmBasic\Res\Templates.asc
- the dialog closes for IDOK, IDCANCEL and any other control whose ID is in the range 100...120; the ID will be in eax
- you can use registers or variables for the strings, but make sure they are Unicode (e.g. wChr$(ecx) or wRes$(123) are fine)
- esi cannot be used between DlgDefine and DlgShow, but ecx can be used and will not be trashed
.data?
whatever dd ?
SetGlobals hMain, hStatic, hEdit
SetGlobals MyDw=123, TheFactor:REAL4=123.456, FileName$="Test.txt" ; variables can be initialised
SetGlobals hMenu, hM1, hM2, rc:RECT
SetGlobals int MyInt=111, MyInt2, float MyR4a=444.44, MyR4b, double MyR8a=888.88 ; C-style
SetGlobals OWORD xm0, xm1, xm2 ; use with xmm registers - if this is the first SG statement, all xm? are aligned 16
SetGlobals msg:MSG, wc:WNDCLASSEX, exeBuffer[MAX_PATH]:BYTE, gBuffer[1024]:BYTE
.code
; no args: set ebx to the right offset, and initialise vars; must be used in callbacks (WndProc, SubEdit, ...)
SetGlobals
Rem - variables return [ebx+x]
- if SetGlobals declarations are present, the Init macro sets ebx to the .data? block reserved for the variables
- for a range of 256 bytes, SetGlobals produces very size-efficient code
- as with LOCAL variables, put large buffers at the end
- WORD, DWORD, REAL4, REAL8 variables and strings with '$' ending can be initialised
Enum IdMenuNew, IdMenuSave, IdMenuCopy, IdTimer
Enum 20:IdEdit, IdButton1, 30:IdButton2, IdStatic, IdFind, IdFindStatic ; using a start ID
Enum b0:opt1, opt2, opt3 ; bitwise: first element 0, then 1, 2, 4, ...
Enum b100:opt1, opt2, opt3 ; elements 100, 200, 400, ...
Enum 40:Button*10 ; create Button0=40, ..., Button9=50
Enum #MYSUBLANG_ENGLISH_, 1:US, UK, AUS, CAN, NZ, EIRE ; use a prefix
Print Str$("NZ=%i\n", MYSUBLANG_ENGLISH_NZ) ; 5; see SUB_LANG... in Windows.inc
Rem - default start is 10, but (as shown above) new start values can be specified with nn:
- multiple IDs can be specified as Enum Button*10, MyEdits*3 etc
- with #, a prefix can be specified
Case WM_CREATE
Enum 20:IdEdit, IdButton1, 30:IdButton2, IdStatic, IdFind, IdFindStatic ; create a list of IDs
; use with CreateWindowEx
Case WM_COMMAND
; ID is loword(wParam), print it as Enum$(ID, list of numeric constants)
PrintLine "command for control ", Enum$(word ptr wParam, IdEdit, IdButton1, IdButton2, IdStatic, IdFind, IdFindStatic)
mov ecx, Enum$(uMsg, WM_CREATE, WM_PAINT, WM_SIZE, WM_SIZING, WM_COMMAND)
.if byte ptr [ecx]!="?" ; if no matching entry is found, Enum$() returns a question mark
PrintLine "Message: ", ecx ; output e.g. Message: WM_PAINT
.endif
Rem for debugging, returns string for use with Print or Let
include \masm32\MasmBasic\MasmBasic.inc ; download
.data
tx123 db "This is tx123", 0
MyNum dd 1111111
Init ; select init and hit F6 to test this snippet
mov esi, Chr$("esi and edi are treated as strings")
mov ecx, 987654321
.While 1
Inkey "Gimme a number: "
.Break .if eax==VK_ESCAPE
.if Choose(eax-"0", 100, esi, 102, "abc", 123456789, ecx, 12345.67, 12345678.90123456789, addr tx123, MyNum)!=ChooseString
.if eax==ChooseReal
Print Str$("You chose the real number %Jf\n", ST(0)v) ; trailing v: fstp st
.elseif eax==ChooseError
PrintLine "No such entry"
.else
Print Str$("You chose the integer %i\n", eax)
.endif
.else
PrintLine "You chose the string [", eax, "]"
.endif
.Endw
For_ ecx=0 To 2
PrintLine Str$("C%i$=[", ecx), Choose$(ecx, "off", "on"), "]" ; 0=off, 1=on, 2...=?
Next
EndOfCode
Rem first arg expects an integer from 0...#args-1, followed by a list of return values. Choose() returns a flag in edx:
- ChooseString: pointer in eax
- ChooseReal: value in ST(0)
- ChooseError: out of range
Text in "quotes" is returned as string in eax, same for esi, edi and global vars like addr tx123.
Otherwise, eax contains an integer.
Let Test$="A little test"+CrLf$+"with two lines"
Let My$(123)=MyOther$(ebx)
Let My$(123)=Str$("A little test: %i", ebx*3+100)
Let esi="Another "+"example" ; using a register is possible but see remarks
wLet MyUnicode$="A test with Unicode: "+FileRead$("Unicode.txt")
invoke MessageBoxW, 0, MyUnicode$, wChr$("Title"), MB_OK
wMsgBox 0, wCat$("The file content:"+wCrLf$+FileRead$("Unicode.txt")), "Unicode is easy:", MB_YESNO
wLet esi=wChr$("A Unicode Str$: ")+wStr$("%i little bugs", 123)+wChr$(" are waiting for you")
invoke MessageBoxW, 0, esi, wChr$("Title"), MB_OK
Rem - returns pointer to resulting string in eax
- examples for using the Let Left$(...)=... syntax:
Dim My$(123) ; we create an array
Let My$(123)="A small test" ; we use an array element (but single strings work, too)
Let Mid$(My$(123), 5, 3)="oke" ; we replace small with smoke ->A smoke test
Let Left$(My$(123), 7)="A funny" ; first 7 chars are A funny ->A funny test
Let Mid$(My$(123), 3)="futile attempt to crack the buffer" ; result ->A futile att
Let Right$(My$(123), 3)="act" ; we replace the last 3 chars ->A futile act
- caution when using registers:
Let esi=eax works if eax is a valid pointer, but Let checks whether esi is in the table of heap pointers; if yes, the previous string
will be HeapFree'd, which is the desired behaviour allowing multiple Let esi="abc"+esi+"def" lines; however, if esi was set to
some other memory location before the Let, then there will be an attempt to free the other one. To avoid such problems, either
stick to global (or zeroed local) variables (Let My$=...), or insert xor esi, esi before Let esi=... other code ... followed by Clr$ esi
Key let
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
- only for fans of AsmC: Cat$(<Left$(MbExeFolder$)>+":\Masm32\bin\")
- every use of Cat$ overwrites content in the dedicated Cat$ buffer; mov myptr, Cat$() is
therefore pretty useless. If you want permanent strings, use Let
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals my$
Init ; < < select Init and hit F6 to test this snippet
StringBuild my$
Let my$="Start: " ; optional
For_ ecx=1 To 50000
Let my$="string "+Str$("#%i\n", ecx) ; 7-11 bytes per string
Next
StringBuild
PrintLine Left$(my$, 40), " ... ", Right$(my$, 40)
EndOfCode
Rem - use to concatenate strings really fast
- by default, 64kB are allocated; if you need more, use e.g. StringBuild some$, 20000h
- with usedeb=1, overflow will be signalled, with a 1k tolerance
include \masm32\MasmBasic\MasmBasic.inc
Init ; << select Ixnit and hit F6 to test this snippet
Let esi=Space$(50)
Lset esi="Let's test this today, "+fDate$(0)+", "+fTime$(0)
PrintLine "[", esi, "]"
Lset esi="Lset a much longer string, like this one: "+String$(100, "abc")
PrintLine "[", esi, "]"
EndOfCode
Rem use to set a string to a fixed length
include \masm32\MasmBasic\MasmBasic.inc
Data 123, "Hello World, how are you?", 'single quotes are allowed', My$, sq$, 456
SetGlobals My$, sq$, c1$, c2$, MyByte, MyDword, MyR4:REAL4, MyR8:REAL8, Last$, MyQ:QWORD
Data 789, 111, 1234567890123456789, 1234567890.123456789, 12345.6789, 12345.6789
Init ; select Init and hit F6 to test this example
Data 33333, "Last string item" ; Data statements can go almost everywhere...
Read ecx, My$, sq$, c1$, c2$, eax ; once My$+sq$ are read, c1$ and c2$ can be copied
Read edx
Read esi
Rewind 2 ; go back 2 items and read them again
Read edx
Read esi
Data 1234567890123456789 ; ... but values must be defined before they are read
Data "This", "is", "an", "entire", "string", "array", 111, 222
Read xmm0, f:xmm1, MyR8, MyR4, edi, Last$, MyQ
deb 4, "Read variables:", ecx, eax, $My$, $sq$, $c1$, $c2$, edx, esi, xmm0, f:xmm1, MyR8, MyR4, edi, MyQ, $Last$
Read my$()
For_ ecx=0 To my$(?)-1
Print my$(ecx), "."
Next
Read eax, ecx
deb 4, "Read again", eax, ecx
$Data No, quotes, in, this, string, array, 333, 444 ; use $Data for a series of strings without quotes
Read my$() ; you can re-use an array, here for the $Data items
For_ ecx=0 To my$(?)-1
Print my$(ecx), "."
Next
$Data "But, if you really, really need commas, 'quotes' are needed for $Data"
Read ecx
PrintLine CrLf$, ecx
EndOfCode
Rem - this is for fans of good ol' BASIC; in general, Recall does a better job
- the Data statements can be in code or data sections
- with string arrays, Read stops when it encounters a non-quoted item, e.g. 111 as shown above
- use the variant $Data for non-quoted strings, but note that 333 and 444 are then treated as strings
include \masm32\MasmBasic\MasmBasic.inc
DefStruct PETS: puppyname, gender: BYTE, cuteness:BYTE, baseprice, DogOrCatProc
Data "Cooper", 0, 33, 100, Dog, "Yorky", 0, 99, 101, Dog
Data "Bella", 1, 77, 128, Cat, "Daisy", 1, 60, 100, Cat
Data "Charlie", 1, 55, 90, Dog, @, 1, 2, 3, "a string" ; @ separates pets section from the following data
Init ; select Init and hit F6 to test this example
Dim pets() As PETS
Read pets()
For_ ecx=0 To eax-1
xCall pets(ecx, DogOrCatProc), ecx ; pass the registered function and the index
PrintLine Str$("\t(cuteness %i", pets(ecx, cuteness)), Str$(" x base %i)", pets(ecx, baseprice))
Next
Inkey "done"
Exit
Dog proc inx
movzx eax, pets(inx, cuteness)
push eax
fild pets(inx, baseprice)
fimul stack
fistp stack
Print pets(inx, puppyname), cfm$("\tis a dog and costs ")
pop eax
Print Str$("%i euros\t", eax)
ret
Dog endp
Cat proc inx
movzx eax, pets(inx, cuteness)
push eax
fild pets(inx, baseprice)
fimul stack
fistp stack
Print pets(inx, puppyname), cfm$("\tis a cat and costs ")
pop eax
Print Str$("%i euros\t", eax)
ret
Cat endp
EndOfCode
Rem Output:
Cooper is a dog and costs 3300 euros (cuteness 33 x base 100)
Yorky is a dog and costs 9999 euros (cuteness 99 x base 101)
Bella is a cat and costs 9856 euros (cuteness 77 x base 128)
Daisy is a cat and costs 6000 euros (cuteness 60 x base 100)
Charlie is a dog and costs 4950 euros (cuteness 55 x base 90)
include \masm32\MasmBasic\MasmBasic.inc ; include this library
.data
MyLongLong LONGLONG 12345678901234567890 ; the standard Masm32 crt lib is not enough to handle this
Init ; initialise the app
Dll "msvcrt" ; good ol' CRT
Declare double sin, C:1 ; the crt sinus function returns a double aka REAL8 in FPU register ST(0)
Print Str$("Sinus(3)= %Jf from CRT\n", sin(3.0))
fstp st ; since doubles are returned via the FPU, ST(0) must be popped
Dll "shimgvw" ; load the shell image view dll aka Windows Picture and Fax Viewer Library
Declare void ImageView_Fullscreen, 4 ; ImageView_Fullscreen expects 4 dwords but returns nothing useful
ImageView_Fullscreen(0, 0, wCL$(1), SW_SHOW) ; we need the wide version of the commandline arg
Err$(1, "ImgView") ; there is no retval - you may test here for errors
SetDllFolder "\Python" ; adjust to your setup; downloaded from here
Dll "python3.dll" ; has many dependencies, therefore the SetDllFolder
Declare PyList_New, C:1
Declare void Py_Initialize
SetDllFolder ; no args=go back to previous folder after declaring the functions
Dll "ntoskrnl.exe"
Declare RtlRandomExNtos, 1 Alias "RtlRandomEx" ; same but native API - will crash in user mode
Dll "msvcr100"
Declare void myprintf, C:? Alias "printf" ; don't return anything, C calling convention, vararg (note: vxoid followed by one space)
printf(cfm$("MyLongLong is %llX aka %llu\n"), MyLongLong, MyLongLong) ; MyLL as hex and decimal figure
; Print "MyLongLong is ", Hex$(MyLongLong), Str$(" = %u\n", MyLongLong) ; standard MasmBasic syntax, for comparison
DllRTE=0
Dll "%ProgramFiles%\FreeArc\Addons\InnoSetup\unarc" ; use environment variables for multilingual apps
Declare FreeArcExtract, C:? ; C calling convention, variable number of arguments
.if eax
Print Str$("\nResult=%i", FreeArcExtract(0, "l", "--", "testfile.arc")) ; pCallback, listing, no more options, archive name
.else
Print "unarc.dll not present?"
.endif
EndOfCode ; do a clean exit, inter alia FreeLibrary
Rem - Dll performs LoadLibrary, Declare initialises the macro with GetProcAddress, Exit frees the libraries
- to perform runtime checks if certain functions are available to your application, you may use the flags DllRTE=? and DecRTE=?
with values 1 meaning throw a runtime error if Dll or Declare not found, 0 to return zero in eax in case of error (default: 1)
- use Declare void SomeFunction, ... if you don't need the return value
- with e.g. Declare SomeFunction, 2 passing a string and an immediate as in SomeFunction("Test", 123.456) will pass a
REAL4 as second argument; to force a double, use SomeFunction("Test", REAL8:123.456)
- the number of arguments is independent of the size of arguments; pass e.g. a RECT stucture counts as one argument
- use Declare SomeFunction, C:3 for C calling convention with three args
- use Declare SomeFunction, C:? for C calling convention with a variable number of arguments
- the Alias keyword allows to declare a self-defined name; use in case of "already defined" errors
- use Declare #123=SomeFunction, 2 to access a function by ordinal number; then mov ecx, SomeFunction(arg1, arg2)
- if the function cannot be found, try the decorated name:
Declare _SomeFunction@12, C:3 ; three args, C calling convention
then use e.g. mov ecx, SomeFunction(arg1, arg2, arg3)
- do not use Dll & Declare for the static libraries of the Masm32 package (user32, kernel32, ..., see Masm32rt.inc)
- up to 9 libraries can be loaded
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6
For_ ecx=0 To 99 ; 0 To 100 would produce an error
Let esi=Str$("This is string #%i", ecx)
lodsb ; forbidden, increments esi!
Next
HeapStrings ; display all strings
EndOfCode
Rem By default, you can create 100 different strings (if you need more, use string arrays). These strings can be reassigned.
This works because Let x$="..." checks if x$ is already assigned; if yes, it will be freed. When using registers (Let esi="..."),
however, you need to be careful not to change the pointer - this would create a fresh string without freeing the previous one.
If you suspect such a problem because you got an error message, use HeapStrings before the error occurs to check which
of the strings misbehave
deb 1, "The first loop", ecx, $esi, $edi, MyReal10, ST, ST(5) ; show a MsgBox ($esi means show ptr esi as a string)
deb 2, "Second loop:", al, ecx, $esi, $My$:60, $MyArray$(n) ; show another MsgBox, limit the display of My$ to 60 bytes
deb 3, "Third loop:", al, ecx, $esi, xmm0, xmm1, ST, ST(5) ; xmm in lowercase, FPU regs in uppercase
deb 4, "#4 will show in the console:", xmm0, f:xmm0 ; display xmm0 as integer (default) and float with f: prefix
deb 7, "Loop A:", ecx, $$addr MyLocalWide$ ; display the string in a loop, but no more than 7 times
deb 5, "#5 will be written to DebLog.txt:", ebx, $My$, $MyArray$(n)
deb 4, "OWORD Bin$() spaced", b:MyOword:4, b:xmm0:4, b:xmm0:3, b:xmm0:2, b:xmm0 ; show 4 DWORDs as Bin$()
deb 4, "OWORD Bin$() dense", b:MyOword:4d, b:xmm0:4d, b:xmm0:3d, b:xmm0:2d, b:xmm0 ; same but no spaces
usedeb=0 ; disable debugging completely (no code inserted - very handy...)
deb 1, "This box will never pop up", eax
ifdeb push eax: push edx ; this line is active only for usedeb=1; see mcs for multiple commands
fdeb 1, "But this one will", eax ; forced deb overrides usedeb=0
ifdeb pop edx: pop eax ; note the order
usedeb=16 ; force hex display
deb 4, "Hexadecimal:", eax, xmm1, ST(3) ; limited to 32 bits, i.e. low dword of xmm regs, FPU as int 32
usedeb=2 ; force binary display
deb 4, "Binary:", eax, xmm1, ST(3) ; limited to 32 bits
usedeb=1 ; decimal display (default)
deb 4, "Multiple:", eax, x:eax, b:eax ; override usedeb: show arguments in decimal, hexadecimal and binary format
Rem - the debug macro preserves ordinary reg32, xmm and all FPU registers, and the flags (caution with macros - test it...)
- it can show xmm and FPU registers ST(0)...ST(7); see also MbHexQ
- numerical global and local variables can be displayed, but do not use different numerical arrays in the
same deb line (numerical arrays use edx, which will be set by the last element and used by all others)
- global and local variables as well as arrays can be shown as strings by using e.g. $ptr
- the string content of registers can be shown by using $eax, $ecx, $esi etc.; with e.g. $esi:50, you can limit the amount
of bytes displayed. Note that console output is limited to 53200 bytes under Win XP, 62600 under Windows 7.
- displaying macro results as strings is possible, as e.g. in $Win$(hWnd), but multiple expansion may occur;
check if this affects results of your code, and if yes, use a register instead: mov ecx, Win$(..) -> deb... $ecx
- cancelling deb 1, ... does not cancel deb 2, ..., so you can test several loops in one go
- deb 1, ... deb 3, ... are being displayed as MsgBox, while deb 4 writes to console, and deb 5 writes to file:
deb 5, "ToFile", eax, $esi, $edi saves contents (without showing them) to DebLog.txt
Remember that you can interrupt console output (deb 4) by pressing Ctrl C
- if FPU regs display incorrectly as 0.0, use Init, or try a void Str$(0) earlier in your code to initialise Str$()
- debCrLfDots=1 ; replace invalid chars with dots
- debTabDots=0 ; 0=keep tabs, 1=insert _ instead
- xmm display precision is REAL8, while 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
% deb 1, "String in highest element of MyStruc.ms1:", $
Note that with early Masm versions you cannot display xmm registers at the same time
- the chg: keyword allows to monitor which WM_ messages have changed a global variable or register; example
(you can insert the code above from the AutoCode menu - place the cursor at the end of the WndProc line):
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL rc:RECT, whatever
inc msgCount ; increment the default counter
deb 4, "# msg #", chg:msgCount ; this console deb will only be shown if the variable behind chg: has changed
SWITCH uMsg
Note that deb picks the messages to discard from an equate; you can adapt it to your needs:
NoDebMsg equ
Key deb
Err$(1) ; call GetLastError, and show a MessageBox with a formatted string if there was an error
Err$(0) ; call GetLastError, and show a MessageBox even if there was no error
Err$(1, "some text") ; show a MessageBox titled "some text" (1: only if there was an error)
PrintLine "Result=", Err$() ; print the formatted string
void Err$() ; no arg: returns
.if !Zero? ; for use with .if, .Break etc
MsgBox 0, eax, "Hey coder, there is an error:", MB_OK
.else
deb 4, "Loop C", $eax ; will write "The operation completed successfully" to the console
.endif
Rem - use wErr$(..) for Utf-16 errors
- the blank argument version returns a pointer to the formatted error desription in eax
Dim My$(99)
SetErrLine
Print My$(98) ; sooner or later, MasmBasic...
SetErrLine
Print My$(99) ; ... will show you a nice little ...
SetErrLine
Print My$(100) ; ... runtime error message ;-)
Rem - inserts mov MbErrLine, @Line+1 into your code - a fat 10-byte instruction
- see also Init SEH above; use mov MbFlags[4], 1 to see errors in console
- no code will be inserted for MbUseErrLine=0 (case-sensitive)
- if you want to see error lines in runtime error messages, put MbUseErrLine=1 early in your code
- many macros (string arrays, Input etc) set error lines automatically for MbUseErrLine=1
Sound "880:200" ; plays frequency 880Hz for 200 ms; allowed separators are space, tab, / and :
Sound "880/200/150" ; plays frequency 880Hz for 200 ms, followed by 150 ms of silence
Sound 111 ; plays a wav resource with ID 111 (in rc file: 111 WAVE "hello.wav")
Sound "hello.wav" ; plays a file directly (wav only)
Sound "hello.mp3" ; plays a file in its associated player
Sound "大桥在混乱的水.mp3" ; Unicode names are allowed
Sound wCL$() ; even if passed via the commandline
Rem - The simple "frequency:duration" syntax works on Windows 7 and higher but not on Windows Vista;
a minimum duration of about 90...100 ms is required
- PlaySound/Beep/ShellExecute results can be checked with Err$()
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6
Say "Hello World"
Say "Low and slow", SetVolume(50), SetRate(-7) ; you can use some methods of the ISpVoice interface
; Say wWin$(hEdit) ; any Unicode string is ok
; Say FileRead$("TTS_Unicode.txt")
; Say wRec$(FileRead$("TTS_Ansi.txt")) ; conversion necessary
EndOfCode
Rem uses ISpVoice
movzx ecx, byte ptr Asc(My$)
mov dl, Asc(esi)
Rem returns BYTE in al
movzx ecx, word ptr Cvi(My$)
mov dx, Cvi(esi)
Rem returns WORD in ax
mov ecx, Cvl(My$)
mov edx, Cvl(esi)
Rem returns DWORD in eax
.if Odd(123)...
.if Odd(eax)...
.if Odd(ax)...
.if Odd(al)...
.if Odd(MyDword)...
Rem returns !Zero?
PrintLine Str$(Abs(123))
PrintLine Str$(Abs(-123))
mov eax, -12345
PrintLine Str$(Abs(eax)+1000)
Rem returns positive DWORD in eax
For_ ecx=0 To 19
PrintLine Str$(ecx), Tb$, Str$(ModZero(ecx, 3))
Next
Rem returns 1 if the modulo is zero
mov eax, Max(12, eax) ; return value is in edx
mov eax, Min(12, eax-2) ; valid syntax
.if Min(eax, MyDword)>99
MsgBox 0, Str$("Too high: %i", Min(eax, MyDword)), "Sorry", MB_OK
.endif
For_ n=0 To Min(9, eax-1) ; GetFiles returns #files in eax: use eax-1 with null-based index
PrintLine Files$(n)
Next
Rem returns DWORD in edx, not eax (since eax is return value of many functions)
Key min(, max(
PopCount(MySource, ecx) ; standalone: set count to ecx
mov ebx, 88888888h
Print Str$("In ebx, %i bits are set", PopCount(ebx)) ; for use with Str$() etc
void PopCount(ebx) ; returns bitcount in eax
mov MyVar, PopCount(MySource)
Rem runs in about 7 cycles on a Celeron M; requires Init or at least one Str$(0) before
movlps xmm0, q3
Print Str$("Bsr64 of xmm0 \t%i\n", Bsr64(xmm0))
Print Str$("Bsr64 of q1 \t%i\n", Bsr64(q1))
For_ ecx=0 To 2
PrintLine Str$("Bsr64 of MyQ(%i)\t", ecx), Str$(Bsr64(MyQ(ecx)))
Next
Rem returns position of msb for 64-bit values; without arguments, edx::eax is assumed
Rand() ; no args = initialise using rdtsc
Rand(seed:abcd) ; use "abcd" as seed (4 letters)
Rand(seed:0FCDFDEABh) ; use any binary seed (5+ letters or numbers)
Rand(0, 100, xmm1) ; 0....+100, dest can be local/global REAL8 var or xmm
Rand(0, ebx, MyReal8) ; 0....ebx, dest can be local/global REAL8 var or xmm
Rand(0, 1) ; 0....+1, left on FPU in ST(0)
Rand(-5.5, 9.3) ; -5.3...+9.3, left on FPU in ST(0)
Print Str$(Rand64()) ; the 64-bit variant returns a QWORD (no args allowed)
void Rand(123) ; leaves dword in eax
mov ecx, Rand(123) ; range 0...123
mov ecx, Rand(MyDword) ; range passed as dword variable
mov ebx, 1000 ; we want 1000 elements (we'll get 1001: 0...1000)
Dim MyArray(ebx) As REAL4 ; could be also REAL8, QWORD, DWORD, WORD, BYTE
Dim MyDW(ebx) As DWORD
.Repeat
Rand(-888.888, 999.999, MyArray(ebx)) ; put random number into Real4 array
mov MyDW(ebx), Rand(1000)
dec ebx
.Until Sign?
Rand(pt:pTable()) ; set probability table: uses a DWORD array (->example)
mov somevar, Rand(pt) ; return index weighted by probabilities
Rand(1, 50, numbers() As DWORD, unique:6) ; min, max, array, unique:n: get 6 unique elements in the range 1...49
Rem - high-speed RNG generates pseudo random sequence with excellent randomness
- for testing, do not initialise; for the release version, use Rand() before the innermost loop (seed is in MbRndSeed[4])
- credits to Alex Bagayev for his AxRand algo
include \masm32\MasmBasic\MasmBasic.inc
Init
Let esi=CL$() ; get filename from commandline
.if Exist(esi)
Let edi=Input$("Password: ", "Masm is great") ; ask for a password
.if dword ptr [esi+Len(esi)-4]==Mirror$(".enc") ; encrypted file
Decrypt esi, edi ; restore the original
ShEx eax ; and show it
.else
Encrypt esi, edi ; filename, password
PrintLine "Encoded as ", eax
.endif
.else
PrintLine "No commandline found: ", esi
.endif
EndOfCode
Rem - returns path in eax, or zero if the Decrypt password was wrong
- with Decrypt, an existing original will be renamed to ~OldFile.tmp
- MbCryptTS=1: timestamp will be kept (default); 0: use current date+time
- password may contain any characters, but only A-Z, a-z, 0-9, /#*$&:+-?\ are interpreted
include \masm32\MasmBasic\MasmBasic.inc ; select Init and hit F6 to test the examples
Init ; FolderOpen$(prompt [, title] [, path] [, BIF flags])
Let esi=FolderOpen$() ; no args = use Windows defaults, start with current folder
Print "Selected: [", esi, "]", CrLf$ ; if the user cancelled, esi will be an empty string
PrintLine "Selected: ", FolderOpen$("Pick a folder:", "Masm32 is great") ; a prompt and a nice title, start with current folder
PrintLine "Selected: ", FolderOpen$("Pick a folder:",, "\Masm32\Examples") ; just a prompt, default title, specific folder
PrintLine "Selected: ", FolderOpen$("9Pick a big folder:") ; a big prompt (font 9), default title, current folder
; prompt, title, path with environment variables, flags
Let esi=FolderOpen$("What is the edit box for?","Locate Office:", "%ProgramFiles%\Microsoft Office", BIF_USENEWUI)
PrintLine esi
PrintLine "Selected folder=[", FolderOpen$("Where are your tools?",, "D:\Masm32\bin"), "]" ; fully qualified initial path
; if you need to check whether the user cancelled, use void and check the zero flag:
void FolderOpen$("5Pick the bin folder, please:","Getting help", "\Masm32\bin") ; use current drive plus initial path
.if !Zero?
MsgBox 0, Launch$(Cat$(eax+"\link.exe /Lib")), "Library manager help:", MB_OK
.else
PrintLine "Nothing selected: [", eax, "]" ; Zero? set = user cancelled, eax points to a Null$
.endif
EndOfCode
.if FileSave$(offset MyFilter, "Test save:") ; needs MyFilter db "RTF", 0, "*.asc", 0, [...etc, 0], 0
Let My$=FileSave$() ; assign to a string by using empty brackets
PrintLine "File openend: [", My$, "]" ; do whatever you need the filename for
.else
PrintLine "You cancelled" ; see Win32 docu of OPENFILENAME, lpstrFilter
.endif
; you may specify the filter using the following syntax, i.e. separate by "|":
.if FileOpen$("Rich sources=*.asc|All sauces=*.as?;*.inc|Resources=*.rc")
PrintLine "You opened [", FileOpen$(), "]" ; empty bracket: return the result
.else
PrintLine "You cancelled"
.endif
; this example uses two more options: a default file and an extra OFN_xx flag, and returns an array of selected files
.if FileOpen$(offset txFilter, "Test open", "MyCurrentFile.txt", ofnFlagsO or OFN_ALLOWMULTISELECT)
push edx ; # of files selected is in edx
mov esi, FileOpen$() ; empty brackets = return pointer to the buffer
.if edx==1
PrintLine "One file selected: [", esi, "]"
.else
PrintLine "Folder: [", esi, "]"
.endif
.While 1
lodsb ; same as mov al, [esi], then inc esi
.if !al
.Break .if !byte ptr [esi] ; two zero bytes means end of file array
PrintLine "File:", Tb$, esi
.endif
.Endw
pop edx
Print Str$("%i files selected\n", edx)
sub esi, FileOpen$()
Print Str$("%i bytes of ", esi), Str$("%i available bytes used\n", MbBufSize/4-1000)
.else
PrintLine "You cancelled"
.endif
Rem - if used with arguments, returns !Zero? for testing
- full syntax: FileOpen$("filter" [, "title"] [, "current"] [, flags] [, sortcolumn])
- if used without arguments, returns a pointer to the filename
- uses two predefined OFN_xx flags called ofnFlagsO and ofnFlagsS that you can modify if really needed
- uses a predefined ofnSortFlags in format 2*column+1, where 1 means down and 0 means sorted up; default is 2*3+1
- see Win32 docu of OPENFILENAME, in particular lpstrFilter and OFN_xx flags
- for example, you may use and MbOfn.nFilterIndex, 0 to clear users' choices
- you may force a different parent window with m2m MbOfn.hwndOwner, MyWindowHandle before .if FileOpen$()
- start folder is where the executable resides; force a different folder with, for example,
FileOpenSetFolder "\masm32\m32lib" (Unicode: wFileOpenSetFolder "\masm32\examples\unicode_extended\UnicodeTest_GUI")
Key fo$, fs$
Open "I", #1, "MyFile.txt" ; open for Input
Input #1, offset MyBuffer, Lof(#1)
Close #1
Open "O", #1, "MyOtherFile.txt" ; open for Output
Print #1, "Test: ", offset MyBuffer
Close
Open "U", #1, "MyOtherFile.txt" ; open for Update
Print #1:4, offset MyBuffer ; write 4 bytes
Close
Open "A", #ecx, "MyOtherFile.txt" ; open for Append (you can use #register instead of an immediate)
Print #ecx, CrLf$, "oops, forgot the end!"
Close
wOpen "O", #1, wRes$(MyFileID) ; use a Unicode resource string as filename - could be Arabic, Chinese, ...
wPrint #1, wChr$("Hello there") ; writes once a Unicode BOM, then Unicode "Hello there"
Close
Rem Open returns the handle in eax - you may check for INVALID_HANDLE_VALUE
Keys opi, opo
Close #3 ; close file #3
Close ; 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"
Close
Open "U", #1, "TestShort.txt" ; open file for updating
Print Str$("The file has %i bytes\n", Lof(#1))
mov ecx, 5 ; just for fun
Seek #1, ecx+7 ; abspos 5+7=12
Seek #1, +ecx ; relseek: 12+5=17, file pointer on long
Print Str$("The file pointer is now at byte %i\n", Loc(#1))
Print #1, "short STRING" ; result: This is a pretty short STRING
Seek #1, -6 ; relseek: move file pointer 6 bytes back to STRING
Print #1, "string" ; result: This is a pretty short string
Print Str$("Now the file has %i bytes\n", Lof(#1))
Close
PrintLine "The result: [", FileRead$("TestShort.txt"), "]"
Rem Seek returns in eax the Win API SetFilePointer return value
Note that:
- you can use one + or - operator, as in Seek #1, eax+20 or Seek #2, Lof(#2)-200 (but not with QWORD offsets)
- if + or - are the first char, however, it means "relative to current pointer": Seek #1, -ecx
- for huge files, you can use Seek #1, MyQword and Seek #1, +MyQword or Seek #1, -MyQword (but not MyQ+eax etc)
- remember that e.g. Let esi=Input$(#1, n) advances the pointer, too
mov ecx, Lof(#1)
Print Str$("File #1 has %i bytes\n", ecx)
Rem - returns length of file in eax; see example under Seek above
- in addition, the high dword is returned in edx, therefore this is valid syntax:
Print Str$("The file has %i bytes\n", edx::Lof(#1))
- Lof() doesn't throw runtime errors, but you can use .if !Sign? for error checking
Print Str$("The file pointer is now at byte %i\n", Loc(#1))
Rem - returns current file pointer in eax (and high dword in edx); see example under Seek above
- if needed, use .if !Sign? for error checking
Open "I", #1, "TypeTest.udt" ; file with user-defined type data
.Repeat
Recall #1, MyUDT
deb 4, "Test", MyUDT.mydouble, MyUDT.myfloat, MyUDT.mydword
.Until Eof(#1) ; no more data
Rem returns the sign flag
Input #1, offset MyBuffer, Lof(#1)
Rem - returns bytes read in eax, zero for failure
- edx returns the start of the buffer
- in contrast to the WinAPI ReadFile, Input zero-delimits the read bytes, so that you
can use the string directly; you can suppress this by adding one more argument
- Input reads data from a file; for reading a line from the console, see Input$ below
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6
Open "I", #1, "\Masm32\include\Windows.inc"
Dim My$()
xor ecx, ecx
.Repeat
LineInput My$(ecx)
.Break .if !edx ; Eof(#1)
PrintLine Str$("Line %i\t", ecx), My$(ecx)
inc ecx
.Until ecx>20
EndOfCode
Rem - provided for compatibility with older Basic dialects; use Recall, it's faster, simpler and safer
- for text files with a max string len of ca. 8k chars
- eax returns ptr to the string, edx flags Eof()
- works only with file #1
Let esi="["+Input$("Type something and hit Enter: ")+"]" ; input from console
Print "You typed ", esi
Let esi=Input$("Your hobby: ", "Asm", flush max 20) ; you may use flush as third para to flush the
wLet esi="["+wInput$("Type something and hit Enter: ")+"]" ; input queue, and/or max nn to limit the input
wMsgBox 0, esi, "This is Unicode:", MB_OK
Print "You typed [", Input$("Type something and hit Enter: "), "]"
Open "O", #1, "YourData.txt"
PrintLine #1, Input$("Hobbies:\t", "Assembler, ") ; the prompt can contain a tab escape,
PrintLine #1, Input$("Profession:\t", "Programmer") ; and you can suggest a prefilled string
Close #1
Open "I", #2, "YourData.txt"
Seek #2, Lof(#2)-10 ; set file pointer to EOF-10
Print "The end of your data: ", Input$(#2, 10) ; input last 10 bytes from file
Close
Rem returns temporary pointer in eax; to get a permanent string, use Let
Let esi="Returned: ["+Prompt$("Title", "proposal")+"]" ; title, suggestion, hFont, x, y, width, height
Rem provides an edit control; for console apps only, 160k max
Rename "MbGuide.rtf", "MbGuide.asc" [, flags] ; source, dest
Rem - uses MoveFileEx, flags e.g. MOVEFILE_REPLACE_EXISTING
- use MOVEFILE_COPY_ALLOWED if dest is on different drive
Key ren
Launch "cmd.exe /C time", SW_RESTORE, cb:hEdit ; launch an app that requires console input; show its output in the edit control
WritePipe "20:40:50" ; set the time
; you may add a 0 as second argument if you don't want a CrLf sequence to be appended:
WritePipe esi, 0 ; write zero-delimited string in esi, do not append CrLf
Rem - will show an error message if you try writing to a pipe that was already closed
- you may use ClosePipe to interrupt a task
- by default, data is written to a handle obtained by Launch ... cb:handle_to_edit; however, you may try writing to multiple pipes
(e.g. for a chat type app) by storing & exchanging the global launch structure variable ls.lsPipeWrite
Let My$="Notepad.exe" ; cmd, show, timeout in ms, flags
Launch My$ ; defaults: SW_NORMAL, 0, 0
Launch "Notepad.exe MyNewFile.txt"
; if you prefer to open the doc via ShellExecute, you can use ShEx instead:
ShEx "MyNewFile.txt"
Launch "Notepad.exe MyNewFile.txt", SW_MINIMIZE
Launch "Notepad.exe MyNewFile.txt", SW_MAXIMIZE, 2000
Launch "Notepad.exe MyNewFile.txt", SW_MAXIMIZE, 2000, CREATE_NEW_CONSOLE
; you can specificy a handle to an edit control that receives console output:
Launch "SendStringsToConsole.exe", SW_MAXIMIZE, cb:hEdit
; while the launched app is active, you may send strings: for example, you can launch the commandline interpreter
Launch "cmd.exe /C time", SW_RESTORE, cb:hEdit ; and see in the edit control a request to enter the new time
WritePipe "20:40:50" ; set the new time and confirm with Return, i.e. CrLf
; using the keyword passdata, Launch can pass a block of memory to the child process - see ParentData$:
Launch "MyApp.exe /some /options", passdata, pointer_or_quoted_string [, numbytes]
Launch "MyApp.exe /some /options", passdata, Chr$("Hello Jochen", 13, 10, "...this is great")
Rem - returns CreateProcess pinfo.hProcess in eax, and the GetExitCodeProcess error code in edx
- expects a Utf8$ that is passed to CreateProcessW as Utf16
- if you specify a callback edit control instead of the timeout, e.g. with cb:hEdit, you may need to send EM_LIMITTEXT
to this control
- if you use cb:hEdit or you specify a timeout of 1 (one millisecond), the process handle will not be closed; you
can then poll the process status e.g. in a WM_TIMER handler: .if ExitCode()!=STILL_ACTIVE ... do cleanup etc
- the default timeout for a "normal" Launch is ten seconds; if you mostly need asynchronous launches,
you can modify this value with e.g. SetLaunchTimeout 1 (in ms), up to a value of 65535, i.e. 65 seconds
- in contrast, the passdata variant returns after 100 ms; typically, the receiving app needs 5-10 ms to grab the buffer;
if this timeout is too long or too short, use e.g.
Externdef MbLaMs:DWORD
mov MbLaMs, 200
before calling Launch
- when passing a Chr$() or any other zero-terminated buffer, the length para is not needed
; the line below launches Arc.exe with option v and returns what SdtOut produces:
Let esi=Launch$(ExpandEnv$(Chr$(34, "%ProgramFiles%\FreeArc\bin\Arc.exe", 34, " v Lib32.arc"))) ; see FreeArc
StringToArray esi, FreeArc$() ; translate linear output to an array
; shorter: StringToArray Launch$(ExpandEnv$(Chr$(34, "%ProgramFiles%\FreeArc\bin\Arc.exe", 34, " v Lib32.arc"))), FreeArcListing$()
For_ ebx=0 To eax-1
PrintLine Str$(ebx), Tb$, FreeArc$(ebx)
Next
; this line appends the current date and time, retrieved via the commandline interpreter, to an edit control:
AddWin$ hEdit=CrLf$+"["+Trim$(Launch$("cmd.exe /C date /T"))+", "+Trim$(Launch$("cmd.exe /C time /T"))+"]"
; a bad example with a user-defined timeout of 2000 ms - don't launch console processes expecting user input:
MsgBox 0, Launch$("cmd.exe /C time", SW_RESTORE, 2000), "Current time:", MB_OK ; will ask for user input and hang
Rem - default timeout is five seconds, use args as in Launch above
- returns string in eax (La$? for failure), and the child process' ExitProcess argument as ExitCode()
mov esi, Win$(hEdit)
mov eax, LaunchEndPos() ; get the position inside the edit control where the last pipe read ended
add eax, esi
MsgBox 0, eax, "This part added by user:", MB_OK
Rem for use with Launch ... cb:hEdit
StringToArray Chr$("msga=Just a test", 10, "msgb=it works!"), my$() ; create a string array: use linefeed as separator
SetLaunchEnvironment my$()
Launch "test.bat" ; test it with e.g. echo %msga%
Rem - sets temporary environment variables for use with batch files
- Recall "MyVars", some$() works only if the file is in Unix format, i.e. linefeed-separated strings
Let My$=Launch$("GetInfo.exe") ; imagine a little proggie that writes something useful to console and ...
.if ExitCode($)==IDYES ; ... finishes with a Yes/No/Cancel MsgBox plus invoke ExitProcess, eax
... do something with My$ ...
.endif
Rem - returns a global variable with the para passed with ExitProcess, i.e. the DOS-style errorlevel
- arguments:
ExitCode() without arguments returns the exit code of the last "standard" Launch with timeout
ExitCode($) returns the outcome of the last Let My$=Launch$("...") attempt
ExitCode(cb) returns the current status of the last Launch "MyConsoleApp", SW_SHOW, cb:handle
- for Launch with timeout (i.e. not the Launch$() or Launch ... cb:handle variants), this value is also returned in edx
.if Exist("\masm32\include\winextra.inc")
MsgBox 0, "winextra.inc is present, yeah!", "Hi", MB_OK
.endif
mov esi, Chr$("NoTest.txt")
.if !Exist(esi)
MsgBox 0, esi, "No such file:", MB_OK
.endif
Rem - returns - do not use Exist("...")==0, use .if !Exist("...") instead
- after Exist(), you can use LastFileSize, LastFileName$, LastFileDosName$
to get more info on the found file, e.g. with Print or debug:
.if Exist("\masm32\include\sh*") ; v v with deb, $ in front means "show as string" v v
Print "Match found: ", LastFileName$, Str$(" with size %i bytes\n", LastFileSize)
deb 1, "Last file:", LastFileSize, $LastFileName$, $LastFileDosName$, $MbExeFolder$, $CurDir$()
.endif
- if the file does not exist, eax and LastFileSize contain -1, otherwise both return the 32-bit size
Keys ex(, exist(
.if IsFolder("\masm32\MasmBasic\") ; with or without trailing backslash
Print "It's a directory"
.endif
Rem returns like Exist(); under the hood is GetFileAttributes
GuiParas equ "controls demo", w180, h120 ; select guiparas and hit F6 to build this code
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl Edit1, "RichEdit", w500
GuiControl Edit2, "Edit", x500, w500
.if IsRichEdit(hEdit1)
SetWin$ hEdit1="This is a RichEdit control"
.else
SetWin$ hEdit1="This is a poor edit control"
.endif
.if IsRichEdit(hEdit2)
SetWin$ hEdit2="This is a RichEdit control"
.else
SetWin$ hEdit2="This is a poor edit control"
.endif
Event Key ; dummy event
GuiEnd
Rem - returns Zero?
Kill "Myfile.dat"
Rem returns eax
Touch "Rec_Test.dat" ; set the file's timestamp to current time
Touch My$(99) ; same if filename is in a string array
Open "U", #7, esi ; open file for updating
Touch #7 ; set the file's timestamp to current time
Close 7 ; close it
.if Exist("MyFile.exe")
Touch "MyFile.ini", GfLastWrite(-1) ; synchronise timestamp of two files
.endif
; if usedeb=1 (default!!), Touch will throw a runtime error if the file doesn't exist or is not accessible
usedeb=1
Touch "Test.dat" ; make sure you are not in the middle of something important
TouchFcs=1 ; you may also change the file creation stamp (default: 0, last write only)
Touch "Test.dat" ; make sure you are not in the middle of something important
Rem - returns SetFileTime result in eax
- second arg may be a FILETIME in xmm0, e.g. as returned by GfLastWrite():
Touch Files$(ecx), GfLastWrite(ecx) ; example: restore timestamp to a modified Files$(ecx)
Touch Files$(ecx), TimeSF("01.02.2013 12:34:56") ; TimeSF returns FILETIME value in xmm0
- Warnings:
- Open "O", #1, ... Touch #1 will destroy your file if you don't write content to it
- Open "I", #1, ... Touch #1 will not change the timestamp
- Touch will display a message if it encounters an error, followed by invoke ExitProcess; disable with usedeb=0
GetFiles creates the Files$() array , containing full paths of all files found:
GetFiles filter [, startpattern] [, endpattern or lines matching] [, case sensitivity and full mode]
GetFiles *.inc ; fill the MbFiles$() array with *.inc files of the current directory
AddFiles Help\*.hlp|*.chm ; add to the Files$() array hlp or chm files from the Help subfolder
mov ebx, eax ; eax=# of files found
Print Str$("\nFound %i files matching *.inc:", ebx)
For_ n=0 To ebx-1
Print CrLf$, Files$(n)
Next
; --- search a folder for all files containing the text "Winsock", and return found lines in Files$(1/3/5 etc): ---
GetFiles \Masm32\include\*.inc, "Winsock", 1, 1 ; 1 = return first match only, 1 = case-insensitive
GetFiles \Masm32\include\*.inc, "Winsock", 3, 0 ; 3 = return up to three matches, 0 = case-sensitive
; --- search a folder for a block of text delimited by "rect" and "ends", and return it in Files$(1): ---
GetFiles \Masm32\include\*.inc, "rect struct", "ends", 1
.if eax
Let Files$(0)=Mid$(Files$(0), 5) ; cut off the "it's a file" flag (SpTbSpSp)
Print "The text was found in ", Files$(0), ":", CrLf$
Print Files$(1), CrLf$ ; RECT STRUCT ... ENDS
Print Files$(2) ; SMALLRECT STRUCT ... ENDS
.endif
Rem - returns # of files both in eax and MbGetFileCount
- the Files$() array is filled with UTF8-encoded strings; you may have to use the SetCpUtf8 macro once to display non-English filenames
- you may use the following switches:
GfNoPaths= 0/1 ; 0=include the path specified by user (default); 1=use file names only
GfNoRecurse= 0/1 ; 0=include subfolders (default); 1=search only in current folder
GfNoUtf8= 0/1 ; 0=write a UTF8 BOM when storing Files$() (default); 1=don't
- the long version returns in Files$(0) the full path of the file where the block of text was found.
The text may have been found in more than one file. Each new file is marked by
space tab space space in the first 4 characters.
- if endpattern is omitted, CrLf will be used, and the first matching line only will be returned;
in this case only, you may used Odd(n) for getting the matching lines in Files$(n)
- if endpattern=1...126, GetFiles returns up to endpattern matches (1: same as omitted)
- if endpattern=-1, all matching lines will be returned in Files$()
- if endpattern=0, GetFiles looks for matches but returns only filenames in Files$()
- you may use GetFiles WM_DROPFILES in the respective handler; the array will contain both files and folders
- similarly, GetFiles CL (Unicode: GetFiles wCL) transfers a list of files in the commandline to the
Files$() array; SortFiles works as expected, also the Gf*** functions; if no arguments are present, eax will be zero
- case & mode (bitwise flag):
0=case-sensitive, +1=insensitive, +2=intellisense (Name=name),
+4=full word search, +8=include start of line in text block search
- see GfCallback below for user-defined filtering
Key gf
GetFolders "\Masm32\Examples" ; fill the Files$() array with folder names
GetFolders ; no arg: start with 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
include \masm32\MasmBasic\MasmBasic.inc
Init ; <<< select init and hit F6 to test this snippet
Let esi=ExpandEnv$("%WINDIR%\SysWow64\drivers") ; usually C:\Windows\...
PrintLine "Searching ", esi
GfCallback cbGetFiles ; define a callback function
GetFolders esi
For_ ecx=0 To eax-1
PrintLine Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, Files$(ecx)
Next
Print Str$("\n%i folders found\n", ecx)
Exit
cbGetFiles: test ecx, 1023 ; file or folder counter
If_ Zero? Then Print "*" ; console mode progress bar ;-)
.if wInstr(edi, "\nti\", 1) ; check Unicode \nti\ in edi (=full path) -> exclude files in nti folder
or ecx, -1 ; combined with the inc ecx below, this sets Zero?
.endif
inc ecx
ret
end start
Rem - The callback function gets invoked every time a file or folder is found and receives the following data:
edx pointer to the WIN32_FIND_DATAW structure used for FindFirstFileEx
ecx current file or folder counter
edi full path used in FindFirstFileExW
esi filename only
- to exclude a file from Files$(), set the zero flag before the ret; recommended, as shown above:
use or ecx, -1 if the exclusion condition is met, plus an inc ecx before the ret
You can do whatever you need in this callback, and you don't have to preserve any registers
GetFiles \masm32\*.asm ; create the Files$() array
SortFiles ; default: sort the Files$() array by date, most recent files first
For_ ecx=0 To eax-1 ; print the results
PrintLine Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, Files$(ecx)
Next
SortFiles date, asc ; sort Files$() array by date, oldest files first
SortFiles size ; sort Files$() array by size, biggest files first
SortFiles size, asc ; same but smallest files first
SortFiles name, asc ; sort alphabetically
Rem - returns elements sorted in eax
- after a sort by name, Files$() cannot be resorted by date or size
include \masm32\MasmBasic\MasmBasic.inc
Init ; <<< select init and hit F6 to test this snippet
GetFiles *.as? ; get all asm or asc sources in the current folder and its subfolders
SortFiles ; sort by date, latest first
xchg eax, ecx ; save #files
For_ ebx=0 To ecx-1
.if GfAgeHours(ebx)<=24 ; pick your latest code
mov esi, Cat$(Files$(ebx)+Space$(40)) ; we simulate LSET edx::eax for QUADWORD size v v
Print Str$("\n#%i ", ebx+1), Left$(esi, 40), GfDate$(ebx), ", ", GfTime$(ebx), Spc2$, Str$("%i bytes", edx::GfSize(ebx))
.endif
Next
EndOfCode
Rem - all numerical arrays associated with GetFiles return eax, except for GfLastWrite, which returns:
a) the ftLastWriteTime FILETIME member of the WIN32_FIND_DATA structure in the lower qword of xmm0
b) the size in the upper qword of xmm0
- GfSize returns the low quad in eax, the high quad in edx
- GfLastWrite returns size and last write time in xmm0, for any index in Files$() and -1 for the last Exist(). In contrast,
GfGetInfo(x) returns size and last x time in xmm0 for the last Exist(), where x can be created, modified, accessed
- Gf....(-1I or Gf...(), i.e. index -1 or no arg: all functions return last write data for the last Exist()
- index -2: GfDate$(-2) and GfTime$(-2) return creation time data for the last Exist()
- you can copy file time and size with e.g. GfSetInfo 7, GfLastWrite(-1) ; Files$(7) receives time and size of last Exist()
- GfAge...() is calculated from the moment when GetFiles was launched, except for index=-1, which returns age
relative to the time of the last Exist() call
- for any other SYSTEMTIME structure, use Age(pSystime, unit)
.if Age(esi, h)<7*24 ; h=hours (valid units: d/h/m/s/ms/µs)
Print "The item in the SYSTEMTIME structure is younger than seven days"
.endif
Rem - returns in eax the age in days, hours, minutes, seconds, milliseconds or nanoseconds/100 units
- if you get unexpected values, try PrintLine Str$(", %i µs\t", edx::Age(pMySystime, µs)), as 32 bits
might not be enough
- you can use e.g. PrintLine Str$(", %2f days, ", Age(pMySystime, h)/24) to get fractions of units
Let esi=CurDir$()+"MyFile.txt" ; e.g. D:\masm32\MasmBasic\Res\MyFile.txt
Let esi=CurDir$(0)+"\MyFile.txt" ; same output, the (0) means "do not append a backslash"
Rem - for use with Let & Print, returns DWORD in eax
- in general identical to MbExeFolder$, but may be different for apps invoked by shortcuts that set the folder
include \masm32\MasmBasic\MasmBasic.inc
Init
GetDrives$ ; fill the Drives$() array
Print Str$("%i drives are present: ", Drives$(?))
For_ ecx=0 To Drives$(?)-1
Print Drives$(ecx), " "
Next
EndOfCode
Rem before using the Drives$() array, refresh it with GetDrives$
include \masm32\MasmBasic\MasmBasic.inc
Init ; select init and hit F6
.if !Ini$()
SetIni$ "crtDate", fDate$()
SetIni$ "crtTime", fTime$()
.endif
PrintLine "Created ", Ini$("crtDate"), ", ", Ini$("crtTime")
SetIni$ "modTime", Cat$(fDate$()+", "+fTime$())
PrintLine "Modified ", Ini$("modTime")
EndOfCode
Rem - searches the
- if no ini file is found, it will be created
SetMru "EditorDemo.ini" ; load text file with most recently used files (in WM_CREATE handler, after menu creation)
...
Event Menu ; (similar for a manual WM_COMMAND handler)
Switch_ MenuID
Case_ 2 ; "save as" menu
.if FileSave$("Assembler=*.asc;asm|Includes=*.inc|Resources=*.rc|All files=*.*")
Let CurrentFile$=FileSave$() ; change name of current document
FileWrite CurrentFile$, Win$(hMyEdit, xxl) ; the xxl means "use a big buffer"
AddMru CurrentFile$
.endif
Case_ MruFirst .. MruLast ; get a file path from Most Recently Used menu
Let CurrentFile$=MruText$() ; load a document from the menu
Endsw_
Event Close
StoreUtf8 Cat$(MbExeFolder$+"\EditorDemo.ini"), Mru$(), 8 ; on exit, write up to 8 strings to the ini file
Rem see the "editor with toolbar" template for a full example
PrintLine "Architecture is ", ExpandEnv$("%PROCESSOR_ARCHITECTURE%, the OS is %OS%, and the CPU is"), CrLf$,\
ExpandEnv$("%PROCESSOR_IDENTIFIER%")
PrintLine "This is the full path: [", ExpandEnv$("%ProgramFiles%\Microsoft Office\OFFICE11\WINWORD.EXE"), "]"
Let esi="This is the full path: ["+ExpandEnv$("%ProgramFiles%\Microsoft Office\OFFICE11\WINWORD.EXE")+"]"
PrintLine esi
MsgBox 0, Cat$("This is the full path: ["+ExpandEnv$("%ProgramFiles%\Microsoft Office\OFFICE11\WINWORD.EXE")+"]"), "Hi", MB_OK
; The Unicode version is preceded by a w, as usual:
wPrint wChr$("Program Files path in Unicode: ["), wExpandEnv$("%ProgramFiles%", 1), wChr$("]"), wCrLf$
wPrint wChr$("Program Files path in Unicode: ["), wExpandEnv$("%ProgramFiles%"), wChr$("]"), wCrLf$
; Both ANSI and Unicode versions allow an optional flag "...", 1 that forces the macro to yield the full name:
Program Files path in Unicode: [C:\Programmi] ; output with full name flag set (here the Italian version)
Program Files path in Unicode: [C:\PROGRA~1] ; output without the flag
Rem returns ptr to temporary buffer in eax; for use with Launch, Print, Let and Cat$
include \masm32\MasmBasic\MasmBasic.inc
Init
Let esi="MbGuide.rtf"
PrintLine esi, " was launched by ", ExeFromWin$(WinByTitle(esi))
PrintLine "files with the ending 'rtf' get launched by [", ExeFromExt$("html"), "]"
PrintLine 'ExeFromWin$("Recent"):', Tb$, ExeFromWin$("Recent Unread", 8)
PrintLine 'ExeFromExt$("html"):', Tb$, ExeFromExt$("html")
EndOfCode
Rem returns ptr to temporary buffer in eax; ExeFromWin$ returns in edx the PID, or zero signalling an error
include \masm32\MasmBasic\MasmBasic.inc
Init
GetFileProps ExeFromExt$("rtf") ; get the props of RichMasm.exe
For_ ecx=0 To FileProp$(?)-1
Print Str$("%_i ", ecx), FilePropName$(ecx) ; show the predefined names
wPrintLine At(30) wRec$(FileProp$(ecx)) ; descriptions may be Utf8
Next
GetFileProps "\Masm32\qEditor.exe"
Inkey "Description of qEditor.exe: [", FileProp$(FileDescription), "]"
EndOfCode
Rem - GetFileProps creates the FileProp$() array
- to read it, you may use a counter or one of the predefined version info names, e.g. Comments, FileDescription
- when using a counter, as shown above, FilePropName$(ecx) shows the predefined names
include \masm32\MasmBasic\MasmBasic.inc
Init
MakeDir "tmp\new folder"
.if Zero?
PrintLine "[", eax, "] successfully created"
.else
PrintLine "Could not create '", eax, "', sorry"
.endif
mov esi, Chr$("\Masm32\A new folder\") ; this path ends with a backslash
MakeDir esi
jne BadFolder ; handle an error
PrintLine "[", eax, "] created" ; [A new folder\] created
BadFolder:
EndOfCode
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
GetFiles \masm32\m32lib\*.asm ; fill the Files$() array with the desired files
ArcFiles "The Masm32 lib" ; the minimum: just the name of the *.arc archive
; with archive name, file count, show, Launch console mode:
ArcFiles "The_Masm32_lib", 10, SW_MINIMIZE, CREATE_NEW_CONSOLE ; use first 10 files and a minimised new console
ArcFiles "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
UnArcFiles "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:
UnArcFiles "The_Masm32_lib", "\masm32\ZipTest", SW_MINIMIZE, CREATE_NEW_CONSOLE
UnArcFiles "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
include \masm32\MasmBasic\MasmBasic.inc
Init
GfNoRecurse=1 ; get folders at current level only
GetFolders "\Masm32\examples\exampl01\qikpad"
AddFiles "\Masm32\examples\exampl01\qikpad\*.asm|*.inc|*.rc|*.ico|*.bmp"
ZipFiles "MyQikpad.zip"
EndOfCode
Rem - uses the Files$() array generated by GetFiles & AddFiles
- returns #files zipped
- no third party library needed
UnzipFile
include \masm32\MasmBasic\MasmBasic.inc
Init
UnzipInit "test.zip" ; UnzipInit expects a filename or URL, returns a comment (if present); edx has #records
.if Sign?
Print eax ; print an error message
.else
push eax ; UnzipInit returns a comment or an empty string
For_ ecx=0 To edx-1 ; #files returned in edx
mov esi, Files$(ecx)
.if Rinstr(esi, "/") ; zipfiles use forward slashes
xchg eax, esi ; we don't display the full path
inc esi
.endif
PrintLine Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, esi
.if Instr_(esi, ".asm", 1) ; assembler source, plain text?
PrintLine "##First 60 chars: [", Left$(UnzipFile(ecx), 60), "]" ; see snippets for an example with UnzipFile(index, pathtowrite)
.endif
Next
pop eax
Print "Zipfile comment: [", eax, "]" ; before UnzipExit
UnzipExit
.endif
EndOfCode
Rem - decompresses files in zip archive to a buffer returned by UnzipFile(ecx)
- you may save the file as FileWrite Cat$("\SomeFolder\"+Files$(ecx)), UnzipFile(ecx)
- even better: UnzipFile(ecx, "D:\SomePath\") ; \SomePath would extract to root+path of the current drive
- UnzipFile(counter, 0) ; extracts file #counter to the current folder
- eax returns either the archive's comment or #error#; a 404 will show as "Archive not found" or as "Archive download failed"
Key uzi
Let esi=FileRead$("MyFile.dat") ; creates a string on the heap and reads in the specified file
PrintLine FileRead$("NoSuchFile.txt") ; shows a MsgBox and terminates program
PrintLine FileRead$("NoSuchFile.txt", err:#) ; prints the # character, program continues
Let esi=FileRead$(77) ; assigns content of RCDATA resource with ID 77 to esi (allowed IDs are 1 ... 126)
; read a file from the Internet, strip all HTML tags, scripts and styles, and display it on the screen:
Inkey NoTag$(FileRead$("http://www.masm32.com/board/index.php?action=unread;all"))
; translate two files and one webpage into a string array:
StringToArray FileRead$("\Masm32\include\Windows.inc")+FileRead$("http://masm32.com/board/index.php?action=help")+FileRead$("\Masm32\include\WinExtra.inc"), x$()
Rem - returns a pointer to heap memory in the reg32 or dword variable after Let
- for asynchronous downloads see Download
- directly after FileRead$(), you can get the numbers of bytes read using mov eax, LastFileSize
Key fr$(
Download "http://somesite.com/somefile.txt" ; [, msg:hWnd] [, cb:callback]
Download "http://somesite.com/somefile.txt", cb:MyCallback
...
MyCallback:
SetWin$ TBarControl(2)=Str$("%i bytes\ndownloaded", edx) ; currrent state in edx
Delay 1
invoke GetKeyState, VK_SHIFT
test ah, ah ; stop download if Sign? flag set
retn
Rem - for use with
Event Download (triggers a WM_DOWNLOADFINISHED message)
- if msg: is not specified, hWnd is assumed
- optional callback function: no paras, bytes read in edx, cancel if the sign flag is set
- wParam points to content, lParam holds length in bytes
- if content starts with #D-, an error occurred; #D-U indicates no URL (offline?), #D-R means reading failed
Let esi=NoTag$(FileRead$("http://www.masm32.com/board/index.php?action=unread;all"))
Let esi=NoTag$(FileRead$("somefile.html"), table, swcd) ; extract the table, swap commas and dots
Rem strips HTML tags, scripts and styles; don't expect miracles - reducing a perfectly styled webpage
to pure text will not look pretty, but it's handy to filter webpages by text content
FileWrite "MyFile.txt", "MyString" ; opens a file and writes a string to file
FileWrite "MyFile.txt", "MyString", 2 ; same but writes only first 2 bytes
FileWrite "MyFile.rtf", stream:hRichEdit ; save contents of the RichEdit control
FileWrite "MyFile.uctxt", stream:hRichEdit, SF_TEXT or SF_UNICODE ; same but specify a format
If_ Not Exist("build.bat") Then FileWrite "build.bat", res:99 ; extract a file from RCDATA resource #99
FileWrite "MyFile.txt", "MyString", xmm0 ; sets the timestamp in xmm0
Rem returns Close retval in eax and bytes written in edx
Key fw
Open "O", #1, Chr$("\masm32\MasmBasic\WinIncs.htm")
; This example joins two major include files, adds html structures and highlights some elements
Let ebx=FileRead$("\masm32\include\Windows.inc")+FileRead$("\masm32\include\WinExtra.inc")
; The '<' and '>' chars will be misinterpreted as html tags and must therefore be removed
Let ebx=Replace$(ebx, offset txBracketLeft, "<") ; src, search, repl [, case] [, count]
Let ebx=Replace$(ebx, offset txBracketRight, ">") ; > becomes 'greater than'
Let ebx="
Let ebx=Replace$(ebx, CrLf$, offset txBrCrLf) ; HTML needs
Let ebx=Replace$(ebx, "struct", "Struct", 1+4) ; ignore case, whole word
Let ebx=Replace$(ebx, "union", "Union", 1+4)
Let ebx=Replace$(ebx, "qword", "qword", 1+4)
Print #1, Replace$(ebx, "PROTO", "Proto", 1+4) ; MixedCase looks nicer ;-)
Close
Rem - must be used with Let, Print or Inkey
- Replace$ uses the same case flags as Instr_
- the optional count parameter allows to limit substitution; 0 or omitted means replace all
Key rep$(
mov My$, ParentData$() ; the parent process may have passed data through Launch xx, passdata...
Rem - returns DWORD in eax
- console apps can receive data
- the sending program can use e.g.
Let esi=FileRead$("\Masm32\include\Windows.inc")
Launch "receiving.exe", passdata, Left$(esi, 400) ; note the console's printing limits around 32k
- receiving Windows apps should use the SendData/CopyData$ combi
SetWin$ hEdit="Just received:"+CrLf$+CopyData$+CrLf$+"--- end of data ---" ; use directly to set a window content
Let My$="Just received:"+CrLf$+CopyData$+CrLf$+"--- end of data ---" ; store away for later use
Rem - for receiving data from other apps; for use inside a WM_COPYDATA message handler, for example:
SWITCH uMsg
CASE WM_COPYDATA
Let My$=CopyData$+CrLf$+"received "+Time$
- for sending data, use SendData
- on IPC, see also MSDN: Interprocess Communications
- for examples, see File/New Masm source, MB client & server
SendData "MyClient", Win$(hEdit)
SendData "MyClient", "How are you?"
Rem - returns DWORD in eax: 0=no client found
- can also be used from a Win32 console application
SendControlKey hWin, VK_V ; paste something to Notepad etc
Rem receiving window must have keyboard focus
SendWordCommands ; prepare a DDE session; Word must be running
.if eax
; adjust J: to your needs; use single quotes outside to allow double quotes inside:
SendWordCommands '[FileNewDefault:InsertFile "J:\Masm32\include\Windows.inc"]'
SendWordCommands Chr$("[InsertFile ", 34, "J:\Masm32\include\WinExtra.inc", 34, "]")
Let esi=FileRead$("\masm32\MasmBasic\Res\OpenDocInWord.bas")
SendWordCommands Replace$(esi, "#:\", Left$(MbExeFolder$, 3)) ; send the contents of the BAS file
SendWordCommands '[MsgBox "Cute"]'
.else
MsgBox 0, "MS Word doesn't answer", "Sorry", MB_OK
.endif
SendWordCommands exit ; finish DDE session
Rem - swc init returns the DdeConnect retval in eax
- DDE uses ancient WordBasic; get the help file here ; to make it appear in
RichMasm's Help menu, save it as \masm32\MasmBasic\Help\WrdBasic.hlp
- a sequence is included in [squared brackets]
- multiple commands can be separated by a colon (:)
- for strings, use either the escape sequence °\" (Alt 248, 92, 34) or Chr$("xx", 34, "xx") - see above
- you can also use Let or Cat$ to send commands to MS Word
- it is your responsibility to respect legality when using this feature
include \masm32\MasmBasic\MasmBasic.inc
Init ; v v v can be xls, xlsx, mdb, dbf, wks or wk4 file
.if OpenAdo("\Masm32\examples\IczelionSQL\jj\LifeExOECD.xls")
For_ ecx=0 To Min(9, OpenAdo(0))-1 ; 0=first sheet
Print Str$("%_i ", ecx), AdoTable$(ecx, 0) ; row ecx, column 0
PrintLine At(60) Spc2$, AdoTable$(ecx, 1) ; row ecx, column 1
Next
Store "MyData.tab", AdoTable$() ; save your data
ShEx "MyData.tab" ; have a look with the default application
.else
MsgBox 0, AdoErr$(), "OpenAdo:", MB_OK
.endif
EndOfCode
Rem - first call has a filename or URL as argument, e.g. CL$()
- you may use the AdoSheet$() array to identify the right sheet
- with usedeb=0, the query string will not be shown
- second call returns #rows
- the AdoTable$() array is two-dimensional (row, column)
- you may use Store "MyData.tab", AdoTable$()
These macros provide an easy-to-use DDE interface to MS Excel. Click below to see
detailed examples: Work with data, Excel and Unicode, and Xls viewer with hotlinks.
; A typical conversation would look like this:
xlsConnect ; no args=Excel, System
.if !Zero? ; non-zero means success
xlsOpen "MyFile.xls" ; we open a file
.if !Zero? ; non-zero signals success
xlsCommand "[app.activate()]" ; optional: activate Excel
xlsCommand '[Run("Personal.xls!!SayHi")]' ; run a VBA macro
xlsConnect "MyDataSheet" ; tell Excel to which sheet you want to talk
.if !Zero? ; non-zero signals success
Print "Current selection=[", xlsRead$(), "]", CrLf$ ; you may print to the console...
SetWin$ hEdit='R1C1:R9C5=['+xlsRead$("R1C1:R9C5")+']'+CrLf$ ; ... or set window content
AddWin$ hEdit="Extract #2=["+xlsRead$(xlsRC$(1, 1, 9, 5))+"]"+CrLf$ ; and add bits & pieces
AddWin$ hEdit="Current selection=["+xlsRead$()+"]"+CrLf$ ; no args means current selection
xlsWrite "R1C1", Cat$("This sheet was modified on "+Date$+", "+Time$) ; writing is allowed, too
.endif
xlsClose 0 ; close the file without saving (0=don't save, 1=save, no arg: ask)
.endif
xlsDisconnect ; say bye to Excel
.endif
Rem - all macros return !Zero? for success
- Excel macros are language sensitive: instead of xlsRead$("R1C1"), you may have to use L1C1 (French: ligne),
Z1S1 (German: Zeile1:Spalte1) or F1C1 (Spanish: fila); try this multilingual page in case of problems
; These macros provide the DDE interface for non-Excel apps, such as browsers:
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to test this code
ddeConnect "Firefox|WWW_GetWindowInfo" ; server|topic: connect e.g. to FF with the WWW_GetWindowInfo topic
.if !Zero?
PrintLine "URL=", ddeRequest$("URL") ; request the current URL
ddeDisconnect ; say bye to window info
ddeConnect "Firefox|WWW_OpenURL" ; connect to the open URL topic
.if !Zero?
Inkey "Open a web page?"
If_ eax=="y" Then ddeCommand "http://www.webalice.it/jj2006/MasmBasicQuickReference.htm" ; open a frequently used page
ddeDisconnect
.endif
.endif
EndOfCode ; combines invoke ExitProcess & end start
Rem see xls macros
MsgBox 0, Win$(hWnd), "The title of the main window:", MB_OK
wMsgBox 0, wWin$(hWnd), "The title of the main window in Unicode:", MB_OK
.if WinByTitle("somefile.inc - Notepad")
xchg eax, ecx ; move handle to a safe register
wPrint "Content=", wWin$(ecx, 15) ; ecx is the parent, 15 is the ID of the Notepad edit control
wInkey wCrLf$, "Title=", wWin$(ecx) ; delete the w if you prefer ANSI
Rem - returns pointer in eax
- length limit is 159,999 bytes; if that is not enough, use e.g. FileWrite "test.txt", stream:hRichEdit, SF_TEXT or SF_UNICODE
- for getting Unicode from RichEdit controls, use wSetWin$ hUcEdit=wRec$(Win$(hRichEdit))
- optional second argument is the ID of a child window; see also App16()
Key win$(
SetWin$ hWnd="A new title for my app"
SetWin$ rv(GetConsoleWindow)="Hello Masm Forum" ; for console applications
SetWin$ ecx, 15="If ecx is Notepad's handle, then we can set its edit control because its ID is 15"
wSetWin$ hEdit=wRes$(1)+wCrLf$+wCrLf$+wRes$(2) ; Unicode from resources
wSetWin$ hEdit=uChr$("Добро пожаловать") ; Unicode directly with UTF-8 build (Ctrl F6 in RichMasm)
Let esi="Добро пожаловать" ; assign string as UTF-8
wSetWin$ hEdit=uChr$(esi) ; translate to wide format using uChr$()
Rem for main windows and their controls; you can use string concatenation as in Let resp wLet
AddWin$ 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=
SetSel$ hRichEdit="доброе утро"
SetSel$ hRichEdit=Cat$("File=["+Files$(0)+"">")
Rem - works with richedit controls only
- direct assembly of UTF8 text (e.g. Russian as shown above) is possible in RichMasm by using Ctrl F6 for the first build
- default mode is "replace selected text"
- users may modify the internal SETTEXTEX structure with, for example, SetSel$ cp:CP_UTF8, flags:ST_DEFAULT
GuiParas equ "wSel$ demo", w240, h120
GuiMenu equ <@Edit, Selection>
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyRich, "richedit", "С Sel$ макрокоманды может показать любой раздел."
invoke UpdateWindow, hMyRich
Event Menu
.if MenuID==0 ; first menu item clicked
wMsgBox 0, wSel$(hMyRich, 35, 47), "Hi", MB_OK
SetWin$ hMyRich="Same with English: Click the menu again to show the range 35...47"
.endif
GuiEnd
Rem works with richedit controls only; if start and end are omitted, the current selection will be returned
GuiParas equ "CreateAndSelect demo", w160, h160, b LiteBlueGreen, icon Globe ; width+height, bgcol
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
push PtDC
call DrawMyRect
EndOfEvents
MyFont LOGFONT <-40, 0, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, 0, 0, "Times New Roman"
DrawMyRect proc hDC
Local hBrush, whatever
CreateAndSelect(hDC) ; one arg: set the DC (right after the locals)
mov hBrush, CreateAndSelect(brush, Blue) ; the hBrush is not really needed
invoke Rectangle, hDC, 3, 3, 202, 78
void CreateAndSelect(brush, LiteYellow)
invoke Rectangle, hDC, 9, 9, 196, 72
invoke SetBkMode, hDC, TRANSPARENT
void CreateAndSelect(font, addr MyFont)
invoke TextOut, hDC, 20, 18, fTime$(), 9
ReleaseObjects ; no leaks please ;-)
ret
DrawMyRect endp
GuiEnd
Rem - use casThrowErrors=1 to see if your bitmap etc works
- a clean way to create, select and release Gdi objects
MakeBrush hRed, RgbCol(255, 0, 0) ; use with Gdi32
MakeBrush hBlue, 0FF8080h ; note the inverse BGR notation in hex format
MakeBrush hRed, RgbCol(200, 255, 0, 0) ; use with GdiPlus; "200" is the alpha
MakeBrush brushes(ecx), SysCol(ecx+1), gdip ; last arg must be gdip if used with gdi+
Rem - place in WM_CREATE handler; no need to define hBrush
- for use with gdi+ (->MakePath, GuiFill, GuiDraw), use either RgbCol() with four parameters, or (e.g. in loops)
SysCol(index) with an additional gdip as shown above; using Gdi32 brushes with gdi+ causes exceptions
MakeFont hHandFont, Height:40, Underline:TRUE, "Lucida Handwriting"
MakeFont hVertFont, Height:32, Escapement:900
MakeFont hFixedFont, PitchAndFamily:FIXED_PITCH
.if FontExist("Segoe UI Emoji") ; check if the font face exists
MakeFont hEmoji, "Segoe UI Emoji"
.else
MakeFont hEmoji, "Arial"
.endif
Rem - in order to facilitate creating font variants, MakeFont keeps settings between calls
- place in WM_CREATE handler
PickFont addr hFont ; use the font dialog to change the font whose handle is in hFont
PickFont addr hFont, hEdit ; apply to edit control (if user didn't cancel)
Rem returns font handle in eax
GetFiles *.jpg ; load all jpg files in this folder and below
AddFiles *.png ; add all png files
ImgPaintInfo 0, Files$(ImgCounter) ; load image file in slot 0 and return image dimensions - width in eax, height in edx
ImgPaint hStatic, 0, Files$(ImgCounter) ; use in the WM_PAINT handler to fill static control with Globe.ico; take slot 0 (of 30)
ImgPaintClr 2 ; unload image file in slot 2
Rem - ImgPaintInfo can be used to adjust the dimensions of the control that takes the image
- ImgPaint should be called from the WM_PAINT handler
- ImgPaint accepts most common image formats, e.g. bmp, gif, png and jpg
- ImgPaintClr frees memory, if really needed. The Exit macro frees all loaded images automatically
- 30 slots are available, i.e. you can fill 30 static controls with images; should be enough for a card game ;-)
ToolTips TTS_BALLOON ; optional: use a specific style
; sm edi, TTM_SETTITLE, TTI_INFO, chr$("Hello") ; optional: set a title
ToolTips hEdit, "Type something" ; immediate text for tooltip
ToolTips hButton1, Res$(100) ; use resource string from rc file
ToolTips hButton2, wRes$(101) ; in case you want Russian or Chinese...
ToolTips end ; not optional: close the tooltips definition
--- below the WM_CREATE handler, you can change the text e.g. using multilingual resources as follows: ---
SetLanguage proc uses ebx
.if eax>=IdMenuEN && eax<=IdMenuCH ; e.g. eax set by a menu event
sub eax, IdMenuEN
imul ebx, eax, 400 ; string table uses ID 1, 2,3/401, 402, 403/801, 802, 803 etc
ToolTips hEdit, wRes$(ebx+1) ; type here in various languages
ToolTips hButton1, wRes$(ebx+2) ; "click on this button"
ToolTips hButton2, wRes$(ebx+2) ; same
.endif
ret
SetLanguage endp
Rem - you can created them in a loop, but make sure you don't use esi for own purposes; on the other hand, you can
send messages directly before ToolTips end:
invoke SendMessage, [esi-4">, TTM_SETMAXTIPWIDTH, 0, 200 ; forces use of Cr, Lf or CrLf
sm [esi-4], TTM_SETTITLE, TTI_INFO, Chr$("I am a tooltip:") ; sm stands for invoke SendMessage,
- must be placed at the very end of the WM_CREATE handler, i.e. when all controls have been created
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to test this code
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 F", 4)
wPrint "Firefox: ", wWin$(eax), 13, 10
.else
.if WinByClass(0, "MozillaWindowClass") ; WinByClass finds e.g. Thunderbird
Print "Mozilla: ", Win$(eax), 13, 10
.else
Print "no browser found", 13, 10
.endif
.endif
EndOfCode
Rem returns handle in eax
.if App16(hWin)
Print "This is a legacy 16-bit app"
.endif
Rem useful to check if you can read text from child windows of other apps (you can't read from a 16-bit app)
Print "[", Clip$(), "]" ; displays the content of the clipboard
MsgBox 0, Clip$(), "Text on the clipboard:", MB_OK ; the same as MessageBox
MsgBox 0,\
Cat$("The text on the clipboard:"+CrLf$+String$(33,"-")+CrLf$+Clip$(20)+CrLf$+String$(0,0)),\
"MasmBasic:", MB_OK ; displays a MessageBox with two horizontal delimiters
MsgBox 0, Clip$(40), "The text on the clipboard, truncated to 40 chars:", MB_OK
invoke lstrcpy, offset my40charbuffer, Clip$(40-1) ; yep, don't forget space for the zero delimiter
.Repeat
invoke Sleep, 100
.if ClipboardChanged()
PrintLine "New clipboard content=[", Clip$(30), "]" ; non-zero means new valid content available
.endif
.Until signed rv(GetKeyState, VK_ESCAPE)<0
Let esi=HtmlClip$() ; for use e.g. with Thunderbird and Firefox
deb 1, "Image on clipboard, width & height", ClipboardImageWH, edx ; for use with GuiImage clipboard
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$ "Today is the "+Date$ ; replace curent clipboard with a text, format CF_TEXT
SetClip "\Masm32\MyPhoto.bmp", CF_BITMAP ; put a bitmap on the clipboard
SetHtmlClip$ "This is bold" ; for use e.g. with Thunderbird
wSetClip$ "Today is the "+wDate$+", "+wTime$ ; the Unicode version
wSetClip$ wRec$("Добро пожаловать") ; if pasting yields ???, hit Ctrl F6 and confirm
SetClip #start ; ---- set multiple clipboard formats ----
SetHtmlClip$ "Text in HTML format" ; use e.g. for Thunderbird and Excel
SetClip 100, CF_BITMAP ; 100 = ID of bitmap resource
SetClip$ offset txTest, CF_RTF ; Rich Text Format
SetClip$ wRes$(123) ; use resource string #123, "This is a sub-title" in Russian (Unicode)
SetClip$ "This is ANSI text"
SetClip #end ; ---- end of multiple clipboard formats ----
Rem returns SetClipboardData retval in eax, copied bytes in edx if successful
WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL rc:RECT, hM1:HWND, hM2:HWND
MsgMonitor
SWITCH uMsg
...
Rem - writes all messages to Messages.txt, in format
- you may add PrintLine #7, "whatever" to certain messages
- do not use file #7 when using the monitor
GuiParas equ "demo" ; select GuiParas and hit F6
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint ; can be Event CanvasPaint if you have a GuiControl xyz, "canvas"
; args are imgsrc$ or resource ID, optional: no args=upper left corner, original size, x=fit: use full canvas, or x, y [, w, h]
GuiImage "\Masm32\examples\exampl04\car\car.jpg", fit ; use full canvas (background image)
GuiImage "\Masm32\examples\exampl04\car\car.jpg", 50, 400, 160, 900 ; use specific x, y, w, h
GuiImage "\Masm32\examples\exampl04\car\car.jpg", 50, 200 ; use x, y, original w, h
GuiImage "\Masm32\MasmBasic\Res\FileOpen.png", 200, 7, 40, 40 ; use specific x, y, w, h
; GuiImageSet L=1, z=50, rf=1 ; crop left 10, zoom 50%, rotateflip 1
GuiImage "\Masm32\MasmBasic\Res\MasmLogo.png" ; upper left corner, original size
GuiImage "http://masm32.com/board/index.php?action=dlattach;attach=6;type=avatar", 100, 0 ; from the Internet ;-)
if 0 ; you may select the white zone and hit F6, but the files v v v need to be provided
GuiImage "Это тест.jpg", fit, RgbCol(255, 0, 0) ; Unicode is allowed; with fit, you can specify a background colour
GuiImage wCL$(), 315, 7 ; also via the commandline
GuiImage 125, 84, 7, 70, 70 ; 125 is a RC_DATA resource ID (IDs must be 48...127)
;GuiImage hdc#edi ; paint to hdc, filename in edi (for example)
GuiImage Files$(ecx), 238, 7, 70, 70 ; you can dynamically load images
GuiImage clipboard, fit ; display what's on the clipboard
endif
GuiEnd
Rem - images can be animated GIFs
- use GuiImage xxx, fit, RgbCol(...) if you need the background to be cleared, i.e. with transparent images
- with usedeb=1, errors are printed to console
For use with GuiImage:
GuiImageSpeed 50 [, 2000] ; sets playing speed to 50% [leave a 2000 ms pause after the last frame]
GuiImageFrame ; force display of a specific frame in an animated GIF image
SaveImageToFile ; for use in GuiImageCallback; attention, only the current frame gets saved - the others are lost
Rem - inside the callback, giImage (=GpImage) and giGraphics (=GpGraphics) are available; plus GuiWidth, GuiHeight etc
- to save an image from the clipboard to file, use this code:
GuiParas equ "Right-click into the image", w600, h600
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyEdit, "canvas"
ArraySet MyRect() As DWORD=1, 2, 3, 4 ; dummy values
Event CanvasPaint
ArrayPlot MyRect()
GuiImage clipboard, fit ; GuiImage must be embedded in ArrayPlot
ArrayPlot exit ; right-click into the image brings up a popup menu
GuiEnd ;
; to test the GuiControl snippet, select GuiParas below and hit F6
GuiParas equ "Test window", x650, y20, w300, h200
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyEdit, "richedit", "Rich text looks better", y0+20, h1000-20 ; correct y and h for SysLink control
GuiControl VisitMoscow, "syslink", x6, h0+20, w1000-64, "Info on Moscow: Развлечения, история, ..." ; Unicode is ok
GuiControl B1, "button", img 77, x=1000-30, h=0+24, w=0+24, "Eye" ; 77: see Rsrc below
GuiControl B2, "button", "B2", bcol LiteYellow, x=1000-60, h=0+24, w=0+30, font -14:FW_BOLD, +WS_BORDER
GuiControl MySbar, "statusbar" ; see SetStatus$
GuiControl MyStatic, "static", icon 77 ; display icon with resID 77
SetWin$ hMySbar="Uses ComCtl32 version "+ComCtl32$() ; returns the dll version used, e.g. 5.82 (XP) or 6.16 (W7)
GuiEnd
Rem - create with GuiControl a full-fledged GUI application in a few lines; see Event below for a list of standard events
- controls implemented are button, canvas, edit, listbox, richedit, scintilla, static, statusbar, syslink,
toolbar (template), progressbar, trackbar, date, time; click here for a full example including GuiGroup
- the syslink control has tooltips, but only the first link will be used; opening with Link$() works correctly
- dimensions can be set as [x/y/w/h]=[per mille value]+fixed offset
- resizing is automatic
- first control has the focus; to override this, use e.g. guiSetFocus equ invoke SetFocus, hEditFind before the first Event
- button controls may have images and text; use guiTxtOnClick=1 to show the text when clicked
To create a toolbar GuiControl:
- provide per-button gif files in a folder
- build CreateTbInfo
- create a link to the executable and move this link into the folder with the gif files
- drag one of the gif files over the link and follow the instructions (PM the author in case of problems)
- RichEdit controls pick the best version found on your machine. You can override that e.g. with two lines before the GuiControl:
guiRichVersion$ equ <"RichEdit60W"
mov RichEditUsed, Chr$("C:\Program Files (x86)\Common Files\Microsoft shared\Office16\Riched20")
Keys gcb, gcc, gce, gcl, gcr, gcs, gcsb
GuiParas equ "Colourful buttons", w320, h110 ; 32% of window width, 11% of height
; GuiButtonPen equ
include \masm32\MasmBasic\Res\MbGui.asm ; select GuiParas and hit F6
GuiControl Button1, "button", "A normal pushbutton", w250, h0+40 ; width 25% of client area, height 0%+40px
GuiControl Button2, "button", "Lite Turquoise"/"Blue", bcol LiteBlueGreen/Blue, x250, w250, h0+40, font -14
GuiControl Button3, "button", "Lite Yellow"/"Full Yellow", bcol LiteYellow/Yellow, fcol Red/Black, x500, w250, h0+40, font -14:FW_BOLD
GuiControl Button4, "button", "Rounded button", bcol LiteGrey/Red, fcol Black/White, rr40, x750, w250, h0+40, font -12:FW_SEMIBOLD
GuiEnd
Rem To define a button, use:
- x, y, w, h for its position, followed by n 1000th of the window width or height
- bcol RgbCol(...) to define the background colour; bcol Blue/Red means "blue, but red if pressed"
- fcol Black/White to define the foreground (text) colour; white if pressed
- "normal text"/"pressed text"
- MbBtnR=3 ; button moves right when pressed
- MbBtnD=2 ; button moves down when pressed
- rr0=no rounded corners, rr100=a circle; use guiRoundRect=n to set the default rounding
- you may define a pen: put GuiButtonPen before the include line
; to test this snippet, select the white zone from GuiParas to GuiEnd, then hit F6
GuiParas equ "See Windows messages", x650, y20, w200, h200
GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm
... add e.g. GuiControl, or your own WM_CREATE stuff ...
Event Menu
deb 1, "You clicked", MenuID
Event Message
inc msgCount
.if uMsg_==WM_MOUSEMOVE
call myproc
.else
deb 4, "msg", chg:msgCount
.endif
EndOfEvents
myproc proc
Print Str$(msgCount), Cr$
ret
myproc endp
GuiEnd
Rem A MasmBasic Gui program has a very simple structure, as shown above:
- GuiParas and GuiMenu declarations (click here for a menu with bitmaps)
- the include line
- any WM_CREATE code
- Event sections: Menu, Key, Size, Paint, Timer, Command, Notify, or Message for any other uMsg
- EndOfEvents, followed by user procedures
- GuiEnd, followed by comments or RichMasm options
GuiParas equ "
Event-based programming is easy", x750, y20, w250, h120, b RgbCol(255, 255, 160)
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyLb, "listbox", y 60, h 940
GuiControl MyEd, "static", text "Double-click to open a file - Дважды щелкните, чтобы открыть файл", h 50
GetFiles *.as?|*.rc ; asc, asm, rc
SortFiles ; latest files on top
SetListbox Files$() ; Fill the listbox with UTF-8 encoded file names
Event Command
.if NotifyCode==LBN_DBLCLK
ShEx LbSel$ ; open selected file with ShellExecuteW (u=assume UTF-8 for LbSel$)
.endif
GuiEnd
Rem - fills a listbox or combobox with the elements of a string array, e.g. Files$()
- if you need to reset the listbox, use e.g. SetListbox other$(), clear to force a LB_RESETCONTENT message
- SetListBox is speed-optimised; use e.g. SetListbox Files$(), short to optimise for code size (55 bytes less)
- use the LBN_DBLCLK, LBN_SELCHANGE or CBN_SELCHANGE notify codes of Event Command to perform specific actions
- you can pass a callback function to SetListBox which decides via the sign flag if a string (in esi) should be added or not:
SetListbox x$(), cb:MyCb
...
EndOfEvents
MyCb: ; a simple filter function
dec Instr_(esi, s1$) ; does esi have a match for string 1?
.if !Sign?
dec Instr_(esi, s2$) ; if yes, match for string 2?
.endif
retn ; return the sign flag
- current selection is returned as LbSel$ or CbSel$, UTF-8 encoded
; to test this snippet, select GuiParas and hit F6
GuiParas equ "
Event-based programming is easy", x750, y20, w250, h120, b RgbCol(255, 255, 160)
GuiMenu equ @File, &Open, &Save, -, &Download, E&xit, @Edit, Undo, Copy, Paste ; define a menu (paras & menu before the include line)
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyEdit, "edit"
; ... implicit Create event here: any code needed once during window creation, e.g. GuiControl, or CreateWindowEx, if you prefer it manually ...
Event Message ; generic event, use e.g. Switch_ uMsg to handle specific messages
inc msgCount
NoDebMsg CATSTR NoDebMsg, <, WM_ENTERIDLE, WM_MOUSEMOVE> ; exclude some messages
deb 4, "msg", chg:msgCount ; simple message monitoring (needs console)
Event Menu ; WM_COMMAND, WM_NOTIFY
.if MenuID==2
Download "http://www.jj2007.eu/Masm32_Tips_Tricks_and_Traps.htm"
.elseif MenuID<=7 ; MenuID is zero for the first entry, 1 for the second etc
MsgBox 0, Str$("You clicked menu #%i", MenuID), "Hi", MB_OK
.endif
Event Timer
Print "Current time is ", Time$, Cr$ ; put e.g. guiTimerMs=200 before the include line to change the frequency; default is 20 ms
Event Download ; WM_DOWNLOADFINISHED
SetWin$ hMyEdit=NoTag$(wParam_) ; lParam holds length, not needed for a textfile
Event DropFiles ; loads the Files$() array; use wRec$() to convert Utf8 file names to true Unicode
wMsgBox 0, wCat$(wStr$("%i files dropped, first one is\n[", Files$(?))+wRec$(Files$(0))+"], last one is "+CrLf$+"["+wRec$(Files$(Files$(?)-1))+"]"), "
Event Dropfiles:", MB_OK or MB_TOPMOST
StoreUtf8 "~tmpDropped.txt", Files$() ; write dropped filenames to a text file in Utf8 format
GuiEnd
Rem - a MasmBasic Gui application provides a simple interface to the standard Windows message loop
- implemented events are Menu, Key, Size, FullPaint, Message, Timer, Command, Notify, Close, DropFiles, Data, Download, CanvasPaint, CanvasMessage and TableMessage
GuiParas equ "
Event-based programming is easy", w200, h120 ; select GuiParas and hit F6
GuiMenu equ @File, &Open, &Save, -, &Download, E&xit, @Edit, Undo, Copy, Paste ; define a menu
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl Button1, "button", "Button 1", h=0+30, w=500 ; 50% of width, 30px
GuiControl Button2, "button", "Button 2", h=0+30, w=500, x=500
GuiControl StatusBar, "statusbar", "Select a menu entry, or click on one of the buttons"
Event Message
inc msgCount
NoDebMsg CATSTR NoDebMsg, <, WM_ENTERIDLE> ; don't show this message
deb 4, "msg", chg:msgCount ; this console deb will only be shown if chg:xxx has changed; needs OPT_Susy Console
GuiEnd
GuiParas equ "Timer demo", x20, w500, h320, b LiteGreen
guiTimerMs=500 ; default is 20; place before the include line
include \masm32\MasmBasic\Res\MbGui.asm
SetGlobals imgY
Event Timer
mov edx, GuiHeight
add imgY, 30
.if imgY>edx
Clr imgY
.endif
GuiCls
Event Paint ; show the Masm forum logo
GuiImage "http://masm32.com/board/Themes/default/images/smflogo.png", 20, imgY
GuiEnd
GuiParas equ "
Event-based programming is easy", w200, h120 ; select GuiParas and hit F6
GuiMenu equ @File, &Open, &Save, -, &Download, E&xit, @Edit, Undo, Copy, Paste ; define a menu
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl Button1, "button", "Button 1", h=0+30, w=500 ; 50% of width, 30px
GuiControl Button2, "button", "Button 2", h=0+30, w=500, x=500
GuiControl StatusBar, "statusbar", "Select a menu entry, or click on one of the buttons"
Event Command ; same as Event Menu
Switch_ MenuID
Case_ 0 .. IDMLAST
SetWin$ hStatusBar=Str$("You selected menu %i", MenuID)
Default_
.if nCode==BN_CLICKED
.if MenuID==Button1
SetWin$ hStatusBar="You clicked Button1"
.else
SetWin$ hStatusBar="You clicked Button2"
.endif
.endif
Endsw_
GuiEnd
Rem MenuID contains the identifier of the control or the menu entry ranging from 0 .. IDMLAST
GuiParas equ "Close me", w200, h120, bLiteGreen ; select GuiParas and hit F6
include \masm32\MasmBasic\Res\MbGui.asm
Event Close
MsgBox 0, "Sure to close?", "Hi", MB_OKCANCEL
sub eax, IDCANCEL
GuiEnd
Rem if eax is zero, the program will not be closed
GuiParas equ "
Event-based programming is easy" ; select GuiParas and hit F6
include \masm32\MasmBasic\Res\MbGui.asm
Event Key
SendData hWnd_, Chr$("Добро пожаловать") ; send a Utf8 string to yourself
Event Data
uMsgBox 0, CopyData$, "You received a WM_COPYDATA string:", MB_OK
GuiEnd
Rem - the string received in CopyData$ may contain binary data
GuiParas equ "
Event-based programming is easy", h100, w240 ; select GuiParas and hit F6
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyEdit, "richedit", h=0+36, text "Select me and hit Return", font -16:FW_SEMIBOLD
Event Key
.if VKey==-VK_RETURN ; -13
MsgBox 0, Cat$("You hit return in the control with text"+CrLf$+Win$(hWnd_)), "Hi", MB_OK
.else
fdeb 4, "key", CtrlVKey(), VK_S
.if CtrlVKey()==-VK_S
SetWin$ hMyEdit="You pressed Ctrl S"
.else
SetWin$ hMyEdit=Str$("You pressed key #%i", VKey)
.endif
.endif
GuiEnd
Rem - returns the VK_? code in eax
- the internal variable ReChanges counts the number of edits in a RichEdit control; clear it when saving a document
- if the key was pressed in a child window, e.g. a single line edit control (h=0+36 or less), the negative VK_? code will be returned
GuiParas equ "
Event-based programming is easy", w400, h300, bLiteBlue ; select GuiParas and hit F6
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl First, "canvas", y=0+20, x200, h=250, w300, +WS_EX_CLIENTEDGE
GuiControl Second, "canvas", x300, y400, w300+120, h180+100, +WS_THICKFRAME
Event CanvasPaint ; this event returns the ID in ecx
Switch_ ecx
Case_ First: GuiEllipse 50.0, 50.0, 49.0, 49.0
Case_ Second
GuiImage "\Masm32\examples\exampl04\car\car.jpg", fit
GuiTextBox 40.0, 50.0, 70, 32, "Masm32's car.jpg", bcol RgbCol(255, 255, 202), font -14
Endsw_
GuiEnd
Rem - the canvas control allows to paint one or more areas of the main window separately, using a Switch_ ecx
- if you paint using ArrayPlot RgbCol(...) ...(your code)... ArrayPlot exit, then the image can be copied & saved with a right-click
- use in particular with GuiImage and ArrayPlot
GuiParas equ "
Event-based programming is easy" ; select GuiParas and hit F6
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
GuiText 20.0, 50.0, "Hello World in a Paint event", bcol RgbCol(255, 255, 255), font 48
GuiEnd
; to test this snippet, double-click on GuiParas and hit F6
GuiParas equ "Hello jj2007", x650, y20, w200, h222, icon Flower, cblack, b LiteGrey ; xpos, ypos, width, height, color, bgcolor (see RgbCol)
; x and/or y omitted=center; xr123=123px from the right
GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm ; select Init and hit F6 to test this snippet
Event Menu
MsgBox 0, Str$("You clicked menu #%i", MenuID), "Hi", MB_OK ; MenuID is zero for the first entry, 1 for the second etc
Event Message
inc msgCount
deb 4, "msg", chg:msgCount ; , wParam, lParam ; see deb
Event Paint
GuiTextBox 99.9-90, 10, 60, auto, "Simple text box", bcol RgbCol(0, 255, 255) ; 99.9-90: lower border-90px
GuiColor Blue, RgbCol(255, 255, 222)
For_ ct=0 To 9
imul ecx, ct, 25
mov ecx, RgbCol(ecx, 0, 0)
GuiText ct*8+7, ct*16+7, Str$("Line %i ", ct+1), fcol ecx
Next
Print Str$("painting took %i ms\n", GuiMs) ; GuiMs measures the duration of the WM_PAINT handler
GuiEnd ; replaces end start here
other Gui macros (see extended example here):
GuiColor foreground, background
GuiCls clears the canvas
GuiRefresh
GuiTextBox left, top, width, height, "text" [, style] ; style: e.g. DT_LEFT or DT_WORDWRAP
GuiText x, y, text [, style] ; style: e.g. transparent/opaque, fcol, bcol
GuiLine x0, y0 [, x1, y1] ; 4 args: from ... to, 2 args: ... to
GuiEllipse x, y, radiusX, radiusY
GuiCircle x, y, radius
GuiSetFill brush
GuiControl MyID, "type" [, "text or file"] [, x, y, w, h] ; type is e.g. edit, richedit, button, ...
Rem A quick-and-dirty way to write a Windows application in a few lines - click here for a full example
GuiParas equ "Hello World", w200, h99, icon Butterfly ; <<< select GuiParas and hit F6 to test this snippet
include \masm32\MasmBasic\Res\MbGui.asm
GuiEnd
Rem - predefined names are Apple, Ball, Bluetooth, Bulb, Butterfly, Clock, Dice, Flower, Football, Globe, Guard, Info, Joystick, Keys, Lens
Monitor, Movie, Music, Music2, Newspaper, Painting, Plot, Printer, Star, Swords, Trafficlight, Tree, Trumpet, Umbrella, Walkman, Wall
- build and use IconExplorer to get more ideas; use e.g. icon Shell32:14 when building a Gui app
MakePath MyBezier, Bezier(150:0, 0:90, 0:120, 300:300) ; 4/7/10 etc * x:y
MakePath MyCircle, Circle(333) ; radius
MakePath MyEllipse, Ellipse(500:300) ; x, y, width, height
MakePen hPen, RgbCol(100, 0, 0, 255), width 3 ; blue, half transparent
MakeBrush hBrush, RgbCol(100, 255, 255, 0)
...
Event Paint
GuiDraw MyCircle, hPen, 100, 200 ; draw outline at x=100, y=200
GuiFill MyEllipse, hBrush, 10.0, 20.0 ; fill ellipse at x=10%, y=20% of client area
Rem - MakePath creates a Gdi+ path object, for use with GuiDraw or GuiFill ; see here for a full example
- Gdi+ has the bad habit to trash the FPU; if you use it, preserve your data between Guixx calls
- use MakePen and MakeBrush for outline and interior, and make sure you specified 4 args in RgbCol(...)
- see PlotData for an example using Legend
- see here for a pie chart example using PieLegend
- available shapes are Arc, Bezier, Circle, ClosedCurve, Ellipse, Pie, Polygon, PolyLine and Rect, see
Graphics Paths in GDI+ for details. The shape 'Text' is not implemented, use GuiTextBox instead
.if FileSave$("Assembler=*.asm|MasmBasic=*.asc|All files=*.*")
FileWrite FileSave$(), stream:hMyEdit, If?(InstrOr(FileSave$(), ".rtf" or ".asc", 1), SF_RTF, SF_TEXT)
SetDoc$ FileSave$() ; assign new document name and set window title accordingly
.endif
SetStatus$ GetDoc$() ; set doc name without path to first column of status bar
SetStatus$ GetDoc$(full) ; same but will full path
SetStatus$ "Добрый Вечер", 1 ; just a test - Unicode is no problem
SetStatus$ GetDoc$(full), 2 ; set doc name with full path to third column of status bar
...
Switch MenuID
Case 3 .. 12 ; the Masm32 Case macro accepts a range
SetMenuLanguage MenuID-3 ; change menu language on the fly
SetTitle$ ; use current document name and menu language
Rem check here for an example
Let A$=New$(100) ; 100 zero-initialised bytes
mov ecx, 500 ; reg32 except eax+edx are allowed to specify size
Let My$(0)=New$(ecx) ; destination can be an array element
Let My$(0)=My$(0)+New$(1000)+My$(0)+"#bye" ; and concatenation is legal code
Clr$ My$(0) ; it is recommended to clear the old string before a new huge allocation,
mov ecx, 900000000 ; precisely because concatenation of the old and the new string is allowed
Let My$(0)=New$(ecx) ; 900 MB may work on many machines, but do thorough testing!
Rem - returns pointer to zero-initialised memory in eax
- you can also use Let Some$=String$(n, 0)
- Resize Some$:nBytes performs a HeapReAlloc (+ or -) on Some$; added nBytes will be zeroed
- you may free this memory with Clr Some$, but it will also be freed automatically with Exit
Key new$(
include \masm32\MasmBasic\MasmBasic.inc ; download
Init
Dim PtrSSE() As DWORD
For_ ct=0 To A16Max-1 ; 100 aligned pointers
Alloc16 Rand(10000)
mov PtrSSE(ct), eax
Print Hex$(al), " "
Next
For_ ct=0 To A16Max-1
Free16 PtrSSE(ct)
Next
EndOfCode
Rem for use with SSE instructions that require 16-bit alignment
MemSet offset somebuffer, 0, 1000 ; dest. pattern, #bytes
MemSet offset somebuffer, "x", 1000 ; 1000 * x
MemSet offset somebuffer, Mirror$("abcd"), 1000 ; 250 * abcd, slightly faster
mov eax, Chr$("Masm32 is great ") ; string must have (at least) 16 bytes
movups xmm0, oword ptr [eax]
mov edx, offset somestring
MemSet edx, xmm0, 99
Rem returns end of buffer in edx
MemState("leaked kBytes: %i") ; prints to console the leaked bytes using Str$("...") format
MemState("leaked %i kB", abs del) ; prints both the current absolute value and the leaked bytes
MemState("leaked kBytes: %i\n", always) ; prints both the current absolute value and the leaked bytes
MemState(dump) ; prints sums and averages for all monitoring points
MemState(reset) ; resets sums and counters
PrintLine "PagefileUsage: 0x", Hex$(MemState(PagefileUsage)) ; print a member of PROCESS_MEMORY_COUNTERS
Rem - use in loops to detect memory leaks
- standalone format prints only if a change was detected; use e.g. msTrigger=8192 to change the threshold (default is 4k)
- function format returns, standalone format leaves all registers intact
- second arg may be a combi of absolute, delta, reset and always (=print even if there was no change); default is del
- with msUse=0, no code will be generated (the function format, e.g. MemState(PagefileUsage), returns -127)
- default unit is kBytes; use msUnit=10 for kBytes, msUnit=20 for MBytes and msUnit=0 for bytes
Let esi=wRes$(401) ; get the resource string with ID 401 from the *.rc string table
invoke MessageBoxW, 0, esi, wRes$(402), MB_OK
Open "O", #1, "TestUnicode.txt"
wPrint #1, wChr$("Unicode, oh yeah!", 13, 10)
wPrint #1, wRes$(401), wTb$, wRes$(402), wCrLf$
Close
Recall "TestUnicode.txt", rc$() ; file is Unicode
For_ ecx=0 To eax-1
wPrintLine wRec$(rc$(ecx)) ; Recall translates Unicode text files to UTF-8; use wRec$() to get Unicode back
Next
Print "A file in resources defined as 77 RCDATA "HelloWorld.txt:", ResFile$(77) ; file content returned in eax
Inkey "bye"
Rem - returns DWORD in edx
- if the resource string does not exist, a "No such string" runtime error will be triggered unless
you set MbUseErrLine = 0. In this case, missing strings will be shown as R? (Ansi and Unicode).
- you can embed resources in your *.asc file by using two Rsrc bookmarks
(case-sensitive) below end Start, for example:
Rsrc
32512 ICON "\\masm32\\MasmBasic\\icons\\Globe.ico" ; Asm, House, Keys, Globe, Hammer, Setup, Disc, Eye, ...
1 24 "\\Masm32\\MasmBasic\\Res\\XpManifest.xml" ; adding a manifest may help to avoid AV problems
STRINGTABLE
BEGIN
401, "Нажмите на эту кнопку" ; "Click on this button" in Russian
402, "Добро пожаловать" ; "Welcome" in Russian
END
Rsrc
include \masm32\MasmBasic\MasmBasic.inc
Init ; *** select Init and hit F6 ***
Lg$(File:"\Masm32\MasmBasic\Res\GuiData\GuiData.tab") ; file:"path" loads the multilingual matrix; or File:nn if you have RCDATA nn in resources
For_ ct=0 To 5
Lg$(Language: Mid$("ENESBRITDEFR", ct*2+1, 2)) ; example: Lg$(Lang:IT) sets Italian
wPrintLine wLg$(LgTx), ":", CrLf$, Tb$, wLg$(_ASM), CrLf$, Tb$, wLg$("_BEL") ; Unicode works better
wPrintLine Tb$, wLg$("_USA"), CrLf$, Tb$, wLg$(_AUS), CrLf$, Tb$, wLg$(_AUT) ; with many languages
Next
EndOfCode
Rem - designed for use with multilingual GUIs; matrices should be edited as spreadsheets, and must be saved in tab format
- Lg$(Language:FR) sets the column defined in the first row of the spreadsheet
- only the first four characters are being used to identify the string.
Print "I live in ", Geo$()
Rem your computer knows roughly where you live ;-)
; returns Unicode string describing common errors. for example "no such path" in Kernel32:
wPrint MsgTable$(3, "kernel32.dll"), wCrLf$ ; can't find the path
wPrint MsgTable$(80000431h, "user32.dll"), wCrLf$ ; attempt xx failed
wPrint MsgTable$(3, "ntdll.dll"), wCrLf$ ; STATUS_WAIT_3
wInkey MsgTable$(6BAh, "kernel32") ; RPC server not available
Let Files$(n)=String$(32767, 0) ; assigns a zero-initialised buffer to Files$(n)
mov eax, String$(32768-1, "A") ; assigns a buffer initialised with A to eax
Print String$(15, "+x") ; prints +x+x+x+x+x+x+x+x+x+x+x+x+x+x+x
Print String$(0, 0), CrLf$ ; String$(0, x) means repeat last result
Print String$(40, Cat$("["+String$(20, "abc")+"]"+CrLf$)) ; fill a screen
Rem - returns DWORD ptr eax; max. allowed size is 32767 bytes
- the buffer remains valid until a new call to String$, except for String$(0,0) which just returns the last result
- two or more String$ in the same Print or Let line will print only the last version
- see also MemSet
Key string$(
.MbSpace$
Let My$=Space$(20)
Print "Twenty spaces: [", Space$(20),"]"
; make sure that FamilyName$ gets printed in column 21 (except if FirstName$ is longer):
PrintLine Space$(20, FirstName$(ecx)), " ", FamilyName$(ecx)
Rem returns pointer in eax
include \masm32\MasmBasic\MasmBasic.inc
Init
PrintLine "123 56 78 90"
Inkey SpaceToTab$("123 56 78 90")
EndOfCode
Rem use to convert a fixed size database to tab-delimited
; 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 "Test", CrLf$, Chr$("the library", 13, 10)
Print #1, esi, " is ", My$
wPrint wRes$(123) ; Unicode defined in a resource stringtable
Print #1:25, "There are many chars here but I print only a few"
Print #1:100, FileRead$("\masm32\include\Windows.inc") ; print the first 100 chars of that file
Rem - predefined strings do not bloat the exe (the same applies to Let):
(w)CrLf$ same as an offset to db 13, 10, 0
(w)Tb$ 9 aka tab - see also \n and \t in Str$()
Spc1$ 32
Spc2$ 32, 32
Spc4$ four spaces
MbExeFolder$ the folder of the current executable, e.g. D:\Masm32\
CurDir$() the current directory, e.g. D:\Masm32\
CurDir$(0) same but no trailing backslash, e.g. D:\Masm32
- Unicode can be printed as e.g. wPrint #1, wCrLf$, wChr$("This is Unicode") or by defining a
string like this: wData MyWide$, "This is Unicode", 0 , followed by wPrint #1, offset MyWide$
- Unicode printing to the console (e.g. Russian, Chinese) is possible but do not assume
it will work on other systems; specific language packs need to be installed.
You may test if the following line works on your system if you assemble with Ctrl F6 and click Yes:
uPrint "Нажмите на эту кнопку" ; "Click on this button" in Russian
- Important: the first occurrence of wPrint in your code triggers (hidden, but you can see it in Olly):
invoke SetConsoleOutputCP, CP_UTF8 ; codepage 65001, Unicode
ConsoleColor cGray, cBlack ; grey on black
Without these two API calls, Unicode strings will not display properly. Note that the
console colors are not automatically set if there is an earlier ConsoleColor statement.
You can try different colours by using for example, before the wPrint, wPrintColor = cYellow
(see full list under ConsoleColor below). Strangely enough, Chinese and Arabic fonts may work
with some colours but not with others - Windows mysteries... ;-)
- Print is powerful and versatile, but it may fail with a HeapAlloc runtime error for huge buffers; in
these cases, use PrintBuffer (see below)
Key pri, pri1
PrintLine "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
GuiParas equ "Test window", x650, y20, w300, h200
include \masm32\MasmBasic\Res\MbGui.asm ; select the white zone and hit F6 to test this
GuiControl MyEdit, "RichEdit" ; create a control
SetWin$ hMyEdit=String$(20, cfm$("select some text and hit Ctrl P to print this\n"))
Event Key
If_ VKey==VK_P || VKey==-VK_P Then <MsgBox 0, Str$("%i pages printed", PrintRtf(hMyEdit, 2)), "PrintRtf:", MB_OK>
GuiEnd
Rem - returns #pages printed
- requires a handle to a RichEdit control
- if text is selected, then only the selection will be printed
- optional second arg: 0=use default printer, 1=use once the printer dialog, 2=use it always, 4+x=use 'no setup' dialog
- optional: margins in mm, e.g. void PrintRtf(hMyEdit, 0, 20, 20, 15, 20) ; left, top, right, bottom
- printer settings like orientation etc are kept while the program runs
PrintCpu ; print full info to the console
PrintCpu 0 ; print basic info
PrintLine "[", Cpu$(), "]" ; basic info, for use in Gui apps
Rem useful for benchmarking different CPUs
SetCpUtf8 ; set codepage to UTF8
SetCpAnsi ; force ANSI codepage
SetCpUpperLower$ CP_UTF8 ; assume source for e.g. Let esi=Upper$(esi) is UTF-8 encoded; decode to same codepage
SetCpUpperLower$ CP_UTF8, 1252 ; encode as UTF-8, decode as standard Windows codepage
SetCpUpperLower$ ; reset to user's systemwide codepage setting (usually CP 1252)
Rem - internal macros for switching between normal Ansi and Unicode Print
- SetCpUpperLower$ source [, dest] may be needed if text comes from a file, and does not have the same encoding
- in RichMasm, by pressing Ctrl F6 the user can decide to build the project entirely in UTF-8; codepages will be set accordingly
Let esi=ConvertCp$(esi, CP_UTF8, 1252) ; convert esi from UTF-8 to standard Windows
SetCpAnsi ; set codepage 1252 for printing
PrintLine "Test: [", ConvertCp$(esi, CP_UTF8, 1252), "]" ; print convert esi from UTF-8 to standard Windows
Rem - may be handy e.g. with UTF-8 encoded Files$()
- note that Recall returns UTF8 when opening a Unicode text file
- for UTF-8 -> Unicode, use wChr$()
- for Unicode -> UTF-8, use Utf8$()
- limit is 40k; use Recall and a loop for bigger files
Cls ; ## miscellaneous console functions ##
Cls ; clear the screen
Rem console apps only, of course
ConsoleColor cBlue, cWhite ; blue text on white background
ConsoleColor cDarkRed ; dark red text on black
ConsoleColor invert ; fore- and background colour inverted
ConsoleColor eax ; any colour that suits SetConsoleTextAttribute
Rem available colors (see also http://support.microsoft.com/kb/319883)
cBlack, cWhite, cGray, cBlue, cGreen, cCyan, cRed, cMagenta, cYellow
cDarkGray, cDarkBlue, cDarkGreen, cDarkCyan, cDarkRed, cDarkMagenta, cDarkYellow
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
Print At(20) "Hello" ; one argument: print in column 20, current row
Print At(ct+1, Locate(y)+1) CColor(cBlack, cYellow) "Hello" ; one column right of some counter, next line, black on yellow:
Print At(5, 5) CColor(cBlack, cYellow) "Attention, no commas after At() and CColor() !!"
Print At(#esi) esi ; esi points to ABCD sometext, where A=col, B=row, C=foreground, D=background colour
Rem for use with Print only; separate with blanks, not commas, from the first Print argument
crtbuf ThisExe$, MAX_PATH ; create a buffer in the uninitialised data section
invoke GetModuleFileName, 0, ThisExe$, MAX_PATH ; use it...
crtbuf pBuffer, 1000000, 16 ; create a 1 Mio bytes buffer in .data?, align 16 for use with SSE2
Rem uses label and ORG $+size, therefore even big values will not make ml.exe freeze
include \masm32\MasmBasic\MasmBasic.inc
.code
somelabel:
mov ecx, [esp+4]
MsgBox 0, ecx, "Hi", MB_OK
retn 4
Init
xCall somelabel, Chr$("Hello World")
EndOfCode
Rem use for passing invoke-style arguments to a label
Init
; whateverproc = any proc or result of GetProcAddress etc
mov CreateInvoke(whatever1, 3*dword, REAL8), whateverproc
; mov CreateInvoke(whatever2, vararg), whateverproc ; Error A2094: Vararg requires C calling convention
mov CreateInvokeC(whatever3, dword, REAL8, dword, REAL4), whateverproc
mov CreateInvokeC(whatever4, vararg), whateverproc
; example how to use the created invoke:
invoke whatever3, 123, FP8(123.456), 456, FP4(456.789)
Inkey "OK"
Exit
whateverproc proc thedd1, thereal8:REAL8, thedd2, thereal4:REAL4
PrintLine Str$("arg2=%f", thereal8)
PrintLine Str$("arg3=%f", thedd2)
PrintLine Str$("arg4=%f", thereal4)
ret
whateverproc endp
end start
Rem make sure the proc specifies the non-dword args correctly
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals timeinfo:SYSTEMTIME, f$="Now it's %I:%M %p"
Init ; select init and hit F6 to test this snippet
Inkey "strftime(): ** ", CRT(strftime, buffer, 80, f$, addr timeinfo), " **"
EndOfCode ; for parameters, see e.g. C++ strftime()
Output: strftime(): ** Now it's 12:00 AM **
Rem - allows to use C RunTime functions inter alia with Let and Print, in a format close to C
- note that some C functions return numbers in ST(0); use void ..., then Print Str$(ST(0)v) to display them
- if one para is called buffer as shown above, it will be allocated and freed automatically
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals REAL16 quadNumA, quadNumB, quadResult
Init quad
MovVal quadNumA, "2.141592653589793238462643383279502884" ; string to flt128
movups quadNumB, Quad(FP4(1.0)) ; REALx to flt128
movups quadResult, QuadMath(__addtf3, quadNumA, quadNumB)
Inkey "PI=", Quad$(quadResult, "%#*.32Qg") ; Quad$ instead of Str$()
EndOfCode
Rem - GCC QuadMath DLLs needed
- over 100 REAL16 functions
- click here for an example how to use the GCC QuadMath library
include \masm32\MasmBasic\MasmBasic.inc
; define the gsl function(s) you want to use with the syntax of the GNU Scientific Library Reference
gslvar int gsl_rng_default()
gsl double gsl_stats_mean(const double data[], size_t stride, size_t n)
gsl double gsl_stats_variance(const double data[], size_t stride, size_t n)
gsl double gsl_stats_sd(const double data[], size_t stride, size_t n)
.data
MyData REAL8 5.0, 6.0, 3.2, 1.8, 9.0 ; define a double precision array
MyReal8 REAL8 ? ; for saving results with fstp
Init ; select init and hit F6 to test this snippet
SetFloat MyReal8=gsl_stats_mean(offset MyData, 1, 5) ; ptr, stride 1, 5 elements
PrintLine Str$("Mean \t%7f", MyReal8) ; print with 7 digits precision
PrintLine Str$("Variance\t%7f", gsl_stats_variance (&MyData, 1, 5))
fstp st ; just print, then cleanup the FPU
PrintLine Str$("Standard Dev\t%7f", gsl_stats_sd (addr MyData, 1, 5)v) ; a trailing v tells Str$() to fstp st
mov eax, gsl_rng_default() ; you can access gsl global variables defined with gslvar
EndOfCode
Output:
Mean 5.000000
Variance 7.620000
Standard Dev 2.760435
Rem - if the GSL dll files are not found, an attempt will be made to get them from the oscats site; in case
of problems, check if your antivirus software interferes with the download, and if yes, act accordingly
- note that MasmBasic is copyrighted and therefore cannot be distributed together with
code that uses GNU components; make sure you understand the legal implications
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 (MbArraySet 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)
ArrayRead x() As REAL8, 90 ; get x+y arrays
ArrayRead y() As REAL8, 91 ; from resources (or from file)
Dim y2() As REAL8 ; this array will hold the regression line
GetLinReg x(), y() ; set the A and B coefficients
For_ ecx=0 To eax-1 ; #elements in eax
SetFloat
Next
Rem - GetLinReg returns #elements in eax
- LinRegY(x) returns the corresponding Y value in ST(0)
Dim My$() ; create a string array (expand the number of elements as necessary)
Dim x$(100000) ; preallocate 100000 elements, i.e. 0...100000 (slightly faster)
Dim My$(tab) ; create a two-dimensional string array, e.g. for a table (expand as necessary, use Tb$ as delimiter)
Dim My$(csv) ; same but using comma-separated values for storing to a csv file
For_ ecx=0 To 99 ; start from index zero to enable autoexpansion
Let My$(ebx)=Str$("This is array element %i", ecx)
Next
MsgBox 0, My$(50), "Re-Dim an array:", MB_OK
Dim My$() ; simply Dim it again
Dim wc(3) As WNDCLASSEX ; an array of structures
mov wc(0, size), SIZEOF WNDCLASSEX ; use mov or m2m as appropriate
Dim rc(3) As RECT
For_ n=0 To 3
m2m rc(n, left), n ; n is a global variable, so you need m2m
Next
Dim MyBytes(999) As BYTE ; a 1000-byte array
Dim MyWords(3, ebx) As WORD ; a two-dimensional WORD array
Rem - see Let, Erase, Swap, Recall and Store
- no Erase() needed before a redimension (string arrays only)
- 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.
The columns limitation does not apply to Dim My$(tab) arrays.
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 ;-)
- ?id$(n, ecx) where n is e.g. My$(id) can be used to pass an array to a proc
- My$(?) returns the number of strings; My$(123, ?) returns #columns in row 123 of a two-dimensional arrays; use My$(?columns) to
get the max #columns, testing only the first 127 elements; the line index with the highest column count is in edx
- with numerical arrays, use MyNum(?) for total #elements, MyNum(?rows) for #rows and MyNum(?cols) for #columns
- use MyNum(?mean), MyNum(?median), MyNum(?sum), MyNum(?min) and MyNum(?max) to get more info about the array
- the max #elements is limited by HeapAlloc, e.g. 88 Mio REAL8, 170 Mio REAL4 or DWORD elements on Win7-64 with 4MB RAM
- with two-dimensional string arrays, Insert and Delete may show buggy behaviour; test yourself
Key Dim
Erase My$() ; free a string array; Dim My$(new:ct) is possible
Erase MyReal8Array() ; free a numeric array; at
Erase MyStructureArray() ; present, no redim possible
Rem no return value; will throw a runtime error if HeapFree fails
Key erase
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
include \masm32\MasmBasic\MasmBasic.inc
Init ; < < select Init and hit F6 to test this snippet
ArrayFill Years() As WORD, 2000, 5, 2 ; create a WORD sized array and fill it; start value is 2000, 5 elements, increment 2
For_ ecx=0 To eax-1
PrintLine Str$("Year #%_u\t", ecx), Str$(Years(ecx))
Next
Dim rent(11) As DWORD ; create a fixed-size DWORD array with 0...11=12 elements
ArrayFill rent(), 1000 ; fill it with the value 1000
Dim MyRect(2) As RECT ; create an array with 0...2=3 elements
.data
rect RECT <12, 34, 56, 78>
.code
ArrayFill MyRect(), rect ; fill array with values from one .data section structure
For_ ecx=0 To eax-1
Print Str$("rect %i: ", ecx), Str$("left=%i", MyRect(ecx, left)), Str$(", right=%i\n", MyRect(ecx, right))
Next
Dim My$(2) ; fixed size string array
ArrayFill My$(), "String created "+Date$+"!!"
xchg eax, ecx
.Repeat
Let My$(ecx)=Str$("String #%i added", ecx)
inc ecx
.Until ecx>=5
For_ ecx=0 To My$(?)-1
PrintLine Str$("String #%i\t", ecx), My$(ecx)
Next
EndOfCode
Rem - use ArrayFill array() As SIZE, start, #elements [, step] to create and fill numeric arrays (BYTE...DWORD, REAL4...REAL10)
- strings and structures like RECT must first be Dim'ed as fixed size arrays; after the ArrayFill, they are dynamic
- see also Let, Erase, Swap, Recall and Store
include \masm32\MasmBasic\MasmBasic.inc
Init ; < < select Init and hit F6 to test this snippet
Dim MyNumbers() As DWORD
For_ ecx=0 To 99999 ; create 100,000 elements
mov MyNumbers(ecx), Rand(20) ; random values 0...19
Next
ArraySort MyNumbers(-) ; sort descending
ArrayStripDuplicates MyNumbers()
For_ ecx=0 To eax-1
Print Str$(ecx), Str$("\t%i\n", MyNumbers(ecx))
Next
EndOfCode
Rem - removes all duplicate elements from a sorted numerical array
- returns new #elements in eax
Dim My3Pts(2) As DWORD ; create an array with 3 elements (0 .. 2)
ArraySet My3Pts() = 12, 34, 56 ; assign three values
Dim MyR8(3) As REAL8 ; create an array with 4 elements (0 .. 3)
ArraySet MyR8() = 1.0, 2.0e3, 2.0, 4,0e3 ; same for REAL4 or REAL8
ArraySet My$() = "abc", "def", "ghij", "klmn" ; same with strings: no Dim before, empty brackets
Let My$(My$(?))="Only strings support" ; My$(?) is the current number
Let My$(My$(?))="dynamic auto expansion" ; of elements in the string array
For_ ecx=0 To My$(?)-1
PrintLine Str$(ecx), Tb$, My$(ecx)
Next
ArraySet t$()="Hello ", "World, ", "how ", "are ", "you?"
For_ each esi in t$(): Print esi
Rem - numerical arrays: allowed sizes are DWORD, REAL4, REAL8; no autoexpand, no boundary
check, #elements must be sufficient, no return value, does not trash any registers
- strings must be initialised with empty brackets, #elements in eax; autoexpanded if needed
; convert string to a string array:
StringToArray Win$(hEdit), My$() ; converts content of the edit control to a MasmBasic string array
Let esi="A string"+Tb$+"in two columns"+CrLf$+"another"+Tb$+"string" ; tab-delimited text
StringToArray esi, My$(), tab ; convert to a two-dimensional array
StringToArray Clip$(), L$() ; converts content of the clipboard to an array
StringToArray 123, L$() ; converts resource; use e.g. 123 RCDATA "names.txt" in the Rsrc section
; convert string to a numerical array:
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals a1$="123 27.5 28.49 -56.78 '20h' 0x40 11111b/123456789" ; string with a wild mix of number formats
Init ; < < select Init and hit F6 to test this snippet
Dim MyDw() As DWORD
For_ ecx=0 To Fn(StringToArray a1$, MyDw())-1 ; strings to dwords conversion
PrintLine Str$("MyDw(%i)=", ecx), Str$(MyDw(ecx))
Next
EndOfCode
Rem - returns #elements in eax; in For_ ... Next loops, you may use Fn(...)-1 as shown above
- numeric arrays must be declared (BYTE ... QWORD, REAL4/8/10), but there is no need to Dim a string array before
include \masm32\MasmBasic\MasmBasic.inc
Init ; < < select Init and hit F6 to test this snippet
For_ ct=0 To Split$("Masm32 is great", " ", My$())-1
PrintLine "[", My$(ct), "]"
Next
EndOfCode
Rem converts a string to an array using the specified delimiter, and returns #elements in eax; under the hood, StringToArray is being used
; converts a string array to one string
include \masm32\MasmBasic\MasmBasic.inc
Init
Dim My$()
Let My$(0)="Masm32"
Let My$(1)="is"
Let My$(2)="great"
PrintLine "[", Join$(My$()), "]" ; with no second arg, output is [Masm32\nis\ngreat], i.e. items separated by 13, 10
Print "[", Join$(My$(), " "), "]" ; shows [Masm32 is great] with " " as second arg
Exit
EndOfCode
Join$(MyArray$(), Dest$) ; convert a string array to one linear Dest$ separated by CrLf$
Rem - returns ptr in eax when used in function form, i.e. mov var, Join$(array$(), "fillstring")
- standalone, i.e. Join$(array$(), dest$) can be slightly faster than Let dest$=Join$(array$())
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to list all structures in Windows.inc
Recall "\Masm32\include\Windows.inc", L$()
Print Str$("%i lines loaded, now filtering for STRUCT:", eax)
For_ ecx=0 To Filter$(L$(), "STRUCT", 1, 5)-1 ; 1=keep if match, 1=case-insensitive+4=full word
Print Str$("\n%i\t", ecx), L$(ecx) ; lists all strings that contain STRUCT as full word
Next
EndOfCode
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to list all macros in \Masm32\macros\macros.asm
Recall "\Masm32\macros\macros.asm", L$()
If_ Filter$(L$(), "MACRO", 1, 4) Then <For_ each esi in L$(): <PrintLine Trim$(esi)>>
EndOfCode
Rem - returns remaining #strings in eax
- syntax: mov ecx, Filter$(array$(), match$ [, include] [instr mode]
- include: 0 (exclude matching strings) or 1 (include, default)
- instr mode: see Instr_(); 1=case-insensitive, 4=full word
-
- in general, Extract$() offers more and better options
include \masm32\MasmBasic\MasmBasic.inc
Init ; << select Ixnit and hit F6 to test this snippet
Dim A$() ; first array
Dim B$() ; second array
For_ ecx=0 To 9 ; ten loops
Let A$(ecx)=Str$("String A %i", ecx) ; fill with more or less...
Let B$(ecx)=Str$("String B %i", ecx) ; ... meaningful stuff
Next
ArrayMerge A$(), B$() ; A$(): 10xA, then 10xB; array B$() gets erased
For_ ecx=0 To 19 ; twenty loops
PrintLine Str$("Element %_i\t", ecx), A$(ecx)
Next
Exit debug ; debug = check integrity of allocations on exit
end start
Rem - returns nothing
- destination array cannot be a Recall array
- destination array cannot be Files$()
- use Delete My$(n), all in case you want to truncate the destination before merging
Swap L$(), Files$()
Swap MyA$, MyB$
Rem - exchanges two arrays or two variables of the same type
- allowed numeric types: DWORD, QWORD, REAL4, REAL8, REAL10
- mixing arrays and strings will fail and trigger an error message
- to Swap two strings of the same array, use Swap #x$(ct), #x$(ct2) (works only with UAsm and AsmC!)
Key swap
Insert My$(5) ; move My$(5) up to My$(6)
Let My$(5)="This is the new string"
Insert My$(6)="This is the new string #6" ; inserts a string and assigns a value
Insert
Insert My$(5), 10 ; move My$(5) up to My$(15), insert 10 empty strings
Dim D2$(tab) ; create a two-dimensional, tab-delimited string array
Insert D2$(0, 1), 3 ; insert 3 cells at row 0, column 1
Dim MyRc() As RECT ; Insert and Delete work with autoexpanding numerical arrays
Insert MyRc(ecx+1) ; triggers runtime error if ecx+1 is beyond the bounds
Rem no return value; does not change any registers (not even eax)
Key ins
Delete My$(5) ; move My$(6) down to replace My$(5)
Delete My$(5), 2 ; move My$(7) down, delete also My$(5+6)
mov ecx, 3 ; regs are allowed but not eax or edx
Delete My$(5), ecx ; move My$(8) down to replace My$(5), delete also My$(6+7)
Dim MyDw() As DWORD ; Insert and Delete work with autoexpanding numerical arrays
Delete MyDw(99) ; triggers runtime error is ecx+1 is beyond the bounds
Rem - no return value, all regs unchanged
- to delete an array element without moving the other elements, use Clr$ My$(5)
- deleting the last element, e.g. with Delete My$(My$(?)), triggers autoexpansion of the array,
i.e. the new array will have (nold+128) and 4095 elements
- with 2-dimensional arrays, Delete My$(1, 2) etc is possible but check carefully the result,
especially if you need to use Delete more than once
- to delete a file, use Kill "MyFile.dat"
Key del
.data
CLSID_InternetExplorer GuidFromString("0002DF01-0000-0000-C000-000000000046") ; with quotes
IID_IWebBrowser2 GuidFromString(D30C1661-CDAF-11D0-8A3E-00C04FC9E26E) ; plain
IID_IWebBrowser2a GuidFromString({D30C1661-CDAF-11D0-8A3E-00C04FC9E26E}) ; registry format
Rem use in .data, .const or .code section, as replacement for MyVar GUID
include \masm32\MasmBasic\MasmBasic.inc
Init ; << select Init and hit F6 to test this snippet
MsgBox 0, Cat$("A Guid: ["+Guid$()+"]"), "Hi", MB_OK
EndOfCode
Rem returns a pointer to the string, plus a pointer to the GUID itself in edx
; compare two GUIDs for equality - for use with OLE
.if GuidsEqual(offset IID_IUnknown, IID_IUnknown) ; offset, direct
PrintLine "EQ"
.else
PrintLine "NE"
.endif
.if GuidsEqual(IID_IOleObject, IID_IUnknown) ; 2*direct
PrintLine "EQ"
.else
PrintLine "NE"
.endif
mov eax, offset IID_IOleClientSite ; one offset in reg32
.if GuidsEqual(eax, IID_IOleObject)
PrintLine "EQ"
.else
PrintLine "NE"
.endif
Rem returns Zero?, trashes xmm0 but no other register
mov edi, offset WebInterface
lea edx, vEmpty
CoInvoke [edi], IWebBrowserVtbl.Navigate, Ole$("www.google.com"), edx, edx, edx, edx
Rem for use with COM; rvCoinvoke(...) returns eax (and S_OK=0)
mov ecx, Chr$("Just a test")
For_ esi=0 To 4
wPrint wStr$("%i [", esi+1), Ole$(ecx), "]", wCrLf$
Next
wPrint "[", Ole$("another test"), "]", wCrLf$
wInkey wChr$(13, 10, "["), Ole$(ecx), " again]"
Rem returns a BSTR in eax; the string is not permanent, since it uses MasmBasic's fat circular buffer. Advantage: There is
no need to free it. However, avoid using it in or after 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 Utf8 source string
mov edi, Ole$(edi) ; translates a Utf8 (or ANSI) string to a Utf16 BSTR
For_ esi=0 To 1505 ; Str$ uses the circular buffer, and will eventually return to edi
wPrint wStr$("Testing the limits of MasmBasic's circular buffer: %i [", esi+1), edi, "] ", wCrLf$
Next
wPrint "Now what happened to edi? Here it is: [", edi, "] ", wCrLf$
mov ebx, "A"
Print CrLf$, Chr$("The alphabet from a loop: ", ebx)
.Repeat
inc ebx
Print Chr$(ebx+32)
.Until ebx>="Z"
For_ ct=0 To 9
Rand("a", "z", c1) ; create random text
Rand("a", "z", c2)
Rand("a", "z", c3)
Rand("a", "z", c4)
Rand("a", "z", c5)
Let some$(ct)=Str$(ct)+Tb$+Chr$("_", c1, c2, c3, c4, c5, "_")
PrintLine some$(ct)
Next
PrintLine Utf8$(wCL$()) ; translate the Unicode commandline to Utf-8
wPrintLine wCL$() ; same effect in this case
mov ebx, 60
Print Chr$(13, 10, "This is ", ebx, "great", ebx+2) ;
wPrint #1, wChr$(13, 10, "This is Unicode"") ; to files or the console (see wPrint for details)
mov eax, Chr$("This is an ANSI string") ; you can use a pointer to an ANSI string...
wPrint wChr$("Not true: "), wChr$(eax) ; ... to print a wide string with wChr$(reg32)
wData MyWide$, "This is Unicode", 0 ; use in code section, access as mov eax, offset MyWide$
invoke TextOut, PtDC, 7, 2, Chr$("Hello"), c$Len ; some functions need the length; use c$Len
Rem returns pointer in eax
Key c$(
MsgBox 0, RichEd$(), "Current selection:", MB_OK
Rem returns edi (preserve as needed)
mov ebx, Len(My$)
mov eax, Len("123") ; Len returns 3 in eax, so Olly will show mov eax, eax
void Len("123") ; put the result into eax (void avoids the mov eax, eax)
mov eax, wLen(MyUnicode$) ; wideLen for Unicode strings
void wLen(wChr$("123")) ; returns 3 chars in eax
Let esi="Добро пожаловатьäöü" ; assign a UTF-8 string
uPrint "[Добро пожаловатьäöü]" ; or print it directly
Print Str$(" is %i characters long (expected: 19)\n", uLen(esi))
Rem returns length in eax; bytes for ANSI, chars for Unicode; for UTF-8 encoded strings, Len returns bytes, uLen returns chars
invoke MbCopy, ptrDest, ptrSource, ctBytes
invoke MbCopyz, ptrDest, ptrSource ; zero-delimited
Bmove ptrSource, ptrDest [, ctBytes]
SafeCopy ptrDest, ptrSource, maxBytes
Rem - returns a pointer to the end of the destination in eax (for fast string concatenation)
- Bmove is identical but uses like Masm32 szCopy the inverted src to dest logic; ctBytes can be omitted
- for ctBytes=-1, MbCopy calculates the length of the source; it thus does the same as lstrcpy
but is (e.g. on an Intel Core i5 for ct=one Million) over three times as fast
- for ctBytes=-2, MbCopy calculates the length of the source but does not include the zero delimiter when
copying; use this for cat$ situations but note that the last element should have have ctBytes=-1.
MsgBox 0, "MasmBasic is cute", "Welcome:", MB_YESNOCANCEL
.if Alert("text", "title", MB_YESNO)==IDYES
... do something ... ; same as MsgBox but returns a value
.endif
Dim My$(9) ; create a string array
ArrayFill My$(), "Ciao" ; set all strings to Ciao
Let My$(9)="Good morning" ; make an exception for #99 ;-)
MsgBox 0, Cat$(My$(3)), My$(9), MB_OK
wArrayFill My$(), "Ciao as Unicode" ; reset all strings to Unicode Ciao
wLet My$(9)="Good morning as Unicode:" ; different exception...
wMsgBox 0, wCat$(My$(3)), My$(9), MB_OK
wMsgBox 0, wCat$("Now:"+wCrLf$+wDate$+", "+wTime$), "Unicode is easy:", MB_OK
Rem MsgBox returns result in eax; ecx is preserved
Key mb, wmb, alert
Clr MyVar1 ; clears local and global dword variables
Clr MyVar1, MyVar2 ; multiple arguments allowed
Clr eax, MyVar1, MyVar2 ; the first one may be a register
Clr$ My$(123) ; clear an array element
Clr$ a$, b$, c$ ; clear one or more strings (free heap mem, assign Null$)
Cls ; clears the console
Rem - Clr: if a register is specified as first of several arguments, code is shorter and the register will be zeroed, too
- to remove an array element, use e.g. Delete My$(123)
ClearLastError
invoke GetTickCount
PrintLine Err$()
Rem for debugging; same as invoke SetLastError, 0, but ecx is safe, and no code will be generated for usedeb=0
include \masm32\MasmBasic\MasmBasic.inc
Init
Let esi="\Masm32\include\Windows.inc"
For_ ecx=0 To 9
ClearFileCache esi ; tell the OS to expel the file from the cache
NanoTimer()
Let edi=FileRead$(esi)
PrintLine Str$("%i bytes read in ", LastFileSize), NanoTimer$()
Next
Print
For_ ecx=0 To 9
NanoTimer() ; no caching this time
Let edi=FileRead$(esi)
PrintLine Str$("%i bytes read in ", LastFileSize), NanoTimer$()
Next
EndOfCode
Rem for benchmarks of algos that involve disk I/O; returns CreateFile error code in eax
include \masm32\MasmBasic\MasmBasic.inc
.code
MyTest proc uses edi esi ebx arg1:DWORD, arg2, TheString
LOCAL v1, v2, rc:RECT, buffer[100]:BYTE ; ordinary Locals first
_Local v3=123, v4:REAL4=123.456
_Local x$="Hello World", y$=TheString ; strings can be initialised, too
ClearLocals ; first line after the LOCALs
deb 1, "Perfect:", v1, v2, v3, v4, $x$, $TheString
ret
MyTest endp
Init ; << select Ixnit and hit F6 to test this snippet
invoke MyTest, 123, 456, Chr$("String passed")
EndOfCode
Rem - very fast, leaves all registers intact
- the order is important: "normal" locals first, then numeric _Local vars, finally strings
- for buffers over 2kB StackBuffer is a tick faster
- you can initialise DWORD, REAL and string variables with _Local (use after the "normal" locals)
Key clv
MyTest proc uses edi esi ebx arg1:DWORD, arg2:RECT
LOCAL rc:RECT, sbuf1, sbuf2, sbuf3, whatever[100]:BYTE
; optional: ClearLocals ; first line after the LOCALs
mov sbuf1, StackBuffer(100000) ; allocate two fat buffers, and make sure
mov sbuf2, StackBuffer(4000h) ; they are 16-byte aligned for use with SSE2
invoke GetFileSize, hFile, 0 ; you may use a register or any other variable to specify the buffer size
mov sbuf3, StackBuffer(eax, nz) ; option nz means "no zeroing" - much faster (the buffer end is zeroed anyway)
PrintLine "Start buffer 1:", Tb$, Hex$(sbuf1)
PrintLine "Start buffer 2:", Tb$, Hex$(sbuf2)
StackBuffer() ; release all buffers (sb without args = free the buffer)
ret
MyTest endp
Rem - buffer size is limited by start address of stack; normally, you can use close to one MB
- if you see an "unmatched block nesting" error, you forgot the StackBuffer() before the ret
- the start address is aligned to 64 bytes for use with SIMD instructions
- you can use StackBuffer anywhere (not only at proc start & end), but make sure esp is unchanged
- StackBuffer zero-inits the buffer, unless option nz is specified (much faster)
- with option nz, only the end of the buffer (+/- 2 bytes, one DWORD) is zeroed
- can be combined with ClearLocals
- StackBuffer does the stack probing for you; up to about half a megabyte, it is significantly faster than New$()/HeapAlloc
include \masm32\MasmBasic\MasmBasic.inc
SetWatch ct ; use SetWatch to find out where this variable gets changed
StackWalk profile, trace ; keep track of calls (e.g. before a crash)
.data?
ct dd ?
.code
LevelTwo proc arg1, arg2
invoke Sleep, 1
ret
LevelTwo endp
MainLevel proc arg1, arg2, arg3
PrintLine "+"
For_ ct=0 To 999
invoke LevelTwo, ct, 12345678h
Next
ret
MainLevel endp
Init ; select Init and hit F6
Cls
For_ ecx=0 To 2
invoke MainLevel, ecx, 222h, 333h
Next
StackWalk show
PrintLine "bye"
EndOfCode
Rem - allows bug chasing and profiling at the same time
- after each PROLOGUE:NONE, use StackWalk again after endp
- in case of a crash, Olly will display the start of the .data section; right-click on the 3rd DWORD (after CiaoCiao) and
Follow DWORD in Disassembler; then press Ctrl *, F9 to trigger a StackWalk show
Print "The current drive is ", Left$(ThisExe$, Instr_(ThisExe$, "\")-1)
mov pos, Instr_(1, L$(n), "equ", 1+4) ; 1=start pos, 1=case-insensitive + 4=full word
Rem - returns relative pos in edx, absolute in eax
- if no match is found, zero is returned in edx, and eax points to the start of the 'haystack'; same if 'needle' is empty
- six syntax variants allowed:
A: has startpos: Instr_(1, "Test", "Te", 2) ; 4 args
B: no startpos: Instr_("Test", "Te", 2) ; 3 args, last one immediate = mode
C: has startpos: Instr_(1, "Test", "Te") ; 3 args, last one not immediate
D: no startpos: Instr_("Test", "Te") ; 2 args
E: test several patterns: InstrOr("Test", "Te" or "st", 1) ; 3 args (startpos is 1)
F: extra fast mode: Instr_(FAST, My$(ecx), "Hello", 0) ; 4 args, no startpos, modes 0+2 only
- case & mode (bitwise flag):
0=case-sensitive, +1=insensitive, +2=intellisense (Name=name, i.e. case of first char ignored),
+4=full word search, +8=include start of line in text block search, +4+16=not full word
Note: full word search returns failure for chars above ASCII 64 to the left or right
Example: Nostructfoundhere, this123struct456isfound, this@struct, too
- note that, for performance reasons, FAST does not preserve xmm0 and xmm1
- the FAST option is typically about twice as fast as CRT strstr, but 3..4 times as fast when used with
string arrays (Intel Core i5 timings for counting a rare word in a file with 800 MB, 6 Mio lines):
232 ms for fast Instr_
795 ms for "normal" Instr_
999 ms for Masm32 InString
929 ms for CRT strstr
- Instr_(some$(ecx), "sometext") uses internally the FAST mode
- using FAST, binary search in haystacks containing zeros is possible by assigning the buffer size to edx:
mov edx, LastFileSize ; any info on length of buffer can be used with edx
Print Str$("Pos in executable: %i", Instr_(FAST, esi, "kernel32", 2 or 64) ; 2=first char case-insensitive, 64=len in edx
- Rinstr is about 10% slower than Instr_ but about 10% faster than Masm32 InString
- wRinstr is available but only as wRinstr(src, pattern) with a one-byte pattern (e.g. "\")
Key instr(
include \masm32\MasmBasic\MasmBasic.inc
Init
Print Str$("The mode 1 index by RinstrX is %i\n", RinstrX("\somepath\somefile.DOCX", 1, .xsl,.xslx,.doc,.docx,.htm))
EndOfCode
Rem args: file.ext, mode (1=ignore case), list of extensions
Count, wCount
include \masm32\MasmBasic\MasmBasic.inc
Init
Let esi=FileRead$("\Masm32\include\Windows.inc") ; read Windows.inc into a buffer
Print Str$("Count equ=\t%i\n", Count(esi, "equ", 1)) ; Count equ (1=case-insensitive)
Print Str$("Count equ=\t%i", Count(esi, "equ", LastFileSize)) ; same but faster and case-sensitive
EndOfCode
Rem - counts occurrences of a pattern in a string
- with no args, the count is case-sensitive; otherwise, options as in Instr_/wInstr,
except for LastFileSize, which is very fast but always case-sensitive
mov ecx, LineCount(pBuffer) ; count the number of carriage returns (Ascii 13, 0Dh) in a zero-delimited buffer
mov ecx, LineCount(pBuffer, 1000) ; count CRs in the first 1000 bytes of the buffer
mov ecx, LineCount(pBuffer, 1000, lf) ; count linefeeds (Ascii 10, 0Ah)
mov ecx, LineCount(pBuffer, 1000, "a") ; count the char a
Rem - returns #lines in eax
- use instead of EM_EXLINEFROMCHAR (which returns wrapped lines)
include \masm32\MasmBasic\MasmBasic.inc
Init
Print Str$("The string 'Duplicate' is in line %i of Windows.inc", LineNumber(FileRead$("\Masm32\include\Windows.inc"), "Duplicate")+1)
EndOfCode
Rem - returns the zero-based line in which the string was found in eax, or -1
- the address of the match is in edx
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6
Let esi="index.php?PHPSESSID=74e64f1f&topic="
PrintLine "before: [", esi, "]"
PrintLine "after: [", Clean$(esi, "PHPSESSID=", "&", 0), "]" ; string, left, right match, mode
EndOfCode
Rem - "cleans" a given string depending on left and right matches
- mode as in Instr_()
- if no right match is given, Cr$ is assumed, i.e. until end of line
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6
PrintLine Strip$("7@89-#456h x123", "1234567890") ; prints 789456123
PrintLine Strip$("7@89-#456h x123", exc "@-# xh") ; 789456123 (exclude chars)
EndOfCode
Rem see Clean$ for comparison
; simple example:
Let esi='This is a link to that can be extracted'
Print "The URL for ", Extract$(esi, Chr$(34, 62), ""), " is " ; 34, 62 = ">
Inkey Extract$(esi, "http://", Chr$(34), xsIncL) ; you could use '"' (single/double/single quote) instead of Chr$(34)
; result: The URL for Google is http://www.google.com
; syntax: Extract$(pSrc, "left match" [, "right match"] [, xsFlags] [, maxlines*)] [, startIndex])
; right match: if omitted, end of line is assumed
; xsFlags: if omitted, search for left match is case-sensitive, left and right matches are excluded
; maxlines: default is 1, i.e. right match must be in same line; for extracting structures etc, put a reasonable value, e.g. 100
; startIndex: default is 1, i.e. beginning of string; if xsLoop is set, search for the left match restarts where the last right match
was found; see also the options for Instr_ - Extract$ uses Instr_ for the left match
xsCaseI ; case-insensitive search for left match (right: always case-sensitive)
xsIs ; intellisense; search is case-insensitive for 1st char only, i.e. Hello = hello
xsI1c ; ignore 1st char in left match, e.g. ?:\Masm32\...
xsFullW ; full word (left match only)
xsIncL ; include left pattern (e.g. http://)
xsIncR ; include right pattern
xsExcL ; exclude left pattern, e.g. {url= ... }
xsExcR ; exclude right pattern
xsTrimL ; trim left side, i.e. strip everything <=ASCII 32 (spaces, tabs, CrLf$...)
xsTrimR ; trim right side (after excluding right match if xsExcL is set)
xsTrim=xsTrimL or xsTrimR ; trim both sides
xsTrim39 ; trim everything <=ASCII 39 aka single quote
xsLineL ; include line of the left match
xsLineR ; include rest of line after the right match (must include right match...)
xsRinstrL ; search first match from the right, exclude left & right patterns
xsScan ; scan line sequentially for e.g. spaces; left match must equal right match
xsLoop ; let Instr_ start behind the last position, for use in loops
xsWildcard ; allow a wildcard for left pattern e.g. for finding
*) if the right pattern contains a linefeed (LF, Ascii 10), the maxlines counter will never stop the pattern search
----------------------------------------------------------
The last flag, xsLoop, is used in the following demo, a Windows console application that extracts
all structures from the two main Masm32 include files. Do not use the result for work, as there are
problems with nested structures (e.g. unions ending with ends) and some structures ending with
lowercase ends.
include \masm32\MasmBasic\MasmBasic.inc ; download
Init
; First, let's get a useful source string:
Let ecx=FileRead$("\Masm32\include\Windows.inc")+FileRead$("\Masm32\include\WinExtra.inc")
Open "O", #1, "Structures.txt" ; open a file for output
xor ebx, ebx ; reset counter
.While 1
inc ebx
PrintLine #1, Extract$(ecx, "STRUCT", 'ENDS', xsFullW or xsLineL or xsIncR or xsLoop, 100)
.Endw
Close #1 ; file #1 closed
Inkey Str$("%i structures found\n", ebx)
EndOfCode
Rem - returns pointer in eax, and len of result in edx
- Let esi=Extract$(CL$(), "\", ".", xsRinstrL)+".inc" ; if arg is path\somefile.txt, esi will be somefile.inc
- 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
.endif
test eax, StringsDiffer("Hallo", "HALLO", 1) ; 1=case-insensitive
.if Zero?
MsgBox 0, "The two strings are equal", "Hi", MB_OK
.endif
Rem - returns byte difference as DWORD in eax, and relative position in edx
- trashes xmm0...xmm2
- you may get a "please split" error message, e.g. with
.if StringsDiffer(Str$(esi), Str$(edi))
The reason for the error is that Str$() uses eax as return value - in both
cases, therefore they would always seem "equal" (eax==eax). To avoid this,
split the line by using a global var or a free register (not edx or eax) as follows:
mov ecx, Str$(esi)
.if StringsDiffer(ecx, Str$(edi))
.if FilesDiffer("MyFileA.txt", "MyFileB.txt")
MsgBox 0, "The two files are different", "Hi", MB_OK
.endif
Let esi="MyFileA.txt"
test eax, FilesDiffer(esi, offset MyFileB)
.if Zero?
MsgBox 0, "The two files are equal", "Hi", MB_OK
.else
MsgBox 0, "The two files are different", "Hi", MB_OK
.endif
Rem - returns DWORD in eax
- you may get a "please split" error message, e.g. with
.if FilesDiffer(Files$(esi), Files$(edi))
The reason for the error is that Files$() uses eax as return value - in both
cases, therefore they would always seem "equal" (eax==eax). To avoid this,
split the line by using a global var or a free register (not edx or eax) as follows:
mov ecx, Files$(esi)
.if FilesDiffer(ecx, Files$(edi))
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals r$="Введите текст здесь" ; "Enter text here" in Russian
SetGlobals c$="在這裡輸入文字" ; "Enter text here" in Chinese
Init ; select and hit F6
PrintLine "[", r$, "] (original string)"
PrintLine "[", uRight$(r$, 5), "_", uMid$(r$, 9, 5)), "_", uLeft$(r$, 7), "] (right_mid_left, fixed)"
PrintLine "[", uRight$(r$, 5), "_", Mid$(r$, Instr_(r$, "текст"), 2*5)), "_", uLeft$(r$, 7), "] (right_mid_left, Instr)"
wMsgBox 0, wRec$("["+uLeft$(c$, 5)+"]"), "Chinese, uLeft$(5):", MB_OK
wMsgBox 0, wRec$("["+uRight$(c$, 3)+"]"), "Chinese, uRight$(3):", MB_OK
EndOfCode
Rem - use uLeft$(src, chars) if you know the #UTF-8 chars needed
- use "normal" Left$() etc if you got the #chars from Instr_(); but note the need to calculate bytes, see 2*5 above
Let My$(123)=Left$("A little test", 8)
Let Left$(My$(123), 5)="A small " ; valid also for Mid$, Right$
Print Left$("test", 20, ".") ; optional: use a padding char if the string is shorter
mov eax, z$(Left$("Test", 3))
Rem - for use with Let and Print; if you need the mov version, use z$()
- with UTF-8 encoded strings, either get the index via Instr_(..), uLen, etc, or use uLeft$()
- with tab-delimited strings, you can also obtain cells from columns 0...n (Left$) or n ... end (Mid$) using @n:
For_ ecx=0 To L$(?)-1
Print Str$("\n%i [", ecx), Left$(L$(ecx), @2), "] [", Mid$(L$(ecx), @5), "]"
Next
Key left$(
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$(
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$(
Print Lower$("This is a test"), CrLf$, Upper$("This is a test")
mov esi, z$(Upper$(eax))
Rem - returns pointer to converted copy of string in eax
- for use with Let and Print; if you need the mov version, use z$()
- example for Unicode message boxes:
wLet esi="Hello, What Is The Purpose Of This String?"
wMsgBox 0, wCat$(wLower$(wLeft$(esi, 5))), "hello", MB_OK
wMsgBox 0, wCat$(wUpper$(wMid$(esi, 8, 4))), "WHAT", MB_OK
wMsgBox 0, wCat$(wLower$(wRight$(esi, 7))), "string?", MB_OK
- attention Left$(Lower$(...)) will throw a "not allowed" error; use Lower$(Left$(...))
- see also SetCpUpperLower$ for dealing with codepage problems
Key lower$(, upper$(
Print CrLf$, " | Trim a string |",13, 10
Print "LTRIM$: |", Ltrim$(" Trim a string "), "|",CrLf$
Print "RTRIM$: |", Rtrim$(" Trim a string "), "|",CrLf$
Print "TRIM$: |", Trim$(" Trim a string "), "|",CrLf$, CrLf$
Let My$=Trim$(" Test ")
Let My$=Trim$(Chr$(9, " Test ", 13, 10))
Let My$=Qtrim$(Chr$(9, ' "quoted" ', 13, 10)) ; removes "double quotes"
Let My$=Qtrim$(CL$()) ; same for complete quoted commandline
Let My$=Ntrim$("$+abc123+'") ; strip chars below the number "0": !"#$%&'()*+,-./
Rem - for use with Let and Print
- removes trailing spaces, tabs and other characters below or equal Ascii 32 (Q:34, N:47)
- old Gfa Ztrim$ syntax possible but has no effect
Key trim$(
cmp eax, Mirror$("Masm") ; easier than cmp eax, "msaM"
PrintLine Mirror$("Masm32 is great") ; uses an entry in the .data section
mov edx, Mirror$("Test")
Let esi=Mirror$("Masm32 is great")
PrintLine esi ; shows taerg si 23msaM
Let esi=Mirror$(esi)
PrintLine esi ; back to Masm32 is great
Rem - with immediate args, Mirror$ returns an immediate string, and does not by itself create any .data entry
- Mirror$(esi) returns a pointer to a temporary buffer that can be used with Let and Print
Print "Today is the ", Date$
Print "This file was created on ", CrtDate$
Rem - by default, returns in eax a string in format dd.MM.yyyy, e.g. 31.12.2013
- in Print or Let lines that use e.g. Str$(), Date$ may misbehave; use fDate$(0) instead
- except for CrtDate$, you can change this format e.g. with
MbDateDmy equ "MMMM dd, yyyy" to get August 02, 2013 (see MSDN GetDateFormat for details)
- with MbDateDmy equ esi (or any other reg32) you can programmatically specify a different format
Print "Now it is ", Time$
Rem - by default, returns in eax a string in format HH:mm:ss, e.g. 13:45:56
- in Print or Let lines that use e.g. Str$(), Time$ may misbehave; use fTime$(0) instead
- except for CrtTime$, you can change this format e.g. with
MbTimeHms equ "hh:mm tt" to get e.g. 03:45 PM (see MSDN GetTimeFormat for details)
- with MbTimeHms equ esi (or any other reg32) you can programmatically specify a different format
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6 to test this example
Print "Yesterday, ", fDate$(-1, "dddd dd MMMM yyyy "), fTime$(0, "HH:mm"), Str$(", we were in ISO week %i\n", IsoWeek(-1))
Print "Today, ", fDate$(0, "dddd dd MMMM yyyy "), fTime$(0, "HH:mm"), Str$(", we are in ISO week %i\n", IsoWeek())
Let esi="11.12.2017"
Print esi, Str$(" will be in week %i", IsoWeek(esi))
EndOfCode
Rem returns ISO 8601 week; you can specify a negative or positive offset, e.g. IsoWeek(-1) for yesterday's week
include \masm32\MasmBasic\MasmBasic.inc
Init ; make sure res #91 and txt files are present, then select and hit F6
Cls 7 ; instead of clearing everything, insert 7 lines
.if Json$(file:"JsonMenu.txt") ; JsonMenu.txt
.While 1
.Break .if !Json$(Obj:"menu") ; search only inside the {menu object}
PrintLine "menu ", Json$("value")
.While 1
.Break .if !Json$("value")
Print "menu item ", eax ; strings are returned in eax
PrintLine At(20) Json$("onclick")
.Endw
Print
Json$(ObjEnd) ; search full text
.Endw
.endif
.if Json$(file:91) ; RCDATA resource #91; has numbers in ppu variable
.While 1
.Break .if Json$("ID")==0
Print "Price per unit of ", Json$("name")
Print At(32) Str$(": %2f€\n", Json$(num:"ppu")v) ; v means pop st(0) after printing
.Endw
.endif
.if Json$(file:"JsonHolidaysIT.txt", "https://jj2007.eu/HolidaysIT.txt")
.While 1
.Break .if Json$("date")==0
Print eax, " is "
wPrint wRec$(Json$("localName"))
PrintLine At(36) " ", Json$("name")
.Endw
.endif
Inkey cfm$("\nJson is fun")
EndOfCode
PrintLine "Time zone is UTC", GetTZ$() ; Time zone is UTC+1
PrintLine "Time zone solar = ", Utf8$(GetTZ$(StandardName)) ; StandardName as Unicode string
PrintLine "Time zone legal = ", Utf8$(GetTZ$(DaylightName))
Rem uses GetTimeZoneInformation; values for other TIME_ZONE_INFORMATION members are returned in xmm0
; formatted date and time; takes a pointer to a SYSTEMTIME structure and an optional format string
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to test this code
PrintLine "Tomorrow is the ", fDate$(1, "dd MMM yy") ; formatted date
PrintLine "In ten minutes, i.e. at ", fTime$(10, "HH:mm"), ", I will go to bed" ; formatted time
Print "Today, ", fDate$(0, "dddd dd MMMM yyyy "), fTime$(0, "HH:mm"), Str$(", we are in ISO week %i\n", IsoWeek())
Print "HKCU\Software\...\Explorer entries changed last week:"
GetRegKeyArray "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer", Explorer$(), LastMod()
For_ ecx=0 To eax-1
.if Age(LastMod(ecx), h)<=7*24
Print Str$("\nKey %i\t", ecx), fDate$(LastMod(ecx), "dd MMMM yyyy"), ", ", fTime$(LastMod(ecx))
Print Tb$, Explorer$(ecx)
.endif
Next
Print fDate$(0, "dddd dd MMMM yyyy "), fTime$(0, "HH:mm:ss.fff") ; long format including milliseconds
Print CrLf$, fDate$(0, "dddd dd MMMM yyyy ", russian), fTime$(0), Str$(", мы находимся в ИСО неделе %i", IsoWeek())
wMsgBox 0, wCat$(wfDate$(0, "dddd dd MMMM yyyy ", hindi)+wfTime$(0)), "This is Unicode:", MB_OK
EndOfCode
; if the registry value is of type REG_QWORD, it might be a FILETIME; you can display it as follows:
void GetRegVal("HKLM\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0", "LastInstallTime", 0)
; if this key and its value exist, then the REG_QWORD (a FILETIME) will be returned in xmm0:
PrintLine "LastInstallTime: [", fDate$(xmm0, "dd MMMM yyyy"), ", ", fTime$(xmm0), "]"
wPrint "LastInstallTime: [", wfDate$(xmm0, "dd MMMM yyyy"), ", ", wfTime$(xmm0), "]"
Rem - for use with SYSTEMTIME structures or arrays, e.g. those filled by the second arg of GetRegKeyArray()
- default is user locale, but you can force a language as shown above with russian and hindi
- see also TimeSF
- immediates -128 to +127 are interpreted as offsets: days for fDate$(dx), minutes for fTime$(dx); fTime$(s:dx) uses seconds
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6 to test this example
void TimeSF("01.02.2013 12:34:56") ; returns FILETIME value in xmm0 for further use
Print Str$("%i days since 1st of January\n", Age(TimeSF("01.01."), d)) ; Age accepts a FILETIME in xmm0; year, hour, minutes as of 'right now'
PrintLine fDate$(TimeSF("11.11.2011"), "dd MMMM yy"), ", ", fTime$(TimeSF("12:34:56.789"), "HH:mm:ss:fff") ; 11 November 11, 12:34:56:789
PrintLine "date+time, day.month.year format: ", fDate$(TimeSF("31.12.2015, 12:34:56")), ", ", fTime$(xmm0)
PrintLine "date+time, year-month-day format: ", fDate$(TimeSF("2015-12-31 12:34:56", ymd)), ", ", fTime$(xmm0)
Print Str$("One minute ago means %i seconds ago\n", TimeDiff(s, now:TimeSF(fTime$(-1, "HH:mm:ss.fff"))))
Print Str$("One day ago means %i minutes ago\n", TimeDiff(m, now:TimeSF(Cat$(fDate$(-1)+", "+fTime$(0, "HH:mm:ss.fff")))))
EndOfCode
Rem returns FILETIME in xmm0 and pointer to SYSTEMTIME structure in eax
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6 to test this example
movaps xmm4, TimeSF("11:22:33")
psubq TimeSF("11:22:53"), xmm4
Print Str$("The two dates are %i seconds apart\n", TimeDiff(s))
movaps xmm1, TimeSF("01.08.20")
psubq TimeSF("02.08.20"), xmm1
Print Str$("A day is %i seconds long", TimeDiff(s)), Str$(", that is %i hours\n", TimeDiff(h))
movaps xmm1, TimeSF("01.02.")
psubq TimeSF("01.03."), xmm1
Print "In ", fDate$(0, "yyyy"), Str$(" February had %i days\n", TimeDiff(d))
Print Str$("It's %i days until Christmas\n", -TimeDiff(d, now:TimeSF("24.12."))) ; minus...
Print Str$("The year is %i days old\n", TimeDiff(d, "01.01."))
EndOfCode
Rem - returns in edx::eax days, hours, minutes, seconds or milliseconds (ms)
- xmm0 must hold the difference using e.g. TimeSF()
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
NanoTimer()
Delay 15
Print "The Delay 15 took ", NanoTimer$() ; returns e.g. 15 ms, i.e. a string with a unit adapted to the result
Rem returns DWORD in eax; uses QPC, the effective resolution is about 0.3 microseconds
CyCtInit, CyCtStart , CyCtEnd cycle count macros (use with PrintCpu)
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to run this snippet
CyCtInit
CyCtStart
fldpi
fmul FP8(100.0)
fdiv FP4(10.0)
fstp st
CyCtEnd PI*100/10 ; describe what the code does
EndOfCode
Rem - output is e.g. 17 Cycles for PI*100/10
- without description, 1,000 individual cycle counts are accessible through the CyCt() DWORD array
- using CyInit store, the counts are saved to the CyCt$() array
include \masm32\MasmBasic\MasmBasic.inc
Init
Inkey "The window associated with ID 1332 has the title ", WinFromID$(1332)
EndOfCode
Rem - get an ID from your TaskManager, and find out which window belongs to it
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to run this snippet
nops 123
Align64 ; align 64 is not available in MASM or JWasm
mov eax, $ ; get current location
Print Hex$(eax)
xor ecx, ecx
Align64
.Repeat ; loop start is aligned to 64 bytes
inc ecx
.Until ecx>100
Exit
EndOfCode
Rem may help to fit a loop into an instruction cache line; use e.g. AlignX 32 for other alignments
Recall "\masm32\include\winextra.inc", MyRec$() ; translate file input to an array of strings
mov lc, Min(eax, 20)
Print Str$("%i lines found", lc)
For_ n=0 To lc-1
Print Str$("\nRec %i\t", n)
Print Left$(MyRec$(n), 50)
Next
Recall 123, some$() ; converts resource; use e.g. 123 RCDATA "names.txt" in the Rsrc section
Recall "MyFile.csv", MyRec$(), csv ; loads a spreadsheet in comma separated values format
Csv2Tab ; optional: replace commas with tabs (needed for QSort by column)
Recall "MyFile.txt", MyRec$(), tab ; loads a spreadsheet in tab-delimited format
Recall "MyRects.dat", myrc() As RECT ; load all previously stored RECT values to the myrc() array
Rem - returns lines in eax (null if an error occurred), and the number of total bytes read in edx
- autodetects Windows (CrLf), Linux (Lf) and Mac (Cr) text files
- see Store below for saving arrays and Eof() for checking end of data
- instead of a filename, you can specify a URL (select init and hit F6 to test this example):
include \masm32\MasmBasic\MasmBasic.inc
Init ; show a data set from the World Health Organisation
Recall "https://extranet.who.int/tme/generateCSV.asp?ds=mdr_estimates", who$(), csv
Print Str$("%i records downloaded", eax)
For_ ct=0 To who$(?)-1
Print CrLf$, who$(ct, 0)
Print At(33, Locate(y)) Spc2$, who$(ct, 4), Space$(40)
For_ ecx=8 To 14 ; columns 8...14 contain the data
Print At(ecx*8-25, Locate(y)) Spc2$, who$(ct, ecx)
Next
Next
EndOfCode
- note the tab and csv versions do not allow direct string concatenation with Print or Let:
PrintLine "fails: A2=", MyCellArray$(1,0), Tb$, "A3=", MyCellArray$(2, 0) ; fails miserably showing twice the same cell
Print "works: A2=", MyCellArray$(1,0), Tb$, "A3=" ; workaround: separate the two ...
PrintLine MyCellArray$(2, 0) ; ... cells by printing over two lines
- there is no wRecall, but if Unicode is detected, Recall converts strings to UTF8; use Print to display them
- with the spreadsheet variants, single cells can be accessed via Let My$=MyRec$(row, column);
for usage, see the spreadsheet demo
- instead of a filename, you can pass the ID of a RC_DATA resource file
Key Rec
Store "MyWin1.inc", MyRec$() ; store the whole array excluding trailing empty strings
Store "MyWin1.inc", MyRec$(), 1000 ; store the first 1000 strings of the array
StoreUtf8 "MyWin1.inc", MyRec$() ; StoreUtf8 prepends a UTF-8 BOM
Open "O", #1, MyFileName$
Store #1, MyWc ; store a WNDCLASSEX from the .data? or .data segment
Store #1, MyRc() ; store an array of RECT structures (->Dim)
Store #1, MyRec$(), 20 ; store the first 20 strings
Store #1, MyOtherRec$() ; add the complete array not including trailing empty strings
Close #1
Rem - will trigger runtime error if file cannot be opened
- if a file name is being used, the file will be closed after writing the strings
- for #n, the file remains open for writing, allowing e.g. to write several arrays to the same file
Key sto
include \masm32\MasmBasic\MasmBasic.inc
.code
PrintStringArray proc arrID, rows ; you can pass it any string array
xor ecx, ecx
.Repeat
Print Str$("\nRow #%i\t", ecx), _Passed$(arrID, ecx)
inc ecx
.Until ecx>=rows
ret
PrintStringArray endp
Init ; select and hit F6 to test this example
Recall "\Masm32\include\Windows.inc", wi$()
Recall "\Masm32\include\WinExtra.inc", wx$()
Print "First 10 lines of Windows.inc:"
invoke PrintStringArray, wi$(), 10
Print CrLf$, "First 10 lines of WinExtra.inc:"
invoke PrintStringArray, wx$(), 10
EndOfCode
Rem - _Passed$(id, args) allows most of the operations of normal string arrays
- first argument is the ID of the array, as passed on the stack; other arguments as usual
QSort, QSortDesc string array sort
Recall "\Masm32\include\Windows.inc", L$() ; load wininc into a string array
mov ecx, eax ; save the linecount
shr eax, 2 ; just for fun, we sort only ...
QSort L$(), eax ; ... one quarter of the strings
Open "O", #1, "SortedAscending.txt"
xor esi, esi
For_ n=0 To ecx-1
.if Len(L$(n)) && esi<5000 ; we skip the empty strings, and write the first 5000
Print #1, L$(n), CrLf$
inc esi
.endif
Next
Print Str$("%i lines written to SortedAscending.txt\n", esi)
QSortDesc L$(), 0 ; now sort descending, all
Store "SortedDescending.txt", L$() ; and write them back to file
; spreadsheet mode - you can sort tab-delimited files by column:
Recall "MyDataBase.tab", db$(), tab ; tab = tell Recall it's a tab-delimited file, Excel style
xchg eax, ecx ; keep #rows read in ecx
QSort db$(), 0, 3 [, 0] ; sort db$(), all rows, by column 3; [optional 0: do not skip the header row]
QSort db$(), 0, 2003h ; same but 2000h added: sort using Val() of column 3
QSort db$(), 20:30, 5 ; starting with row 20, sort 30 rows, by column 3
QSort db$(), 50:, 4 ; sort all rows after row 50 by column 4
Rem - returns #elements sorted
- for sorting the Files$() array, see SortFiles
- for testing, use Scramble My$() to randomise the string array
- by default, comparisons are case-insensitive; to make sorts case-sensitive, use
QSortMode casemode, skipmode
where casemode can be 0 (=case-sensitive), 1 or cis, and skipmode can be 0 (=don't skip), 1 or sls. Examples:
QSortMode 0, sls ; case-sensitive, skip leading spaces
QSortMode cis, 0 ; case-insensitive, don't skip leading spaces
- QSortMode may take, as a third parameter, a DWORD array:
QSortMode casemode, skipmode, MyIndex()
On return, this array contains the original index of the sorted strings. Note: when using Insert or Delete on the
string array, do the same for the index array, otherwise a new sort will recreate it.
- when sorting by column, the header row is skipped by default; use 0 as 3rd arg after the column to override this
- when sorting by column in case-insensitive mode, QSort is a stable sort (under the hood, it's a MergeSort)
- csv files can be sorted by column only if you use Csv2Tab directly below Recall .., csv
- sorting by column and skip leading spaces (sls) mode cannot be combined
- case-insensitive sorting depends on a 256 byte buffer; ExternDef qsCaseTable:BYTE gets filled using CharLowerBuff
Key qst
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6 to test this example
ArraySet MyDw() As DWORD=12, 456, 99, 123, 20, 99
BitSort MyDw()
For_ ecx=0 To eax-1
Print Str$(ecx), Str$("\t%i\n", MyDw(ecx))
Next
EndOfCode
Rem - returns #of sorted elements in eax
- eliminates duplicates
- DWORD only
- blazing fast for positive, not too high numbers
ArraySort
mov ebx, 1000 ; we want 1000+1 elements (arrays are zero-based, 0...1000=1001)
Dim MyR4(ebx) As REAL4 ; numerical arrays only; for strings, use QSort
Dim MyDW(ebx) As DWORD
Dim MyR8(ebx) As REAL8
Dim MyQW(ebx) As QWORD
Dim KeyArr(ebx) As DWORD
.Repeat
Rand(-888.888, 999.999) ; fill the Real4 array with random numbers between -888 and +999
fstp MyR4(ebx)
mov MyDW(ebx), Rand(1000) ; same for the dword array, numbers between 0 and 1000
mov KeyArr(ebx), ebx ; we may need to know the original position before sorting
dec ebx
.Until Sign?
lea edi, MyDW(0) ; get the start address of the dword array
; ArraySort edi ; illegal - arrays are not zero terminated, so we must know the count
ArraySort edi:Elements ; edi is pointer to a dword array; Elements is # of dwords
lea edi, MyR4(0) ; get the start address of the Real4 array
ArraySort REAL4 ptr edi:Elements ; same but with single floats array (and we must declare them)
ArraySort MyR4() ; sort a MasmBasic Real4 array ascending
ArraySort MyR4(+) ; same, the + is optional
ArraySort MyR4(+:123) ; same but first 123 elements only
mov ecx, 123
ArraySort MyR4(+:ecx) ; same but using a register (or any other dword variable)
ArraySort MyR4(-) ; sort a MasmBasic Real4 array descending
ArraySort MyR4(-:123) ; same but first 123 elements only
lea esi, KeyArr(0) ; load start address of an array containing keys (e.g. original position)
ArraySort MyR4(-), esi ; sort Real4 array descending, keep key values with Real4 values
ArraySort MyR4(+), KeyArr(), fill ; use key array directly, fill with original unsorted order (0, 1, 2, ... n)
Rem - returns #of sorted elements in eax
- use for signed DWORD, signed QWORD, REAL4 and REAL8 arrays; for strings, see QSort
- ArraySort sorts ascending if no - is found in the first argument. In case you need to determine the order based on
a runtime parameter, you need to use the invoke syntax as follows:
MbArrSort PROTO :DWORD, :DWORD, :DWORD, :DWORD
invoke MbArrSort, ptr to first element, #elements, ptr to key array, mode
with mode=size (4, 8) or (32 and real) or (64 and ascending) or (1 and MinMaxOnly) or (2 and fill the key)
- uses a very fast algo inspired by Marwin's site, often faster than QuickSort (and much faster than the crt qsort)
- Real4 and Dword use the same algo; the only difference is that the Real4 variant applies an extra
pass to invert the order of negative elements. Speedwise there is no measurable difference
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals REAL4 min4, max4, DWORD min32, max32, REAL8 min8, max8
Init ; select and hit F6 to test this example
ArraySet My4() As REAL4=1.1, 2.2, 3.3, 4.4
ArrayMinMax My4(), min4, max4
Print Str$("Min=%f", min4), Str$(", max=%f\n", max4)
ArraySet My8() As REAL8=5.5, 6.6, 7.7, 8.8
ArrayMinMax My8(), min8, max8
Print Str$("Min=%f", min8), Str$(", max=%f\n", max8)
ArraySet My32() As SDWORD=4, 1, 2, -9, 4, 9, 2
ArrayMinMax My32(), min32, max32
Print Str$("Min=%i", min32), Str$(", max=%i\n", max32)
; ArrayMinMax MyR4(XY) ; uppercase XY: the array consists of x,y coordinates
EndOfCode
Rem - if destination variables are not specified:
- minimum in eax, maximum in edx for DWORD arrays and REAL4 arrays
- 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
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6 to test this example
Dim MyArray() As REAL4 ; can be DWORD, REAL4 or REAL8
For_ ecx=0 To 99
Rand(-888.8, 999.9, MyArray(ecx)) ; put random numbers into the array
Next
PrintLine Str$("The elements:\t%i", MyArray(?))
PrintLine Str$("The columns:\t%i", MyArray(?cols)) ; will return 0: this array is one-dimensional
PrintLine Str$("The minimum:\t%f", MyArray(?min)v) ; the functions marked with v return the
PrintLine Str$("The maximum:\t%9f", MyArray(?max)v) ; value in ST(0); the v tells Str$() to discard
PrintLine Str$("The median:\t%f", MyArray(?median)v) ; ST(0) with fstp st after printing
PrintLine Str$("The mean:\t%9f", MyArray(?mean)v) ; ?average does the same as ?mean
PrintLine Str$("The sum: \t%9f", MyArray(?sum)v) ; sum of all elements
EndOfCode
Rem returns mean(s) on the FPU and total number of elements in eax
Dim rc4() As REAL4
ArrayRead rc4(), "MyReal4.dat"
ArrayRead somearray() As REAL8, "MyReal8.dat" ; no need to Dim the array before
Rem - returns #elements in eax
- if a two-dimensional array was saved using Store "MyFile.dat", nums(), then ArrayRead gets a two-dimensional array back
ArrayStore #1, rc4()
Rem writes array to disk; same as Store #1, rc4()
include \masm32\MasmBasic\MasmBasic.inc
Init ; select init and hit F6
Recall "\Masm32\include\Windows.inc", L$()
Print Str$("%i lines found in Windows.inc\n", eax)
.if ArrayFind(L$(), "Hutch")>=0 ; find one string in an array
PrintLine Trim$(L$(eax)) ; Original file 1998 hutch
.endif
xor ecx, ecx ; find all matches
.While 1
.Break .if ArrayFind(L$(), "MACRO", 1, ecx)<0 ; mode 1: case insensitive; see Instr_(...)
xchg eax, ecx
PrintLine Str$("line %i\t", ecx), L$(ecx)
inc ecx
.Endw
EndOfCode
Rem returns the index of the string, or -1 if no match was found
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals tmp8:REAL8 ; binary search demo
Init ; select init and hit F6
Dim table() As REAL8 ; create an array (could also be As DWORD)
Rand() ; generate a random seed
For_ ecx=0 To 255
Rand(-1000, 1000, table(ecx)) ; fill array with random values
Next
ArraySort table() ; sort ascending
For_ ecx=0 To 9
Rand(-500, -400, tmp8) ; check a narrow range for tmp8
Print Str$("position=%i\n", ArrayIndex(table(), tmp8)) ; should be around 70 +/- 10
Next
EndOfCode
Rem - returns the position of a value in a sorted array of REAL8 or DWORD numbers
- for use with MasmBasic arrays; with pointers to other arrays, use ArrayIndex(pArray, pattern, lengthoftable))
- binary search, over twice as fast as repnz scasd; see also ArraySearch
- if an exact match is found, dl will be zero; if not
; mov eax, ArraySearch(pSrc, sizeof Src, pattern [, size]) ; src, len(src), pattern, byte/word/dword
Print Str$("Len %i\n", ArraySearch(esi, sizeof Src, 0, BYTE)) ; look for a nullbyte
mov eax, dword ptr bins ; where bins is db "al", 0, 0
Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, eax, WORD)) ; word given in eax
Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, 6c61h, WORD)) ; immediate word
Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, "al", WORD)) ; same but as string
Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, MyPattern, word)) ; taken from mem, i.e. "al" is MyPattern dd "la"
Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, "algo", DWORD)) ; dword as string
Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, 'algo')) ; same, dword assumed
Print Str$("Pos %i\n", ArraySearch(esi, sizeof Src, "olgo", DWORD)) ; this one won't be found
Rem - returns offset into first occurrence of pattern: if src is "This is my algo", then
pattern algo as dword will not be found (eax=-1), but for "This was my algo", pos 12 would be returned
pattern my as word will be found at pos 8
pattern i as byte will be found at pos 2
- ArraySearch uses SSE2 and is fast: it searches a 400 byte string more than four times
as fast as repne scasd and 5 times as fast as BinSearch
- by specifying zero as a byte pattern, you can use ArraySearch as a Len() substitute;
however, MasmBasic Len() is twice as fast
- do not confuse with Masm32 BinSearch, which finds patterns at "odd" positions, too, i.e.
in the example above, invoke BinSearch, 0, chr$("This is my algo"), 16, chr$("al"), 3 would
return a valid position 11
GuiParas equ "Map viewer" ; double-click on GuiParas and hit F6 to build this example
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl map, "canvas" ; define a canvas control
GuiControl Sbar, "statusbar" ; status bar
; CanvasMap(0, 0) ; canvas control 0 gets initially map 0 (default)
SetFolder "\Masm32\MasmBasic\Res" ; you may use SetFolder to set the map's folder
ArrayLoadMap 0, "Europe"
MapColours(0, "abcdefghiabcdefghiabcdefghiabcdefghiabcdefghiabcdefghi") ; set colours to map 0 (a=red, i=green)
MapColours(0, 25, 20) ; give blue to Spain
Event Message ; use MapRegion/MapRegion$ in a WM_MAPCLICKED handler
If_ uMsg_==WM_MAPCLICKED && MapRegion>=0 Then SetWin$ hSbar=Str$("You clicked on region %i, ", MapRegion)+MapRegion$
Event CanvasPaint
ArrayPlot RgbCol(0, 240, 255) ; init and set background colour
PaintMap RgbCol(127, 127, 127), lines=2 ; map with grey borders 2px thick
ArrayPlot exit, "Europe"
GuiEnd
Rem - requires a map in *.map/*.dmi format; the Europe map is included in MasmBasic (PM the author for details)
- to see the numbers of individual regions, use this loop:
lea edi, Maps(0, numPoly)
For_ ecx=0 To MapName$(?)-1
PrintLine Str$("Region %i\t", ecx), MapName$(ecx)
Next
- use ArrayMapRegion in a WM_SETCURSOR handler to check if the mouse is hovering over a particular state or region
- colours can be set for all regions, as shown above with the "abc" string, or for individual regions
Event Key
Switch_ VKey
Case_ VK_I: CanvasZoom MyImg ; type "i" to zoom the image, "m" to zoom the map
Case_ VK_M
CanvasZoom MyMap, MyList, MyEdit ; zoom the map, hide two non-canvas controls
Endsw_
Rem - if more than one GuiControl
- calling it repeatedly toggles the effect
- if non-canvas controls are present, add their IDs as shown above
GuiParas equ "Plot sinus & cosinus curves", x600, y100, w600, h500, bRgbCol(192, 255, 255)
include \masm32\MasmBasic\Res\MbGui.asm ; select the white zone and hit F6
Dim MySinus() As REAL4 ; REAL4 precision is enough ...
Dim MyCosinus() As REAL8 ; ... but REAL8 is ok, too
For_ ecx=0 To 360
SetFloat MySinus(ecx)=Sinus(ecx) ; fill an array
SetFloat MyCosinus(ecx)=Cosinus(ecx) ; you may use ArrayRead for reading data from a file
Next
MakeFont hTextFont, Height:16, Weight:FW_SEMIBOLD
MakeFont hHorzFont, Height:18, Italic:TRUE
MakeFont hVertFont, Escapement:900, Italic:FALSE
MakePen hPenAxis, RgbCol(255, 0, 0)
MakePen hPenGrid, RgbCol(255, 255, 222), width 2
SetAxisX "degrees", s 0, d 15.0/3, xmax 300, grid 1, penx hPenAxis, peng hPenGrid, font hHorzFont, format "%i" ; s=start value, d=difference between gridlines
SetAxisY "sinus & cosinus", s -1, d 0.2/5, grid 2, font hVertFont, format "%2f" ; the text can be Unicode
$Data "Plotting an array of points is very easy: All you need is the array itself, plus a Paint event that draws it."
$Data "The axes can be set using SetAxisX and SetAxisY, taking arguments as shown in the source. Now resize this window to see how it behaves"
Read desc$() ; read a string array from $Data
ArraySet myLeg$()="Sinus", "Cosinus" ; define legend strings
Event Paint
ArrayPlot MySinus(), RgbCol(255, 222, 160), 2, 00000110h, 0, -1, 1 ; red sinus, 2px wide; use margins left top right bottom, min0, max-1, filled=1
Legend myLeg$(), 950:300 ; x:y; you may use ll=lower left, ur=upper right etc
ArrayPlotValues "%2f", 8, -12, 15 ; format$, dx & dy positions, step (here: 8px right, 12px up, every 15th value)
ArrayPlot MyCosinus(), RgbCol(0, 0, 255), 2 ; plot the cosinus array in blue, same margins as previous plot
ArrayPlot exit, 0150006028# "Sinus and Cosinus", fcol Red ; finish with a title; optional: xxxxyyyyFF#: x, y pos, font size
GuiTextBox 12, 100.0-80, 440, 66, Join$(desc$(), " "), bcol RgbCol(192, 255, 192), font hTextFont
GuiEnd
Rem - try resizing the window to see how the plots behave
- array elements above #100000 will not be displayed
- to combine several plots with a common range you can set an arbitrary range e.g. with
ArraySet range() As REAL4=113.05, 70.0, 180, 260.0 ; create an array with xmin, ymin, xmax, ymax values
Event Paint
ArrayPlot range(XY), 0, 0, 05052020h, setrange ; use the array to set fixed ranges; use lefttoprightbottom margins
ArrayPlot TheRealData(), RgbCol(..), ... ; plot the data as usual
- optional: use xxxxyyyyhh# text to modify title position and font, e.g. ArrayPlot exit, -120015024# "title to the left and deeper, font 24"
Key apl
mov eax, RgbCol(ebx, 0, 0) ; any mix of reg32, vars and immediates is allowed
mov bl, 127 ; only bl will be used
invoke SetTextColor, hDC, RgbCol(ebx, 0, 0) ; Red Green Blue for Gdi32 functions
invoke SetTextColor, hDC, SysCol(ebx) ; use the system palette
invoke SetTextColor, hDC, RgbCol(?) ; use the colour picker
mov hPG, rv(GdipCreatePen1, RgbCol(alpha, red, green, blue), FP4(3.0), UnitWorld, addr pPen)
Rem - Gdi functions want RGB values, i.e. three parameters
- in contrast, GdiPlus expects ARGB, i.e. four parameters; the first one, alpha, defines
transparency ranging from 0=fully transparent to 255=opaque
- predefined colours are Red, Green, Blue, GreenBlue, LiteRed, LiteGreen, LiteBlue, LiteYellow,
LiteGrey, LiteGreenBlue, DarkRed, DarkGreen, DarkBlue, DarkYellow, LiteGrey, Grey, DarkGrey
- CgaCol(index) may be used with console colours; see ConsoleColor for identifiers
- SysCol(index) returns a GetSystemPaletteEntries colour for use in Gui applications
Print Str$(ebx) ; simple
Print Str$(MyReal10) ; works for most kinds of arguments
Print Str$(ebx*MyReal8+123.0e45) ; multiply two arguments, add an immediate float
MsgBox 0, Str$("Profits increased by %3f per cent", 103.45/100-1*100), "Three digits precision:", MB_OK
Print Str$("The number PI is %Jf", PI) ; precision J = 19 digits, f=float
Print Str$("The number PI is %Je", PI) ; precision J = 19 digits, e=force exponential
Print Str$("The number 19 is %__i", 19) ; integers only: two understrokes to get one leading space with a two-digit number
Print Str$("The number 9 is %000i", 9) ; integers only: 3 zeros to get four leading zeros with a one-digit number
mov eax, Val(Str$(12*34-8)) ; slow and not very elegant, but it works
Print "Real4: ", Str$(MyR4) ; Real4 variable
Print "qWord: ", Str$(MyQword) ; print a qword; see also edx::eax in remarks
mov eax, 31416 ; you can mix xmm registers with FPU and ordinary registers and
movd xmm0, eax ; directly print the result
fldpi ; load 3.14159 onto the FPU
mov ecx, 123 ; \n is CrLf, \t is tab in Str$()
Print Str$("\nresult=\t%f", xmm0/ST(0)*ecx) ; output: [newline] Xmm0= 1230003.0
fstp st ; cleanup the FPU
fldpi ; PI again
Print Str$("\nresult=\t%f", xmm0/ST(0)v*ecx) ; same but cleanup done by the v after ST(0)
invoke TextOut, APs.apMemDC, 25, ptY, Str$("%3f", fct), s$Len ; s$Len returns chars used for Str$()
Str$(ecx, dest:edi) ; DWORD and QWORD integers only; pass start of buffer e.g. in edi, get end back in edx
Rem - returns address to buffer; default precision is 7 digits for floats, 16 for doubles, 19 for REAL10 variables and ST(0); range 1.0e+-309
- if a string is given as input, %i (integer) or %f (float) mark its insertion point, and e.g. %9f its precision
- floating point precision is 1...9A...J digits, with J meaning 19 digits. If the number starts with 9.23 or higher,
digit #19 may be incorrectly rounded up or down; use DefNum 18 if this is unacceptable; at 19 digits precision, rounding
may be incorrect, and the last digit may be one higher or lower than expected.
- Warning: Str$() takes up to 5 numeric arguments. However, they are processed
in order of appearance, i.e. Str$("Expected: 5+6*7-8*9=-25, result=%i", 5+6*7-8*9)
yields 621: 5+6=11, *7=77, -8=69, *9=621; workaround: do multiplications first.
- Str$ trashes the FPU registers ST(6) and ST(7); in case you have valuable data on the FPU and do not want to
trash it, use a FpuSave/Str$/FpuRestore sequence
- you can also print qwords moved into the edx::eax pair as follows:
mov eax, 123456789 ; some code that generates an edx::eax pair
mov ecx, eax
mov edx, 1000000000
mul edx
Print Str$(edx::eax+ecx)
- for REAL16 variables, use Quad$(MyReal16)
Key s$(
Dim MySinus() As REAL8
For_ ct=-400 To 400
SetFloat MySinus(ct+400)=Sinus(ct)
AddFloat MySinus(ct+400)=0.1
Next
fldpi
fmul FP4(100.0)
SetInt ecx ; mov ecx, 314
movups xmm0, Int128(0DDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAAh)
movlps xmm0, Int64(0BBBBBBBBAAAAAAAAh)
Rem - use SetFloat with functions that return values in ST(0), e.g. Float(); do not use fstp st afterwards
- use SetInt to convert ST(0) to a reg32, xmmreg or a DWORD variable
shrXmm
shlXmm xmm0, ecx ; shift xmm0 ecx bytes left
shrXmm xmm0, eax ; shift xmm0 eax bytes right
Rem - there is no SIMD equivalent to shl/shr reg32, cl
- these macros use pslldq and psrldq
- self-modifying code, slow and potentially unsafe
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals MyR4:REAL4, MyW:WORD=-123, MyDw:DWORD=-123, MyQ:QWORD=123456789012345678
Init ; select init and hit F6
Dim float() As REAL4
For_ ct=0 To 9
SetFloat float(ct)=Float(ct) ; converts integer ct to ST(0)
Print Str$("i=%i", ct), Str$(", f=%2f\n", float(ct))
Next
Inkey "hit any key"
EndOfCode
Rem int->float cast, returns ST(0); for use with SetFloat
include \masm32\MasmBasic\MasmBasic.inc
.data
MyQ1 dq 12345678901
MyQ2 dq 11111111111
result dq ?
Init ; select init and hit F6
MulQQ(MyQ1, MyQ2, addr result)
Print Str$("MyQ1*MyQ2\t%u \t(expected: 137174210009739369011 - overflow!!)\n", result)
Print Str$("12345*Q2\t%u \t(expected: 137166666665295, OK)\n", MulQQ(12345, MyQ2))
movlps xmm0, MyQ2
mov eax, 12345
movd xmm1, eax
Print Str$("12345*xmm0\t%u \t(expected: 137166666665295, OK)\n", MulQQ(xmm1, xmm0))
movlps xmm0, MyQ2
mov eax, 12345
Print Str$("12345*xmm0\t%u \t(expected: 137166666665295, OK)\n", MulQQ(eax, xmm0))
MulQQ(12345, addr MyQ2, edx::eax) ; if you need top speed, use the edx::eax pair as destination
Print Str$("12345*Q2\t%u \t(expected: 137166666665295, OK)\n", edx::eax) ; Str$() allows edx::eax as QWORD arg
EndOfCode
Rem - multiplies two QWORDs
- first arg can be an immediate DWORD or a 32-bit register
- use MulQQ(q1, q2, pDest) to transfer the result to a QWORD destination
- xmm regs are valid args, but xmm0 and xmm1 will be trashed after the call
- beware of overflow: the result is a QWORD, not an OWORD; the difference is irrelevant in many cases,
e.g. for random number generation, but do not expect miracles here!
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals MyR4:REAL4, MyW:WORD=-123, MyDw:DWORD=-123, MyQ:QWORD=123456789012345678
Init ; select init and hit F6
PrintLine cfm$("\nnumber\t\tFloor(number)\tCeil(number)")
For_ ecx=0 To 29
Rand(-99.9, +99.9, MyR4)
PrintLine Str$("%+4f ", MyR4), Str$(" \t%+5f", Floor(MyR4)v), Str$(" \t%+5f", Ceil(MyR4)v) ; v = fstp st
Next
Floor(123.456, MyDw) ; second arg is the destination (int or real variable)
Print Str$("dw(123.456)=%i\n", MyDw)
EndOfCode
Rem - returns a double in ST(0)
- source can be immediate or WORD ... QWORD, resp. REAL4 ... REAL10 variable
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to run this snippet
Print "x", Tb$, "sin x", Tb$, Tb$, Tb$, "error" ; ## compare results to fsin ##
FpuFill
testStep=30 ; change as needed
For_ ecx=-90 To 450 Step testStep
Print CrLf$, Str$(ecx), Tb$, Str$(Sinus(ecx)), Tb$
void Sinus(ecx, 1) ; 1=fpu fsin
fsub
Print Str$("%3f", ST(0))
fstp st
Next
PrintLine CrLf$
Print "x", Tb$, "cos x", Tb$, Tb$, Tb$, "error"
FpuFill
For_ ecx=-90 To 450 Step testStep
Print CrLf$, Str$(ecx), Tb$, Str$(Cosinus(ecx)), Tb$
void Cosinus(ecx, 1) ; 1=fpu fcos
fsub
Print Str$("%3f", ST(0))
fstp st
Next
Print Str$("\nSinus(60)=\t%5f\n", Sinus(60)v) ; the v means "pop the fpu", i.e. fstp st
Print Str$("Sinus(1.0472)=\t%5f\n", rSinus(1.0472)v) ; pass the angle as a radian, here: 60*PI/180
EndOfCode
Rem - returns REAL10 value in ST(0); store it to a variable with fistp or fstp. If you use it in Str$(), it must either
be followed by fstp st, or you use Str$(Cosinus(ecx)v)
- if your degrees are rad values like e.g. 0.5, use rSinus(0.5) or rCosinus(0.707)
include \masm32\MasmBasic\MasmBasic.inc
Init ; select Init and hit F6 to run this snippet
Print Str$("The angle returned by ArcSinus(0.866) is %i degrees\n", ArcSinus(0.5)v)
Print Str$("The angle returned by ArcCosinus(0.5) is %i degrees\n", ArcCosinus(0.5)v)
Print Str$("The angle returned by ArcTangens(9.9) is %i degrees\n", ArcTangens(9.9)v)
Print Str$("The angle returned by ArcSinus(0.866) is %4f radians\n", rArcSinus(0.5)v)
EndOfCode
Rem - returns the angle in degrees in ST(0)
- ArcSinus() etc (without argument) uses the value in ST(0)
- if you need radians, use rArcSinus() etc
FpuSave 3 ; save three FPU regs, i.e. store ST(5) ... ST(7) on the stack
... do FPU calculations - stack pointer must be the same afterwards ...
FpuRestore ; get ST(5) ... ST(7) back, correct the stack
FpuSave ; no args means save 2 regs: ST(6) and ST(7)
Print Str$("A test with eax=%i", eax) ; Str$ trashes ST(6) and ST(7)
FpuRestore ; get ST(6) and ST(7) back, correct the stack
Rem returns nothing, trashes nothing, but is e.g. for 4 saved FPU regs about 10 times faster than fsave/frstor
FpuPush 123 ; pushes 123 onto ST(0)
FpuPush FP8(123.456) ; pushes a REAL8 value onto ST(0)
MyQ dq 1234567890123456789
...
movlps xmm1, MyQ
FpuPush xmm1 ; pushes the QWORD in xmm1 into ST(0)
FpuPush MyReal16 ; requires a GCC installation
movlps xmm0, MyR8
FpuPush f:xmm0 ; pushes the DOUBLE in xmm1 into ST(0)
Inkey Str$("\nOn FPU:\t%If", ST(0))
FpuPush f:xmm1
Rem does not trash any reg32; valid args as in Str$()
FpuFill ; no args means push 1001...1008 on the FPU
FpuFill 5 ; push 1001...1005
deb 4, "FPU 5:", ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7)
Rem for testing purposes; does not trash any reg32
FpuSet MbNear64 ; set full precision, rounding near
FpuSet ; no arg = set back to previous state
FpuSet MbDown24, exall ; set low precision, rounding down, force all exceptions except precision
Rem - trashes only edx; uses fldcw & fnstcw with equates composed of rounding mode and precision:
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
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals fct:REAL10
Init ; select and hit F6
FastMath FastLog10 ; define a math function
For_ fct=0.0 To 10.0 Step 0.5 ; max 10,000 iterations
fld fct ; X
fstp REAL10 ptr [edi]
void Log10(fct) ; Y (built-in MasmBasic function)
fstp REAL10 ptr [edi+REAL10]
add edi, 2*REAL10
Next
FastMath
Print Str$("Log(5.0)=%Jf", FastLog10(5)v) ; roughly 3.3 times faster then the FPU
EndOfCode
Rem - between the two lables, X/Y pairs must be saved to edi as REAL10
- if you save the REAL10 pairs to disk, you can use e.g. the one-liner FastMath MyLog10, "log10.dat"
- the new function expects an X value and sets ST(0)
- for usedeb=1, range errors are printed to the console
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals Res8:REAL8
Init ; select and hit F6
m2m eax, 11
MbMod(123.45, eax, Res8) ; standalone with destination
Print Str$("123.45 mod 11=%3f", Res8) ; v v function v v
MsgBox 0, Str$("800 mod 60=%i", MbMod(800, 60)v), "Modulus:", MB_OK
EndOfCode
Rem - returns
Log2, LogE, Log10
include \masm32\MasmBasic\MasmBasic.inc
SetGlobals ct:REAL8
Init ; select and hit F6
For_ ct=1.5 To 10.0 Step 0.5
Print Str$(" Log2(%2f)", ct), Str$("\t%Jf ", Log2(ct)v), Str$("\t%Jf ", LogE(ct)v), Str$("\t%Jf\n", Log10(ct)v)
Next
EndOfCode
Rem these functions return the logarithms in ST(0); use the v before the closing bracket of Str$() to pop ST
; four exponential functions are available, for 10^y, 2^y, e^y and x^y
Exp10(0.5, MyDest10) ; calculates 10^0.5 and saves it to a REAL10 variable called MyDest10
Print Str$("Res=%Jf\n", MyDest10)
ExpXY(4, 0.5, MyDest8) ; calculates 4^0.5 and saves it to a REAL8 variable called MyDest8
Print Str$("Res=%Jf\n", MyDest8) ; 4^0.5 = 2
Print Str$("Exp10 \t%Jf\n", Exp10(0.5)) ; if no destination is specified as last arg, the result is returned in ST(0)...
fstp st ; ... and you should pop it when no longer needed
Print Str$("Exp2 \t%Jf\n", Exp2(0.5)) ; 2^0.5 = 1.414
fstp st
Print Str$("ExpE \t%Jf\n", ExpE(3)v) ; 2.718^3 = 20.085 (the v replaces fstp st)
Rem - returns result in ST(0) if no destination specified
- non-zero edx signals infinity error
- beware of precision problems; FpuSet MbNear64 is recommended, although the rounding mode has no
influence when 64bit precision is set
include \masm32\MasmBasic\MasmBasic.inc
.data
srcr10 REAL10 12345.6789
pcr10 REAL10 12.3
srcdd dd 123
Init
Print Str$("f4pc(srcr10, 33.33)=\t%If\n", f4Percent(srcr10, 33.33)) ; f4: returns REAL4
Print Str$("f8pc(srcr10, 33.33)=\t%If\n", f8Percent(srcr10, 33.33)) ; f8: returns REAL8 e.g. for use with xmm regs
Print Str$("fpupc(srcr10, pcr10)=\t%If\n", fpuPercent(srcr10, pcr10)) ; fpu: leaves result in ST(0)
Print Str$("fpc(12345.678, ebx)=%f\n", f8Percent(12345.678, ebx))
Print Str$("fpc(12345.6, ebx)=%f\n", f8Percent(12345.6, ebx))
Print Str$("pc(12345.0, ebx)=%f\n", Percent(12345.0, ebx)) ; with immediate real source and reg32 percentage
Print Str$("pc(srcdd, ebx)=%f\n", Percent(srcdd, ebx)) ; with DWORD source and reg32 percentage
Print Str$("pc(12345, 20)=%f\n", Percent(12345, 20)) ; with immediate integer source and percentage
movq xmm0, f8Percent(srcr10, 33.33)
Print Str$("xmm0=%f\n", f:xmm0) ; use the f: prefix to force interpretation as float
push 0AB54A98Ch ; 12345678901234567890
push 0EB1F0AD2h ; pushed as two dwords
movlps xmm1, qword ptr [esp]
add esp, QWORD
Print Str$("xmm0=%u\n", xmm1) ; use %u in the format string to force interpretation as unsigned
movd xmm0, Percent(srcr10, 33.33)
Print Str$("xmm0=%f\n", xmm0)
Inkey "ok"
Exit
end start
Rem source can be almost anything, same for percentage
DefNum 3
Print Str$("PI=%f", PI) ; print PI at 3 digits precision
Print Str$("PI=%4f", PI) ; print PI at 4 digits precision, i.e. override DefNum
DefNum -1
Print Str$("PI=%f", PI) ; print PI at max digits precision (i.e. 18)
DefNum 19
Print Str$("PI=%f", PI) ; tickle out one more digit (same as Str$("PI=%Jf", PI), may yield rubbish)
Rem sets default precision; override with Str$("%nf", number), where n=12...ABCDEFGHIJ as shown above
Print Hex$(eax) ; reg32, 12345678
Print Hex$(cx) ; reg16, 1234
Print Hex$(dl) ; reg8, 12 - same for dh, ah etc
Print Hex$(123) ; immediate
Print Hex$(MyDword) ; global and local variables
Print Hex$(MyWord)
Print Hex$(MyByte)
Print Hex$(MyQword) ; QWORD will be displayed with one space as 12345678 90123456
Print Hex$(xmm1) ; if QWORD is not enough, get e.g. 11AA22BB 33CC44DD 55AA66BB 77CC88DD
Print Hex$(MyReal8) ; same as QWORD
Print Hex$(MyR4(ecx)) ; numerical arrays can be used, too
Let H$="Hex="+Hex$(1a2b3c4dh)+"h" ; in case you need the trailing h or a leading 0x, use Let or Cat$()
PrintLine "FastHex =", Hex$(eax, fast) ; dwords only
movlps somebuffer, Hex$(123h, xmm0) ; dwords only - 8 bytes are returned in xmm0
Hex$(0BAADF00Dh, edi) ; write to memory
Hex$(12345678h, somebuffer) ; write to memory
Rem - returns DWORD in edx
- MbHexSpaces=0: no spaces between DWORDs
- MbHexQ=1: limit xmm0 output to the two lower DWORDs
Print HexDump$(pBuffer) ; prints 512 bytes in format 00000010 ab cd ef ... text
Print cfm$("\nGetModuleHandle(0):\n"), HexDump$(rv(GetModuleHandle, 0), 90h)
Output:
GetModuleHandle(0):
00400000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ.........ÿÿ..
00400010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ¸.......@.......
00400020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00400030 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ............€...
00400040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ..º..´.Í!¸.LÍ!Th
00400050 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno
00400060 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS
00400070 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode....$.......
00400080 50 45 00 00 4C 01 03 00 FB BB BF 59 00 00 00 00 PE..L...û»¿Y....
Let edi=HexDump$(esi, eax, 0) ; assigns eax bytes to edi with offset 0
Let edi=HexDump$(esi, 128, db) ; uses db 0ABh, 0CDh for use in code
Let edi=HexDump$(esi, 4096, notext) ; assigns 4kB, no ascii textreout
Let edi=HexDump$(esi, file, dq) ; esi holds filename, translate whole file, use dq for code
Rem returns ptr in eax; maxlen 40,000 bytes
Print Bin$(123)
Let esi=Bin$(-1)+"y"
MsgBox 0, Bin$(ebx, f), "The Bin$", MB_OK
Rem returns DWORD in edx - use only once in Let
option f adds a formatted bit counter
include \masm32\MasmBasic\MasmBasic.inc
Init ; select and hit F6 to test this example
PrintLine "8575", Tb$, IntAsWords$(8575) ; eight thousand five hundred and seventy-five
mov eax, -1234
PrintLine "-1234", Tb$, IntAsWords$(eax) ; minus one thousand two hundred and thirty-four
EndOfCode
Rem range is -10999...+10999
.data ; for testing
qSmall qWORD 7700000000000001h
qBig qWORD 7700000000000003h
oSmall OWORD 77000000000000000000000000000001h
oBig OWORD 77000000000000000000000000000003h ; OWORD for JWasm and higher ML.exe versions
; oBig qWORD 00000000000000003h, 7700000000000000h ; 2 QWORDS for ML 6.15
.code
Qcmp qBig, qSmall ; compare two global variables
mov ecx, offset oBig ; use a pointer (not edx, please) ...
Ocmp ecx, oSmall ; ... for one (or both) of them
oqDeb=1 ; if this flag is set, Qcmp or Ocmp will print e.g.
"ecx GREATER xmm0" or "MyOWORD LESSER xmm2" to console
movups xmm0, OWORD PTR oSmall ; ML 6.15 accepts OWORD PTR as used here, but not in .data
Ocmp ecx, xmm0 ; a pointer and an XMM reg (xmm0...xmm2 will be trashed)
deb 4, "Result", flags ; CzSo, i.e. Carry? and Sign? set
Rem - returns flags as in a normal cmp eax, edx comparison (control for overflow!)
- you may assign immediates with e.g. movaps xmm0, Oword16(1234567890); the "16" means that data is aligned
- will trash eax and edx, xmm0 and xmm1; do not use edx as input pointer
- you cannot use both ecx and ebx as input pointers (an error will be thrown)
.if Fsign(MyRealVar)
MsgBox 0, "MyRealVar is negative", "Fsign:", MB_OK
.else
MsgBox 0, "MyRealVar is not negative", "Fsign:", MB_OK
.endif
Rem returns Sign?, and works with all sizes (Real4, Real8, Real10)
MyPI_hi REAL4 3.14160
...
Fcmp MyPI_hi, PI, medium ; PI is what you think it is
.if FcmpLess
Print Str$("MyPI_hi at %f is lower than the real PI\n", MyPI_hi)
.elseif Zero?
Print Str$("MyPI_hi at %f is exact\n", MyPI_hi)
.else
Print Str$("MyPI_hi at %f is higher than the real PI\n", MyPI_hi)
.endif
Fcmp st, st(1), xtra ; compare st(0) and st(1) with maximum precision
Rem - returns Zero? and Sign? flags (and only these are valid): Sign? means "first arg below second arg"
- you may use FcmpGreater and FcmpLess (aka !Sign? and Sign?)
- the desired precision can be indicated as third argument: low, medium, high, top and
xtra precision; caution in loops that exit only if Zero? is set!
- single arg, e.g. Fcmp xmm1, tests for zero
- see also QCmp and Ocmp for comparing QWORDs and OWORDs
- almost any number formats can be compared, including xmm registers etc
mov eax, Val(Chr$("123")) ; eax=123, edx=3
mov eax, Val("123") ; eax=123, edx=3
mov esi, Chr$(9, " 12345") ; eax=12345, edx=9 (5 plus a tab and three spaces)
mov ebx, Val(esi)
mov ebx, Val("12345h") ; hex
mov ebx, Val("0x12345") ; hex, C notation
mov ebx, Val("$12345") ; hex, leading $ notation
mov ebx, Val("10101b") ; binary
mov ebx, Val("12:34:56", 3) ; take the third value, e.g. 56 seconds
Dim MyArray(1000) As DWORD
mov MyArray(n), Val("12345678") ; assign to a dword array member
mov ebx, Val("$12345")
.if signed edx>0 ; signed is an equate for sdword ptr
.if dh==1 ; dh set means it was a Bin$; use movzx edx, dl if you need the real # of chars used
MsgBox 0, Str$("We found a Bin$: %i", ebx), "Val:", MB_OK ; 10101b or 10101y
.elseif dh==2
MsgBox 0, Str$("We found a Hex$: %i", ebx), "Val:", MB_OK
.else
MsgBox 0, Str$("We found a decimal: %i", ebx), "Val:", MB_OK
.endif
.else
MsgBox 0, "Sorry, the format was no good", "Val:", MB_OK
.endif
Print Str$("#%i digits used", Val?("123h") ; 4 digits used
deb 4, "Hex strings only", x:HexVal("1111aBcd") ; result in eax; for HexVal(pStr, 64) and HexVal(pStr, 128) in xmm0
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
- ultrafast HexVal accepts only 123Abc or 123AbcH formats; HexVal(pStr, 64) and HexVal(pStr, 128) return results in xmm0
Key val(
MovVal MyDword, Chr$("123.4567") ; assign a dword
MovVal MyDword, esi ; variable from
MovVal MyDword, offset MyString ; a string
Print Str$("MyDword=\t%f\n", MyDword) ; and print it
MovVal MyR4, Input$("Type a number and hit Enter: ") ; get a number from the console
Print Str$("MyR4=\t%f\n", MyR4)
MovVal f:xmm0, Left$(Chr$("123.4567"), 6) ; to use xmm regs in float mode, use the
Print Str$("MyXmm=\t%f\n", f:xmm0) ; f: prefix both for MovVal and Str$
MovVal xmm0, Left$(Chr$("123.4567"), 6) ; same xmm reg but in integer mode
Print Str$("MyXmm=\t%f\n", xmm0)
MovVal MyQword, "123.4567" ; qwords
Print Str$("MyQword=\t%f\n", MyQword)
MovVal MyR4, Chr$("123.4567") ; REAL4
Print Str$("MyR4=\t%f\n", MyR4)
MovVal ST(0), "12345789123456789120000000000000000000000000000000"
Print Str$("V=%Jf\n", ST(0)v) ; V=1.234578912345678912e+50; v means "pop ST with fstp st"
push eax ; create a DWORD slot
MovVal stack, "12345678" ; looks elegant but mov ecx, Val(...) is shorter
pop ecx ; pop a DWORD
Print Str$("Popped from stack: %i\n", ecx)
MovVal eax, Chr$("-1234") ; same result as Val(..)
MovVal MyR10, Chr$(9, " -123.45678901234567890e-123 cute, insn't it?") ; edx=30 (incl. tab + 2 spaces)
Rem returns # of used chars in dl - see Val() above
Print "All tests for a value of 32", CrLf$
mov edx, 32
Print Str$("Square root reg32 \t%If\n", Sqrt(edx))
MovVal xmm0, "32"
MovVal f:xmm1, "32"
Print Str$("Square root xmm0 \t%If\n", Sqrt(xmm0)) ; xmm0 in integer mode
Print Str$("Square root xmm1 \t%If\n", Sqrt(f:xmm1)) ; xmm1 in float mode
Print Str$("Square root Word \t%If\n", Sqrt(V32W)) ; Word variable
Print Str$("Square root DWord \t%If\n", Sqrt(V32DW)) ; Dword
Print Str$("Square root QWord \t%If\n", Sqrt(V32QW)) ; Qword
Print Str$("Square root Real4 \t%If\n", Sqrt(V32R4)) ; Real4
Print Str$("Square root Real8 \t%If\n", Sqrt(V32R8)) ; Real8
Print Str$("Square root Real10 \t%If\n", Sqrt(V32R10)) ; Real10
Print Str$("Square root left on FPU \t%If\n", Sqrt(2, ST(0))) ; you may provide a destination as second arg,
fstp st ; e.g. ST(0); for the latter, fstp st cleanup is needed
Rem returns a Real10 variable (named MbDebugR10) for use with Str$()
MyRecord RECORD SlotHigh:4, SlotMid:7, SlotLow:7, SlotRest:32-4-2*7 ; the order is high to low
.data?
MyRec MyRecord <> ; define an empty 32-bit record with 4 fields
.code
SetField MyRec.SlotHigh, 15 ; set all 4 bits
SetField MyRec.SlotMid, eax ; 7 bits available
SetField MyRec.SlotLow, 10, clear ; clear all 7 bits, then set value 10
Rem - argument must be immediate or DWORD (register or variable)
- important: if there is any third argument, the existing field bits will be cleared with and MyRec, not mask field;
then, bits will set with or MyRec, eax. The and costs 10 bytes (!) for a global variable, and can easily be
avoided by clearing the entire record (and MyRec, 0) before setting individual fields
- for immediate arguments, an error will be thrown if the value exceeds the available range
Print Str$("The content of MyRec.SlotHigh is %i", GetField(MyRec.SlotHigh))
mov MyVar32, GetField(MyRec.SlotLow)
Rem returns field value in eax
include \masm32\MasmBasic\MasmBasic.inc
Init
If_ GetHash(FileRead$(
Let esi=FileRead$("\Masm32\qEditor.exe")
.if GetHash(esi, LastFileSize, sha) ; FileRead$ sets LastFileSize, also for binary files
PrintLine "The SHA of qEditor.exe is ", Tb$, Hex$(xmm0), " ", Hex$(ecx)
.else
PrintLine "Hashing failed: ", Err$()
.endif
void GetHash(esi, LastFileSize)
mov ecx, edx ; fifth word returned in edx
Print "Fast MD5 of qEditor.exe is ", Tb$, Hex$(xmm0), CrLf$
GetHashRev=1 ; use official MD5 byte order
.if GetHash(FileRead$("\Masm32\qEditor.exe"))
PrintLine "qEditor, byte order MD5 specs: ", Tb$, Hex$(xmm0) ; MD5, len of string will be calculated
.endif
EndOfCode
Rem - returns success (1) or failure (0) in eax
- four dwords are returned in xmm0
- in case of SHA, the fifth dword will be in edx
- results can be displayed as a Hex$(xmm0), plus Hex$(edx) for SHA. The byte order will be reversed, compared to
online hash calculators. You may use GetHashRev=1 to follow the MD5 specs, but more code will be generated
- with only one para, GetHash calculates the length; this is meaningful only for text files
- uses CryptGetHashParamfiles; try also e.g. PrintLine Launch$(Cat$("certutil -hashfile "+CL$()+" MD5"))
SetFlags 3, 1 ; Set flag #3
SetFlags 3, 0 ; Clear flag #3
FileIsDirty = 31 ; define your own name (0...31)
SetFlags FileIsDirty, 1
Rem - use with Flags(), see below
- returns previous flag value in Carry?
.If Flags(3)
MsgBox 0, "Flag 3 was set", "Hi", MB_OK
.else
MsgBox 0, "Flag 3 was not set", "Hi", MB_OK
.endif
Rem returns current flag value in Carry?
.if MouseK==1 && MouseY>600 && MouseX<9
MsgBox 0, "You clicked in the lower left corner", "Hi", MB_OK
.endif
Rem returns result in eax
Delay 1000 ; wait a second...
PrintLine fTime$(0, "HH:mm:ss.fff"), Tb$, "started"
Delay until fTime$(s:1, "HH:mm:ss.123") ; use time$(current plus one second)
PrintLine fTime$(0, "HH:mm:ss.fff "), Tb$, NanoTimer$(), " restarted"
Rem - uses Sleep but preserves ecx
- the until variant uses a time string to wait until a precise moment; use e.g. fTime$(1, "HH:mm") to
suspend running until the current minute is finished; fTime$(s:10, ...) would mean 10 seconds
void Len("a test") ; use Len but avoid a mov eax, eax
voidTrue Len(ecx) ; same but print "zero eax in line xx" if Len is zero and usedeb is on
voidFalse rv(SendMessage, hEdit, WM_GETTEXTLENGTH, 0, 0) ; prints "non-zero eax in line xx" to console if usedeb!=0 and eax!=0
Rem returns DWORD in eax
PrintLine GetRegVal("HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment","Path", "no path found"), CrLf$
EnvVars equ "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" ; long paths: use an equate
Print GetRegVal(EnvVars,"PROCESSOR_IDENTIFIER", "unknown"), CrLf$ ; get to know your CPU...
PrintLine GetRegVal("HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment","PROCESSOR_IDENTIFIER", "unknown"), CrLf$ ; same with full path
PrintLine GetRegVal("HKCR\.doc", 0, "documents"), " are files with ending *.doc"
; this line would fail with a run-time error because the key does not exist and there is no default value given:
; PrintLine GetRegVal("HKEY_CLASSES_ROOT\.bax", 0), " are files with ending *.bax"
PrintLine "Your default console font is ", GetRegVal("HKCU\Console","FaceName", "Arial")
; you can use the MbRegValRTE equate to determine the behaviour in case of errors:
; by default, you see a box "Line n: Get/SetRegVal failed" if you gave no default and the key or value does not exist
MbRegValRTE = 1 ; default mode: bark if there is a problem
void GetRegVal("HKCR\.dox", 0) ; no default, MbRegValRTE = 1: box "registry access failed in line xx"
void GetRegVal("HKCR\.dox", 0, "dox files") ; default, MbRegValRTE = 1: default value returned
PrintLine eax, " are files with ending *.dox"
MbRegValRTE = 0 ; silent mode - no Run-Time Error in case of problems
void GetRegVal("HKCR\.dox", 0) ; no default, MbRegValRTE = 0: pointer to RV? in eax
.if dword ptr [eax]==Mirror$("RV?") ; not found
PrintLine "Failure: [", eax, "]"
.else
PrintLine "Success: [", eax, "]"
.endif
; REG_QWORD values and REG_BINARY are returned in xmm0; you need void plus fDate$(xmm0) to see them:
void GetRegVal("HKLM\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0", "LastInstallTime", 0)
PrintLine "LastInstallTime: [", fDate$(xmm0, "dd MMMM yyyy"), ", ", fTime$(xmm0, "HH:mm"), "]"
Rem - returns in eax either a pointer to a string, or a DWORD, and in edx the REG_xx type or failure (0)
- in case you expect REG_NONE (=0), check [eax] for the string Rg? indicating failure
- for types REG_QWORD (i.e. FILETIMEs) and REG_BINARY, eax is a pointer to the data, while
the first 16 bytes are returned in xmm0, e.g. for use with fDate$() and fTime$()
- remember that Print and Let trash eax and edx. Use void GetRegVal to test for edx, then Let xx=eax
- returned values can be up to 128 kBytes long
- if you provide a default value, GetRegVal returns it even if the key and/or the value are not present
- if a default was given, edx is always 1, without default string or dword edx=0 means failure
- error codes for RegOpenKeyEx are in rvRegKey, those for RegQueryValueEx are in rvRegQuery
- if you can't see certain keys on 64-bit systems, try SetReg64
Key grv(
SetRegVal "HKCU\TheKey","TheValueName", "NewString"
SetRegVal "HKEY_CURRENT_USER\Console\JJ", "NewValue", 12345 ; creates new REG_DWORD
SetRegVal "HKCU\Console\JJ", "NewValue", 54321 ; changes existing value
SetRegVal "HKEY_CURRENT_USER\Console\JJ", 0, "New default value" ; 0: sets (Default) to a string
; do not replace an existing REG_SZ with a dword - this will produce an exception:
; SetRegVal "HKEY_CURRENT_USER\Console\JJ", 0, 123
; existing REG_DWORD receives a DWORD string pointer - this works but is meaningless and bug-prone:
; SetRegVal "HKEY_CURRENT_USER\Console\JJ", "NewValue", "A string"
; tries to create a new key, will fail with a run-time error:
; SetRegVal "HKEY_CURRENT_USER\Console\NoSuchKey", "NewValue", 123
Rem - returns in eax the original new value, in edx success (1) or failure (0); see also remarks for GetRegVal
- you are not allowed to create a new key, but you can create a new REG_SZ or REG_DWORD value
- with MbRegValRTE=1, attempts to create a new key will trigger a run-time error; =0 will fail, and edx will be zero
PrintLine "Keys in ... \CurrentVersion\Explorer:"
; key name, names array, optional: last modification array; the latter is a SYSTEM_TIME array; you can
; get the time elapsed since the last modification with Age(LastMod(index), x), with x=d, h, m, s, ms or µs
; SetReg64 ; optional: force seeing 64-bit registry values
GetRegKeyArray "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer", My$(), LastMod()
For_ ecx=0 To eax-1
.if Age(LastMod(ecx), h)<=7*24 ; h=hours (valid units: d/h/m/s/ms/µs)
Print fDate$(LastMod(ecx), "dd MMMM yyyy"),\ ; the dd MM... format is optional
", ", fTime$(LastMod(ecx)), Tb$, My$(ecx), CrLf$ ; default time format is e.g. HH:mm:ss
.endif
Next
PrintLine "My HKCU environment variables:"
GetRegArray "HKCU\Environment", MyEnv$(), MyData$() ; the optional second array takes the values
For_ ecx=0 To eax-1
PrintLine MyEnv$(ecx), Tb$, MyData$(ecx) ; print names and values
Next
PrintLine CrLf$, "HKLM:"
GetRegArray "HKLM\SYSTEM\ControlSet001\Control\Session Manager\Environment", MyEnv$(), MyData$() ; one more
Rem - returns #added strings in eax, i.e. not the total; if you use the same array repeatedly, either keep track
of the start value, or use some$(?) to get the total #elements
- if regedit.exe shows more items, try SetReg64
- see also two detailed examples in MbSnippets.asc
include \masm32\MasmBasic\MasmBasic.inc
Init ; select init and hit F6 to test this snippet
Print "ct", Tb$, "ID", Tb$, "path"
For_ ecx=0 To GetProcessArray(?)-1
Print Str$("\n%i\t", ecx), Str$(MbProcID(ecx)), Tb$, MbProc$(ecx)
Next
Inkey CrLf$, "--- hit any key ---"
EndOfCode
Rem - creates a string array MbProc$() and an array of process IDs, MbProcID(), using GetProcessIoCounters
- returns # strings when used with (?)
- or use it standalone without args: GetProcessArray()
include \masm32\MasmBasic\MasmBasic.inc
Init ; select init and hit F6
If_ FindProcess("Explorer.exe") Then MsgBox 0, Str$("FindProcess returned ID %i\n", [eax.PROCESSENTRY32.th32ProcessID]), "Hi", MB_OK
EndOfCode
Rem - returns pointer to a PROCESSENTRY32 structure, or zero if no process found
- accepts UTF-8 process names
include \masm32\MasmBasic\MasmBasic.inc
Init ; select init and hit F6 to test this snippet
Print "ct", Tb$, "device"
For_ ecx=0 To GetDevicesArray(?)-1
Print Str$("\n%i\t", ecx), MbDevices$(ecx)
Next
Inkey CrLf$, "--- hit any key ---"
EndOfCode
Rem - creates a string array MbDevices$()
- returns # strings when used with (?)
- or use it standalone without args: GetDevicesArray()
include \masm32\MasmBasic\MasmBasic.inc
Init
PushText "The new PushText macro provides a simple\noption to place strings on the stack"
PrintLine esp
PushText "Добро пожаловать", 1 ; Unicode is ok; an optional 1 obscures the text
PrintLine esp
PushText ; no args: balance the stack
EndOfCode
Rem pushes text as dwords on the stack
include \masm32\MasmBasic\MasmBasic.inc
Init
PrintLine TitleCase$("hello world, how are you today?") ; Hello World, How are You Today?
EndOfCode
Rem from the series "macros that the World didn't need" ;-)
print use Print
fopen use Open
fread use Input #
fclose use Close
inkey use Inkey
mtxt use Mirror$
szRev use Mirror$
str$ use Str$
hex$ use Hex$
cat$ use Cat$
exit use Exit
len use Len
Resources must be put below the end label; when pressing F6, content between the two Rsrc lines will be written to [filename].rc
ID 59 is reserved for the toolbar in GuiData.zip. Note opt_icon will be ignored - what counts is the icon specified in the resource file.
You may delete these three lines once you don't need them any more.
Rsrc
#include "resource.h"
IDI_APPLICATION ICON "\\Masm32\\MasmBasic\\icons\\Smiley.ico" // Calc, Disk, Editor, Eye, Globe, MasmBasic, Smiley
77 RCDATA "\\Masm32\\MasmBasic\\icons\\Eye.ico"
01 RT_MANIFEST "\\Masm32\\MasmBasic\\Res\\XpManifest.xml" // works with tooltips
Rsrc
RichMasm options
OPT_Susy Console
OPT_Tmp2Asm 1
OPT_arg1 "first argument passed"
OPT_arg2 "second argument passed"