曲径通幽论坛

标题: 循环及循环指令 [打印本页]

作者: beyes    时间: 2009-12-13 15:48
标题: 循环及循环指令
在 IA-32 平台上提供了在汇编语言程序中实现循环的简单的机制:循环指令

循环指令使用 ECX 寄存器作为计数器并且随着循环指令的执行自动递减它的值。下表为循环指令说明:
指令
描述
LOOP
循环直到ECX寄存器为零
LOOPE/LOOPZ
循环直到 ECX 寄存器为零,或者没有设置ZF标志
LOOPNE/LOOPNZ
循环知道ECX寄存器为零,或者设置了ZF标志

LOOPE/LOOPZ 和 LOOPNE/LOOPNZ 提供了监视零标志的附加功能。
这些指令的格式是:
loop  address
上面,address 是要跳转到的程序代码位置的标签名称。不幸的是,循环指令只支持 8 位偏移量,所以只能进行短跳转。

循环开始之前,必须在 ECX 寄存器中设置执行迭代的次数值。通常会像下面这样的代码:
   < code before the loop >
movl $100, %ecx
loop1:
   < code to loop through >
    loop loop1
   < code after the loop >
需要注意的是,循环内部的代码要小心对待,要防止 ECX 寄存器被修改了,这样就会影响循环的操作。
循环的额外好处在于它们递减 ECX 寄存器的值,而不影响 ELFAGS 寄存器的标志位。当 ECX 寄存器值到达零时,零标志不会被设置。
作者: beyes    时间: 2009-12-13 17:18
标题: 循环范例
测试程序
.section .data
output:
    .asciz "The value is: %d\\n"

.section .text
.globl _start
_start:
    movl $100, %ecx
    movl $0, %eax
loop1:
    addl %ecx, %eax
    loop loop1
    pushl %eax
    pushl $output
    call printf
    add $8, %esp
    movl $1, %eax
    movl $0, %ebx
    int $0x80
运行输出
$ ./loop
The value is: 5050
说明
LOOP 指令用于持续地循环调用 ADD 指令,直到 ECX 的值为零。
作者: beyes    时间: 2009-12-13 19:05
标题: 防止 LOOP 灾难
在上面的程序中,将 ECX 寄存器设置为零会怎么样呢?实际的运行结果是:
$ ./loop
The value is: -2147483648
这不是正确答案。问题的原因在于 LOOP 指令的行为方式。当执行 LOOP 指令之前 ECX 的值已经为零,LOOP 指令会把它递减 1,使它成为 -1 。因为这个值非零,所以 LOOP 指令继续执行下去,循环回到低能给的标签。循环最终会在寄存器溢出时退出,并且显示错误的值。解决这个问题的一个方便方法是,使用 JCXZ 指令; JCXZ 指令为应用程序提供某种初步的错误检查:
.section .data
output:
    .asciz "The value is: %d\\n"

.section .text
.globl _start
_start:
    movl $0, %ecx
    movl $8, %eax
    jcxz done
loop1:
    addl %ecx, %eax
    loop loop1
done:
    pushl %eax
    pushl $output
    call printf
    movl $1, %eax
    movl $0, %ebx
    int $0x80
运行输出
$ ./loop
The value is: 8
在循环开始前添加了单一指令 --- JCXZ 。并且使用单一标签引用指令码的结尾。现在,如果 ECX 寄存器包含零值,JCXZ 就会捕获到它,并且立即输到输出段。运行程序结果显示这样确实解决了问题。




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2