定义标准宏
标准宏定义如下:
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®num
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
! 运算符
感叹号运算符 ('!') 用于指示要按字面方式将特殊字符传递给宏。使用此运算符,可以将逗号 (',') 和尖括号字符('<' 和 '>')(通常被解释为分隔符)传递给宏。