Hello. Ealier today I ran into a problem when trying something like this:
dostuff MACRO
LOCAL string1, string2, string3
FOR arg, <1, 2, 3>
string&arg byte "&arg", "$"
ENDM
FOR arg <1, 2, 3>
mov dx, OFFSET string&arg
moh ah, 9
int 21h
ENDM
ENDM
ENDM
When I would invoke the macro once, no problem. If I did it a second time, I'd get a lot of messages like this:
test.asm(56) : error A2005: symbol redefinition : debugstringE
MacroLoop(2): iteration 1: Macro Called From
test_doif(11): Macro Called From
test.asm(56): Main Line Code
I would expect these errors if I had not scoped the string1, string2, etc strings as LOCAL, but for some reason, my LOCAL doesn't seem to be respected. Is there some difference between string1 and string&arg where arg=1?
I would love to have been able to do
FOR arg, <1, 2, 3>
local &arg
ENDM
but sadly, local must always preceed a MACRO or repeat loop, so that's illegal, and I'm not sure it would help anyway. Can someone post an example of how to localize a bunch of macrofied vars?
First thing is your macro has no arguments to it.
MyMacro MACRO arg1,arg2,arg3
Try using CATSTR to concantenate strings in a macro.
You can put local in a for loop.
I think the problem is that you have an extra ENDM at the end.
Here is what it should be:
dostuff MACRO
FOR arg, <1, 2, 3>
LOCAL string&arg
ENDM
FOR arg, <1, 2, 3>
string&arg byte "&arg", "$"
ENDM
FOR arg <1, 2, 3>
mov dx, OFFSET string&arg
mov ah, 9
int 21h
ENDM
ENDM
AeroASM, thanks for the tip, but that was pseudo code to illustrate my problem. The problem is, let's say in my for loop, that arg (the variable that holds the current item in the for list) is equal to the string "1" (the first iteration of the for loop). If in my for loop i define string&arg, then I expect it to be local, because I had "local string1" at the top of my macro. However, this is not the case...
the "string1" symbol is somehow different than the string&arg symbol (when arg=1 in this case).
The actual code I was working with looked more like this:
; Run doif on every operator
test_doif macro description, expr_string
local DEBUGM, newline, debugstringE, debugstringNE, debugstringZ, debugstringNZ, debugstringB, debugstringA, debugstringBE, debugstringAE, debugstringC, debugstringNC, debugstringG, debugstringNG, debugstringGE, debugstringL, debugstringNL, debugstringLE
pusha
.data
DEBUGM byte "&description", 0dh, 0ah, "$"
FOR arg1, <E,NE,Z,NZ,B,A,BE,AE,C,NC,G,NG,GE,L,NL,LE>
debugstring&arg1 byte "&arg1: $"
ENDM
newline byte 0dh, 0ah, "$"
.code
mov dx, offset DEBUGM
mov ah, 9
int 21h
FOR arg, <E,NE,Z,NZ,B,A,BE,AE,C,NC,G,NG,GE,L,NL,LE>
doif arg, <&expr_string>
mov dx, offset newline
mov ah, 9
int 21h
ENDM
popa
ENDM
I want to define debugstring&arg1 over and over where arg1 is all of those conditionals (E, NE, etc.). However, even though I made all of them local explicitely, I get the errors I mentioned before when I call the macro more than once.
Any time I try using local inside a for loop I was getting PROC, MACRO, or macro repeat directive must precede LOCAL...
I'll look at CATSTR, thanks... but I wasn't focusing on the macro parameters in my example, just on the concept of building var declarations within a for loop with the substitiution operator (&). No params were required in that example.
obscurite, it is interesting problem, but I've tried different things and nothing works. It seems to be a limitation of MASM - when a symbol is concatenated at time of macro expansion, it probably doesn't test whether the symbol is local or not.
Anyway, do you really need to use such concatenated symbols? Real locals are useless for you?
Quote from: obscurite on May 03, 2005, 06:46:32 AM
The problem is, let's say in my for loop, that arg (the variable that holds the current item in the for list) is equal to the string "1" (the first iteration of the for loop). If in my for loop i define string&arg, then I expect it to be local, because I had "local string1" at the top of my macro. However, this is not the case...
the "string1" symbol is somehow different than the string&arg symbol (when arg=1 in this case).
It seems to me that they are not the same because the names following LOCAL are local identifiers, and string&arg is expanding to a non-local label (a symbol).
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
.NOLIST
.NOCREF
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\macros\macros.asm
.LISTMACROALL
dostuff MACRO
LOCAL string1,string2,string3
.data
string1 db 0
string2 db 0
string3 db 0
FOR arg,<1,2,3>
string&arg byte "&arg",0
ENDM
.code
ENDM
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
.data
.code
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
dostuff
.NOLIST
print ADDR string1
print ADDR string2
print ADDR string3
mov eax,input(13,10,"Press enter to exit...")
exit
; ««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
makeit.bat (modified from the one generated by QE):
@echo off
if not exist rsrc.rc goto over1
\masm32\bin\rc /v rsrc.rc
\masm32\bin\cvtres /machine:ix86 rsrc.res
:over1
if exist "test.obj" del "test.obj"
if exist "test.exe" del "test.exe"
rem -----------------------------------------------
rem \masm32\bin\ml /c /coff "test.asm"
rem Output preprocessed listing to stdout and redirect to file.
rem This will always return an assembly error, but the listings
rem of interest will be easy to find near the end of the file.
rem \masm32\bin\ml /c /coff /EP "test.asm" > listing.txt
rem This will cause MASM to generate a first pass listing and
rem place it in a file named test.lst.
\masm32\bin\ml /c /coff /Sf "test.asm"
rem -----------------------------------------------
if errorlevel 1 goto errasm
if not exist rsrc.obj goto nores
\masm32\bin\Link /SUBSYSTEM:WINDOWS "test.obj" rsrc.res
if errorlevel 1 goto errlink
dir "test.*"
goto TheEnd
:nores
\masm32\bin\Link /SUBSYSTEM:WINDOWS "test.obj"
if errorlevel 1 goto errlink
dir "test.*"
goto TheEnd
:errlink
echo _
echo Link error
goto TheEnd
:errasm
echo _
echo Assembly Error
goto TheEnd
:TheEnd
pause
The listings of interest:
.LISTMACROALL
dostuff MACRO
LOCAL string1,string2,string3
.data
string1 db 0
string2 db 0
string3 db 0
FOR arg,<1,2,3>
string&arg byte "&arg",0
ENDM
.code
ENDM
; ««««««««««««««««««««««««««««««««««««««««««
00000000 .data
00000000 .code
; ««««««««««««««««««««««««««««««««««««««««««
00000000 start:
; ««««««««««««««««««««««««««««««««««««««««««
dostuff
1 LOCAL string1,string2,string3
1
00000000 1 .data
1
00000000 00 1 ??0019 db 0
00000001 00 1 ??001A db 0
00000002 00 1 ??001B db 0
1
1 FOR arg,<1,2,3>
1 string&arg byte "&arg",0
1 ENDM
00000003 31 00 2 string1 byte "1",0
00000005 32 00 2 string2 byte "2",0
00000007 33 00 2 string3 byte "3",0
1
00000000 1 .code
Why do you need to define debugstring&arg1 repeatedly? If you don't, then you could use fixed labels and limit that part of the macro to a single expansion. If you do, then you must use unique labels. To refer to these labels in code I think you would need to equate the unique (unknown) labels to unique (known) names.
What exactly are you trying to do?
Michael, MazeGen, you guys hit the nail on the head. Thanks for the listing file, it was very illuminating.
When MASM expands a macro like this:
test_symbols MACRO
local string1
.data
string1 byte 0
.code
FOR arg, <1>
string1 byte 0
ENDM
ENDM
The symbol "string1" inside the FOR repeat block is evaluated as a local symbol, and you will get a symbol redefinition error (with a symbol name like ??0000 as in the listing file from Michael). That's because we already have a ??000 from the first definition of string1.
However, in a macro like this:
test_symbols MACRO
local string1
.data
string1 byte 0
.code
FOR arg, <1>
string&arg byte 0
ENDM
ENDM
The symbol "string&arg" is NOT evaluated as a local symbol. It evades the same rules that the previous macro follows. Because the symbol becomes "string1" instead of ??0000 or whatever local symbol MASM would normally assign, if we call the macro more than once, we WILL get a symbol redefinition error, because "string1" will be defined in every macro invocation (as opposed to ??000 being redefined a second time as above).
It's been fun figuring this out. Now, if anyone figures out a way to get MASM to create a LOCAL symbol for a string symbol that's generating using the substitution operator (i.e. dynamically), or if you have a better explanation of how this works, please share your wisdom.
Thanks for the help guys. The reason I'm using a FOR repeat block to define variables is because sometimes, for example, you want to output a custom error message. The error message should contain the error code. Therefore, you loop through the error codes and define a string for each error code, rather than manually define them... The example is a little contrived, but the concept is, I want to use repeat blocks to define massive numbers of strings, to save me a lot of time. I'm still not sure how this can be done, as I still need to dynamically create the variable names, but as you can tell, I haven't figured out how to create those variable names (Symbols) in a way that works in macros... I have these local vs literal redefinition problems.
Small note: Also construct similar to the following one is not working:
local string&arg
Quote from: MichaelW on May 03, 2005, 07:45:49 PM
...
rem This would work but I could find no way to suppress the bulk
rem of the listings, and the listings of interest are too hard
rem to find in the ~5MB file.
rem \masm32\bin\ml /c /coff /Sf /Fl "test.asm"
rem -----------------------------------------------
...
My method: Let's say I would like to see what is my macro call doing in all of passes. I copy the line with the macro call from the source file. Then I open the listing file and paste the line in the "Search for" dialog. This way I can very easily see what happened regardless of the listing size. BTW, I use the FAR manager for editing/viewing my assembly files.
You can also control the listing generation with .NOLIST, .LISTALL, .LISTMACROALL and similar directives, check the programmer's manual out.
Hi MazeGen,
Thanks for the suggestions. I am aware of the directives to control listing generation, but in the time I spent on it I could not eliminate the symbol listing. I did a more thorough test just now, and by combining .NOLIST with .NOCREF I was able to get the size of the listing down to ~960KB, and this placed the listings of interest near the end of the file. This did not really provide any more useful information than the /EP method, but it did avoid an assembly error so the batch file could create a listing and an EXE. I have updated my posting.