《Intel汇编语言程序设计》(第四版)第六章中的代码,如下:
.data
array SWORD -3 , -6 , -1 , -10 , 10 , 30 , 40 , 4
sentinel SWORD 0
.code
mov esi , OFFSET array
mov ecx , LENGTHOF array
next:
test WORD PTR [esi] , 8000h ; test sign bit
pushfd ; push flags on stack
add esi , TYPE array
popfd ; pop flags from stack
loopnz next ; continue loop
jnz quit ; none found
sub esi , TYPE array ; ESI points to value
quit:
让我们逐句分析。
程序在数据段定义了一个数组和一个变量:
.data
array SWORD -3 , -6 , -1 , -10 , 10 , 30 , 40 , 4
sentinel SWORD 0
然后,程序对 esi 和 ecx 进行赋值:
mov esi , OFFSET array
mov ecx , LENGTHOF array
将 esi 指向了数组array的第一个元素地址,并将array数组长度赋值给ecx。
接着,开始在数组中查找是否有正数,代码如下:
next:
test WORD PTR [esi] , 8000h ; 使用test伪指令对最高位进行“与”操作,如果为1则零标志ZF会被置1 pushfd ; 保存标志位到堆栈(不明白这里为什么要保存标志位,但肯定是因为下面的操作会影响到标志位)
add esi , TYPE array ; 将esi指向下一个数组元素地址
popfd ; 从堆栈中弹出标志位
loopnz next ; 在ECX中的无符号值大于0并且零标志位被清除的状态下进行循环
jnz quit ; 如果数组中未找到正数,则跳到quit标号处
sub esi , TYPE array ; 如果找到了正数,则将esi减掉array元素类型即为正数地址
quit:
首先是几个伪指令的含义:
LOOPNZ指令的作用是,在ECX中的无符号值大于0并且零标志位被清除的状态下进行循环。
而TEST指令的作用是,只有当所有的测试位都被清除的时候,零标志位ZF才被置1。
JNZ指令的作用是,ZF=0时跳转。
整个程序的思路是这样的:
程序对每一个元素值的高位进行test测试,当是负数的时候,最高位是1,此时使用8000h(8000h 即 1000 000 000 000b)进行test的结果是测试位并不全为零,所以此时零标志位ZF被置为0,满足loopnz的两个条件,会继续执行。当最高位不是1的时候,则为正数,此时使用8000h进行test的结果是,所有测试位都被清除,则ZF会被置为1,因为不满足LOOPNZ的条件,所以不会接着执行循环,会跳出循环向下执行,因为也不满足下一条语句jnz的条件,所以不会跳转到quit标号。接着往下执行,因为之前 esi 被前移了一个元素,所以此时将 esi 减去一个元素,即得到刚刚检测到的正数的地址值。
所有程序完成。