News:

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

Main Menu

MasmBasic

Started by jj2007, May 23, 2012, 10:16:07 PM

Previous topic - Next topic

jj2007

The DDE demo in \Masm32\RichMasm\Res\Masm2Excel.asc works fine with DDE and Excel 2010 Starter Edition on Italian Win7-64. Note that the "starter" editions do not run any macros... but DDE works just fine. Probably some big corporations running legacy software had a chat with the M$ marketing department. Won't go into detail here because it would violate the forum rules  ;-)

                xlsCommand "[new(1)]"                          ; let Excel create a new workbook (works fine on non-English Excel versions!)
                Let esi=xlsSysRead$("Topics")        ; we must find the sheet
                mov ecx, Instr_(esi, "[Book")                ; this might fail for a non-English Excel version
                .if !ecx
                        mov ecx, Instr_(esi, "[Cartel")                ; let's try Italian - OK on Win7-64, Excel Starter 2010 (does not support macros!!)
                .endif
                .if ecx
                        .if Instr_(ecx, esi, Tb$)
                                xlsConnect Mid$(esi, ecx, edx-ecx)          ; connect to the new sheet

jj2007

Update 15 April (download from post #1):
- fixed a bug in Replace$()
- Clip$() had a 160k limit, now the limit is available memory
- Exp10, Exp2, ExpE and ExpXY implemented (special thanks to qWord :t)

jj2007

Update 18 April (download from post #1):

- fixed a bug in StringToArray (the string to numerical array variant pushed one dword too much; the string to string array variant was OK)

- Pelles C "Hello World" example added (see New Masm source in the RichMasm editor)

- SetPoly3 MyArray(ecx) is now possible, i.e. take 3 xy pairs starting with element ecx (where ecx = 0, 2, 4...)

- Str$() accepts now single quotes (rarely needed, but I had a problem with DDE...):
                        mov row, 3
                        xlsCommand Str$('[select("R%iC5")]', row)                ; select("R3C5")

Finally, the handling of resource strings was improved, so that you can use two of them in MsgBoxes, as shown below for Unicode (same for the Ansi version):

include \masm32\MasmBasic\MasmBasic.inc
        Init
        wMsgBox 0, wRes$(2), wRes$(3), MB_OK
        Exit
end start

Rsrc
STRINGTABLE                ; RichMasm creates the rc file "on the fly"; edit the strings, then press F6 to assemble, link & run
BEGIN
  2,        "Нажмите на эту кнопку"        ; "Click on this button" in Russian
  3,        "Добро пожаловать"        ; "Welcome" in Russian
END
Rsrc

Gunther

You're a hard working man, Jochen.  :t Thank you for the update.

Gunther
You have to know the facts before you can distort them.

jj2007

Thanks, Gunther ;-)

In the meantime, I had to fix a little bug: CL$() and wCL$() choked after arg #126. Now the number of arguments from the commandline is apparently limited by Windows; for example, under Win7-32 you can pass up to 32767 bytes via CreateProcess aka Launch(). Little test app attached.

I seized the occasion to add one more macro:

        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)

jj2007

Quote from: jj2007 on May 06, 2013, 08:43:25 PM
In the meantime, I had to fix a little bug: CL$()...

And I introduced a fresh one on that occasion: CL$() with no args (i.e. "return the complete commandline") crashed. Concerns only version 6 May and is now fixed with version 8 May attached on top of this thread. Apologies :redface:

jj2007

It is possible to share data between two applications using the same DLL. In Win-16, that was the default, in Win-32 you need to declare a shared data segment (more at MSDN).

Strangely enough, it is even possible to have a joint memory area between two instances of an application:

include \masm32\MasmBasic\MasmBasic.inc        ; download
MyShared SEGMENT read write shared "BSS"
TheCount        dd ?
MyShared ends

        Init
        inc TheCount
        .if TheCount<=2
                Delay 500        ; if you see "1" on screen more than once, press Ctrl C
                Launch CL$(0)        ; launch another instance
        .endif
        Inkey Str$("n=%i\n", TheCount)
        Exit
