• 汇编语言.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 运算符。

更多...

加载中...