News:

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

Trouble with MACRO locals and subs operator

Started by obscurite, May 03, 2005, 04:42:11 AM

Previous topic - Next topic

obscurite

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?

hutch--

First thing is your macro has no arguments to it.

MyMacro MACRO arg1,arg2,arg3

Try using CATSTR to concantenate strings in a macro.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

AeroASM

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

obscurite

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.

MazeGen

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?

MichaelW

#5
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.
eschew obfuscation

AeroASM


obscurite

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.

MazeGen

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.

MichaelW

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