I still haven't fully grasped the complete functionality and possibilities of macros in MASM (well, not many people have I guess :)), and I'm having trouble with getting around a limitation that feels like it might very well be possible. I'll now try to explain.

The limitation that I am stuck on is that the execution of macros seem to be of a strictly linear nature. I.e. a macro that is called on line X in the listing cannot it any way affect the code that is generated at line Y, when Y < X.

Is this statement wrong, or is there really no way around this limitation?
It practically has the same limiting effect as if programs would be without any kind of jump or call instructions, simply forcing them to execute linearly like a simple static script, and it would indeed be very sad if that was the case. :(


Allow me to present a simple example to make the question a little more clear:

Let's say I have a global struct in my program. Depending on which procedures are used in the program, different fields are needed in this struct. I would like to add any field to the struct as soon as the program includes code that uses this field, but I don't want to have any unused fields in the struct.

Here is sample code for such a situation:



dynamic_field_1 TEXTEQU <> ; Declaration of global macro var 1
dynamic_field_2 TEXTEQU <> ; Declaration of global macro var 2

; This macro builds the dynamic struct
BUILD_DYNAMIC_STRUCT MACRO
.data
dyn_struct_type STRUCT
default_field dword ?
% dynamic_field_1
% dynamic_field_2
dyn_struct_type ENDS
.code
ENDM

; This macro defines and references a field
; in the global dynamic structure
DEFINE_AND_USE_DYN_FIELD_1 MACRO
dynamic_field_1 TEXTEQU <dyn_field_1 dword ?>
mov eax, dyn_struct.dyn_field_1
ENDM

; This macro defines and references another field
; in the global dynamic structure
DEFINE_AND_USE_DYN_FIELD_2 MACRO
dynamic_field_2 TEXTEQU <dyn_field_2 dword ?>
mov eax, dyn_struct.dyn_field_2
ENDM


; ####### This is the code of the actual program #######

.code
BUILD_DYNAMIC_STRUCT
DEFINE_AND_USE_DYN_FIELD_1
DEFINE_AND_USE_DYN_FIELD_2

.data
dyn_struct dyn_struct_type {}



When attempting to compile this, it will report an error, saying that "dyn_field_1" and "dyn_field_2" are undefined symbols. This is because the macros are executed linearly, so when the dynamic struct is built, the macro variables "dynamic_field_1" and "dynamic_field_2" are still empty, even though they are set later, before the actual reference. :(


And I know that in this specific example I could simply put the struct generation after the definitions, like this:



DEFINE_AND_USE_DYN_FIELD_1
DEFINE_AND_USE_DYN_FIELD_2
BUILD_DYNAMIC_STRUCT


But this would not solve my problem or answer my question, it would only be a workaround for this simple example.


A more general example would be the following:



INIT_ALL_USED_RESOURCES ; Macro initializing all used resources
DEFINE_AND_USE_RESOURCE_1 ; Macro defining and using resource 1
DEFINE_AND_USE_RESOURCE_2 ; Macro defining and using resource 2



Is there really no way to make macros affect code/data output that is located on a lower line-number in the listing? Not in any way? It feels like a relatively simple thing to implement, MASM being a multi-pass compiler and everything, and it would add so much more power to the macros, so I still haven't lost hope that it might actually be possible.

If anyone would have the slightest idea or clue about how to do this, I would be eternally grateful. :)

Thanks!
Posted on 2003-01-08 08:55:47 by dELTA
The limitation that I am stuck on is that the execution of macros seem to be of a strictly linear nature. I.e. a macro that is called on line X in the listing cannot it any way affect the code that is generated at line Y, when Y < X.
Generally speaking this is a limitation of MASM due to the linear nature of the assembly passes. Specific mechanisms can be devised to overcome this limitation. For example, see my switch/case macro.

The switch/case macro shows how to use a dynamic array of values. Other basic data structures can be created and managed within TEXTEQU's to accomplish more complex features.

Your example is flawed in that the structure in BUILD_DYNAMIC_STRUCT can't be redefined unless it has a different name. To accomplish this you would have to abstract out the structure name into a TEXTEQU and change that to reflect the current structure being used. Alternately, you could manage the structure with the macro. We can quickly see how convoluted this will be.

I must say I am quite impressed with your understanding and detailed questions. :alright:
Posted on 2003-01-08 11:33:12 by bitRAKE
I don't think I've even seen any assembler that allows a macro "backward reference" like you're talking about. Even the macro processor in the IBM mainframe assembler, which is very powerful, is still top to bottom.

:)
Posted on 2003-01-08 17:00:21 by S/390
bitRAKE, I looked at your switch/case macros, but sadly I found it a bit hard to isolate the exact code you are referring to. :( Could you possibly post a short conceptual example here of what you mean? Would it be applicable to my example? Even the more generic example?


The best solution I can come up with for the generic example is the following:



call init_all_used_resources ; Calls a label in the INIT_ALL_USED_RESOURCES macro
DEFINE_AND_USE_RESOURCE_1 ; Macro defining and using resource 1
DEFINE_AND_USE_RESOURCE_2 ; Macro defining and using resource 2
jmp exit_program
INIT_ALL_USED_RESOURCES ; Macro initializing all used resources,
; ending with a ret instruction

It adds one call-instruction and one ret-instruction to the code, and makes it a bit uglier just in general, but I guess I'll just have to live with that then. :)

And for dynamic structures, the structure definition can simply be moved below the code, as in my suggested "work-around solution" for the example in my original post. This will add no extra size or complexity to the produced exe, only some uglyness to the source code. :)