end start

Output:
1
2
3

(hit any key three times to exit - there are three inkey statements)

Sharing segments as shown above works for JWasm and Masm 8.0 and higher. For Masm 6.15, there is a workaround as shown below, but it needs the linker option...
/SECTION:.bss,rws
... which fails miserably with POLINK (warning: /SECTION:.bss ignored; section is missing)

.data?
TheCount        dd ?


Credits go to Japheth and qWord

hutch--

JJ,

I have seen IPC done this way but its rather clunky along side a memory mapped file and conventional messaging. The memory mapped file provides the shared memory area of more or less any size you like while the messaging using the HWND_BROADCAST identifier can be used to signal when data is available to any other app that can access the shared memory.

jj2007

Hutch,

I wouldn't call it clunky, it's really as simple as declaring myvar dd 123 in the .data section. Memory-mapped files are great for many purposes, sure.

But I have to admit that I don't see many uses for shared segments. As a proof of concept, I attach a little app that launches itself three times, declaring instance 0 as "server" and 1+2 as "clients". The common segment has an array where each instance stores its window handle, so no need for broadcasting:
MyShared SEGMENT read write shared "BSS"
MyCount   dd ?
hWinByInstance   dd 20 dup(?)
MyShared ends


The server can send a WM_COPYDATA message, the clients can handle them. Instead of that message, shared memory could be used to copy strings from server to client. IIRC, WM_COPYDATA eats around 4,000 cycles, which is quite a lot. Mem to mem should be faster...

On startup, MyCount is increased. Each instance knows its identity, and uses it e.g. to decide the x position on the screen, and whether to add pushbuttons or not.

Antariy

Hi Jochen :t

Yes, I can confirm that this way of IPC in suitable cases is the best possible, just unbeatable: it's simple, reliable and fastest.

As one more proof of the concept: RAM Clear working in Advanced Mode - when it is unlimited with the size of memory it may free - working in that way. It launch "subsystems" - "clients" in your terminology here - and all interaction between server-controller-interface-father and client-subsystem-child is going via shared section of the same EXE.

RAM Clear has multiprocess, multithreaded concept, and in the extents it was designed for, special shared section does its job for IPC best of any other method.

But one needs to remember that with such an IPC way the designed controlling system should be properly designed for preemptiveness and reenterantness - i.e., multithreading (here this is equal to multiprocessing) there is no serialization like in message pump of the target-window process, like if "messages-way" IPC is using. I.e., all working programs can change data in the shared section just simultaneously, and there should be a mechanism to protect "atomical values" from changing simultaneously.

On the alpha-stage RAM Clear there were a fullscale tests with 96 working subsystems (96 child processes launched by a father process). So, this concept is really good one - when it is suitable :t

jj2007

After over 150 downloads, it seems time for an update...

Apart from fixing minor glitches, there are three significant changes in MasmBasic of 26.7.2013:

1. MasmBasic's debug macro has learned to display variables and registers also in hex and binary format:
        deb 1, "On loop entry:", al, ecx, $esi, xmm0, xmm1, ST, ST(5)        ; xmm in lowercase, FPU regs in uppercase
        deb 4, "#4 will show in the console:", xmm0, f:xmm0                ; display xmm0 as integer (default) and float with f: prefix
        deb 5, "#5 will be written to DebLog.txt:", ebx, $My$, $MyArray$(n)
        usedeb=0                        ; disable debugging completely (no code inserted - very handy...)
        deb 1, "This box will never pop up", eax
        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

        If you are still not desperate enough to launch Olly, give deb a try.
        Nothing is more powerful for bug-chasing.

