News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

szMultiCat

Started by jj2007, December 11, 2007, 11:06:21 PM

Previous topic - Next topic

jj2007

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]

MichaelW

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"))

eschew obfuscation

jj2007

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

jj2007

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.

MichaelW

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$.
eschew obfuscation

jj2007

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?

MichaelW

See Chapter 9, Using Macros, of the MASM Programmer’s Guide, available here.
eschew obfuscation