5.20 Migration of armasm macros to integrated assembler macros

The armclang integrated assembler provides similar macro features to those provided by armasm. The macro syntax is based on GNU assembler macro syntax.

Note:

This topic includes descriptions of [COMMUNITY] features. See Support level definitions.

Note:

The following GNU assembly directives are [COMMUNITY] features:

  • .macro and .endm
  • .rept and .endr
  • .error

Additional information about macro features is available:

  • The armasm User Guide provides more detail about the macro directives supported, and examples of using macros.
  • The GNU Binutils - Using as document provides more detail about GNU assembly macro directives.

Macro directive features

The following table describes the most common armasm macro directive features, and shows the equivalent features for the armclang integrated assembler.

Table 5-10 Comparison of macro directive features provided by armasm and the armclang integrated assembler

armasm feature armclang integrated assembler feature Description

MACRO,

MEND directives

.macro,

.endm directives

Directives to mark the start and end of the definition of a macro.
{$label} macro parameter Use a normal macro parameter. Optionally define an internal label to use within the macro.
{$cond} macro parameter Use a normal macro parameter. Optionally define a condition code to use within the macro.
{$parameter{,$parameter}...} custom macro parameter specification {parameter{:type}{,parameter{:type}}...} custom macro parameter and parameter type specification

With armasm, any number of custom macro parameters can be defined. Unspecified parameters are substituted with an empty string.

With the armclang integrated assembler, the custom macro parameters can optionally have a parameter type type. This can be either req or vararg. Unspecified parameters are substituted with an empty string.

The req type specifies a required parameter. The assembler generates an error when instantiating a macro if a required parameter is missing and a default value is not available.

The vararg type collects all remaining parameters as one parameter. It can only be used as the last parameter within the list of parameters for a given macro. Only one vararg parameter can be specified.

MEXIT directive .exitm directive Exit early from a macro definition.

IF,

ELSE,

ELIF,

ENDIF conditional assembly directives

.if family of directives,

and the .else,

.elseif,

.endif directives

The directives allow conditional assembly of instructions.

With armasm, the conditional assembly directives use a logical expression that evaluates to either TRUE or FALSE as their controlling expression.

With the armclang integrated assembler, multiple variants of the GNU assembly .if directive are available, referred to as the .if family of directives.

For the .if and .elseif directives, the controlling expression is a logical expression that evaluates to either TRUE or FALSE.

For other directives in the .if family of directives, the controlling expression is an implicit part of the directive used, and varies for each such directive.

WHILE,

WEND directives

.rept,

.endr directives

The directives allow a sequence of instructions or directives to be assembled repeatedly.

With armasm, the WHILE directive uses a logical expression that evaluates to either TRUE or FALSE as its controlling expression. The sequence enclosed between a WHILE and WEND directive pair is assembled until the logical expression evaluates to FALSE.

With the armclang integrated assembler, the GNU assembly .rept directive takes a fixed number of repetitions as a parameter. The sequence enclosed between a .rept and .endr directive pair is assembled the specified fixed number of times.

To replicate the effect of using a logical expression to repeatedly assemble a code sequence, the .rept directive can be used within a macro. See the example provided later in this section.

ASSERT directive Use a combination of the .if family of directives and the .error directive.

With armasm, the ASSERT directive generates an error message during assembly if a given assertion is false. A logical expression that evaluates to TRUE or FALSE is used as the assertion.

With the armclang integrated assembler, this functionality can be achieved by using a GNU assembly directive from the .if family of directives to conditionally display an error message during assembly using the GNU assembly .error directive.

Macros can be created to simplify this process. See the example provided later in this section.

Notable differences between armasm macro syntax and GNU macro syntax

The following syntax restrictions apply to GNU macro syntax in addition to the differences due to macro directives:

  • In armasm macro syntax, using the pipe character | as the parameter value when instantiating a macro selects the default value of the parameter. In GNU macro syntax, leaving the parameter value empty when instantiating a macro selects the default value of the parameter. If a default value is not specified in the macro definition, an empty string is used.
  • In armasm macro syntax, a dot can be used between a parameter and subsequent text, or another parameter, if a space is not required in the expansion. In GNU macro syntax, a set of parentheses () can be used between a parameter and subsequent text, if a space is not required in the expansion. There is no need to separate a parameter from another subsequent parameter.
  • Although the integrated assembler is case-insensitive to register names, the GNU assembly .ifc directive always performs a case-sensitive comparison. Manually check that the register names use the same case-sense when comparing them using the directive.

Migration of macro examples provided in the armasm User Guide

Table 5-11 NOT EQUALS assertion

armasm syntax implementation
    ASSERT  arg1 <> arg2
GNU syntax implementation
    /* Helper macro to replicate ASSERT <> directive
       functionality from armasm.
       Displays error if NE assertion fails. */
    .macro  assertNE arg1:req, arg2:req, message:req
    .ifc \arg1, \arg2
        .error "\message"
    .endif
    .endm

Table 5-12 Unsigned integer division macro

armasm syntax implementation

The macro takes the following parameters:

$Bot

The register that holds the divisor.

$Top

