前言
程序不可能完全的顺序去执行,有时候我们会使用选择、循环结构去设计,本章记录汇编中的选择结构程序设计,也就是学习条件转移指令,在Win32汇编中有两种方式根据条件执行不同的逻辑,第一种是条件转移指令,这类指令根据标志位的值进行跳转,另一种是.IF、.ELSE等伪指令。
条件转移指令
条件转移指令就是根据测试的条件是否满足,如果满足,则进行转移,条件测试指令测试标志寄存器中一个或多个标志是否满足要求,如果满足要求,则控制转移到目的地址,如果不能满足要求,CPU就忽略条件转移指令并顺序执行,比如,在逻辑左移指令执行后,可以测试CF标志位判断操作数移出的最高位是否为1。
示例JZ
JZ指令就是当ZF标志位等于1时跳转,ZF标志位是运算结果位0时该标志位等于1,否则为0。
在下面代码中,TEST按逻辑与运算,8 AND 7结果位0,那么ZF标志位为1,紧跟后面的jz 就会发生跳转,如果8 AND 8时,结果不为0,所以ZF为0,所以不跳转。
.386
.Model Flat, StdCall
Option Casemap :None
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib
includelib C:\masm32\lib\msvcrt.lib
printf PROTO C : dword,:vararg
system PROTO C : dword,:vararg
.data
szMessage db 'a',10,13,0
bTest1 dword -1
szPause db 'pause',10,13,0
szOutFmt1 byte '未跳转',10,13,0
szOutFmt2 byte '跳转',10,13,0
.code
x dword 5
START:
mov eax,8
test eax,7
jz Exit
invoke printf,addr szOutFmt1
invoke system ,addr szPause
Exit:
invoke printf,addr szOutFmt2
invoke system ,addr szPause
END START
.IF 伪指令
我们也可是使用高级语言中类似的IF、ELSE来实现,这种方法更简单,更直观,主要是以前都要先执行一条能影响这些指令关心的标志位的指令,这样我们就必须要知道哪些指令能影响这些标志位,所以MASM汇编器就提供了.IF类似高级语法的伪指令。
下面是判断eax寄存器中如果值为8,那么输出为等于,否则输出不等于,当然我们可以加入.ELSEIF在进一步判断。
.386
.Model Flat, StdCall
Option Casemap :None
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib
includelib C:\masm32\lib\msvcrt.lib
printf PROTO C : dword,:vararg
system PROTO C : dword,:vararg
.data
szMessage db 'a',10,13,0
bTest1 dword -1
szPause db 'pause',10,13,0
szOutFmt1 byte '等于',10,13,0
szOutFmt2 byte '不等于',10,13,0
.code
x dword 5
START:
mov eax,8
.if eax == 8
invoke printf,addr szOutFmt1
invoke system ,addr szPause
.else
invoke printf,addr szOutFmt2
invoke system ,addr szPause
.endif
Exit:
END START
表达式操作符
eq(等于) 、ne(不等于)、 gt(大于)、 lt(小于 )、 ge(大于等于 ) 、le(小于等于)这六个比较操作符用来比较两个常数,如果比较结果为真,则返回-1,否则返回0。但是这些不能用于比较寄存器或者内存变量,否则会出现编译错误。
除此之外还有常见的==、!=、>等比较方式,这些可以比较寄存器和内存变量值,但是MASM规定操作符左边只能是变量或是寄存器,操作符的两边不能同时为变量,但可以同时为寄存器。
.386
.Model Flat, StdCall
Option Casemap :None
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib
includelib C:\masm32\lib\msvcrt.lib
printf PROTO C : dword,:vararg
system PROTO C : dword,:vararg
.data
szMessage db 'a',10,13,0
bTest1 dword -1
szPause db 'pause',10,13,0
szOutFmt1 byte '等于',10,13,0
szOutFmt2 byte '不等于',10,13,0
.code
x dword 5
START:
mov eax,8
.if 8 eq 8
invoke printf,addr szOutFmt1
invoke system ,addr szPause
.else
invoke printf,addr szOutFmt2
invoke system ,addr szPause
.endif
Exit:
END START
还有CARRY?、OVERFLOW?、PARITY?、SIGN?、ZERO?操作符,这五个操作符分别代表标志寄存器里的五个标志位,也就是进位位、溢出位(CF)、奇偶位(PF)、符号位(SF)、零位(ZF)。
下面是示例符号位,符号位是SF标志,结果为负数时,SF等于1,否则为0。
首先将寄存器中原有的8减去9,结果肯定是负数,所以SF等于1,那么.if SIGN?就是TRUE,否则执行.else。
.386
.Model Flat, StdCall
Option Casemap :None
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib
includelib C:\masm32\lib\msvcrt.lib
printf PROTO C : dword,:vararg
system PROTO C : dword,:vararg
.data
szMessage db 'a',10,13,0
bTest1 dword -1
szPause db 'pause',10,13,0
szOutFmt1 byte '是',10,13,0
szOutFmt2 byte '否',10,13,0
.code
x dword 5
START:
mov eax,8
sub eax, 9
.if SIGN?
invoke printf,addr szOutFmt1
invoke system ,addr szPause
.else
invoke printf,addr szOutFmt2
invoke system ,addr szPause
.endif
END START
实例
- 输入两个整数,输出第一个数和第二个数关系.
.386
.Model Flat, StdCall
Option Casemap :None
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib
includelib C:\masm32\lib\msvcrt.lib
printf PROTO C : dword,:vararg
system PROTO C : dword,:vararg
scanf PROTO C : dword,:vararg
.data
inputFmt byte '%d%d',0
szPause db 'pause',10,13,0
szOutFmt1 byte '%d>%d',10,13,0
szOutFmt2 byte '%d<%d',10,13,0
szOutFmt3 byte '%d=%d',10,13,0
one dword 0
two dword 0
.code
START:
invoke scanf,addr inputFmt,addr one, addr two
mov eax,one
mov ebx,two
.if eax>ebx
invoke printf,addr szOutFmt1,eax,ebx
.elseif eax<ebx
invoke printf,addr szOutFmt2,eax,ebx
.elseif eax==ebx
invoke printf,addr szOutFmt3,eax,ebx
.endif
invoke system ,addr szPause
END START
- 输入一个小写字符,把他转化成大写,如果输入的不是小写字符,则输出"输入错误"。
.386
.Model Flat, StdCall
Option Casemap :None
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\kernel32.lib
includelib C:\masm32\lib\msvcrt.lib
printf PROTO C : dword,:vararg
system PROTO C : dword,:vararg
scanf PROTO C : dword,:vararg
.data
x byte ?
inputFmt BYTE '%c',0
szPause db 'pause',10,13,0
szOutFmt1 byte '%c',10,13,0
szError byte '输入错误',10,13,0
.code
START:
invoke scanf,addr inputFmt,addr x
cmp x,'A'
JB EXIT
cmp x,'Z'
JB EXIT
sub x,32
invoke printf,addr szOutFmt1,x
invoke system,addr szPause
EXIT:
invoke printf,addr szError
invoke system,addr szPause
END START