| Win32 Assembly Chapter 11 | |
|---|---|
| Topic Started: Jan 10 2008, 03:37 PM (231 Views) | |
| astropirate | Jan 10 2008, 03:37 PM Post #1 |
![]()
Not Lurking
![]() ![]() ![]()
|
Macros Another Way For Modularization Welcome Hi! Welcome to the eleventh chapter of this series. I hope that you really understood the subroutine concept we discussed in the last chapter. Now, we're going to learn about another way to do code modularization: by grouping them into macros. C/C++ folks probably familiar with macros but others may have no clue. In explaining macro, I don't really detail the definition of macro, but rather to have you examine it and use it in the program. Then, I will wrap up all of these in the conclusion. For now, to simplify matters, think that the macro is just like subroutine, of course it has some differences. You will understand after I explain the whole stuff. Note that I will tightly link this discussion with what we have understood from the last chapter. So, probably you'd like to go back and forth. Macro Syntax How can we define a macro? It's pretty much like defining subroutine. Recall our revised printout subroutine discussed in the last chapter TASM
MASM
Hmm, pretty similar isn't it? You'll quickly notice some differences: * We use macro and endm keyword instead * We may not specify the parameter type. The msgptr is supposedly a word variable, but in macro we may not specify that. * There is no ret instruction at the end. Uhuh... so, how can we call the macro then? It's simple:
So, there is no call or invoke keyword too. The rules in subroutines you know so far also apply here in macros, except that the usual place to declare macros is just before the codeseg keyword (instead of the area 1 and 2). Under the Hood OK, now the real difference between macros and subroutines. Whenever the assembler (not the processor) encounters macro invocation while assembling the program, it replaces that invocation with the definition. It also replaces the arguments appropriately. So, suppose we have this main program:
This program will actually converted to this first:
Ah, so the macros just behave like a broiler-plate of codes. Notice that the parameter msgptr get replaced appropriately. The assembler just stamp the codes out and replaces whenever it encounter a match. Because of this behavior, we (usually) don't need to have ret in macros. Of course we don't want to make our program quit early, right? The subroutines doesn't behave like this. Instead it keeps one copy of the subroutine intact. Everytime there is an invocation, an actuall call instruction is inserted appropriately. Notice also that the assembly do the replacement "dumb-foundedly". It means, prior to the replacement it doesn't check the types (whether legal or not) and other stuffs. Rather, it checked the validity of your program after it gets expanded. This behavior can create a lot of headaches for programmers because: For one particular macros, there are cases that the result of replacement are legal, some aren't depending on how you invoke macros or how the parameters get passed. You probably don't realize this at this moment, but as you go along, you will notice this too. I don't want to explain this now as I think it would be premature. One rule of thumb for building macros is "program defensively" think all possibilities that it may go wrong. Note that: The source code of expanded program is not dumped out or some sort, it exist only in your mind or in the debugger. So, employing macros may hamper you in debugging because you don't know what is actually replaced and the correspondence between the executable code with your .asm files may blur. However, there is a significant advantage too. Since macros use string-based subtitution, you can rearrange your program to benefit from this approach. Notice that the expanded version of the program employs unnecessary pushes and pops. Since you know the context of all printout macro invocation in the program, you know that these can be thrown out. Thus, our macro can be slimmed down by four bytes -- trimming out pushes and pops. This way, you can optimize a bit more than you can do in subroutines. Warning: Do this with great caution since the elimination may make the macros unsafe. But of course, in this example, it's perfectly safe. Pay more attention if your macro uses labels. It is likely that the labels will conflict each others. Why? I leave this for you to answer. :-) Since macro do not need call and ret, macro is considerably faster than subroutines because the calling and returning time penalty is usually high (in more advanced processors, it may involve trashing caches and so on). Recap OK, so the main differences (behavior-wise) are: * Macros use string replacement for its invocation whereas subroutines use calls. * Due to replacement nature, macro can exist multiple copies in the programs whereas subroutine can exist only in one copy. * Because of multiple copies possibility, you cannot obtain a macro's address, whereas you can obtain a subroutine's address. * Macros can be faster since it doesn't have calling and return time penalty. * Macros can be harder to debug since the replacement may obfuscate the resulting code. OK, now.... So when we should use macros or subroutines? The rule of thumb is that: You should generally use subroutines. Macros are ideal to specify small helper routines which tend to be oftenly used, just like printout example above, which involves less than 10 instructions. These kind of macros are easy to make and will speed up your overall code. For larger ones, just go for subroutines. Closing OK, I think that's all for now. See you next time. Roby Joehanes © 2001 |
You will Bow before me!![]() | |
![]() |
|
| 1 user reading this topic (1 Guest and 0 Anonymous) | |
| « Previous Topic · Tutorials · Next Topic » |






![]](http://209.85.62.24/static/1/pip_r.png)




6:56 AM Jul 31