📄 汇009.txt
字号:
方法1:使用条件汇编伪指令IF
INPUT MACRO
IF DOS ;当符号DOS不为0时,则使用DOS的功能调用
MOV AH, 1H
INT 21H
ELSE ;否则,将使用BIOS的功能调用
MOV AH, 10H
INT 16H
ENDIF
ENDM
在引用宏INPUT时,汇编程序会根据DOS是否为0来生成调用不同输入功能的程序段。
方法2:使用条件汇编伪指令IFDEF
INPUT MACRO
IFDEF DOS ;当定义了DOS,则使用DOS的功能调用
MOV AH, 1H
INT 21H
ELSE ;否则,将使用BIOS的功能调用
MOV AH, 10H
INT 16H
ENDIF
ENDM
在引用宏INPUT时,汇编程序会根据符号DOS是否已定义来生成调用不同输入功能的程序段。
例9.15 编写一个可用功能调用输入字符的宏定义。
解:
READCH MACRO char
MOV AH, 1H
INT 21H ;接受一个字符,并存入AL中
IFNB <char> ;若参数char有实参与之对应
IFDIF <char>, <AL> ;若参数char≠AL,则把所输入字符保存到实参中
MOV char, AL
ENDIF
ENDIF
ENDM
9.6 宏的扩充
MASM 6.11编程系统对宏定义及其相关语句进行了一定程度的扩充。虽然这些扩充给编程带来了一些方便,但它们不一定能被其它的汇编语言编程系统所接受,所以,程序员在使用这些方便的扩充功能时,要注意到可能带来的限制。
下面介绍MASM 6.11编程系统对宏及其相关语句的扩充。
9.6.1 宏定义形式
在MASM 6.11编程系统中,其宏定义的一般形式如下:
宏名
MACRO [参数1[:tag]] [,参数2[:tag]...]
[LOCAL varlist]
…
[EXITM [value]]
ENDM ;宏定义体内的局部变量和标号
;宏的定义体
对上述宏定义的说明与9.1.1节中的说明完全一致,其需要增加的说明如下:
tag—— 其值可以是REQ、=<缺省值>或VARARG
REQ 指定该参数是不可缺少。在宏引用时,若该参数不对应某个“实参”,那么,汇编程序会报错;
=<缺省值> 在宏引用时,若不指定该参数所对应的“实参”,那么,该参数就取其缺省值;
VARARG 该参数对应一个可变长的实参表,各实参之间用逗号分开;若参数的属性指定为VARARG,那么,该参数一定要是最后一个参数。
有关该属性的应用,请见随后9.6.7节中的举例。
value—— 宏功能的返回值,其为可选项。
9.6.2 重复伪指令REPEAT
重复伪指令REPEAT与前面9.4.1节中伪指令REPT在功能和使用方式方面完全一致,设置该伪指令的主要原因是保证与先前版本的兼容性。
伪指令REPEAT的使用方式如下:
REPEAT
数值表达式
语句序列 ;被重复的汇编语言语句
ENDM
9.6.3 循环伪指令WHILE
循环伪指令WHILE的使用方式如下:
WHILE
Exp
语句序列 ;被重复的汇编语言语句
ENDM
其功能是先判断表达式Exp是否为假(或为0),若是,则终止该伪指令的功能,否则,循环汇编下面的指令块。表达式Exp是能在汇编时计算出其值的数值表达式。
例9.16:编写一个带有参数result和k的宏,其功能是把1+2+…+k的累加和存入result之中,其中:result是不可缺省的,k的缺省值为1。
解:
SUM
MACRO result:REQ, k:=<1>
LOCAL n
n = k
mov result, 0
WHILE n
add result, n
n = n - 1
ENDM
ENDM
有了上面的宏定义,就可书写下面的宏引用来实现其相应的功能:
SUM ax, 10 ;寄存器ax=1+2+3+…+10
SUM bh ;寄存器bh=1,因为第二个形参取其缺省值
SUM ecx, 100 ;寄存器ecx=1+2+3+…+100
SUM data, 20 ;存储单元data=1+2+3+…+20
9.6.4 循环伪指令FOR
循环伪指令FOR与9.4.2节中伪指令IRP在功能上完全一致,设置该伪指令的原因也是为了保证与先前版本的兼容性。
伪指令FOR的使用方式如下:
FOR
parameter[:REQ|:=<default>], <argument [, argument]...>
语句序列 ;被重复的汇编语言语句
ENDM
其中各参数的说明如下:
parameter
一个合法的标识符,它依次取后面参数表中的值。在指令序列中,该变量的每次出现都用其值所替换;
:REQ 说明该变量的取值不能为空;
:=<default> 指定该变量的缺省值,若后面的参数表缺省某个参数(用连续的逗号),这时,该循环变量将取其缺省值;
Argument 参数表中可含有文本、符号、字符串或数值常量,每个参数之间要用逗号分割。
例如:
FOR
data:=<?>, <"123", , 21, 0>
DB data
ENDM
……
FOR
reg:REQ, <ax, bx, dx>
push reg
ENDM
该语句在宏展开时,将得到下列语句:
DB "123"
DB ?
DB 21
DB 0
……
push ax
push bx
push dx
9.6.5 循环伪指令FORC
循环伪指令FOR与9.4.3节中伪指令IRPC在功能上完全一致,它也是为保证与先前版本的兼容性而设置的。
伪指令FORC的使用方式如下:
FORC
parameter, <string>
语句序列 ;被重复的汇编语言语句
ENDM
其中各参数的说明如下:
parameter: 一个合法的标识符,它依次取字符串中的每个字符。在语句序列中,该变量的每次出现都用其值所替换;
String: 一个字符串或被定义为字符串的符号名,字符串中的空格也被算为一个字符。括号"<"、">"是必不可少的。
例如:
FORC
data, <1?3>
DB data
ENDM
……
FORC
reg, <abd>
push reg&x
ENDM
该语句在宏展开时,将得到下列语句:
DB 1
DB ?
DB 3
……
push ax
push bx
push dx
9.6.6 转移伪指令GOTO
转移伪指令GOTO用于实现宏定义体内的转移功能,其使用方式如下:
GOTO 标号
…
:标号 ;标号后不能写指令,但可写注释
…
该伪指令的功能是使汇编程序转移到“标号”处汇编,它只能在宏定义MACRO、REPEAT、WHILE、FOR和FORC等语句块内使用,该标号也只在该语句块内有效。
9.6.7 宏扩充的举例
例9.17:编写一个给任意寄存器或存储单元清零的宏定义。
解:
Clear
list : VARARG
FOR data:REQ, <list>
mov data, 0
ENDM
ENDM
有了上面的宏定义,下面的二个宏引用都是正确的,尽管宏引用时所带的参数个数是不同的。这正是参数属性VARARG的作用所在。
Clear ax, bx
……
Clear si, cl, MemVar, edx ;MemVar是内存变量,任意类型都可以
这二个宏引用展开时所得到的指令如下:
mov ax, 0
mov bx, 0
……
mov si, 0
mov cl, 0
mov MemVar, 0
mov edx, 0
9.6.8 系统定义的宏
MASM 6.11系统定义了大量的标准宏,程序员能很方便地使用它们。在使用这些系统宏之前,要象C语言那样用伪指令INCLUDE把有关“宏库”文件包含在用户的源程序中。主要的系统宏库文件有:DOS.INC和BIOS.INC,它们存放在系统的include子目录中。
例9.18:使用系统宏定义,编写从键盘上读取一个字符。
解:
include dos.inc ;把系统宏定义文件包含在源程序之中
……
@getchar 1, 1 ;引用系统宏定义
……
下面是系统宏@getchar的使用参数描述和定义,其它系统宏的有关信息请参阅相关的宏定义文件。
1、系统宏@GetChar的使用说明
宏的功能: 从键盘读字符
使用语法: @GetChar [echo] [,[break] [,clearbuf]]
参数说明: <echo> ;常量,非零表示“回显”,缺省值为“回显”
<break> ;常量,非零表示接受“^C”,缺省值为“接受”
<clearbuf> ;常量,非零表示清键盘缓冲区,缺省值为“不清”
返回参数: AL=ASCII码
内容破坏: AX,DL(若回显,且不接受^C)
参见内容: INT 21h — 01h、07h、08h和0Ch,@GetStr
2、系统宏@GetChar的定义
该宏定义在宏库文件dos.inc中,其具体宏定义如下:
@GetChar
MACRO ech:=<1>, cc:=<1>, clear:=<0>
LOCAL funct, disp
disp = 1
IF ech
IF cc
funct = 01h ;使用功能1
ELSE
funct = 07h ;使用功能7
disp = 02h ;设置需要回显标志
ENDIF
ELSE
IF cc
funct = 08h ;使用功能8
ELSE
funct = 07h ;使用功能7
ENDIF
ENDIF
IFE clear
mov ah, funct ;置功能号
ELSE
mov ah, 0Ch ;先清输入缓冲区,再接受键盘输入
mov al, funct
ENDIF
Int 21h ;调用DOS功能中断
IF disp EQ 02h ;检查是否需要回显
mov dl, al
mov ah, 02h
int 21h
ENDIF
ENDM
对于上面的宏定义,程序员完全可以把它修改成其它形式的宏定义。
9.7 习题
9.1、在宏定义时,使用的关键字是什么?宏名是否需要成对出现?
9.2、在宏引用时,是否要求实参与形参的个数相等?若不要求,请简述当二者个数不一致时,会出现什么情况。
9.3、宏和子程序的主要区别有哪些?一般在什么情况下选用宏较好,在什么情况下选用子程序较好?
9.4、宏的参数如何传入宏定义体的?宏的参数传递与子程序的参数传递有哪些区别?
9.5、在有标号的宏定义体中,为什么最好使用LOCAL伪指令来说明标号?它在宏定义体中应处于什么位置?
9.6、子程序和宏中的LOCAL伪指令的作用有哪些不同?
9.7、编写32位相加的宏ADD32,它把32位寄存器组BX-AX加到DX-CX中。
9.8、编写符号扩展的宏CBD,它将存于AL中的有符号数扩展成ECX-EBX中64位有符号数(其中:ECX是64位有符号数的高位)。
9.9、编写字母大小写互换的宏Exchange,其有一个形参,允许字节型的寄存器或存储单元作为实参来引用。
9.10、编写一个宏AddList Para1, Para2, num,其功能是将从Para2开始的内存单元的值加到以Para1开始的内存单元中,num是相加的字节数。
9.11、编写一个宏SUM Data, Length, Result,其功能是求从Data开始的字节累加和,并把结果存入字类型参数Result中,Length是需要累加的字节数。
9.12、编写一个宏来定义26个大写字母表。
9.13、编写一个宏,它产生n条NOP指令,其中n是宏的形式参数。
9.14、INCLUDE指示符的作用是什么?
9.15、编写只有一个形式参数的宏PRINT,其具体功能如下:
1)、若引用时带有参数,则在屏幕上显示其参数字符,如:PRINT 'A',则显示字符'A';
2)、若引用时不带实参,则显示回车和换行,如:PRINT。
提示:用IFB或IFNB语句来测试是否有参数。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -