汇编语言.IF、.ELSE、.ELSEIF、.ENDIF伪指令
.IF、.ELSE、.ELSEIF 和 .ENDIF 伪指令使得程序员易于对多分支逻辑进行编码。它们让汇编器在后台生成 CMP 和条件跳转指令,这些指令显示在输出列表文件中。语法如下所示:
在使用 MASM 条件伪指令之前,一定要彻底了解怎样用纯汇编语言实现条件分支指令。此外,在包含条件伪指令的程序汇编时,要查看列表文件以确认 MASM 生成的代码确实是编程者所需要的。
要控制 MASM 生成代码是否显示在源列表文件中,可以在 Visual Studio 中配置 Project 的属性。步骤如下:在 Project 菜单中,选择 Project Properties,选择 Microsoft Macro Assembler,选择 Listing File,再设置 Enable Assembly Generated Code Listing 为 Yes。
.IF conditionl
statements
[.ELSEIF condition2
statements ]
[.ELSE
statements ]
.ENDIF
eax > 10000h
val1 <= 100
val2 == eax
val3 != ebx
(eax > 0) && (eax > 10000h)
(val1 <= 100) || (val2 <= 100)
(val2 != ebx) && !CARRY?
运算符 | 说明 |
---|---|
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 生成的代码确实是编程者所需要的。
生成 ASM 代码
当使用如 .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:
1) 有符号数比较
如果 .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:
2) 寄存器比较
那么,现在可能会有一个问题:如果是两个寄存器进行比较,情况又是怎样的?显然,汇编器无法确定寄存器中的数值是有符号的还是无符号的: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 运算符:
.IF expression1 || expression2
statements
.ENDIF
.IF expression1 && expression2
statements
.ENDIF
1) SetCursorPosition 示例
下例给出的 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 ENDPMASM 对 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
2) 大学注册示例
假设有一个大学生想要进行课程注册。现在用两个条件来决定该生是否能注册:第一个条件是学生的平均成绩,范围为 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 运算符。