====== Macros ====== Macros are elements of language that enable the replacement of one text with another. In assembler language, macros can be implemented differently in different assemblers understood as programming software. In this chapter, we'll present the MASM implementation. Although in other assemblers (NASM, NASM) the syntax can differ, the idea remains the same. There are a few types of macros in MASM: * Text macros, * Macro procedures, * Repeat blocks, * Macro functions, * Predefined macro functions, * String directives. We'll go through all of them in the following sections. ===== Text macros ===== Text macros are a simple replacement of one text with another. They are defined with the directive **TEXTEQU**, which creates a new symbolic name and assigns the text to it. The text assigned should be closed in angle brackets, can be a previously defined macro, or can be a text representation of an expression. In the last case, the percent operator calculates the result of an expression and converts it into a text representation. This process is named expansion, and the percent operator is the expansion operator. name TEXTEQU name TEXTEQU macroId | textmacro name TEXTEQU %constExpr Using text macros, it is possible to name some constants like PI, shorten other directives, and even create your own instruction names. pi TEXTEQU <3.1416> ; Floating point constant WPT TEXTEQU ; Sequence of keywords copy TEXTEQU ; New name for mov instruction Previously defined text macro can be redefined later in the program. message TEXTEQU ; Define message with the text "Hello World!" message TEXTEQU ; Redefine message with new text "GoodBye!" The explanation of percent operator is shown in the following code. sum TEXTEQU <3+9> ; sum is the text "3+9" sum TEXTEQU %(3+9) ; sum is the text "12" ===== Macro procedures ===== Macro procedure is a definition of a fragment of code that can be used later in the resulting program. In some assemblers, they are named multi-line macros. The use of the name of a macro procedure later in the source program causes the so-called expansion of the macro. It is worth noting that for every usage of the macro, the assembler will place in the resulting program all elements specified in the statements inside the macro. These include: * instructions, * directives, * pseudoinstructions. The simplest definition of a macro procedure contains the name, **MACRO** keyword, a list of statements inside the macro and **ENDM** at the end name MACRO statements ENDM Please note that no macro name is repeated before the ENDM directive. Macro procedure can have parameters. They are treated as macros themselves, so the use of the parameter name inside the macro results in the replacement of it with the actual value. name MACRO parameterlist statements ENDM An example of a simple macro which makes a copy of data from the source to the destination can be seen in the following code. copy MACRO arg1, arg2 mov RAX, arg1 mov arg2, RAX ENDM Note that the arguments of the macro must fit the instructions used inside it. They can be previously defined variables, the first argument can be a constant, or you can even pass the names of other registers. ; Three following lines with "copy" macro use: copy 65, x_coeff copy x_coeff, RBX copy RBX, RCX ; Will be expanded as mov RAX, 65 mov x_coeff, RAX mov RAX, x_coeff mov RBX, RAX mov RAX, RBX mov RCX, RAX If the actual parameter is empty, it may result in an error in the expanded line, because empty parameters are assumed as empty texts. ; Use of "copy" macro without two parameters copy RBX ; Will result in mov RAX, RBX mov , RAX ; this line is invalid and will cause an error The solution to avoid such a situation is to assign the default parameter value or mark the parameter as required. In the first case, the default value will be assigned to the missing parameter. ; The "copy" macro with default values of parameters copy MACRO arg1:=0, arg2:=RCX mov RAX, arg1 mov arg2, RAX ENDM ; Use of "copy" macro without parameters copy ; Will result in mov RAX, 0 mov RCX, RAX In the second case, the error will be signalled, but at the line with a macro use, not inside the macro, which makes it easier to localise the error. ; The "copy" macro with required parameters copy MACRO arg1:REQ, arg2:REQ mov RAX, arg1 mov arg2, RAX ENDM ; Use of "copy" macro without two parameters copy RBX ; this line will cause an error If a label needs to be defined inside the macro, it must be declared as local to avoid ambiguity. In general, any symbol can be declared as local. In such a case, it will only be visible inside the macro. The **LOCAL** directive can be used for this purpose. It must appear in the line next to the **MACRO** statement. copy MACRO arg1, arg2 LOCAL jump_over, jump_end cmp RAX, arg1 ; test if RAX = arg1 je jump_over ; if yes, do not copy arg1 to RAX mov RAX, arg1 jump_over: cmp RAX, arg2 ; test if RAX = arg2 je jump_end ; if ye,s do not copy RAX to arg2 mov arg2, RAX jump_end: ENDM ===== Repeat blocks ===== Repeat blocks are macros automatically repeated. The number of repetitions depends on the condition, or is determined by the constant value, or the list of argument values. The **REPEAT** block is expanded the number of times specified in the constant expression. REPEAT constexpr Statements ENDM The **WHILE** block is expanded as long as the condition is true. The condition can be any expression that evaluates to zero (false) or nonzero (true). WHILE expression Statements ENDM The **FOR** block is expanded for each argument in a list. The parameter is a placeholder that represents the name of each argument The argument list must contain comma-separated arguments and must be enclosed in angle brackets. FOR parameter, Statements ENDM The **FORC** is similar to **FOR** but takes a string of text rather than a list of arguments. The text must be enclosed in angle brackets. Statements inside the block are assembled once for each character from the string. FORC parameter, Statements ENDM Now, let's examine four repeat blocks that result in the same data definitions. All of them define five bytes with initialising values from 1 to 5. i = 1 REPEAT 5 DB i i = i + 1 ENDM i = 1 WHILE i < 6 DB i i = i + 1 ENDM FOR i,<1,2,3,4,5> DB i ENDM FORC i,<12345> DB i ENDM ===== Macro functions ===== A macro function is a macro which returns a value. As all macros are tex processing feature macro functions always return text. Returning the value is possible with the use of the **EXITM** directive. Argument of the **EXITM** must be text or the result of another macro function. Numeric values must be converted to text form with the expansion operator %. ===== Predefined macro functions ===== Masm offers a set of predefined macro functions, usually used to process texts. There are two versions of them. First is the macro function, which returns the resulting text. Second is the directive, which returns the same text and additionally creates a new symbol. The macro function **@SubStr** returns part of a source string. The **SUBSTR** directive assigns part of a source string to a new symbol. The macro function **@InStr** searches for one string within another and returns its position. The **INSTR** creates a new symbol containing the position of one string in another. The macro function **@SizeStr** determines the string size. The **SIZESTR** creates a new item and assigns to it the size of a string. The macro function **@CatStr** concatenates one or more strings to a single string and returns a concatenated string. The **CATSTR** directive concatenates one or more strings to a newly defined string. The following code demonstrates the equivalent use of both versions. name SUBSTR string, start [, length ] name INSTR [[start]], string, substring name SIZESTR string name CATSTR string [[, string ]] name TEXTEQU @SubStr (string, start [, length ]) name TEXTEQU @InStr ([[start]], string, substring) name TEXTEQU @SizeStr (string) name TEXTEQU @CatStr (string [[, string ]])