📄 汇009.txt
字号:
ABS MACRO word1
CMP word1, 0
JGE next
NEG word1
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:
ABS MACRO word1
LOCAL next
CMP word1, 0
JGE next
NEG word1
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在子程序和宏中的比较
在子程序中 在宏定义中
语句的位置 在所有指令之前 在所有指令之前
伪指令的作用 说明局部变量 说明局部标号
伪指令的格式 可用一条伪指令来说明多个局部变量,也可连续用多条伪指令来说明 可用一条伪指令来说明多个局部标号,也可连续用多条伪指令来说明
调用或引用 子程序的不同调用,其局部变量名保存不变 在每次宏引用的扩展时,将会自动产生出一个唯一的局部标号
9.4 重复汇编伪指令
在编写源程序时,有时会出现连续相同或相似的语句(组)。当出现这种情况时,可利用重复伪指令来重复语句,从而达到简化程序的目的。
重复汇编伪指令所定义的重复块是宏的一种特殊形式,也是由伪指令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来实现
…
MOV AX, 0
MOV CX, 1000
again: ADD AX, CX
LOOP again
…
由例9.10,不难看出:伪指令REPT与循环指令起作用的时期和方式是截然不同的。它们之间的主要差异如表9.1所列。
表9.1 伪指令REPT与循环指令LOOP之间的主要差异
伪指令REPT
循环指令LOOP
起作用的时期
汇编程序把源文件翻译成目标文件时期
程序的执行时期
起作用的方式
把被重复的指令(组)直接重复写入目标文件
通过反复执行同一指令(组)来实现重复
重复次数对目标文件的影响
由于重复次数决定着被重复指令(组)写入目标文件的次数,所以,改变重复次数一定会改变目标文件的字节数
由于重复的指令数与重复次数无关,所以,改变重复次数不会改变目标文件的字节数
9.4 重复汇编伪指令
在编写源程序时,有时会出现连续相同或相似的语句(组)。当出现这种情况时,可利用重复伪指令来重复语句,从而达到简化程序的目的。
重复汇编伪指令所定义的重复块是宏的一种特殊形式,也是由伪指令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来实现
…
MOV AX, 0
MOV CX, 1000
again: ADD AX, CX
LOOP again
…
由例9.10,不难看出:伪指令REPT与循环指令起作用的时期和方式是截然不同的。它们之间的主要差异如表9.1所列。
表9.1 伪指令REPT与循环指令LOOP之间的主要差异
伪指令REPT
循环指令LOOP
起作用的时期
汇编程序把源文件翻译成目标文件时期
程序的执行时期
起作用的方式
把被重复的指令(组)直接重复写入目标文件
通过反复执行同一指令(组)来实现重复
重复次数对目标文件的影响
由于重复次数决定着被重复指令(组)写入目标文件的次数,所以,改变重复次数一定会改变目标文件的字节数
由于重复的指令数与重复次数无关,所以,改变重复次数不会改变目标文件的字节数
9.4.2 伪指令IRP
伪指令IRP的作用是用每个参数创建一组语句,其重复次数由伪指令后面参数表中参数的个数来确定。其一般使用格式如下:
IRP 形式参数, <实参1, 实参2, ……, 实参n>
重复的语句组
ENDM
例9.11 把16位通用寄存器之值相加,并把结果存入寄存器AX。
解:由于16位通用寄存器名是一些不同的符号,不能用计数的方法来依次访问它们,所以,我们需要用伪指令IRP来实现。
IRP REG, <BX, CX, DX, SP, BP, SI, DI>
ADD AX, REG
ENDM
9.4.3 伪指令IRPC
伪指令IRPC的作用与IRP相似,其实参表是一个字符串,并对字符串中的每个字符创建一组语句,所以,其重复次数是由该字符串中的字符数来确定。其一般使用格式如下:
IRPC 形式参数, 字符串
重复的语句组
ENDM
例9.12 定义10个字节存储单元,保存数字0~9的平方数。
解:
IRPC X, 0123456789
DB X*X
ENDM
例9.13 把16位数据寄存器之值相加,并把结果存入寄存器DI。
解:由于16位数据寄存器是AX、BX、CX和DX,它们的名称中只有第一个字符不同,所以,可以用伪指令IRPC来实现。
XOR DI, DI
IRPC REG, ABCD
ADD DI, REG&X ;符号&是连接运算符
ENDM
9.5 条件汇编伪指令
条件汇编伪指令是告诉汇编程序:根据某种条件确定一组程序段是否加入到目标程序中。使用条件汇编伪指令的主要目的是:同一个源程序能根据不同的汇编条件生成不同功能的目标程序,增强宏定义的使用范围。
条件汇编伪指令与高级语言(如: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功能调用输入字符的宏定义。
解:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -