• 基于Keil a51汇编 —— 标准宏定义


    定义标准宏

    标准宏定义如下:

    macro-name MACRO <[>parameter-list<]>
    <[>LOCAL local-labels<]>
    .
    .
    .
    macro-body
    .
    .
    .
         ENDM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    macro-name 宏的名称
    parameter-list 可以传递给宏的形式参数的可选列表

    LOAD_R0 MACRO R0_Val
            MOV   R0, #R0_Val
            ENDM
    
    • 1
    • 2
    • 3

    定义一个名为 LOAD_R0 的宏,该宏使用传递给宏的第一个参数的值加载寄存器 0。

    注意:

    • 与 MPL 宏不同,一旦定义了标准宏,就可能无法重新定义它。
    • 宏可以定义,也可以不带正式参数列表。
    • 标准宏定义最多可嵌套 9 个级别。
    • 标准宏可以递归调用,深度可达 9 级。

    宏参数
    宏主体中的参数用参数名称(在上面的示例中为 src、dst 和 cnt)表示。它们可以在宏体内以任意次数和任何顺序使用。如果参数与宏同名并在宏主体中使用,则会展开该参数。不调用宏。

    标准宏最多可以有 16 个参数。在宏定义和调用中,参数必须用逗号 (‘,’) 分隔。例如:

    mymacro MACRO   P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15,P16
    
    • 1

    您将按如下方式调用此宏:

    mymacro A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P
    
    • 1

    您可以通过从宏调用中省略参数来传递参数的 NULL 值。分隔逗号仍然是必需的。例如:

    mymacro A,,C,,E,,G,,I,,K,,M,,O,
    
    • 1

    空参数可以使用无宏运算符。

    调用标准宏

    下面定义了一个名为 BMOVE 的宏,该宏采用三个参数:源、目标和计数。宏生成的代码将任意数量的字节从内存的一部分复制到另一部分。

    BMOVE   MACRO   src, dst, cnt
    LOCAL   lab
            MOV     R2,#cnt
            MOV     R1,#src
            MOV     R0,#dst
    lab:    MOV     A,@R1
            MOV     @R0,A
            INC     R0
            INC     R1
            DJNZ    R2,lab
            ENDM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    若要调用此宏,请指定宏名称和参数列表。例如,可以按如下方式调用上述宏:

    BMOVE array1, array2, 10
    
    • 1

    宏扩展为:

    		   MOV     R2,#10
               MOV     R1,#array1
               MOV     R0,#array2
    ??0000:    MOV     A,@R1
               MOV     @R0,A
               INC     R0
               INC     R1
               DJNZ    R2,??0000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:

    • 上面的示例会产生程序集错误,因为源文件不包含段定义。
    • 这根和玄美指令可用于在汇编程序列表文件中包括宏定义和宏调用。

    局部符号(LOCAL)

    标准宏可能包括作为分支目标的本地标签。

    固定的标签名称会导致错误,如果多次调用宏。发生这种情况是因为同一标签名称在源文件中只能使用一次。此问题可以通过使用局部符号来解决。本地宏符号将替换为唯一的顺序编号标签,该标签在每次调用宏时递增。

    下面的示例演示如何使用宏定义中的 LOCAL 语句创建本地标签实验室。

    BMOVE   MACRO   src, dst, cnt
    LOCAL   lab
            MOV     R2,#cnt
            MOV     R1,#src
            MOV     R0,#dst
    lab:    MOV     A,@R1
            MOV     @R0,A
            INC     R0
            INC     R1
            DJNZ    R2,lab
            ENDM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意:

    • 必须在紧跟在宏定义后面的行中声明本地标签。
    • 一个宏中最多可以定义 16 个本地标签。

    退出宏(EXITM)

    EXITM 指令立即终止宏扩展。检测到此指令时,宏处理器将停止扩展当前宏,并在下一个 ENDM 指令之后恢复处理。EXITM 指令在条件语句中很有用。例如:

    WAIT    MACRO X            ; macro definition
            IF NUL X           ; make sure X has a value
              EXITM            ; if not then exit
            ENDIF
            REPT X             ; generate X NOP instructions
                    NOP
            ENDM               ; end REPT
    ENDM                       ; end MACRO
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    内置宏

    标准宏处理器具有三个内置宏,可以单独使用,也可以在宏定义中使用。它们是:

    • 这REPT宏,将块重复指定次数的宏。
    • 这IRP宏,为每个指定的参数重复一个块一次。
    • 这IRPC宏,为字符串中的每个字符重复一个块一次。

    这些内置宏中的每一个都必须以 ENDM 语句终止。

    REPT
    REPT 内置宏定义如下:

    REPT count 
    	macro-body
    ENDM
    
    • 1
    • 2
    • 3
    • count 是宏观主体展开的次数
    • macro-body 是调用宏时展开的文本。

    例如:

    REPT 5
            NOP
    ENDM
    
    • 1
    • 2
    • 3

    扩展为:

    		NOP
            NOP
            NOP
            NOP
            NOP
    
    • 1
    • 2
    • 3
    • 4
    • 5

    IRP
    IRP 内置宏定义如下:

    IRP parm-name, 
    	macro-body
    ENDM
    
    • 1
    • 2
    • 3
    • parm-name 是宏主体中使用的参数名称。每次展开宏时,都会使用参数列表中的后续值来替换宏主体中的参数名称。
    • parm-list 是参数名称替换值的列表。

    例如:

    IRP regnum, 
            PUSH    A®num
    ENDM
    
    • 1
    • 2
    • 3

    扩展为:

            PUSH    AR0
            PUSH    AR1
            PUSH    AR2
            PUSH    AR3
            PUSH    AR4
    
    • 1
    • 2
    • 3
    • 4
    • 5

    IRPC
    IRPC 内置宏定义如下:

    IRPC parm-name, parm-string
    	macro-body
    ENDM
    
    • 1
    • 2
    • 3

    parm-name

    • List item 是宏主体中使用的参数名称。每次展开宏时,都会使用参数字符串中的后续字符来替换宏主体中的参数名称。
    • parm-string 是文本字符串,其字符用于替换宏正文中的参数名称。

    例如:

    IRPC sendchar, TEST
            MOV     R0, #'sendchar'
            CALL    outchar
    ENDM
    
    • 1
    • 2
    • 3
    • 4

    扩展为:

            MOV     R0, #'T'
            CALL    outchar
            MOV     R0, #'E'
            CALL    outchar
            MOV     R0, #'S'
            CALL    outchar
            MOV     R0, #'T'
            CALL    outchar
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    标准宏运算符

    有几个运算符可用于标准宏。

    符号描述
    NULNUL 运算符用于确定宏参数是否为 NULL
    &与号运算符用于连接文本和参数
    <>尖括号运算符用于文字化逗号和空格等分隔符
    %百分号运算符用于表示解释为表达式的宏参数
    ;;双分号运算符指示应忽略该行的后续文本
    !感叹号运算符用于表示文字字符

    NUL 运算符
    当宏调用中省略形式参数时,将为其分配值 NULL。可以通过在宏的 IF 控制语句中使用 NUL 运算符来检查 NULL 参数。

    NUL 运算符需要一个参数。如果未找到参数,NUL 将返回值 0。

    EXAMPLE MACRO   X
            IF NUL X
              EXITM
            ENDIF
            NOP
            ENDM
    
    EXAMPLE            ; Invoke the macro with no parameters
    ;Nothing is output since the macro exits (because the x parameter is NULL)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意:

    • 空白字符 (’ ') 的 ASCII 值为 20h,不等同于 NULL。

    & 运算符
    与号运算符 (‘&’) 可用于连接文本和宏参数,如以下示例所示:

    MAK_NOP_LABEL MACRO X
            LABEL&X: NOP
            ENDM
    
    • 1
    • 2
    • 3

    此宏为每个调用插入一个新标签和一个 NOP 指令。传递给宏的参数将追加到文本 LABEL 中,以便为每行创建标签。例如:

    LOC     OBJ     LINE  SOURCE
                    1     MAK_NOP_LABEL MACRO X
                    2             LABEL&X: NOP
                    3             ENDM
                    4
                    5
                    6     MAK_NOP_LABEL 1
    0000    00      7+1   LABEL1: NOP
                    8     MAK_NOP_LABEL 2
    0001    00      9+1   LABEL2: NOP
                    10    MAK_NOP_LABEL 3
    0002    00      11+1  LABEL3: NOP
                    12    MAK_NOP_LABEL 4
    0003    00      13+1  LABEL4: NOP
                    14
                    15    END
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    <> 运算符
    尖括号字符(“<”、“>”)将应按字面传递到宏的文本括起来。某些字符(如逗号 (‘,’)必须括在尖括号中才能传递给宏。

    下面的示例演示在尖括号内传递参数列表的宏声明和调用。

    1 FLAG_CLR MACRO FLAGS
    2 MOV A, #0
    3 IRP F, 
    4 MOV FLAG&F, A
    5 ENDM
    6 ENDM
    7
    8 DSEG
    0000 9 FLAG1: DS 1
    0001 10 FLAG2: DS 1
    0002 11 FLAG3: DS 1
    0003 12 FLAG4: DS 1
    0004 13 FLAG5: DS 1
    0005 14 FLAG6: DS 1
    0006 15 FLAG7: DS 1
    0007 16 FLAG8: DS 1
    0008 17 FLAG9: DS 1
    18
    19 CSEG
    20
    21 FLAG_CLR <1>
    0000 7400 22+1 MOV A, #0
    23+1 IRP F, <1>
    24+1 MOV FLAG&F, A
    25+1 ENDM
    0002 F500 26+2 MOV FLAG1, A
    27 FLAG_CLR <1,2,3>
    0004 7400 28+1 MOV A, #0
    29+1 IRP F, <1,2,3>
    30+1 MOV FLAG&F, A
    31+1 ENDM
    0006 F500 32+2 MOV FLAG1, A
    0008 F501 33+2 MOV FLAG2, A
    000A F502 34+2 MOV FLAG3, A
    35 FLAG_CLR <1,3,5,7>
    000C 7400 36+1 MOV A, #0
    37+1 IRP F, <1,3,5,7>
    38+1 MOV FLAG&F, A
    39+1 ENDM
    000E F500 40+2 MOV FLAG1, A
    0010 F502 41+2 MOV FLAG3, A
    0012 F504 42+2 MOV FLAG5, A
    0014 F506 43+2 MOV FLAG7, A
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在前面的示例中,声明了 FLAG_CLR 宏以清除多个标志变量中的任何一个。FLAGS 参数指定第 3 行中的 IRP 指令使用的参数列表。IRP 指令重复指令 MOV FLAG&F,A 表示 FLAGS 参数中的每个参数。

    FLAG_CLR宏在第 21、27 和 35 行中调用。在第 21 行中,只传递一个参数。在第 27 行中传递三个参数,在第 35 行中传递四个参数。参数列表括在尖括号中,因此可以将其称为单个宏参数 FLAGS。宏生成的代码位于第 26、32-34 和 40-43 行。

    & 运算符
    百分比运算符 (‘%’) 用于将表达式的值传递给宏,而不是传递文本表达式本身。下面的示例演示一个宏定义,该定义需要一个数值以及使用百分比运算符将表达式的值传递给宏的宏调用。

    OUTPORT MACRO   N
            MOV     R1, #N
            ENDM
    
    RESET_SIG EQU 0FFh
    CLEAR_SIG EQU 0
    
    OUTPORT %(RESET_SIG AND NOT 11110000b)
    ; Expands into MOV R1, #15
    
    OUTPORT %(CLEAR_SIG OR 11110000b)
    ; Expands into MOV R1, #240
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    无法将突出显示的行中计算的表达式传递给宏,因为宏需要数值。此类表达式必须在宏之前计算。百分比运算符强制汇编程序生成一个数值,然后将其传递给宏。

    ;; 运算符
    双分号运算符 (‘;;’) 用于指示在展开宏时不应输出行上的其余文本。此运算符通常用于在宏扩展中不需要的注释之前。例如:

    MOV_R_A MACRO   n                       ;; helper macro
            MOV     R&n,A                   ;; set R# to 0
            ENDM
    
    
    REGCLR  MACRO   CNT
            REGNUM  SET 0
            MOV     A, #0                   ;; load A with 0
                    REPT    CNT             ;; rpt for CNT registers
                    MOV_R_A %REGNUM         ;; set R# to 0
                    REGNUM  SET REGNUM+1
                    ENDM
            ENDM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ! 运算符
    感叹号运算符 (‘!’) 用于指示要按字面方式将特殊字符传递给宏。使用此运算符,可以将逗号 (‘,’) 和尖括号字符(‘<’ 和 ‘>’)(通常被解释为分隔符)传递给宏。

  • 相关阅读:
    微信小程序开发02 授权模型: 小程序的用户体系与 OAuth 规范
    Fast-DDS库的安装教程
    递归/回溯刷题(上)
    二十、SpringCloud Alibaba Seata处理分布式事务
    常用设计模式
    手写商用Java虚拟机HotSpot,疯狂磨练技术中
    打包工具webpack的学习
    LTE时域、频域资源
    SpringCloud OpenFeign
    成功实施持续测试的 3 个关键
  • 原文地址:https://blog.csdn.net/elvis02/article/details/133701499