News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Miscellaneous snippets

Started by jj2007, August 20, 2017, 08:02:31 AM

Previous topic - Next topic

jj2007

To use it, drag a file (e.g. ...FreeBasic\examples\unicode\hello_UTF32LE.bas) over the attached exe.

include \masm32\MasmBasic\MasmBasic.inc         ; download
  Init
  Let esi=FileRead$(CL$())      ; read UTF-32 content into a buffer
  .if dword ptr [esi]!=0FEFFh   ; no BOM found!
        MsgBox 0, CL$(), "Not a UTF-32 file, sorry", MB_OK
  .else
        mov ecx, LastFileSize
        sar ecx, 1
        Let edi=New$(ecx)       ; create a buffer for UTF-16
        sar ecx, 1
        push edi
       .Repeat
                lodsd
                stosw
                dec ecx
       .Until Sign?
       pop edi
       FileWrite "test.txt", edi
       ShEx "test.txt"         ; open in your preferred editor
  .endif
EndOfCode

jj2007

Source and exe attached. This snippet generates a text file with integers.

include \masm32\MasmBasic\MasmBasic.inc         ; download
  SetGlobals total
  Init
  Dim MyDw() As DWORD
  mov ecx, Val(Input$("How many numbers: ", "1000000"))         ; suggest one Million
  mov total, ecx
  sar ecx, 4                                    ; divide by 16
  jle bye
  Open "O", #1, "numbers.txt"
  For_ ct=1 To ecx
        For_ ecx=0 To 15
                Print #1, Str$("%i ", Rand(500000)-250000)      ; generate numbers from -250000...249999
        Next
        Print #1
  Next
  Close #1
  NanoTimer()
  StringToArray FileRead$("Numbers.txt"), MyDw()        ; reload textfile and convert to numbers
  PrintLine NanoTimer$(), Str$(" for converting %i numbers", MyDw(?))
  NanoTimer()
  ArraySort MyDw()
  PrintLine NanoTimer$(), " for sorting them"
  For_ ecx=0 To 9                       ; show the smallest numbers
       PrintLine Str$("MyDw(%i)=", ecx), Str$(MyDw(ecx))
  Next
  PrintLine "..."
  For_ ecx=total-9 To total-1           ; show the biggest numbers
       PrintLine Str$("MyDw(%i)=", ecx), Str$(MyDw(ecx))
  Next
bye:
EndOfCode


Sample output (Intel Core i5):
How many numbers: 1000000
98 ms for converting 1000000 numbers
103 ms for sorting them
MyDw(0)=-250000
MyDw(1)=-249999
MyDw(2)=-249999
MyDw(3)=-249999
MyDw(4)=-249998
MyDw(5)=-249998
MyDw(6)=-249997
MyDw(7)=-249997
MyDw(8)=-249997
MyDw(9)=-249996
...
MyDw(999991)=249994
MyDw(999992)=249994
MyDw(999993)=249995
MyDw(999994)=249996
MyDw(999995)=249997
MyDw(999996)=249997
MyDw(999997)=249998
MyDw(999998)=249999
MyDw(999999)=249999

jj2007

GuiParas equ "Save all frames of a GIF image to file", w300, h200
include \masm32\MasmBasic\Res\MbGui.asm         ; needs MasmBasic 24.1.2020
GuiImageCallback MySave                         ; tell GuiImage to call MySave
Event Paint
  GuiImage wCL$(), fit                          ; load the image specified in the commandline (Unicode is allowed)
EndOfEvents
MySave: If_ Not signed eax<0 Then <SaveImageToFile Str$("frame_%i.gif", eax)>   ; saves current frame to frame_0...n.gif
  ret
GuiEnd


Project attached. Drag an animated *.gif file over the exe, then check its folder for the component files. The exe will also display jpg, bmp, png and tiff, but only multi-frame gif files will trigger the creation of the frame*.gif components. This works also with animated TIFF files, see second attachment, but the output will be *.gif components. No idea how to join them, though :cool:

jj2007

What's new?

- GuiImage now respects the delays of frames in animated GIFs, and also stops looping if the GIF specifies a limit. Simple example (make sure you run it in a folder that contains images; source and exe attached):

