WIN32汇编语言教程:第03章 使用MASM · 3.5 高级语法(1)
以前高级语言和汇编的最大差别就是条件测试、分支和循环等高级语法。高级语言中,程序员可以方便地用类似于if,case,loop和while等语句来构成程序的结构流程,不仅条理清楚、一目了然,而且维护性相当好。而汇编程序员呢?只能在cmp指令后面绞尽脑汁地想究竟用几十种跳转语句中的哪一种,这里就能列出近三十个条件跳转指令来:ja,jae,jb,jbe,jc,je,jg,jge,jl,jle,jna,jnae,jnb,jnbe,jnc,jne,jng,jnge,jnl,jnle,jno,jnp,jns,jnz,jo,jp,jpe,jpo和jz等。虽然其中的很多指令我们一辈子也不会用到,但就是这些指令和一些loop,loopnz以及被loop涉及的ecx等寄存器纠缠在一起,使在汇编中书写结构清晰、可读性好的代码变得相当困难,这也是很多人视汇编为畏途的一个原因。
现在好了,MASM中新引入了一系列的伪指令,涉及条件测试、分支和循环语句,利用它们,汇编语言有了和高级语言一样的结构,配合对局部变量和调用参数等高级语言中常见元素的支持,为使用Win32汇编编写大规模的应用程序奠定了基础。
3.5.1 条件测试语句
在高级语言中,所有的分支和循环语句首先要涉及条件测试,也就是涉及一个表达式的结果是“真”还是“假”的问题,表达式中往往有用来做比较和计算的操作符,MASM也不例外,这就是条件测试语句。
MASM条件测试的基本表达式是:
寄存器或变量 操作符 操作数
两个以上的表达式可以用逻辑运算符连接:
(表达式1)逻辑运算符(表达式2) 逻辑运算符(表达式3)...
允许的操作符和逻辑运算符如表3.5所示。
表3.5 条件测试中的操作符
操作符和逻辑运算 | 操作 | 用途 |
== | 等于 | 变量和操作数之间的比较 |
!= | 不等于 | 变量和操作数之间的比较 |
> | 大于 | 变量和操作数之间的比较 |
>= | 大于等于 | 变量和操作数之间的比较 |
续表
操作符和逻辑运算 | 操作 | 用途 |
< | 小于 | 变量和操作数之间的比较 |
<= | 小于等于 | 变量和操作数之间的比较 |
& | 位测试 | 将变量和操作数做“与”操作 |
! | 逻辑取反 | 对变量取反或对表达式的结果取反 |
&& | 逻辑与 | 对两个表达式的结果进行逻辑“与”操作 |
|| | 逻辑或 | 对两个表达式的结果进行逻辑“或”操作 |
举例如下,左边为表达式,右边是表达式为“真”的条件:
x==3 ;x等于3 eax!=3 ;eax不等于3 (y>=3)&&ebx ;y大于等于3且ebx为非零值 (z&1)||!eax ;z和1进行“与”操作后非零或eax取反后非零 ;也就是说x的位0等于1或者eax为零
细心的读者一定会发现,MASM的条件测试采用的是和C语言相同的语法。如!和&是对变量的操作符(取反和“与”操作),||和&&是表达式结果之间的逻辑“与”和逻辑“或”,而==、!=、>、< 等是比较符。同样,对于不含比较符的单个变量或寄存器,MASM也是将所有非零值认为是“真”,零值认为是“假”。
MASM的条件测试语句有几个限制,首先是表达式的左边只能是变量或寄存器,不能为常数;其次表达式的两边不能同时为变量,但可以同时是寄存器。这些限制来自于80x86的指令,因为条件测试伪操作符只是简单地把每个表达式翻译成cmp或test指令,80x86的指令集中没有cmp 0,eax之类的指令,同时也不允许直接操作两个内存中的数,所以对这两个限制是很好理解的。
除了这些和高级语言类似的条件测试伪操作,汇编语言还有特殊的要求,就是程序中常常要根据系统标志寄存器中的各种标志位来做条件跳转,这些在高级语言中是用不到的,所以又增加了以下一些标志位的状态指示,它们本身相当于一个表达式:
CARRY? 表示Carry位是否置位 OVERFLOW? 表示Overflow位是否置位 PARITY? 表示Parity位是否置位 SIGN? 表示Sign位是否置位 ZERO? 表示Zero位是否置位
要测试eax等于ebx同时Zero位置位,条件表达式可以写为:
(eax==ebx) && ZERO?
要测试eax等于ebx同时Zero位清零,条件表达式可以写为:
(eax==ebx) && ! ZERO?
和C语言的条件测试同样,MASM的条件测试伪指令并不会改变被测试的变量或寄存器的值,只是进行“测试”而已,到最后它会被编译器翻译成类似于cmp或test之类的比较或位测试指令。
3.5.2 分支语句
分支语句用来根据条件表达式测试的真假执行不同的代码模块,MASM中的分支语句的语法如下:
.if 条件表达式1 表达式1为“真”时执行的指令 [.elseif 条件表达式2] 表达式2为“真”时执行的指令 [.elseif 条件表达式3] 表达式3为“真”时执行的指令 ... [.else] 所有表达式为“否”时执行的指令 .endif
注意:关键字if/elseif/else/endif的前面有个小数点,如果不加小数点,就变成宏汇编中的条件汇编伪操作了,结果可是天差地别。
为了说明编译器究竟是如何处理这些伪指令的,先写一段如下的源代码:
.if eax && (ebx >= dwX) || !(dwY != ecx) mov esi,1 .elseif edx mov esi,2 .elseif esi & 1 mov esi,3 .elseif ZERO? && CARRY? mov esi,4 .endif
然后反汇编:
; .if eax :00401000 0BC0 or eax, eax :00401002 7408 je 0040100C ; (ebx = dwX) :00401004 3B1D00304000 cmp ebx, dword ptr [00403000] :0040100A 7308 jnb 00401014 ; (dwY != ecx) :0040100C 390D04304000 cmp dword ptr [00403004], ecx :00401012 7507 jne 0040101B :00401014 BE01000000 mov esi, 00000001 :00401019 EB23 jmp 0040103E ; elseif edx :0040101B 0BD2 or edx, edx :0040101D 7407 je 00401026 :0040101F BE02000000 mov esi, 00000002 :00401024 EB18 jmp 0040103E ; elseif esi & 1 :00401026 F7C601000000 test esi, 00000001 :0040102C 7407 je 00401035 :0040102E BE03000000 mov esi, 00000003 :00401033 EB09 jmp 0040103E ; ZERO? :00401035 7507 jne 0040103E ; CARRY? :00401037 7305 jnb 0040103E :00401039 BE04000000 mov esi, 00000004 :0040103E ...