And yes, I know that the struct-code in my example is flawed, generally crappy and far from nice or optimal :), I just wanted to make it as simple as possible, to show the concept in the cleanest possible way, so that people wouldn't get tired of reading it after getting half-way through it. :)


I must say I am quite impressed with your understanding and detailed questions

Just typical, here I am, getting complimented for my understanding and everything, and then I blow it all by not understanding some things that you write in the very same post. ;) Well, I try my best anyway. :rolleyes:

Thanks!
Posted on 2003-01-08 18:02:22 by dELTA
S/390, thanks for the info. I wonder why noone did this though? It would obviously increase the power of the macro language quite much, and at first glance it should not be very hard to implement either? At least not compared to some other things that are already present in most compilers already, it would seem to me.

One might e.g. be allowed to assign an "evaluation layer" number to the macro calls, defining in which sweep the macro call in question should be executed/evaluated.

In my example (from my original post in this thread) it might look like this:




INIT_ALL_USED_RESOURCES{2} ; Macro initializing all used resources
DEFINE_AND_USE_RESOURCE_1{1} ; Macro defining and using resource 1
DEFINE_AND_USE_RESOURCE_2{1} ; Macro defining and using resource 2


Where the "evaluation layer" number is inside the curly braces for each macro call.

On compile time, the compiler would then repeatedly sweep over the source listing, only evaluating macro statements, until all specified sweeps are done (macro statements without an "evaluation layer" number should be considered belonging to the first sweep), and after this the compiler would proceed with its normal business, checking the syntax of the complete listing, producing binary code and so on.

I cannot foresee any possible problems or hardships implementing this? Have I missed to consider something, or did the compiler developers just underestimate the potentially huge usefulness of such a feature? What do you think?
Posted on 2003-01-08 18:24:34 by dELTA
dELTA, why isn't a UNION effective? I have not seen enough of what your trying to accomplish to code a working example. The layering feature would be nice - I've had the same idea myself. Usually, there are sufficient abstraction layers to linearize the code - usually easier to manage code, too. ;)
Posted on 2003-01-08 23:14:02 by bitRAKE
bitRAKE, a UNION data structure is not the kind of solution I'm looking for. It would maybe work as a work-around solution in my stupid example (I already admitted that this example sucked, you don't have to rub it in :tongue: ), but as I said, that example is just a way to demonstrate the following, more general problem:



INIT_ALL_USED_RESOURCES ; Macro initializing all used resources
DEFINE_AND_USE_RESOURCE_1 ; Macro defining and using resource 1
DEFINE_AND_USE_RESOURCE_2 ; Macro defining and using resource 2

In which I want the execution of "DEFINE_AND_USE_RESOURCE_1" and "DEFINE_AND_USE_RESOURCE_2" to affect the output of "INIT_ALL_USED_RESOURCES".

Without moving any of these three macros relative to each other in the code (like I did in my call/ret solution above), but with all other methods allowed, would your tricks be able to accomplish anything like that?

If anyone else has any suggestions or tips too, they are of course just as welcome.

Thanks!
Posted on 2003-01-09 05:11:57 by dELTA
dELTA, I do not know of a general solution, but specific work arounds are possible. Generally speaking though, you would create all the data in TEXTEQU's and finally do the code generation on the end - see the switch/case macro. :)
Posted on 2003-01-09 08:38:11 by bitRAKE
Yes, but as long as the code-generating macro is positioned higher up in the listing than any of the macros that define the TEXTEQU:s, this is still not possible, right? Because it is impossible to communicate data with TEXTEQU:s "upward" in the listing, right? And then you'd still have to resort to my call/ret work-around solution to make sure that the generated code is located before the code in all of the other referencing macros in the control flow during execution of the program, and that is exactly what I'm trying to avoid. :(

Hence, as far as I can see, in order to get your TEXTEQU-technique to work, the involved macros can be seen as a set, in which the code generation always occurs in the macro that is located last in this set (farthest down in the listing), as seems to be the case with your switch/case macros (?), and then we have again just made a work-around solution, not addressing the problem of affecting the execution of a macro higher up in the listing with another macro lower down in the listing, right? :(

In my resource-init example this would not work (without using my earlier mentioned call/ret solution), since the resource initialization code will have to be executed before the first reference to a resource during the execution of the program. :(

Please tell me if there's something that I still don't seem to understand, and in that case it would be really great if you could show how to communicate data with TEXTEQU:s upward in the listing, causing the code generation to occur in a macro that is not the last one in the set (with "last one" I mean the one that is called farthest down in the listing).

Thanks!
Posted on 2003-01-09 10:56:04 by dELTA