include \masm32\MasmBasic\Res\MbGui.asm        ; MasmBasic version 1 Feb 2020 needed for building this code
GetFiles *.gif|*.jpg|*.png|*.jpeg              ; load the Files$() array
SortFiles                                      ; latest on top
GuiControl MyList, "listbox", Files$(), x=800, w=200   ; create a listbox to the right, 20% of width; use Files$() to fill it
GuiControl MyCanvas, "canvas", w=800, WS_THICKFRAME    ; create the canvas to paint on
GuiImageSpeed 50, 2000                         ; 50% speed, 2 seconds pause after an animation

Event CanvasPaint
  .if LbSel>=0
        GuiImage 99, fit                ; background image may be needed for animations with transparency
        GuiImage Files$(LbSel)          ; draw the selected image, use original size (or use fit as above)
  .else
        GuiTextBox 0, 0, 200, auto, "Click into the listbox to select a file", bcol RgbCol(255, 255, 255), font 40
  .endif
GuiEnd


- new CheckStackBalance macro:
include \masm32\MasmBasic\MasmBasic.inc
  Init

  CodeSize MyTest
  call MyTest
  useCsb=1 ; put 0 to see a crash

  Exit

  MyTest_s:
  MyTest proc
CheckStackBalance
PrintLine "Hello World"
pushf ; that's a word, right ;-)
pushad ; <<<<<<<<<<<<<<<< a bug! <<<<<<<<<<<
CheckStackBalance
ret
  MyTest endp
  MyTest_endp:
  CodeSize MyTest, echo
end start


If enabled via useCsb=1, it shows e.g. these lines, plus a MsgBox:

244     bytes for MyTest
Hello World
** line 16: 8 push instructions in excess - stack is 34 bytes off **


If disabled with useCsb=0, no code is generated but the output looks different:

18      bytes for MyTest
Hello World


Plus a "MsgBox" designed by Microsoft :tongue:

- the CodeSize macro may serve to calculate the size of a whole proc or a few instructions between the *_s: and *_endp: labels. In the CodeSize xx, echo variant, the size appears in the output window, and no extra code will be generated

jj2007

