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

定义标准宏

标准宏定义如下:

macro-name MACRO <[>parameter-list<]>
<[>LOCAL local-labels<]>
.
.
.
macro-body
.
.
.
     ENDM

macro-name 宏的名称

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

LOAD_R0 MACRO R0_Val
        MOV   R0, #R0_Val
        ENDM

定义一个名为 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

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

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

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

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

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

调用标准宏

下面定义了一个名为 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

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

BMOVE array1, array2, 10

宏扩展为:

		   MOV     R2,#10
           MOV     R1,#array1
           MOV     R0,#array2
??0000:    MOV     A,@R1
           MOV     @R0,A
           INC     R0
           INC     R1
           DJNZ    R2,??0000

注意:

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

局部符号(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

注意:

  • 必须在紧跟在宏定义后面的行中声明本地标签。
  • 一个宏中最多可以定义 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

内置宏

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

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

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

REPT

REPT 内置宏定义如下:

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

例如:

REPT 5
        NOP
ENDM

扩展为:

		NOP
        NOP
        NOP
        NOP
        NOP

IRP

IRP 内置宏定义如下:

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

例如:

IRP regnum, <R0,R1,R2,R3,R4>
        PUSH    A&regnum
ENDM

扩展为:

        PUSH    AR0
        PUSH    AR1
        PUSH    AR2
        PUSH    AR3
        PUSH    AR4

IRPC

IRPC 内置宏定义如下:

IRPC parm-name, parm-string
	macro-body
ENDM

parm-name

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

例如:

IRPC sendchar, TEST
        MOV     R0, #'sendchar'
        CALL    outchar
ENDM

扩展为:

        MOV     R0, #'T'
        CALL    outchar
        MOV     R0, #'E'
        CALL    outchar
        MOV     R0, #'S'
        CALL    outchar
        MOV     R0, #'T'
        CALL    outchar

标准宏运算符

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

符号 描述
NUL NUL 运算符用于确定宏参数是否为 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)

注意:

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

& 运算符

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

MAK_NOP_LABEL MACRO X
        LABEL&X: NOP
        ENDM

此宏为每个调用插入一个新标签和一个 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 FLAG_CLR MACRO FLAGS
2 MOV A, #0
3 IRP F, <FLAGS>
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

在前面的示例中,声明了 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

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

;; 运算符

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

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

! 运算符

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

相关推荐
Crossoads1 天前
【汇编语言】call 和 ret 指令(一) —— 探讨汇编中的ret和retf指令以及call指令及其多种转移方式
android·开发语言·javascript·汇编·人工智能·数据挖掘·c#
Crossoads2 天前
【汇编语言】转移指令的原理(三) —— 汇编跳转指南:jcxz、loop与位移的深度解读
android·汇编·人工智能·redis·单片机·深度学习·机器学习
zhuqiyua3 天前
深入解析Kernel32.dll与Msvcrt.dll
汇编·microsoft·windbg·二进制·dll
Crossoads4 天前
【汇编语言】数据处理的两个基本问题(三) —— 汇编语言的艺术:从div,dd,dup到结构化数据的访问
android·linux·运维·服务器·汇编·机器学习·数据挖掘
Crossoads4 天前
【汇编语言】数据处理的两个基本问题(二) —— 解密汇编语言:数据长度与寻址方式的综合应用
android·java·开发语言·javascript·汇编·数据挖掘·c#
Coding~5 天前
逆向攻防世界CTF系列38-xxxorrr
c语言·汇编·安全
Crossoads5 天前
【汇编语言】数据处理的两个基本问题 —— 汇编语言中的数据奥秘:数据位置与寻址方式总结
android·汇编·人工智能·redis·单片机·深度学习·机器学习
Crossoads6 天前
【汇编语言】更灵活的定位内存地址的方法(一)—— 字符操作:and与or指令、ASCII码及大小写转换
android·linux·运维·服务器·汇编·机器学习·数据挖掘
不会写算法的小沈6 天前
函数栈帧的创建与销毁
c语言·汇编·数据结构
zhuqiyua7 天前
windows二进制安全零基础(二)
汇编·安全·二进制