Author Topic: MasmBasic  (Read 153176 times)

anta40

  • Member
  • ***
  • Posts: 293
Re: MasmBasic - a fast and easy-to-use library
« Reply #150 on: December 11, 2013, 12:59:12 PM »
Hi jj,

I think there's a bug with the installer.
After I selected C:\masm32\macros\macros.asm, I could see this on the installer:

"The MasmBasic library will be installed to La$?"

"Can't create destination folder
La$?"

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: MasmBasic - a fast and easy-to-use library
« Reply #151 on: December 11, 2013, 05:12:01 PM »
Hi anta,

"La$?" is what Launch$("whatever.exe") returns if whatever.exe hangs and/or a timeout occurs. Apparently the default timeout is too short - I will fix this asap.

Thanks a lot for the feedback,
jj

EDIT: It's fixed, see version 11 Dec 2013b
« Last Edit: December 12, 2013, 04:12:14 AM by jj2007 »

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Bug in Str$() - and workaround
« Reply #152 on: December 15, 2013, 09:50:37 AM »
There is a problem with Str$() when used with .data section arrays - it will not digest e.g. Str$(MyArray[3*REAL8]):

include \masm32\MasmBasic\MasmBasic.inc

.data
MyArray        REAL8 1.23, 4.56, 7.89, 9.87

  Init

  Print Str$("Element 0 is %3f\n", MyArray)        ; Str$(REAL8_var) works, but only for element zero

  el1 equ MyArray[1*8]        ; workaround for other elements - no <brackets> please
  Print Str$("Element 1 is %3f\n", el1)

  mov ecx, offset MyArray+1*REAL8        ; use a register to access array elements
  Print Str$("Element 2 is %3f\n", real8 ptr [ecx+8])        ; OK if exactly one blank between ptr and [

  ; no problems with Basic-style arrays (byte/word/dword/qword/R4/r8/r10):
  Dim MyR8(3) As REAL10        ; create an array of long doubles (same for REAL4, REAL8)
  fldpi
  fstp MyR8(3)                ; put PI into element 3
  Print Str$("Element 3 is %Jf\n", MyR8(3))

  Inkey "ok"
  Exit
end start


I had found a fix, but unfortunately it chokes with JWasm, so for the time being, and if you really need it, use the equate.

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Printing unsigned LONGLONG variables
« Reply #153 on: December 23, 2013, 02:54:37 AM »
With MasmBasic of 22 December 2013, you can print unsigned qwords like 12345678901234567890:

include \masm32\MasmBasic\MasmBasic.inc  ; download
MyLongLong    LONGLONG    12345678901234567890
MyDword       LONG        1234567890
MyWord        WORD        12345
MyByte        BYTE        123
MySingle      REAL4       12345678901234567890.0
MyDouble      REAL8       12345678901234567890.0
MyR10         REAL10      1234567890123456789012.0

  Init                        ; ## deb and the Art of Type Checking ##
  mov eax, MyDword
  mov bx, MyWord
  mov cl, MyByte
  fldpi                         ; PI, 3.14159
  fldl2e                        ; Log2(e), 1.4427
  fldlg2                        ; Log10(2), 0.3013
  movlps xmm0, MyLongLong
  movlps xmm1, MyDouble
  DefNum 16                ; set precision (only f:xmm1 affected)
  deb 4, "Any type missing?", u:MyLongLong, u:xmm0, f:xmm1, ST(0), ST(1), ST(2), MyDword, MyWord, MyByte, cl, bx, eax, MySingle, MyDouble, MyR10
  Exit
end start


Output:

Any type missing?
u:MyLongLong    12345678901234567890
u:xmm0          12345678901234567890
f:xmm1          1.234567890123457e+19
ST(0)           0.301029995663981195
ST(1)           1.44269504088896341
ST(2)           3.14159265358979324
MyDword         1234567890
MyWord          12345
MyByte          123
cl              123
bx              12345
eax             1234567890
MySingle        1.234568e+19
MyDouble        1.234567890123457e+19
MyR10           1.23456789012345679e+21


This example is included in the attached MbSnippets.asc

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Arrays: Insert and Delete
« Reply #154 on: December 28, 2013, 10:10:32 AM »
MasmBasic 28 Dec 2013 supports Insert and Delete for numerical arrays:

  Init                        ; we create several numeric arrays,
  Dim MyByte() As BYTE        ; from byte to ...
  Dim MyWord() As WORD
  Dim MyDw() As DWORD
  Dim MyQw() As QWORD        ; ... qword
  Dim MyRc() As RECT        ; any other structure would work, too
  Dim My$()                ; plus one string array

  call FillTheArrays        ; fill the arrays with some nice values
  call PrintTheArrays        ; display the original values
  xor ecx, ecx                ; we test different index positions:
  Delete MyQw(ecx)        ; 0        reg32
  Delete MyDw(ecx+1)        ; 1        reg plus offset
  Delete MyWord(gct2)        ; 2        global var
  Delete MyByte(3)        ; 3        immediate
  Delete MyRc(ecx+4)        ; 4
  Delete My$(ecx+3)        ; 3
  PrintLine CrLf$, "Elements 0 1 2 3 4 3 deleted:"
  call PrintTheArrays        ; display the modified arrays


Output:
Code: [Select]
   Qword             Dword    Word Byte rcLeft   rcBottom (8 elements)
0  00000000 00000000 00000000 0000 00   00000000 1000   String #0
1  11111111 11111111 11111111 1111 11   11111111 1001   String #1
2  22222222 22222222 22222222 2222 22   22222222 1002   String #2
3  33333333 33333333 33333333 3333 33   33333333 1003   String #3
4  44444444 44444444 44444444 4444 44   44444444 1004   String #4
5  55555555 55555555 55555555 5555 55   55555555 1005   String #5
6  66666666 66666666 66666666 6666 66   66666666 1006   String #6
7  77777777 77777777 77777777 7777 77   77777777 1007   String #7

Elements 0 1 2 3 4 3 deleted:
   Qword             Dword    Word Byte rcLeft   rcBottom (8 elements)
0  11111111 11111111 00000000 0000 00   00000000 1000   String #0
1  22222222 22222222 22222222 1111 11   11111111 1001   String #1
2  33333333 33333333 33333333 3333 22   22222222 1002   String #2
3  44444444 44444444 44444444 4444 44   33333333 1003   String #4
4  55555555 55555555 55555555 5555 55   55555555 1005   String #5
5  66666666 66666666 66666666 6666 66   66666666 1006   String #6
6  77777777 77777777 77777777 7777 77   77777777 1007   String #7
7  00000000 00000000 00000000 0000 00   00000000 0

Elements 3 4 5 6 7 6 inserted:
   Qword             Dword    Word Byte rcLeft   rcBottom (8 elements)
0  11111111 11111111 00000000 0000 00   00000000 1000   String #0
1  22222222 22222222 22222222 1111 11   11111111 1001   String #1
2  33333333 33333333 33333333 3333 22   22222222 1002   String #2
3  00000000 00000000 44444444 4444 44   33333333 1003   String #4
4  44444444 44444444 00000000 5555 55   55555555 1005   String #5
5  55555555 55555555 55555555 0000 66   66666666 1006   String #6
6  66666666 66666666 66666666 6666 00   77777777 1007
7  77777777 77777777 77777777 7777 77   00000000 0      String #7

Testbed attached (*.asc opens with \Masm32\MasmBasic\RichMasm.exe)

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: MasmBasic - a fast and easy-to-use library
« Reply #155 on: January 08, 2014, 10:29:10 AM »
Update 8 January 2014:

- fixed a problem with MyNumericArray(?) and non-autoexpanding arrays (when used with ArrayRead, ? erroneously returned the initial count only)
- fixed a problem with Str$() and signed QWORDs
- introduced the option to leave a MovVal result on the FPU in ST(0):
  MovVal ST(0), "3.141592653589793238"

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: MasmBasic - a fast and easy-to-use library
« Reply #156 on: January 11, 2014, 01:31:44 PM »
Update 11 January fixed a minor problem with Trim$() and introduced voidTrue and voidFalse macros (see Assert and \Masm32\MasmBasic\MbGuide.rtf):

include \masm32\MasmBasic\MasmBasic.inc     ; download
  Init
  Dim My$()                                 ; create a string array
  mov ecx, Chr$("          Hello ")         ; you can't use eax or edx ...
  Let My$(0)=Trim$(ecx)                     ; ...  when assigning to an array,
  Let My$(1)=Str$(123)+Trim$(ecx)+Str$(456) ; but ecx is persistent
  Inkey "My$(0)=[", My$(0), "]", CrLf$, "My$(1)=[", My$(1), "]"
  Exit
end start

Output:
My$(0)=[Hello]
My$(1)=[123Hello456]

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
PeekNamedPipe better than WaitForSingleObject?
« Reply #157 on: January 14, 2014, 12:38:23 PM »
It seems so :P

I've been fighting with pipes for a while, and google says I'm in good company - it's messy. After a lot of testing, it seems PeekNamedPipe is the best way to decide when to leave the read loop, so the new Launch$() uses this internally.

The sample below launches a console app that returns strings unsteadily, i.e. normally it takes around 50ms to see the next output, but occasionally it chokes for a whole second. This is a typical behaviour e.g. of archivers, and the coder must impose a timeout to decide if the delay is OK or if the child process hangs. In other words, the third arg of Launch$() is now the tolerated inactivity period:

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

  PrintLine "## Launching SlowPrint.exe ##"        ; slowprint.exe must be in the same folder
  Let esi=Launch$("SlowPrint", SW_MINIMIZE, 1000)        ; max allowed inactive period of child process is 1000ms
  PrintLine Str$("\nFirst attempt, timeout=1000: exit code=%i\n", ExitCode($)), "Output:", Tb$, esi

  Let esi=Launch$("SlowPrint", SW_MINIMIZE, 1020)
  PrintLine Str$("\nSecond attempt, timeout=1020: exit code=%i\n", ExitCode($)), "Output:", Tb$, esi

  Let esi=Launch$("SlowPrint", SW_MINIMIZE, 1040)
  PrintLine Str$("\nThird attempt, timeout=1040: exit code=%i\n", ExitCode($)), "Output:", Tb$, esi, CrLf$

  Inkey "bye"
  Exit
end start

Output:
## Launching SlowPrint.exe ##

First attempt, timeout=1000: exit code=259
Output: La$?

Second attempt, timeout=1020: exit code=0
Output: sp15 sp14 sp13 sp12 sp11 sp10 sp9 sp8 sp7 sp6 sp5 sp4 sp3 sp2 sp1 sp0 2656 ms

Third attempt, timeout=1040: exit code=0
Output: sp15 sp14 sp13 sp12 sp11 sp10 sp9 sp8 sp7 sp6 sp5 sp4 sp3 sp2 sp1 sp0 2656 ms

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
50+ snippets
« Reply #158 on: January 16, 2014, 09:07:56 PM »
MasmBasic update of 17 January:

The snippets file is now integrated into the package, see the "try 50+ snippets" link in the Hello World example.

Latest feature is a much easier handling of strings (e.g. paths) in structures:

include \masm32\MasmBasic\MasmBasic.inc        ; download

MyStruct STRUCT
  xText20$       db 20 dup(?)       ; 20 bytes of text
  xDword         dd ?               ; a dword, just for fun
  xText4$        db 4 dup(?)        ; more text
  xText1$        db 1 dup(?)        ; and one byte of text
MyStruct ENDS

.data?
myS        MyStruct <>
  Init
  ; assign strings with Let:
  Let myS.xText20$="This string can take a maximum of 20 chars"
  Let myS.xText4$="That one can take only 4 chars"
  Let myS.xText1$="1 char only..."
  ; print the strings:
  PrintLine "{", myS.xText20$, "}"
  PrintLine "{", myS.xText4$, "}"
  Print "{", myS.xText1$, "}"
  Exit
end start

Output:
{This string can take}
{That}
{1}


EDIT: Now it works also for structures defined with SetGlobals.

include \masm32\MasmBasic\MasmBasic.inc

PATHS STRUCT
path1        db MAX_PATH dup(?)
path2        db MAX_PATH dup(?)
PATHS ENDS

  SetGlobals MyPaths:PATHS             ; define a global structure

  Init
  SetGlobals                           ; let ebx point to the global memory
  mov ecx, CurDir$(0)                  ; get the current folder (0=no backslash)
  Let MyPaths.path1=ecx
  Let MyPaths.path2=MbExeFolder$       ; assign the folder from which the exe was started
  PrintLine "Path1=", MyPaths.path1
  Inkey "Path2=", MyPaths.path2
  Exit
end start
« Last Edit: January 17, 2014, 06:18:34 PM by jj2007 »

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
RegEdit won't tell you when a key was last modified
« Reply #159 on: January 24, 2014, 03:12:02 AM »
But MasmBasic will :biggrin:

include \masm32\MasmBasic\MasmBasic.inc  ; download
  Init
  Let esi="HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion"
  PrintLine "Checking ", esi
  GetRegKeyArray esi, Lev1$(), LastMod1()
  For_ ecx=0 To eax-1
  .if Age(LastMod1(ecx), d)<=31  ; changed in the last 31 days
      Print Str$("\nKey %i\t", ecx), fDate$(LastMod1(ecx)),\
       ", ", fTime$(LastMod1(ecx)), Tb$, "[", Lev1$(ecx), "]"
      GetRegKeyArray Cat$(esi+"\"+Lev1$(ecx)), Lev2$(), LastMod2()
      For_ ebx=0 To eax-1
        .if Age(LastMod2(ebx), h)<=24*3  ; changed in the last 3 days
            Print CrLf$, "- ", Tb$, fDate$(LastMod2(ebx)),\
            ", ", fTime$(LastMod2(ebx)), Tb$, Lev2$(ebx)
        .endif
      Next
    .endif
  Next
  Exit
end start


Sample output:

Checking HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion

Key 7   23.01.2014, 07:11:19    Explorer
Key 10  23.01.2014, 15:27:22    Group Policy
-       23.01.2014, 15:42:00    Regedit
-       23.01.2014, 11:35:39    SysTray
Key 11  23.01.2014, 07:11:30    HomeGroup
Key 13  08.01.2014, 07:11:42    Internet Settings
Key 18  23.01.2014, 14:08:57    Run
Key 19  23.01.2014, 07:11:20    RunOnce


Source & exe attached - the very latest version of MasmBasic is required.

EDIT: Typo Lev1$(ebx) corrected - it must be Lev1$(ecx), of course.
« Last Edit: January 24, 2014, 05:22:07 PM by jj2007 »

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Nested For ... Next loops
« Reply #160 on: January 25, 2014, 02:17:03 PM »
Update 25 January: For ... Next loops will now not enter the first iteration if begin>end, in line with (most if not all) other Basic dialects. Example (from \Masm32\MasmBasic\Res\MbSnippets.asc):

include \masm32\MasmBasic\MasmBasic.inc        ; download
  Init                        ; ## nested For ... Next loops ##
  mov eax, 3
  For_ ebx=0 To eax-1        ; 0...2 (using eax is allowed here)
        ; first iteration is 1 to 0 and will be rejected (2*ebx=0, 2, 4, ...)
        For_ ecx=1 To ebx+ebx
                call MyTest
        Next
        Print
  Next
  Inkey "ok"
  Exit

MyTest proc
LOCAL ct
  For_ ct=0 To 9
        Print "*"
  Next  ct
  Print Str$("\tebx=%i, ", ebx), Str$("ecx=%i\n", ecx)
  ret
MyTest endp
end start

Output:

++++++++++      ebx=1, ecx=1
++++++++++      ebx=1, ecx=2

++++++++++      ebx=2, ecx=1
++++++++++      ebx=2, ecx=2
++++++++++      ebx=2, ecx=3
++++++++++      ebx=2, ecx=4

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: MasmBasic
« Reply #161 on: January 31, 2014, 10:24:23 AM »
31 January 2014 - improved Dll & Declare:

include \masm32\MasmBasic\MasmBasic.inc                ; include this library
.data
MyLongLong        LONGLONG 12345678901234567890
        Init                                       
; initialise the app

        Dll "shimgvw"                ; load the Windows Picture and Fax Viewer Library
        Declare void ImageView_Fullscreen, 4                ; ImageView_Fullscreen expects 4 dwords but returns nothing useful, therefore void
        ImageView_Fullscreen(0, 0, wCL$(1), SW_SHOW)        ; we need the wide version of the commandline arg
       
Err$(1)                                ; there is no retval, so we have to test for errors

        Dll "msvcr100"
        Declare void printf, C:?        ; don't return anything, C calling convention, vararg
        printf(cfm$("MyLongLong is %llX aka %llu\n"), MyLongLong, MyLongLong)        ; MyLL as hex and decimal figure
        Print "MyLongLong is ", Hex$(MyLongLong), Str$(" =  %u\n", MyLongLong)        ; standard MasmBasic syntax, for comparison

        Dll "NtDll"
        Declare RtlRandomEx, 1                ; one arg, _Inout_  PULONG Seed

        Dll "ntoskrnl.exe"
        Declare RtlRandomExNtos, 1 Alias "RtlRandomEx"        ; same but native API - will crash in user mode
        PrintLine "A random number: ", Hex$(RtlRandomEx(addr MyLongLong))

        Exit                        ; do a clean exit, inter alia FreeLibrary
end start

        - Dll performs LoadLibrary, Declare initialises the macro with GetProcAddress, Exit frees the libraries
        - use Declare void SomeFunction, ... if you don't need the return value
        - 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

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Obscure a string
« Reply #162 on: February 26, 2014, 10:25:59 AM »
Update 26 February (download):
- Declare accepts now entire structures:
      Dll Cat$(MbExeFolder$+"ZipDll\Zip32")
      Declare ZpArchive, 1      ; the arg is a ZCL structure with three DWORDs


- the second new feature is in response to questions how to protect one's code. It concerns the RichMasm editor, which has a new entry in the Edit & Format menu:

include \masm32\include\masm32rt.inc

.code   ; You can bet that Olly cannot recognise the code sequence which creates this string!
start:
   exit

end start


Now select the "You can bet..." part and click on Edit & Format, Obscure string.
Result:
include \masm32\include\masm32rt.inc

.code      ; You can bet that Olly cannot recognise the code sequence which creates this string!
start:
; ---- start ----
      call @F
@obs$      dd 0DB771968h,0DB567E06h,0B2240A75h,09257631Dh,0E6771078h,09216750Ah,0F1361D69h,0985E6A49h,0FD3D042Ch,0884C615Fh,0A8290530h,
0CB096058h,0BF29052Bh,0D6476244h,0B5221064h,0C14D7E0Ah,0A02E5E73h,0CC421153h,0B8237927h,098571C45h,0B8397D26h,0984C127Fh
@@:      pop edx
      mov ecx, sizeof @obs$/4
      .Repeat
            mov eax, [edx]
            xor eax, [edx-4]
            add edx, 4
            push eax
            dec ecx
      .Until Zero?

      PrintLine esp        ; that one is for MasmBasic, but...
      print esp            ; ... Masm32 print works fine, too

      add esp, sizeof @obs$
; ---- end ----
      exit

end start


I guess the code is self-explanatory for those who need this feature :P

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4924
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: MasmBasic
« Reply #163 on: February 26, 2014, 10:53:11 AM »
You could try a tool in the masm32 bin directory called "mangle.exe". Once the string is assembled into the binary its a genuine joy to track it down.  :P
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

jj2007

  • Moderator
  • Member
  • *****
  • Posts: 7734
  • Assembler is fun ;-)
    • MasmBasic
Re: Obscure a string
« Reply #164 on: February 26, 2014, 07:51:31 PM »
You could try a tool in the masm32 bin directory called "mangle.exe". Once the string is assembled into the binary its a genuine joy to track it down.  :P
It's in \Masm32, not in \bin, and it works fine :t
My code is shorter, though, and confuses Olly a lot more (and no need to mfree).

Attached a test piece. The exe has two int 3s for use with Olly or any other debugger.
The source requires MasmBasic, of course.