The register that holds the dividend before the instructions are executed. After the instructions are executed, it holds the remainder.

$Div

The register where the quotient of the division is placed. It can be NULL ("") if only the remainder is required.

$Temp

A temporary register used during the calculation.

        MACRO
$Lab    DivMod  $Div,$Top,$Bot,$Temp
        ASSERT  $Top <> $Bot        ; Produce an error message if the
        ASSERT  $Top <> $Temp       ; registers supplied are
        ASSERT  $Bot <> $Temp       ; not all different
        IF      "$Div" <> ""
            ASSERT  $Div <> $Top    ; These three only matter if $Div
            ASSERT  $Div <> $Bot    ; is not null ("")
            ASSERT  $Div <> $Temp   ;
        ENDIF
$Lab
        MOV     $Temp, $Bot               ; Put divisor in $Temp
        CMP     $Temp, $Top, LSR #1       ; double it until
90      MOVLS   $Temp, $Temp, LSL #1      ; 2 * $Temp > $Top
        CMP     $Temp, $Top, LSR #1
        BLS     %b90                      ; The b means search backwards
        IF      "$Div" <> ""              ; Omit next instruction if $Div
                                          ; is null
            MOV     $Div, #0              ; Initialize quotient
        ENDIF
91      CMP     $Top, $Temp               ; Can we subtract $Temp?
        SUBCS   $Top, $Top,$Temp          ; If we can, do so
        IF      "$Div" <> ""              ; Omit next instruction if $Div
                                          ; is null
            ADC     $Div, $Div, $Div      ; Double $Div
        ENDIF
        MOV     $Temp, $Temp, LSR #1      ; Halve $Temp,
        CMP     $Temp, $Bot               ; and loop until
        BHS     %b91                      ; less than divisor
        MEND
GNU syntax implementation

The macro takes the following parameters:

Lab

A label to mark the start of the code. This parameter is required.

BotRegNum

The register number for the register that holds the divisor. This parameter is required.

TopRegNum

The register number for the register that holds the dividend before the instructions are executed. After the instructions are executed, it holds the remainder. This parameter is required.

DivRegNum

The register number for the register where the quotient of the division is placed. It can be NULL ("") if only the remainder is required. This parameter is optional.

TempRegNum

The register number for a temporary register used during the calculation. This parameter is required.

    .macro  DivMod Lab:req, DivRegNum, TopRegNum:req, BotRegNum:req, TempRegNum:req
    assertNE \TopRegNum, \BotRegNum, "Top and Bottom cannot be the same register"
    assertNE \TopRegNum, \TempRegNum, "Top and Temp cannot be the same register"
    assertNE \BotRegNum, \TempRegNum, "Bottom and Temp cannot be the same register"
    .ifnb \DivRegNum
        assertNE \DivRegNum, \TopRegNum, "Div and Top cannot be the same register"
        assertNE \DivRegNum, \BotRegNum, "Div and Bottom cannot be the same register"
        assertNE \DivRegNum, \TempRegNum, "Div and Temp cannot be the same register"
    .endif
\Lab:
    mov     r\TempRegNum, r\BotRegNum           // Put divisor in r\TempRegNum
    cmp     r\TempRegNum, r\TopRegNum,  lsr #1  // double it until
90:
    movls   r\TempRegNum, r\TempRegNum, lsl #1  // 2 * r\TempRegNum > r\TopRegNum
    cmp     r\TempRegNum, r\TopRegNum,  lsr #1
    bls     90b                 // The 'b' means search backwards
    .ifnb \DivRegNum            // Omit next instruction if r\DivRegNum is null
        mov r\DivRegNum, #0     // Initialize quotient
    .endif
91:
    cmp     r\TopRegNum, r\TempRegNum               // Can we subtract r\TempRegNum?
    subcs   r\TopRegNum, r\TopRegNum, r\TempRegNum  // If we can, then do so
    .ifnb \DivRegNum            // Omit next instruction if r\DivRegNum is null
        adc r\DivRegNum, r\DivRegNum, r\DivRegNum   // Double r\DivRegNum
    .endif
    mov     r\TempRegNum, r\TempRegNum, lsr #1  // Halve r\TempRegNum
    cmp     r\TempRegNum, r\BotRegNum           // and loop until
    bhs     91b                                 // less than divisor
    .endm

Notable differences from the armasm syntax implementation:

  • A custom macro, assertNE, is used instead of the armasm ASSERT directive.
  • Register numbers are used instead of registers as parameters. This is because the GNU assembly .ifc directive used for the assertNE assertions treats its operands as case-sensitive.
  • The GNU assembly .ifnb directive is used to check if the parameter DivRegNum has been defined. In the armasm syntax implementation, the armasm IF directive is used.

Table 5-13 Assembly-time diagnostics macro

armasm syntax implementation
        MACRO                        ; Macro definition
        diagnose  $param1="default"  ; This macro produces
        INFO      0,"$param1"        ; assembly-time diagnostics
        MEND                         ; (on second assembly pass)
; macro expansion
        diagnose            ; Prints blank line at assembly-time
        diagnose "hello"    ; Prints "hello" at assembly-time
        diagnose |          ; Prints "default" at assembly-time