2. ClearLocals replaces ClearLocalVariables and is now much simpler (and a lot faster, too):
        MyTest proc uses edi esi ebx arg1:DWORD, arg2:RECT
        LOCAL v1, v2, rc:RECT, buffer[100]:BYTE
          ClearLocals        ; first line after the LOCALs
          MsgBox 0, Str$("The value of v1: %i", v1), "Test clv:", MB_OK
          ret
        MyTest endp

        ClearLocals is fast, compact (5 bytes per call), and leaves all registers intact.

3. StackBuffer has become simpler, and you can use more than one buffer:
        MyTest proc uses edi esi ebx arg1:DWORD, arg2:RECT
        LOCAL rc:RECT, sbuf1, sbuf2, 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
          PrintLine "Start buffer 1:", Tb$, Hex$(sbuf1)
          PrintLine "Start buffer 2:", Tb$, Hex$(sbuf2)
          StackBuffer()        ; release second buffer
          StackBuffer()        ; release first buffer (sb without args = free the buffer)
          ret
        MyTest endp

   - buffer size is limited by start address of stack;
   - the start address is aligned to 16 bytes for use with SIMD instructions
   - can be combined with ClearLocals, but no zero-init performed for the stack buffer
   - StackBuffer does the stack probing for you

Download the library from the top of this thread.

jj2007

A small addition for version 29 July concerns the crtbuf macro (see also StackBuffer() above):
        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

So SIMD fans can now specify 16-byte alignment (default is DWORD). Note that StackBuffer() is always aligned to 16 bytes but not zeroed.

Gunther

Jochen,

Quote from: jj2007 on July 29, 2013, 07:16:26 PM
So SIMD fans can now specify 16-byte alignment (default is DWORD). Note that StackBuffer() is always aligned to 16 bytes but not zeroed.

good feature. It avoids a GPF in some cases. Thank you.  :t

Gunther
You have to know the facts before you can distort them.

jj2007

MasmBasic version 5 August 2013 is much improved on the deb and Hex$() macros. Example:

include \masm32\MasmBasic\MasmBasic.inc        ; download
        Init
        ; create a WORD array (byte, dword, qword, realX are also valid sizes):
        Dim MyW() As WORD
        mov MyW(0), 100        ; give it a start value
        xor ecx, ecx
        .Repeat
                mov ax, MyW(ecx)
                add ax, cx
                mov MyW(ecx+1), ax
                deb 6, "Fill a WORD array", ecx, MyW(ecx), MyW(ecx+1)        ; print the first 6 iterations to the console
                inc ecx
        .Until ecx>999

        Print Str$("\n%i loops OK\n", ecx)
        Print Str$("MyW(ecx-2)=%i\n", MyW(ecx-2))        ; show the last two values assigned
        Inkey Str$("MyW(ecx-1)=%i\n", MyW(ecx-1))
        Exit
end start


Output:
Fill a WORD array
ecx             0
MyW(ecx)        100
MyW(ecx+1)      100

Fill a WORD array
ecx             1
MyW(ecx)        100
MyW(ecx+1)      101

Fill a WORD array
ecx             2
MyW(ecx)        101
MyW(ecx+1)      103

Fill a WORD array
ecx             3
MyW(ecx)        103
MyW(ecx+1)      106

Fill a WORD array
ecx             4
MyW(ecx)        106
MyW(ecx+1)      110

Fill a WORD array
ecx             5
MyW(ecx)        110
MyW(ecx+1)      115

1000 loops OK
MyW(ecx-2)=38851
MyW(ecx-1)=39849


The Hex$() macro can now handle more sizes, including the 4 DWORDS of xmm registers, and has learned to display exotic formats, such as FPU and XMM registers:

