汇编语言.IF、.ELSE、.ELSEIF、.ENDIF伪指令

广告位

.IF、.ELSE、.ELSEIF 和 .ENDIF 伪指令使得程序员易于对多分支逻辑进行编码。它们让汇编器在…

.IF、.ELSE、.ELSEIF 和 .ENDIF 伪指令使得程序员易于对多分支逻辑进行编码。它们让汇编器在后台生成 CMP 和条件跳转指令,这些指令显示在输出列表文件中。语法如下所示:

.IF conditionl
    statements
[.ELSEIF condition2
    statements ] [.ELSE
    statements ] .ENDIF

方括号表示 .ELSEIF 和 .ELSE 是可选的,而 .IF 和 .ENDIF 则是必需的。condition(条件)是布尔表达式,使用与 C++ 和 Java 相同的运算符 ( 比如:<、>、== 和 !=)。表达式在运行时计算。下面的例子给出了一些有效的条件,使用的是 32 位寄存器和变量:

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

同样,符号 && 表示的是逻辑 AND 运算符:

.IF expression1 && expression2
    statements
.ENDIF

下面的程序示例中将使用逻辑 OR 运算符。

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 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

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 运算符。

关于作者: 汇编语言

为您推荐

广告位

发表评论