Variadic macro instructions

In certain cases, we do not know how many parameters would be passed to the same macro instruction used in different places in our code, and FASM provides a great and easy solution for such a problem--support for variadic macro instructions. The term variadic means that an operator, a procedure, or a macro can take a varying number of operands/parameters.

Syntactically, variadic macro instructions are very simple. We begin with the macro keyword, then the name of the macro followed by a comma-separated list of parameters, if any. The variadic portion of the list of parameters is enclosed in square brackets. For example, should we have a macro instruction that expands to the printf() function or invokes it, and we want it to have a similar declaration, then the macro declaration would start like this:

macro printf fmt, [args]

Here, fmt stands for the format argument of the printf() function and args represents all optional parameters.

Let's consider a very simple example of the reworked prolog macro, which, in addition to the size of a stack frame, receives the list of registers that need to be stored on the stack as they would be altered in the body of a procedure:

macro prolog frameSize, [regs]
{
common
push ebp
mov ebp, esp
sub esp, frameSize
forward
push regs
}

Here, you've definitely noticed the common and forward keywords, which are essential for the correctness of the expansion of this macro instruction. The interesting feature of variadic macro instructions is that the content thereof is expanded for each and every variadic parameter (parameters specified in square brackets). As the creation of the stack frame after each and every register (specified by the regs parameter) is pushed onto stack would look weird, we have to instruct the preprocessor to expand a specific portion of the macro instruction only once, and this is what the common keyword does.

The forward keyword (and its counterpart, the reverse keyword) instructs the preprocessor about the order the variadic parameters should be processed in. The line push regs expands into the push instruction for each parameter specified in regs and the preceding forward keyword instructs the preprocessor to process parameters in exactly the order they were written in. For example, consider the following code:

my_proc:
prolog 8, ebx, ecx, edx
; body of the procedure

This piece of code would expand to the following:

my_proc:
push ebp
mov ebp, esp
sub esp, 8
push ebx
push ecx
push edx

Let's apply proper fixes to the return macro instruction for the sake of completeness:

macro return [regs]
{
reverse
pop regs
common
mov esp, ebp
pop ebp
ret
}

Here, for the sake of an example, we use the reverse keyword, as we specify registers that should be retrieved from stack in exactly the same order in which they were passed to the prolog macro instruction. The procedure would then look like this:

my_proc:
prolog 8, ebx, ecx, edx
; body of the function
return ebx, ecx, edx
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset