汇编常见伪指令
转自: http://blog.chinaunix.net/uid-20547722-id-1647296.html
一、与宏有关的伪指令
在宏定义时,为了满足某种特殊需要,汇编语言还提供了几个伪指令。
9.3.1 局部标号伪指令LOCAL
在宏定义体中,如果存在标号,则该标号要用伪指令LOCAL说明为局部标号,否则,当在源程序中,有多于一次引用该宏时,汇编程序在进行宏扩展后将会给出:标号重复定义的错误。
伪指令LOCAL的一般格式如下:
LOCAL 标号1, 标号2, ……
伪指令LOCAL必须是伪指令MACRO后的第一条语句,并且在MACRO和LOCAL之间也不允许有注释和分号标志。
汇编程序在每次进行宏扩展时,总是把由LOCAL说明的标号用一个唯一的符号(从??0000到??FFFF)来代替,从而避免标号重定义的错误。
例9.7 编写求一个求绝对值的宏。
解:
方法1:
ABSMACROword1
CMPword1, 0
JGEnext
NEGword1
next:
ENDM
假设对宏ABS有以下两次引用,点击它们将会显示汇编程序对它们进行宏扩展后所得到程序片段:
ABS BX
1 CMP BX, 0
1 JGE next
1 NEG BX
1 next:
…
ABS AL
1 CMP AL, 0
1 JGE next
1 NEG AL
1 next:
在上述程序片段中,显然标号next定义了二次,所以,汇编程序将显示“标号重复定义”的错误信息。为了避免这种情况的发生,我们需要用下面的方法来定义该宏。
方法2:
ABSMACROword1
LOCALnext
CMPword1, 0
JGEnext
NEGword1
next:
ENDM
假设对宏ABS有以下两次引用,点击它们将会显示汇编程序对它们进行宏扩展时所得到程序片段:
ABS BX
1 CMP BX, 0
1 JGE ??0000
1 NEG BX
1 ??0000:
…
ABS AL
1 CMP AL, 0
1 JGE ??0001
1 NEG AL
1 ??0001:
在上述程序片段中,宏体内部的局部标号next分别用符号??0000和??0001来对应它的二次引用。因此,汇编程序不会再显示“标号重复定义”的错误信息。
伪指令LOCAL在子程序中也可起作用(参见7.5.10节),但它的作用与宏定义的作用是不同的,有关该伪指令在子程序的宏定义中功能的主要差异如表9.1所列。
表9.1 伪指令LOCAL在子程序和宏中的比较
在子程序中在宏定义中
语句的位置在所有指令之前在所有指令之前
伪指令的作用说明局部变量说明局部标号
伪指令的格式可用一条伪指令来说明多个局部变量,也可连续用多条伪指令来说明可用一条伪指令来说明多个局部标号,也可连续用多条伪指令来说明
调用或引用子程序的不同调用,其局部变量名保存不变在每次宏引用的扩展时,将会自动产生出一个唯一的局部标号
二、取消宏定义伪指令
伪指令PURGE的一般格式如下:
PURGE 宏名1, 宏名2, ……
该伪指令通知汇编程序取消“宏名1, 宏名2, ……”宏名表中的宏定义。在此语句后,如果还有这些宏的引用语句,则汇编程序不会把它们当作宏引用来进行扩展,并且还将显示出错信息。
伪指令PURGE的使用频率较低。
9.3.3 中止宏扩展伪指令
伪指令EXITM的一般格式如下:
EXITM
该伪指令书写在宏定义体中,用来告诉汇编程序:如果遇到该伪指令,那么,立即中止对该伪指令之下语句的扩展。如果在嵌套的内层宏中遇到了该伪指令,则退出到宏嵌套的外层。
在一般情况下,伪指令EXITM与条件伪指令一起使用,以便在不同的条件下挑选出不同的语句。
伪指令EXITM的使用频率也很低。
三、重复汇编伪指令
在编写源程序时,有时会出现连续相同或相似的语句(组)。当出现这种情况时,可利用重复伪指令来重复语句,从而达到简化程序的目的。
重复汇编伪指令所定义的重复块是宏的一种特殊形式,也是由伪指令ENDM来结束重复块。用重复汇编伪指令定义的重复块也可带有参数,并在汇编过程中参数被实参代替,但重复块不会被命名,不能在程序的其它地方引用。
9.4.1 伪指令REPT
伪指令REPT的作用是把一组语句重复指定的次数,该重复次数由伪指令后面的数值表达式来确定。其一般使用格式如下:
REPT数值表达式
重复的语句组
ENDM
例9.8 定义100个初值为32的字节单元,该存储单元的起始符号地址为Table。
解:
方法1:用伪指令REPT来实现左边重复块的汇编结果如下:
Table LABEL TYPE Table LABEL TYPE
REPT 100 DB 32
DB 32 …
ENDM DB 32 ;上述字节定义重复100次
方法2:用重复操作符DUP来实现
Table DB 100 DUP(32)
从上例来看,用伪指令REPT重复定义的存储单元可以用重复操作符DUP来代替,其实前者的功能会更灵活、更强大。
例9.9 定义100个初值分别为1,2,…,100的字节单元,该存储单元的起始符号地址为Table。
解:
Table LABEL TYPE 左边重复块的汇编结果相当于:
COUNT = 1 Table LABEL TYPE
REPT 100 DB 1
DB COUNT DB 2
COUNT = COUNT + 1 …
ENDM DB 100
上面定义了100个字节,其初值为1,2,…,100。本例好象不能用重复操作符DUP来说明字节存储单元。
例9.10 计算1+2+…+1000,并把其值存入寄存器AX。
解:
方法1:用伪指令REPT来实现左边重复块的汇编结果与下面程序段相一致:
… …
MOV AX, 0 MOV AX, 0
COUNT = 1 ADD AX, 1
REPT 1000 ADD AX, 2
ADD AX, COUNT …
COUNT = COUNT + 1 ADD AX, 1000 ;把AX从1累加到1000
ENDM …
… 虽然上面这些语句的执行能完成本例所指定的功能,但它是用1000条加法指令来直接计算的,这1000条指令无疑会大大增加目标代码的长度。
方法2:用循环指令LOOP来实现
…
MOVAX, 0
MOVCX, 1000
again:ADDAX, CX
LOOPagain
…
由例9.10,不难看出:伪指令REPT与循环指令起作用的时期和方式是截然不同的。它们之间的主要差异如表9.1所列。
表9.1 伪指令REPT与循环指令LOOP之间的主要差异
伪指令REPT循环指令LOOP
起作用的时期汇编程序把源文件翻译成目标文件时期程序的执行时期
起作用的方式把被重复的指令(组)直接重复写入目标文件通过反复执行同一指令(组)来实现重复
重复次数对目标文件的影响由于重复次数决定着被重复指令(组)写入目标文件的次数,所以,改变重复次数一定会改变目标文件的字节数由于重复的指令数与重复次数无关,所以,改变重复次数不会改变目标文件的字节数
四、重复汇编伪指令 伪指令IRP
伪指令IRP
伪指令IRP的作用是用每个参数创建一组语句,其重复次数由伪指令后面参数表中参数的个数来确定。其一般使用格式如下:
IRP形式参数, <实参1, 实参2, ……, 实参n>
重复的语句组
ENDM
例9.11 把16位通用寄存器之值相加,并把结果存入寄存器AX。
解:由于16位通用寄存器名是一些不同的符号,不能用计数的方法来依次访问它们,所以,我们需要用伪指令IRP来实现。
IRPREG, <BX, CX, DX, SP, BP, SI, DI>
ADD AX, REG
ENDM
9.4.3 伪指令IRPC
伪指令IRPC的作用与IRP相似,其实参表是一个字符串,并对字符串中的每个字符创建一组语句,所以,其重复次数是由该字符串中的字符数来确定。其一般使用格式如下:
IRPC形式参数, 字符串
重复的语句组
ENDM
例9.12 定义10个字节存储单元,保存数字0~9的平方数。
解:
IRPCX, 0123456789
DB X*X
ENDM
例9.13 把16位数据寄存器之值相加,并把结果存入寄存器DI。
解:由于16位数据寄存器是AX、BX、CX和DX,它们的名称中只有第一个字符不同,所以,可以用伪指令IRPC来实现。
XORDI, DI
IRPCREG, ABCD
ADD DI, REG&X ;符号&是连接运算符
五、条件汇编伪指令
条件汇编伪指令是告诉汇编程序:根据某种条件确定一组程序段是否加入到目标程序中。使用条件汇编伪指令的主要目的是:同一个源程序能根据不同的汇编条件生成不同功能的目标程序,增强宏定义的使用范围。
条件汇编伪指令与高级语言(如:C/C++)的条件编译语句在书写形式上相似,在所起作用方面是完全一致的。
9.5.1 条件汇编伪指令的功能
条件汇编伪指令的一般格式如下:
IFnnnn条件表达式
语句组1
[ELSE
语句组2]
ENDIF
其中:IFnnnn是表9.2中的伪指令,“[…]”内的语句是可选的。
条件汇编伪指令是在汇编程序把源程序转换成目标程序时起作用,其一般含义是:若条件汇编伪指令后面的“条件表达式”为真,那么,语句组1将被汇编;否则,语句组2将被汇编(如果含有ELSE伪指令)。
语句组1或语句组2内还可以包有条件汇编伪指令,这时,就形成了嵌套的条件汇编伪指令。一个嵌套的ELSE伪指令总是与最近的、还没有与其它ELSE伪指令相比配的IFnnnn伪指令相比配。
每条条件汇编伪指令的具体含义如表9.3所示。
表9.3 条件汇编伪指令及其功能一览表
伪指令含义
IF exp若数值表达式exp的值不为0,则语句组1包含在目标文件中
IFE exp若数值表达式exp的值为0,则语句组1包含在目标文件中
IFDEF label若标号label有定义或被说明为EXTRN,则语句组1包含在目标文件中
IFNDEF label若标号label没有定义,也没被说明为EXTRN,则语句组1包含在目标文件中
IFB <参数>在宏引用时,若该形参没有相应的实参相对应,则语句组1包含在目标文件中
IFNB <参数>在宏引用时,若该形参没有相应的实参相对应,则语句组1包含在目标文件中
IFIDN <参数1>, <参数2>若参数1=参数2,则语句组1包含在目标文件中
IFDIF <参数1>, <参数2>若参数1≠参数2,则语句组1包含在目标文件中
IF1若汇编程序在第一遍扫描时,则语句组1包含在目标文件中
IF2若汇编程序在第二遍扫描时,则语句组1包含在目标文件中
9.5.2 条件汇编伪指令的举例
例9.14 编写一个可用DOS或BIOS功能调用输入字符的宏定义。
解:
方法1:使用条件汇编伪指令IF
INPUTMACRO
IFDOS;当符号DOS不为0时,则使用DOS的功能调用
MOV AH, 1H
INT 21H
ELSE;否则,将使用BIOS的功能调用
MOV AH, 10H
INT 16H
ENDIF
ENDM
在引用宏INPUT时,汇编程序会根据DOS是否为0来生成调用不同输入功能的程序段。
方法2:使用条件汇编伪指令IFDEF
INPUTMACRO
IFDEFDOS;当定义了DOS,则使用DOS的功能调用
MOV AH, 1H
INT 21H
ELSE;否则,将使用BIOS的功能调用
MOV AH, 10H
INT 16H
ENDIF
ENDM
在引用宏INPUT时,汇编程序会根据符号DOS是否已定义来生成调用不同输入功能的程序段。
例9.15 编写一个可用功能调用输入字符的宏定义。
解:
READCHMACRO char
MOVAH, 1H
INT21H;接受一个字符,并存入AL中
IFNB<char>;若参数char有实参与之对应
IFDIF <char>, <AL>;若参数char≠AL,则把所输入字符保存到实参中
MOV char, AL
ENDIF
ENDIF
ENDM
六、宏的扩充
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的使用方式如下:
WHILEExp
语句序列;被重复的汇编语言语句
ENDM
其功能是先判断表达式Exp是否为假(或为0),若是,则终止该伪指令的功能,否则,循环汇编下面的指令块。表达式Exp是能在汇编时计算出其值的数值表达式。
例9.16:编写一个带有参数result和k的宏,其功能是把1+2+…+k的累加和存入result之中,其中:result是不可缺省的,k的缺省值为1。
解:
SUMMACRO result:REQ, k:=<1>
LOCAL n
n = k
movresult, 0
WHILEn
add result, n
n = n - 1
ENDM
ENDM
有了上面的宏定义,就可书写下面的宏引用来实现其相应的功能:
SUMax, 10;寄存器ax=1+2+3+…+10
SUMbh;寄存器bh=1,因为第二个形参取其缺省值
SUMecx, 100;寄存器ecx=1+2+3+…+100
SUMdata, 20;存储单元data=1+2+3+…+20
七、循环伪指令FOR
循环伪指令FOR与9.4.2节中伪指令IRP在功能上完全一致,设置该伪指令的原因也是为了保证与先前版本的兼容性。
伪指令FOR的使用方式如下:
FOR parameter[:REQ|:=<default>], <argument [, argument]...>
语句序列;被重复的汇编语言语句
ENDM
其中各参数的说明如下:
parameter一个合法的标识符,它依次取后面参数表中的值。在指令序列中,该变量的每次出现都用其值所替换;
:REQ说明该变量的取值不能为空;
:=<default>指定该变量的缺省值,若后面的参数表缺省某个参数(用连续的逗号),这时,该循环变量将取其缺省值;
Argument参数表中可含有文本、符号、字符串或数值常量,每个参数之间要用逗号分割。
例如:
FORdata:=<?>, <"123", , 21, 0>
DB data
ENDM
……
FORreg: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的使用方式如下:
FORCparameter, <string>
语句序列;被重复的汇编语言语句
ENDM
其中各参数的说明如下:
parameter:一个合法的标识符,它依次取字符串中的每个字符。在语句序列中,该变量的每次出现都用其值所替换;
String:一个字符串或被定义为字符串的符号名,字符串中的空格也被算为一个字符。括号"<"、">"是必不可少的。
例如:
FORCdata, <1?3>
DB data
ENDM
……
FORCreg, <abd>
push reg&x
ENDM
该语句在宏展开时,将得到下列语句:
DB 1
DB ?
DB 3
……
push ax
push bx
push dx
八、转移伪指令GOTO
转移伪指令GOTO用于实现宏定义体内的转移功能,其使用方式如下:
GOTO 标号
…
:标号 ;标号后不能写指令,但可写注释
…
该伪指令的功能是使汇编程序转移到“标号”处汇编,它只能在宏定义MACRO、REPEAT、WHILE、FOR和FORC等语句块内使用,该标号也只在该语句块内有效。
9.6.7 宏扩充的举例
例9.17:编写一个给任意寄存器或存储单元清零的宏定义。
解:
Clearlist : VARARG
FORdata: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