汇编语言条件跳转应用及示例


汇编语言做得最好的事情之一就是位测试。通常,不希望改变进行位测试的数值,但是却希望能修改 CPU 状态标志位的值。

条件跳转指令常常用这些状态标志位来决定是否将控制转向代码标号。例如,假设有一个名为 status 的 8 位内存操作数,它包含了与计算机连接的一个外设的状态信息。如果该操作数的位 5 等于 1,表示外设离线,则下面的指令就跳转到标号:

mov al, status
test al, 00100000b          ;测试位 5
jnz DeviceOffline

如果位 0、1 或 4 中任一位置 1,则下面的语句跳转到标号:

mov al, status
test al, 00010011b          ;测试位 0、1、4
jnz InputDataByte

如果是位 2、3 和 7 都置 1 使得跳转发生,则还需要 AND 和 CMP 指令:

mov al, status
and al,10001100b             ;屏蔽位 2、3 和 7
cmp al, 10001100b           ;所有位都置 1 ?
je ResetMachine               ;是:跳转

两个数中的较大数

下面的代码比较了 EAX 和 EBX 中的两个无符号整数,并且把其中较大的数送入 EDX:

    mov    edx, eax                ;假设EAX存放较大的数
    cmp eax, ebx                   ;若 EAX ≥ EBX
    jae L1                               ;跳转到 L1
    mov    edx, ebx                ;否则,将 EBX 的值送入 EDX
L1:                                       ;EDX 中存放的是较大的数

三个数中的最小数

下面的代码比较了分别存放于三个变量 VI、V2 和 V3 的无符号 16 位数值,并且把其中最小的数送入AX:

.data
V1 WORD ?
V2 WORD ?
V3 WORD ?
.code
        mov    ax, V1     ;假设 V1 是最小值
        cmp    ax, V2     ;如果 AX ≤ V2
        jbe    L1             ;跳转到 L1
        mov    ax, V2     ;否则,将 V2 送入 AX
LI:     cmp    ax, V3     ;如果 AX ≤ V3
        jbe L2                ;跳转到L2
        mov    ax, V3     ;否则,将V3送入AX
L2 :

循环直到按下按键

下面的 32 位代码会持续循环,直到用户按下任意一个标准的字母数字键。如果输入缓冲区中当前没有按键,那么 Irvine32 库中的 ReadKey 函数就会将零标 志位置1:

.data
char BYTE ?
.code
L1: mov eax, 10         
        call Delay              ;创建 10 毫秒的延迟;
        call ReadKey        ;检查按键
        jz L1                     ;如果没有按键则循环
        mov char, AL       ;保存字符)

上述代码在循环中插入了一个 10 毫秒的延迟,以便 MS-Windows 有时间处理事件消息。如果省略这个延迟,那么按键可能被忽略。

【示例 1】:顺序搜索数组

常见的编程任务是在数组中搜索满足某些条件的数值。例如,下述程序就是在一个 16 位数组中寻找第一个非零数值。如果找到,则显示该数值;否则,就显示一条信息,以说明没有发现非零数值:
;扫描数组    (ArrayScan.asm)
;扫描数组寻找第一个非零数值
INCLUDE Irvine32.inc

.data
intArray SWORD 0,0,0,0,1,20,35,-12,66,4,0
;intArray SWORD 1,0,0,0            ;候补测试数据
;intArray SWORD 0,0,0,0            ;候补测试数据
;intArray SWORD 0,0,0,1            ;候补测试数据
noneMsg BYTE "A non-zero value was not found",0

.code
main PROC
    mov ebx,OFFSET intArray        ;指向数组
    mov ecx,LENGTHOF intArray      ;循环计数器
L1: cmp WORD PTR [ebx],0           ;将数值与0比较
    jnz found                      ;寻找数值
    add ebx,2                      ;指向下一个元素
    loop L1                        ;继续循环
    jmp    notFound                ;没有发现非零数值
found:
    movsx eax,WORD PTR[ebx]        ;送人EAX并进行符号扩展
    call WriteInt
    jmp quit

notFound:
    mov edx,OFFSET noneMsg         ;显示“没有发现”消息
    call WriteString

quit:
    call Crlf
    exit
main ENDP
END main
本程序包含了可以替换的测试数据,它们已经被注释出来。取消这些注释行,就可 以用不同的数据配置来测试程序。

【示例 2】:简单字符串加密

XOR 指令有一个有趣的属性。如果一个整数 X 与 Y 进行 XOR,其结果再次与 Y 进行 XOR,则最后的结果就是 X:

( ( X ㊉ Y ) ㊉ Y) = X

XOR 的可逆性为简单数据加密提供了一种方便的途径:明文消息转换成加密字符串,这个加密字符串被称为密文,加密方法是将该消息与被称为密钥的第三个字符串按位进行 XOR 操作。预期的查看者可以用密钥解密密文,从而生成原始的明文。

下面将演示一个使用对称加密的简单程序,即用同一个密钥既实现加密又实现解密的过程。运行时,下述步骤依序发生:

1) 用户输入明文。

2) 程序使用单字符密钥对明文加密,产生密文并显示在屏幕上。

3) 程序解密密文,产生初始明文并显示出来。

程序清单完整的程序清单如下所示:
;加密程序    (Encrypt.asm)

INCLUDE Irvine32.inc
KEY = 239                    ;1-255之间的任一值
BUFMAX = 128                 ;缓冲区的最大容量

.data
sPrompt BYTE "Enter the plain text:",0
sEncrypt BYTE "Cipher text",0
sDecrypt BYTE "Decrypted:",0
buffer BYTE BUFMAX+1 DUP(0)
bufSize DWORD ?

.code
main PROC
    call InputTheString        ;输入明文
    call TranslateBuffer       ;加密缓冲区
    mov edx,OFFSET sEncrypt    ;显示加密消息
    call DisplayMessage
    call TranslateBuffer       ;解密缓冲区
    mov edx,OFFSET sDecrypt    ;显示解密消息
    call DisplayMessage
    exit
main ENDP

;-----------------------------------------
InputTheString PROC
;
;提示用户输入一个纯文本字符串
;保存字符串和它的长度
;接收:无
;返回:无
;-----------------------------------------
    pushad                     ;保存32位寄存器
    mov edx,OFFSET sPrompt     ;显示提示
    call WriteString
    mov ecx,BUFMAX             ;字符计数器最大值
    mov edx,OFFSET buffer      ;指向缓冲区
    call ReadString            ;输入字符串
    mov bufSize,eax            ;保存长度
    call Crlf
    popad
    ret
InputTheString ENDP

;-----------------------------------------
DisplayMessage PROC
;
;显示加密或解密消息
;接收:EDX指向消息
;返回:无
;-----------------------------------------
    pushad
    call WriteString
    mov edx,OFFSET buffer        ;显示缓冲区
    call WriteString
    call Crlf
    call Crlf
    popad
    ret
DisplayMessage ENDP

;-----------------------------------------
TranslateBuffer PROC
;
;字符串的每个字节都与密钥字节进行异或
;实现转换
;接收:无
;返回:无
;-----------------------------------------
    pushad
    mov ecx,bufSize                ;循环计数器
    mov esi,0                      ;缓冲区索引初始值赋0
L1:
    xor buffer[esi],KEY            ;转换一个字节
    inc esi                        ;指向下一个字节
    loop L1
    popad
    ret
TranslateBuffer ENDP
END main