GNU syntax implementation
//  macro definition
    .macro  diagnose, param1="default"
    .warning "\param1"
    .endm
//  macro instantiation
    .section "diagnoseMacro", "ax"
    diagnose ""         // Prints a warning with an empty string at assembly-time
                        // Cannot print blank line as the .print directive is not supported
    diagnose "hello"    // Prints a warning with the message "hello" at assembly-time
    diagnose            // Prints a warning with the default message "default"
                        // at assembly-time

Notable differences from the armasm syntax implementation:

  • It is not possible to print a blank line at assembly-time using the GNU assembly .warning directive. Only a warning with an empty message can be printed.
  • The format of the diagnostic message displayed is different between armasm and the armclang integrated assembler.

    With armasm, the diagnostic messages displayed at assembly-time by the macro example are:

    "macros_armasm.S", line 11: 
    "macros_armasm.S", line 12: hello
    "macros_armasm.S", line 13: default

    With the armclang integrated assembler, the diagnostic messages displayed at assembly-time by the macro example are:

    <instantiation>:1:1: warning: 
    .warning ""
    ^
    macros_armclang.S:11:5: note: while in macro instantiation
        diagnose ""
        ^
    <instantiation>:1:1: warning: hello
    .warning "hello"
    ^
    macros_armclang.S:13:5: note: while in macro instantiation
        diagnose "hello"
        ^
    <instantiation>:1:1: warning: default
    .warning "default"
    ^
    macros_armclang.S:14:5: note: while in macro instantiation
        diagnose
        ^

Table 5-14 Conditional loop macro

armasm syntax implementation

The macro takes the following parameters:

$counter

The assembly-time variable for the loop counter. This parameter is required. The {$label} parameter for the MACRO directive has been used for this parameter. If a normal macro parameter is used, the parameter cannot be instantiated as a label.

$N

The maximum number of iterations for the loop. This parameter is required.

$decr

The loop decrement value. This parameter is optional.

do

The text to which $counter is appended in each iteration of the loop. This parameter is required.

    MACRO
$counter    WhileLoop $N, $decr="1", $do  ; macro definition
    ASSERT  "$counter" <> ""              ; check that $counter has been specified
    ASSERT  "$N" <> ""                    ; check that $N has been specified
    ASSERT  "$do" <> ""                   ; check that $do has been specified
    GBLA    $counter                      ; create new local variable $counter
$counter    SETA $N                       ; initialise $counter
    WHILE   $counter > 0                  ; loop while $counter > 0
            $do$counter                   ; assemble in each iteration of the loop
$counter    SETA $counter-$decr           ; decrement the counter by $decr
    WEND
    MEND
; macro instantiation
    AREA    WhileLoopMacro,CODE
    THUMB
counter     WhileLoop 10, 2, "mov r0, #"
    END
GNU syntax implementation

The macro takes the following parameters:

counter

The assembly-time variable for the loop counter. This parameter is required.

N

The maximum number of iterations for the loop. This parameter is required.

decr

The loop decrement value. This parameter is optional.

do

The text to which \counter is appended in each iteration of the loop. This parameter is required.

    /* Macro that inserts the \counter value
       at the end of all \do varargs,
       up to N times. */
    .macro WhileLoop, counter:req, N:req, decr=1, do:vararg
    .set \counter, \N    // initialise the variable \counter to 0
    .rept \N             // loop up to \N times
    .ifgt \counter       // assemble only if \counter is greater than zero
        \do\counter
        .set \counter, \counter-\decr    // decrement the counter by \decr
    .endif
    .endr
    .endm
//  macro instantiation
    .section "WhileLoopMacro", "ax"
    WhileLoop counter, 10, 2, mov r0, #

Note:

The order in which the GNU assembly.ifgt, .endif, .rept, and .endr directives are used is important. Including the .endr directive as a statement within the .ifgt ... .endif structure produces an error. Similarly, placing the .endif directive outside the .rept ... .endr structure produces an error.

The macro expansion produces the following code:

    mov r0, #0xa
    mov r0, #8
    mov r0, #6
    mov r0, #4
    mov r0, #2

Notable differences from the armasm syntax implementation:

  • In the armasm syntax implementation, the ASSERT directive is used to raise an error if a required parameter is missing. In the GNU syntax implementation, this can be achieved by using the parameter type req for required parameters in the macro definition.
  • In the armasm syntax implementation, the macro instantiation uses a string as the value to the $do parameter. The quotes are implicitly removed at assembly-time. Quotes are required as the parameter value contains spaces. In the GNU syntax implementation, this is achieved using the parameter type vararg for the \do parameter in the macro definition.
  • In the GNU syntax implementation, the .rept ... .endr structure is always evaluated \N times at assembly-time. This is because the .ifgt ... .endif structure must be placed within the .rept ... .endr structure. In the armasm syntax implementation, the WHILE...WEND structure is only evaluated the required number of times at assembly-time based on the controlling expression of the WHILE directive.
Non-ConfidentialPDF file icon PDF version100068_0609_00_en
Copyright © 2014–2017 Arm Limited (or its affiliates). All rights reserved.