include \masm32\MasmBasic\MasmBasic.inc        ; download
.data
MyByte        db 12h
MyWord        dw 1234h
MyDword        dd 12345678h
MyQword        dq 1234567812345678h
MyReal8        REAL8 1234567812345678r        ; r means the hex representation of a real number
MyPackedQwords dq 11aa22bb33cc44ddh, 55aa66bb77cc88ddh
        Init
        mov ecx, 12345678h
        PrintLine Hex$(cl)        ; reg8, 12 - same for dh, ah etc
        PrintLine Hex$(ch)        ; reg8, 12 - same for dh, ah etc
        PrintLine Hex$(cx)        ; reg16, 1234
        PrintLine Hex$(ecx)        ; reg32, 12345678
        PrintLine Hex$(123)        ; immediate
        PrintLine Hex$(MyByte)        ; global and local variables
        PrintLine Hex$(MyWord)
        PrintLine Hex$(MyDword)
        PrintLine Hex$(MyQword)        ; QWORD will be displayed with one space as 12345678 90123456
        movups xmm1, oword ptr MyPackedQwords
        PrintLine Hex$(xmm1)        ; if QWORD is not enough, get e.g. 11AA22BB 33CC44DD 55AA66BB 77CC88DD
        PrintLine Hex$(MyReal8)        ; same as QWORD
        fldpi
        PrintLine CrLf$, "PI=", Hex$(ST)        ; PI as 10-BYTE hex
        Let esi=CrLf$+"Result = "+Hex$(287454020)+"h"        ; in case you need the trailing h or a leading 0x, use Let or Cat$()
        Inkey esi
        Exit
end start


Output:
78
56
5678
12345678
0000007B
12
1234
12345678
12345678 12345678
11AA22BB 33CC44DD 55AA66BB 77CC88DD
12345678 12345678

PI=4000 C90FDAA2 2168C235

Result = 11223344h


If you find a problem, please let me know.

jj2007

Version 5 August of MasmBasic displayed OWORDs in the incorrect order. The bug is fixed in version 12 August.
The reason, or rather: my bad excuse, is that I try to keep compatibility to good old MASM 6.15 - and the latter doesn't know about OWORDs. My workaround is usual to declare two QWORDs instead:

; MyO   OWORD 00112233445566778899aabbccddeeffh   ; no good for ML 6.15 and lower
; MyO   QWORD 0011223344556677, 8899aabbccddeeffh ; solution #0 - WRONG
MyO     QWORD 8899aabbccddeeffh, 0011223344556677 ; solution #1 - RIGHT


As you can easily guess, I had tested the new Hex$(xmm1) with solution #0 :redface:

Below a simple demo (the deb macro uses Hex$() internally):

include \masm32\MasmBasic\MasmBasic.inc        ; download
.data
O0        OWORD 0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFh
O1        OWORD 0FFFF99FFFFFFFFFFFFFF11FFFFFFFFFFh
O3        OWORD 0ffeeddccbbaa99887766554433221100h

        Init
        movups xmm0, O0
        movups xmm1, O1
        movups xmm3, O3
        movaps xmm2, xmm0        ; copy for pcmpeqb
        PCMPEQB xmm2, xmm1
        deb 4, "x0/1/2", x:xmm0, x:xmm1, x:xmm2, x:xmm3
        Print CrLf$, "16 bits", Tb$, Tb$, Tb$, "5432109876543210", CrLf$
        PMOVMSKB eax, xmm2        ; show in ax where xmm0 differs to xmm1
        xor ecx, ecx
        not ax        ; we are interested in bits set
        deb 4, "MSKB", b:ax
        bsr ecx, eax
        deb 4, "msb set", ecx
        bsf ecx, eax
        deb 4, "lsb set", ecx
        Inkey
        Exit
end start

Output:
x0/1/2
x:xmm0          FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
x:xmm1          FFFF99FF FFFFFFFF FFFF11FF FFFFFFFF
x:xmm2          FFFF00FF FFFFFFFF FFFF00FF FFFFFFFF
x:xmm3          FFEEDDCC BBAA9988 77665544 33221100

16 bits                 5432109876543210
MSKB    b:ax            0010000000100000
msb set ecx             13
lsb set ecx             5