.IF、.ELSE、.ELSEIF 和 .ENDIF 伪指令使得程序员易于对多分支逻辑进行编码。它们让汇编器在后台生成 CMP 和条件跳转指令,这些指令显示在输出列表文件中。语法如下所示:
方括号表示 .ELSEIF 和 .ELSE 是可选的,而 .IF 和 .ENDIF 则是必需的。condition(条件)是布尔表达式,使用与 C++ 和 Java 相同的运算符 ( 比如:<、>、== 和 !=)。表达式在运行时计算。下面的例子给出了一些有效的条件,使用的是 32 位寄存器和变量:
下面的例子给出的是复合条件:
下表列出了所有的关系和逻辑运算符。
运算符 | 说明 |
---|---|
expr1 == expr2 | 若 expr1 等于 expr2,则返回“真” |
expr1 != expr2 | 若 expr1 不等于 expr2,则返回“真” |
expr1 > expr2 | 若 expr1 大于 expr2,则返回"真” |
expr1 ≥ expr2 | 若 expr1 大于等于 expr2,则返回“真” |
expr1 < expr2 | 若 expr1 小于 expr2,则返回“真” |
expr1 ≤ expr2 | 若 expr1 小于等于 expr2,则返回“真” |
!expr1 | 若 expr 为假,则返回“真” |
expr1expr2 | 对 expr1 和 expr2 执行逻辑 AND 运算 |
expr1 || expr2 | 对 1xprl 和 expr2 执行逻辑 OR 运算 |
expr1 & expr2 | 对 expr1 和 expr2 执行按位 AND 运算 |
CARR1? | 若进位标志位置 11则返回“真” |
OVERFLOW ? | 若溢出标志位置 1,则返回“真” |
PARITY ? | 若奇偶标志位置 1,则返回“真” |
SIGN ? | 若符号标志位置 1,则返回“真” |
ZERO ? | 若零标志位置 1,则返回“真” |
在使用 MASM 条件伪指令之前,一定要彻底了解怎样用纯汇编语言实现条件分支指令。此外,在包含条件伪指令的程序汇编时,要查看列表文件以确认 MASM 生成的代码确实是编程者所需要的。
当使用如 .IF 和 .ELSE 一样的高级伪指令时,汇编器将为程序员编写代码。例如,编写一条 .IF 伪指令来比较 EAX 与变量 val1:
mov eax,6
.IF eax > val1
mov result,1
.ENDIF
假设 val1 和 result 是 32 位无符号整数,当汇编器读到前述代码时,就将它们扩展为下述汇编语言指令,用 Visual Studio 调试器运行程序时可以查看这些指令,操作为:右键点击, 选择 Go To Disassembly。
mov eax,6
cmp eax,val1
jbe @C0001 ;无符号数比较跳转
mov result, 1
@C0001:
标号名 @C0001 由汇编器创建,这样可以确保同一个过程中的所有标号都具有唯一性。
要控制 MASM 生成代码是否显示在源列表文件中,可以在 Visual Studio 中配置 Project 的属性。步骤如下:在 Project 菜单中,选择 Project Properties,选择 Microsoft Macro Assembler,选择 Listing File,再设置 Enable Assembly Generated Code Listing 为 Yes。
当使用 .IF 伪指令来比较数值时,必须认识到 MASM 是如何生成条件跳转的。如果比较包含了一个无符号变量,则在生成代码中插入一条无符号条件跳转指令。如下还是前面的例子,比较 EAX 和无符号双字变量 val1:
.data
val1 DWORD 5
result DWORD ?
.code
mov eax,6
.IF eax > val1
mov result,1
.ENDIF
汇编器用 JBE(无符号跳转)指令对其进行扩展:
mov eax,6
cmp eax,val1
jbe @C0001 ;无符号比较跳转
mov result,1
@C0001:
如果 .IF 伪指令比较的是有符号变量,则在生成代码中插入一条有符号条件跳转指令。例如,val2 为有符号双字:
.data
val2 SDWORD -1
result DWORD ?
.code
mov eax,6
.IF eax > val2
mov result,1
.ENDIF
因此,汇编器用 JLE 指令生成代码,即基于有符号比较的跳转:
mov eax,6
cmp eax,val2
jle @C0001 ;有符号比较跳转
mov result,1
@C0001:
那么,现在可能会有一个问题:如果是两个寄存器进行比较,情况又是怎样的?显然,汇编器无法确定寄存器中的数值是有符号的还是无符号的:
mov eax,6
mov ebx,val2
.IF eax > ebx
mov result,1
.ENDIF
下面生成的代码表示汇编器将其默认为无符号数比较(注意使用的是 JBE 指令):
mov eax, 6
mov ebx,val2
cmp eax, ebx
jbe @C0001
mov result,1
@C0001:
很多复合布尔表达式使用逻辑 OR 和 AND 运算符。用 .IF 伪指令时,符号 || 表示的是逻辑 OR 运算符:
同样,符号 && 表示的是逻辑 AND 运算符:
下面的程序示例中将使用逻辑 OR 运算符。
下例给出的 SetCursorPosition 过程,根据两个输入参数 DH 和 DL,执行范围检查。Y 坐标(DH)范围必须为 0〜24。X 坐标(DL)范围必须为 0〜79。不论发现哪个坐标超出范围,都显示一条错误消息:
SetCursorPosition PROC
; 设置光标位置
; 接收: DL = X坐标, DH = Y坐标
; 检查 DL 和 DH 的范围
; 返回:无
;------------------------------------------------
.data
BadXCoordMsg BYTE "X-Coordinate out of range!",0Dh,0Ah,0
BadYCoordMsg BYTE "Y-Coordinate out of range!",0Dh,0Ah,0
.code
.IF (DL < 0) || (DL > 79)
mov edx,OFFSET BadXCoordMsg
call WriteString
jmp quit
.ENDIF
.IF (DH < 0) || (DH > 24)
mov edx,OFFSET BadYCoordMsg
call WriteString
jmp quit
.ENDIF
call Gotoxy
quit:
ret
SetCursorPosition ENDP
MASM 对 SetCursorPosition 进行预处理时,生成代码如下:
.code
;.IF (dl < 0) || (dl > 79)
cmp dl, OOOh
jb @C0002
cmp dl, 04Fh
jbe @C0001
@C0002:
mov edx,OFFSET BadXCoordMsg
call WriteString
jmp quit
;.ENDIF
@C0001:
;.IF (dh < 0) || (dh > 24)
cmp dh, OOOh
jb @COOO5
cmp dh, 018h
jbe @C0004
@COOO5:
mov edx,OFFSET BadYCoordMsg
call WriteString
jmp quit
;.ENDIF
@C0004:
call Gotoxy
quit:
ret
假设有一个大学生想要进行课程注册。现在用两个条件来决定该生是否能注册:第一个条件是学生的平均成绩,范围为 0〜400,其中 400 是可能的最高成绩;第二个条件是学生期望获得的学分。可以使用多分支结构,包括 .IF、.ELSEIF 和 .ENDIF。示例如下。
.data
TRUE = 1
FALSE = 0
gradeAverage WORD 275 ; 要检查的数值
credits WORD 12 ; 要检查的数值
OkToRegister BYTE ?
.code
main PROC
mov OkToRegister,FALSE
.IF gradeAverage > 350
mov OkToRegister,TRUE
.ELSEIF (gradeAverage > 250) && (credits <= 16)
mov OkToRegister,TRUE
.ELSEIF (credits <= 12)
mov OkToRegister,TRUE
.ENDIF
汇编器生成的相应代码如下所示,用 Microsoft Visual Studio 调试器的 Dissassembly 窗口可以查看该表。(为了便于阅读,已经对其进行了一些整理。)
mov byte ptr OkToRegister,FALSE
cmp word ptr gradeAverage,350
jbe @C0006
mov byte ptr OkToRegister,TRUE
jmp @C0008
@C0006:
cmp word ptr gradeAverage,250
jbe @C0009
cmp word ptr credits,16
ja @COOO9
mov byte ptr OkToRegister,TRUE
jmp @C0008
@C0009:
cmp word ptr credits,12
ja @C0008
mov byte ptr OkToRegister,TRUE
@COOO8:
汇编程序时,如果使用 /Sg 命令行就可以在源列表文件中显示 MASM 生成代码。被定义常量的大小(如当前代码示例中的 TRUE 和 FALSE)为 32 位。所以,把一个常量送入 BYTE 类型地址时,MASM 会插入 BYTE PTR 运算符。