include \masm32\MasmBasic\MasmBasic.inc         ; download
  SetGlobals backslash, pattern$
  Init
  .While 1
        PrintLine CrLf$, "Press Escape, Return to exit", CrLf$
        Let pattern$=Input$("What to search for: ", Cat$(CurDir$()+"*.*"))
        .Break .if !Len(eax)
        Cls
        Dim score() As DWORD
        GetFiles pattern$       ; .jpg|*.inc|*.pdf
       For_ ct=0 To eax-1
                xor ecx, ecx
                dec GfAgeHours(ct)
                .if !Sign?
                              shr eax, 2      ; more shifting = less importance for date
                              push eax
                              shr GfSize(ct), 7
                              pop edx
                              mul edx
                              shrd eax, edx, 12
                              xchg eax, ecx
                .endif
                mov score(ct), ecx
        Next
        ArraySort score(-), key() As DWORD, fill
        PrintLine CrLf$, "Size__", Tb$, "Date______  Time____    Path", String$(60, "_")
        fldz
        For_ ct=0 To score(?)-1
                mov ecx, key(ct)
                .if ct<=50
                                push GfSize(ecx)
                                fiadd stack
                                pop edx
               .endif
                .if ct<20
                                mov esi, Files$(ecx)
                                ; mov backslash, Rinstr(esi, "\")
                               PrintLine Str$("%_____i\t", GfSize(ecx)/1024/1024), GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, Mid$(esi, backslash+1)
                .endif
        Next
        fmul FP4(1.0e-9)
        mov ecx, Cat$("\nDeleting the first 50 files in "+Left$(pattern$, Rinstr(pattern$, "\")-1)+" would free %3f GB of disk space")
        PrintLine Str$(#ecx, ST(0)v)
  .Endw
EndOfCode


Unzip the attachment to some folder and run the exe (or open the *.asc in RichMasm and hit F6). Typical output:
Size__  Date______  Time____    Path____________________________________________________________
     2  27.09.2013  02:01:00    C:\Masm32\OllyDbg\ollydbg.exe
     0  19.03.1998  07:28:18    C:\Masm32\bin\link.exe
     0  13.01.1995  05:10:00    C:\Masm32\bin\link16.exe
     1  10.01.2012  00:21:10    C:\Masm32\include\winextra.inc
     0  10.01.2012  00:21:10    C:\Masm32\include\windows.inc
     0  16.03.2000  15:20:48    C:\Masm32\bin\ml.EXE
     0  16.03.2000  15:20:48    C:\Masm32\bin\mlv615.EXE
     0  12.11.2011  08:04:41    C:\Masm32\examples\unicode_generic\diskfile\WININC.INC
     0  13.03.2004  16:30:08    C:\Masm32\examples\advanced\wrep\winequ.inc
     0  16.04.2011  19:09:37    C:\Masm32\bin\poasm.exe
     0  25.01.2010  13:18:04    C:\Masm32\bin\dumppe.exe
     0  22.12.2008  15:15:08    C:\Masm32\lib\msvcrt.000

Deleting the first 50 files in C:\Masm32 would free 0.0410 GB of disk space


So far, it just shows files, i.e. nothing will get deleted. Of course, it becomes more interesting if your \Masm32 folder is a little bit older. My message is Deleting the first 50 files in C:\Masm32 would free 16.8 GB of disk space :cool:

Here is another interesting folder:
Size__  Date______  Time____    Path____________________________________________________________
   272  23.04.2011  21:41:44    C:\Windows\Installer\21386cd2.msp
   189  28.03.2011  18:00:22    C:\Windows\Installer\42fec0e.msp
   164  28.03.2011  17:45:18    C:\Windows\Installer\42fec0d.msp
   114  27.07.2007  09:03:06    C:\Windows\Installer\21386a4c.msp
   378  01.09.2017  01:43:52    C:\Windows\Installer\30be8f0e.msi
   245  21.06.2016  05:45:27    C:\Windows\Installer\2ad6e39c.msi
   207  06.11.2015  16:00:20    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SetupCache\v4.6.01055\NetFx_Full.mzz
    69  22.03.2010  19:36:56    C:\Windows\Installer\$PatchCache$\Managed\00004159FA0090400000000000F01FEC\14.0.4763\MSORES.DLL
    63  10.06.2009  23:02:16    C:\Windows\IME\IMESC5\DICTS\PINTLGT.IMD
    63  10.06.2009  23:02:16    C:\Windows\winsxs\amd64_microsoft-windows-i..e-trigramdictionary_31bf3856ad364e35_6.1.7600.16385_none_12d6b2e35
    55  22.07.2009  10:17:30    C:\Windows\Installer\$PatchCache$\Managed\1D763DBFF246FC747BB9B94EF83B0470\10.0.1600\ENG_RE_sqlservr_exe_64
    69  18.01.2012  02:24:38    C:\Windows\Installer\$PatchCache$\Managed\00004159FA0090400000000000F01FEC\14.0.7015\MSORES.DLL
    55  22.09.2011  21:07:34    C:\Windows\Installer\$PatchCache$\Managed\1D763DBFF246FC747BB9B94EF83B0470\10.3.5500\ENG_RE_sqlservr_exe_64
    37  17.12.2007  14:57:48    C:\Windows\Installer\27f64005.msi
    36  17.12.2007  14:57:48    C:\Windows\Installer\27f64006.msp
    43  11.06.2010  17:52:10    C:\Windows\Installer\42cdaff.msp
    96  21.06.2016  05:44:58    C:\Windows\Installer\2ad6e0ec.msp
    32  10.06.2009  22:43:23    C:\Windows\Fonts\mingliub.ttc
    32  10.06.2009  22:43:23    C:\Windows\winsxs\amd64_microsoft-windows-font-truetype-mingliub_31bf3856ad364e35_6.1.7600.16385_none_251699455
    77  20.09.2015  10:27:56    C:\Windows\Installer\$PatchCache$\Managed\00006109C10000000000000000F01FEC\16.0.4288\MSORES.DLL

Deleting the first 50 files in C:\Windows would free 3.83 GB of disk space

jj2007

Under the hood of GetFiles, FindFirstFile and FindNextFile are working. Unfortunately, for these API functions, *.xls and *.xlsx are just the same thing. Here is a workaround that filters unwanted *.xlsx files out - it sets the zero flag in a callback function thus telling GetFiles not to include the file in the Files$() array:

include \masm32\MasmBasic\MasmBasic.inc         ; download
.code

cbGetFiles:                     ; the callback function
  .if wInstr(edi, ".xlsx")
        or ecx, -1              ; combined with the inc ecx below, this sets Zero?
  .endif
  inc ecx
  ret

  Init
  GfCallback cbGetFiles         ; define a callback function
  GetFiles *.xls
  SortFiles                     ; latest on top
  For_ ebx=0 To eax-1           ; print the results
       PrintLine Str$(GfSize(ebx)), Tb$, GfDate$(ebx), Spc2$, GfTime$(ebx), Tb$, Files$(ebx)
  Next
EndOfCode


Project attached.

jj2007

GuiParas equ "adding two numbers", w560, h84, icon Lens
include \masm32\MasmBasic\Res\MbGui.asm
GuiControl MyEdit, "edit", h=0+28, "1234+1000h", font -16:FW_SEMIBOLD
GuiControl MyStatusBar, "statusbar", "Change the number to see the sum in dec, hex and binary format", font -14:FW_MEDIUM
Event Command
  .if nCode==EN_CHANGE
        mov ecx, Win$(hMyEdit)          ; get the text from the edit control
        push Val(ecx)                   ; save first integer to stack
        and edx, 127                    ; get #chars used
        lea ecx, [ecx+edx+1]            ; skip the plus sign
        void Val(ecx)
        If_ edx==-127 Then xor eax, eax ; second value was invalid or empty
        pop ecx                         ; pop first value
        add ecx, eax                    ; sum
        SetWin$ hMyStatusBar=Str$("Sum is %i = ", ecx)+Hex$(ecx)+"h = "+Bin$(ecx)+"b"
  .endif
GuiEnd

jj2007

include \masm32\MasmBasic\MasmBasic.inc         ; download
  SetGlobals num:QWORD=1, num10:REAL10=1
  Init
  PrintLine "Factorials from 1!!!! to 25!!!!"
  For_ ct=1 To 25
        fild num
        fimul ct
        fistp num
        fld num10
        fimul ct
        fstp num10
        Print Str$(ct), Tb$, Str$(num)
        PrintLine At(40) Str$("%Jf", num10)
  Next
EndOfCode


Output (QWORD overflows already at n=21):
Factorials from 1! to 25!
1       1                               1.000000000000000000
2       2                               2.000000000000000000
3       6                               6.000000000000000000
4       24                              24.00000000000000000
...
18      6402373705728000                6402373705728000.000
19      121645100408832000              121645100408832000.0
20      2432902008176640000             2432902008176640000.
21      -9223372036854775808            5.109094217170944000e+19
22      -9223372036854775808            1.124000727777607680e+21
23      -9223372036854775808            2.585201673888497664e+22
24      -9223372036854775808            6.204484017332394394e+23
25      -9223372036854775808            1.551121004333098598e+25

jj2007

include \masm32\MasmBasic\MasmBasic.inc         ; download
  SetGlobals oldPos, ctDiff
  Init
  Cls
  GetFiles *.a
  For_ ecx=0 To eax-1
        mov esi, Files$(ecx)
        .if Rinstr(esi, "\")!=oldPos    ; take one from each folder
               mov oldPos, edx
                inc ctDiff
                PrintLine Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, Files$(ecx)
        .endif
  Next
  Inkey Str$("\n%i different folders containing *.a files found", ctDiff)
EndOfCode


To test it, just run the exe in your FreeBasic folder. Sample output:
98954   16.03.2019  12:23:26    doc\Print_to_Pdf-master\version_1.1\libPrint2Pdf_32.a
45108   08.03.2019  19:51:06    doc\Print_to_Pdf-master\libPrint2Pdf32.a
647940  31.01.2016  14:47:37    Fb64\lib\freebasic\win64\libfb.a
21808   31.03.2018  17:27:29    Fb64\lib\libLinked_Lists.a
3028    11.08.2008  09:14:00    FbEdit\Projects\Addins\fbShowVars\addins\libfbshowvars.dll.a
38560   21.08.2007  12:00:04    FbEdit\Projects\Addins\fbShowVars\needed\freebasic\lib\win32\libdisasm.a
6256    21.08.2007  12:00:04    FbEdit\Projects\Addins\fbShowVars\sample\libshowvars.a
8918    05.11.2007  08:25:38    FbEdit\Projects\CustCtrl\FBEPictView\libFBEPictView.a
3890    13.05.2007  19:50:32    FbEdit\Projects\CustCtrl\FBEVideo\libFBEVideo.a
3980    13.05.2007  19:50:34    FbEdit\Projects\CustCtrl\FBEWeb\libFBEWeb.a
6256    31.12.2000  23:00:00    FbEdit\Projects\Samples\Debug\libshowvars.a
349806  22.09.2016  16:23:58    lib\pcre-839-static\win32\libpcre.a
24286   27.12.2010  17:47:09    lib\win32\Cairo\libcairo-gobject.dll.a
3658    31.01.2016  12:09:12    lib\win32\libaclui.dll.a
23214   01.04.2013  12:30:12    PngLib\libpnglib2.a
3028    16.12.2008  21:29:25    Projects\Addins\fbShowVars\addins\libfbshowvars.dll.a
38560   16.12.2008  21:29:26    Projects\Addins\fbShowVars\needed\freebasic\lib\win32\libdisasm.a
6256    16.12.2008  21:29:25    Projects\Addins\fbShowVars\sample\libshowvars.a
8918    03.11.2007  22:33:32    Projects\CustCtrl\FBEPictView\libFBEPictView.a
3890    23.08.2007  21:57:18    Projects\CustCtrl\FBEVideo\libFBEVideo.a
3980    23.08.2007  21:57:20    Projects\CustCtrl\FBEWeb\libFBEWeb.a
6256    23.08.2007  21:57:24    Projects\Samples\Debug\libshowvars.a
3658    31.01.2016  11:09:14    WinFBE_Suite\FreeBASIC-1.06.0\lib\win32\libaclui.dll.a
24456   25.03.2019  20:31:49    libminz_32.a

27 different folders containing *.a files found

jj2007

GuiParas equ "Gdi+ is easy", w400, h300, b DarkGrey
include \masm32\MasmBasic\Res\MbGui.asm         ; *** Draw two persons - hit F6 to build & run ***
  GuiControl Sbar, "statusbar", "You can size the window"
  MakePath Head, Ellipse(150:180)      ; w:h
  MakePath Person, Polygon(180:160, 420:160, 500:400, 440:420, 380:250, 370:420, 450:750, 370:750, 300:500, 230:750, 160:750, 230:420, 220:250, 160:420, 100:400, 180:160)
  MakePen hPen, RgbCol(160, 0, 0, 255), width 9   ; semi transparent blue pen
  MakeBrush hBrushRed, RgbCol(255, 255, 0, 0)
  MakeBrush hBrushGreen, RgbCol(100, 0, 255, 0)
Event Paint
  GuiTextBox 99.9-100, 100.0-60, 98, auto, "Thanks for the inspiration, Nidud", bcol LiteYellow, font -14       ; -> post
  GuiDraw Head, hPen/hBrushRed, 51.0, 13.0      ; big person
  GuiDraw Person, hPen/hBrushRed, 22.0, 6.0     ; x, y
  GuiDraw Head, hPen/hBrushGreen, 27.0, 8.0, 500, 500  ; small person
  GuiDraw Person, hPen/hBrushGreen, 0.0, 6.0, 900, 400  ; x, y, scale x, scale y
GuiEnd

jj2007

include \masm32\MasmBasic\MasmBasic.inc         ; Start a program just one time
  Init
  Let esi=CL$(0)                             ; use the executable name as unique string
  .if rv(GlobalFindAtom, esi)
        MsgBox 0, Cat$(esi+" is already running"), "Sorry:", MB_OK or MB_TOPMOST
  .else
        push rv(GlobalAddAtom, esi)
        MsgBox 0, Cat$(esi+" successfully started"), "Yeah:", MB_OK or MB_TOPMOST
        call GlobalDeleteAtom
  .endif
EndOfCode

jj2007

include \masm32\MasmBasic\MasmBasic.inc         ; download
  Init
  PrintLine "option 1=[", Trim$(Extract$(Cat$(CL$()+"/"), "/opt1", "/")), "]"  ; print the option
  Let esi="option 2=["+Trim$(Extract$(Cat$(CL$()+"/"), "/opt2", "/"))+"]"      ; assign to a string, then print it
  Print esi
EndOfCode

OPT_Arg1        /opt1 That's fun /opt2 No problem       ; the RichMasm way to test commandlines


Output:
option 1=[That's fun]
option 2=[No problem]

jj2007

include \masm32\MasmBasic\MasmBasic.inc         ; download
  Init
  Let esi=FileRead$("\Masm32\include\Windows.inc")+FileRead$("\Masm32\include\WinExtra.inc")
  mov ebx, Len(esi)             ; 2045966 bytes
  PrintCpu 0
  REPEAT 3
        push 99
        xor edi, edi
       
.Repeat
                NanoTimer()
                mov edx, ebx    ; FAST with mode 64 needs the size of the buffer in edx
                push Instr_(FAST, esi, "Duplicate include file winextra.inc", 64)
                add edi, NanoTimer(µs)
                pop edx
               dec stack
        .Until Sign?
        pop eax                 ; stack correction
        If_ Not edx Then Print "String not found - "
        Print Str$("average: %i µs\n", edi/100)
  ENDM
EndOfCode


Typical output:
Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
average: 495 µs
average: 492 µs
average: 506 µs

jj2007

Caballero inspired this little demo showing how the old-fashioned question mark next to the close box works. Source + exe attached.

GuiParas equ "Context help demo", w500, h320, icon Star, b GreenBlue, s WS_SYSMENU or WS_CAPTION or WS_THICKFRAME
GuiMenu equ @File, &Open, &Save, -, E&xit, @Edit, Undo, Copy, Paste ; define a menu (only Open is implemented)
GuiWsExMain equ WS_EX_CONTEXTHELP
include \masm32\MasmBasic\Res\MbGui.asm ; hit F6 to build this template
  GuiControl RichEdit, "richedit", wCL$(), h900, w1000-60
  GuiControl PoorEdit, "edit", "Click into the question mark in the caption, then down here", y900, h100, w1000-60
  GuiControl B1, "button", x=1000-60, h=0+30, w=0+60, "Button 1"
  GuiControl B2, "button", x=1000-60, y=0+33, h=0+30, w=0+60, "Button 2"
  GuiControl B3, "button", x=1000-60, y=0+66, h=0+30, w=0+60, "Button 3"
  GuiControl Status, "statusbar", wCat$(wDate$+", "+wTime$) ; GuiControls require Unicode
  StringToArray cfm$("richedit\nedit control\n1st button\n2nd button\n3rd button\nstatus bar\nlistbox"), help$()
  GuiControl MyList, "listbox", help$(), x=1000-60, w=0+60, y=0+100, h=1000-100
Event Message
  .if uMsg_==WM_HELP
mov ecx, lParam_
mov ecx, [ecx.HELPINFO.iCtrlId]
.if signed ecx>40
sub ecx, 41
SetWin$ hStatus="You clicked into the "+help$(ecx)
.endif
  .endif
Event Menu
  Switch_ MenuID
  Case_ 0 ; menu entries start with 0
  If_ FileOpen$("Rich sources=*.asc|All sauces=*.as?;*.inc|Resources=*.rc") Then SetWin$ hRichEdit=FileRead$(FileOpen$())
  Endsw_
GuiEnd

Richard

@jj2007   re reply #2

As I have finally a workable "bare metal" (no WINDOWS whatsoever) environment - where I boot off a usb stick which is now the C:\  drive -
I wish to establish whether or not FreeBASIC DOS version can work with in-line assembly (prefer DOS version at this stage but will also consider windows version which I have used a bit).

Would you be able to supply a very simple FreeBASIC program that uses in-line assembly and I can verify if it is possible? Also advise precisely all the Command Line Switches for fbc.exe sampleprogram.exe that would be needed. I will try to compare things (code size, timings, etc) to calling an external program (that does what the in-line assembly does) via a SHELL like command from within FreeBASIC program.

My laptop is intel i7 x64 with 32 GByte RAM.