In the course of learning the use of macros, I stumbled over the following:
.nolist
include \masm32\include\masm32rt.inc
CrLf EQU 13,10
.data?
pBuf dd ?
StrBuffer db 256 dup (?)
.code
start:
invoke SetConsoleTitle,chr$("szMultiCat test:")
mov pBuf,offset StrBuffer
print rv(szMultiCat,2,pBuf, chr$(CrLf,CrLf,"Test 1:"), str$(1))
print rv(szMultiCat,2,pBuf, chr$(CrLf,"Test 2:"), str$(2))
print rv(szMultiCat,2,pBuf, chr$(CrLf,"Test 3:"), str$(3))
call ret_key
invoke ExitProcess,0
end start
The three print statements produce the following output:
Test 1:1
Test 1:1
Test 2:2
Test 1:1
Test 2:2
Test 3:3
In a nutshell: szMultiCat appends strings to what it finds in pBuf resp. StrBuffer
To produce the desired behaviour, i.e. new text for every "print", one would have to poke
a zero byte into pBuf. So I wrote an adaptation of szMultiCat called cs2b, "concat string to buffer"
In contrast to szMultiCat, cs2b will always write the first string to the start of the buffer.
Source code and exe are attached. Don't get scared by the length of certain code lines - I am
just testing the limits of macros.
QUESTION: Am I reinventing the wheel? Is there already a macro or function with this behaviour?
P.S.: I also attach my latest AsmBuild, fixing a tiny bug (a reappearing message box asking whether this was a console app - now it will ask only once)
[attachment deleted by admin]
I know of no single macro or procedure that will do this, but you could make a function version of the zero1 macro and use it inline.
zero$ MACRO membuf
 mov membuf[0], 0
 EXITM <ADDR membuf>
ENDM
print cat$(zero$(buff),cfm$("\n\nTest 1:"),str$(1))
print cat$(zero$(buff),cfm$("\n\nTest 2:"),str$(2))
print cat$(zero$(buff),cfm$("\n\nTest 3:"),str$(3),cfm$("\n\n"))
Thanks, Michael, that was very helpful :thumbu
However, cat$ seems to work even without the zero$ macro, at least on my machine... ::)
The High Level Reference help says (confirming your post) "the first byte MUST be set to a
zero byte". The behaviour I observe with the code posted below is different...
I also don't understand why the help calls lpBuffer the source buffer - shouldn't that be destination buffer?
Cheers, jj
cat$
mov lpbuffer, cat$(lpBuffer,[variable number of strings])
Description
Concantenate multiple string into a single buffer.
Parameters
1. lpBuffer The address of the source buffer to append other strings to.
2. A variable number of string addresses to append to the source buffer.
Return Value
The return value is the address of the source buffer that can be assigned back to itself or to another pointer (string handle).
Comments
This is a very flexible and useful macro that can handle multiple strings in different formats.
If the source buffer does not already have string data in it, the first byte MUST be set to a
zero byte so it is seen as a zero length string. The source string buffer must be large enough
to receive all of the string data appended to it.
include \masm32\include\masm32rt.inc
CrLf EQU 13,10
zero$ MACRO membuf
mov membuf[0], 0
EXITM <ADDR membuf>
ENDM
.code
start: invoke SetConsoleTitle,chr$("String testing")
call TestProc
call ret_key
invoke ExitProcess,0
TestProc proc
LOCAL buffer[1024]:BYTE ; max 32000 bytes
LOCAL pBuf:DWORD
lea eax, buffer
mov pBuf,eax
print cat$(zero$(pBuf),cfm$("\nTest 1:"),str$(1))
print cat$(zero$(pBuf),cfm$("\nTest 2:"),str$(2))
print cat$(zero$(pBuf),cfm$("\nTest 3:"),str$(3),cfm$("\n"))
print cat$(cfm$("\nTest 4:"),str$(4))
print cat$(cfm$("\nTest 5:"),str$(5))
print cat$(cfm$("\nTest 6:"),str$(6),cfm$("\n"))
ret
TestProc endp
end start
Just to confirm that cat$ does the job, and is 6 bytes shorter than my cs2b modification of szMultiCat.
39 bytes each:
print cat$(chr$(CrLf,"Test "), chr$("line"))
45 bytes each:
print rv(cs2b,2,pBuf, chr$(CrLf,"Test "), chr$("line"))
Thanks also for pointing me to cfm$. Really handy.
QuoteHowever, cat$ seems to work even without the zero$ macro, at least on my machine... ::)
I used cat$ because it was easier to type than rv(szMultiCat… (IOW because I’m lazy), assuming that it would work the same, and now that I test it, it does.
print cat$(zero$(buff),cfm$("\n\nTest 1:"),str$(1))
print cat$(zero$(buff),cfm$("\n\nTest 2:"),str$(2))
print cat$(zero$(buff),cfm$("\n\nTest 3:"),str$(3),cfm$("\n\n"))
Test 1:1
Test 2:2
Test 3:3
print cat$(ADDR buff,cfm$("\n\nTest 1:"),str$(1))
print cat$(ADDR buff,cfm$("\n\nTest 2:"),str$(2))
print cat$(ADDR buff,cfm$("\n\nTest 3:"),str$(3),cfm$("\n\n"))
Test 1:1
Test 1:1
Test 2:2
Test 1:1
Test 2:2
Test 3:3
Also, the zero$(pBuf) will cause cat$ to use a buffer that starts at the address of pBuf, overwriting anything that follows and/or causing an exception.
And cat$(cfm$("\nTest 4:"),str$(4)) will cause cat$ to write past the end of the data allocated by cfm$.
Quote from: MichaelW on December 12, 2007, 06:50:42 PM
QuoteHowever, cat$ seems to work even without the zero$ macro, at least on my machine... ::)
And cat$(cfm$("\nTest 4:"),str$(4)) will cause cat$ to write past the end of the data allocated by cfm$.
Yep, you are right :red
That's why "it works" as I wanted it to work - cfm$ allocates a fresh buffer every time. In real life, it seems to tolerate about 2k of string len before it produces a GPF. I tried to understand where it allocates the buffer but got a bit lost... what does "buffer CATSTR buffer,..." mean (in macros.asm)? Is CATSTR an EQU?
See Chapter 9, Using Macros, of the MASM Programmer’s Guide, available here (http://webster.cs.ucr.edu/Page_TechDocs/index.html).