2010-02-05 67 views
13

到目前为止,我已经写了代码:如何在x86汇编语言中创建一个循环?

.code 

main 
Clrscr 

    mov dh,10   ;row 10 

    mov dl,20   ;column 20 

    call Gotoxy   ;locate cursor 

    PromptForIntegers 

    WriteString  ;display string 
    ReadInt   ;input integer 
    ArraySum 

    WriteString  ;display string 
    WriteInt   ;display integer 

DisplaySum ENDP 

END main 

我如何得到它重复使用三次循环相同的步骤,每次循环迭代后清屏?

+5

在哪里这些宏('WriteInt','WriteString')从何而来? x86组装现在是混蛋。 – 2010-02-05 18:41:26

+0

这是什么汇编语言? http://en.wikipedia.org/wiki/List_of_programming_languages_by_category#Assembly_languages – Dolph 2010-02-05 18:42:14

+2

@mmyers:什么是删除架构标签? *在装配中很重要。 – dmckee 2010-02-05 18:45:54

回答

2

使用CX寄存器来统计循环

 
mov cx, 3 
startloop: 
    cmp cx, 0 
    jz endofloop 
    push cx 
loopy: 
    Call ClrScr 
    pop cx 
    dec cx 
    jmp startloop 
endofloop: 
    ; Loop ended 
    ; Do what ever you have to do here 

这只是循环3倍左右调用ClrScr,推CX登记到堆栈,对比为0,跳跃如果ZeroFlag设置然后跳转到endofloop。请注意CX的内容是如何被推入/弹出堆栈以保持循环的流动。

+1

有一些优化:(1)你可以使用'jcxz label'而不是'cmp cx,0'和'jz label'。 (2)您可以使用'loop label'而不是'dec cx'和'jnz label' – 2010-02-05 18:46:53

+0

@PA:根据您的处理器,'jcxz label'和'cmp/jz'是等价的。最近的x86处理器使用宏融合技术将cmp/jmp指令合并到单个循环指令中,实质上复制了'jcxz'行为。 – 2010-02-05 19:01:30

+0

这个循环效率非常低。如果一个循环可能运行零次,那么测试循环外的计数器。然后在循环结尾使用条件分支。并且使用一个你不需要将/ push作为循环变量的寄存器。 (例如,如果你的循环中有函数调用,那么保留一个像(e/r)bx或(e)si这样的保留寄存器的寄存器)。 Jeff B是对的:在英特尔Haswell等现代CPU上,'cmp/jz'或'dec/jz'比'jcxz'便宜*。请参阅http://agner.org/optimize/ – 2016-03-13 11:34:20

0

您需要使用条件jmp命令。这与您使用的语法不同;貌似MASM,但这里使用天然气是从一些代码,我写来计算GCD的例子:

gcd_alg: 
    subl %ecx, %eax  /* a = a - c */ 
    cmpl $0, %eax  /* if a == 0 */ 
    je  gcd_done  /* jump to end */ 
    cmpl %ecx, %eax  /* if a < c */ 
    jl  gcd_preswap  /* swap and start over */ 
    jmp  gcd_alg   /* keep subtracting */ 

基本上,我比较了CMPL指令(比较长)两个寄存器。如果它少于JL(跳转减少)指令跳转到预先切换位置,否则它跳回到同一个标签。

至于清理屏幕,这取决于你使用的系统。

+0

如果结果为零,那么'sub%ecx,%eax'已经设置了ZF,您不需要'cmp $ 0,%eax'。你应该在循环结尾以另一种方式构建分支:'jnl gcd_alg'/else通过'gcd_preswap'。实际上,标志仍然是从'sub'设置的,所以你可以'sub%ecx,%eax/jnl gcd_alg/je gcd_done /直通到gcd_preswap',所以主循环是两条指令。或者,如果'a'可能比'c'多很多倍,就用div来获得余数。 – 2016-03-13 11:39:00

16
mov cx,3 

loopstart: 
    do stuff 
    dec cx   ;Note: decrementing cx and jumping on result is 
    jnz loopstart ;much faster on Intel (and possibly AMD as I haven't 
        ;tested in maybe 12 years) rather than using loop loopstart 
+4

只要'do stuff'保留cx寄存器 – 2010-02-05 18:43:38

+5

@PA,就可以使用'loop loopstart'而不是'dec cx'和'jnz loopstart':即使不使用'loop',保留cx也是必需的。 – 2010-02-05 18:50:28

+1

这是绝对正确的,因为问题是关于循环*三次。然而,问题标题更为通用,我认为可以补充一点,如果cx值来自变量而不是来自常量,那么在进入循环之前跳转到循环的JBE零指令将会是有用的需要。 – 2011-11-30 10:25:41

9

另一种方法是使用LOOP指令:

mov cx, 3 

myloop: 
    ; Your loop content 

    loop myloop 

循环指令自动递减CX,并且只有当跳跃CX = 0也有LOOPE和LOOPNE变种,如果你想要为你的循环做一些额外的检查,以提前爆发。

如果你希望你的循环过程中修改CX,请务必将其推到循环内容之前堆栈,后弹出它关闭:

mov cx, 3 

myloop: 
    push cx 
    ; Your loop content 
    pop cx 

    loop myloop 
+0

感谢您的帮助 – user267288 2010-02-05 23:33:48

+0

'loop' [很慢,并且使用它不是一个好习惯](http:// stackoverflow。COM /问题/ 35742570 /为什么 - 是 - 在 - 环 - 指令慢想不出,英特尔已经实现,它高效地)。如果你需要在你的循环内部使用ecx(例如移位指令),那么只需使用一个不同的寄存器作为循环计数器。在循环计数器依赖链中有一个push/pop是愚蠢的。通常循环需要某种感应变量,所以只需测试一下。例如循环中每次增加一个指针4,将循环条件作为结束指针的“cmp/jb”。 – 2016-03-13 11:27:29

0

我一直在寻找相同的答案&发现此信息从维基有用: 循环指令

环路指令递减ECX,并跳转到由arg指定的地址,除非递减ECX引起它的值变为零。例如:

mov ecx, 5 
start_loop: 
; the code here would be executed 5 times 
loop start_loop 

循环未设置任何标志。

loopx ARG

这些环路指令递减ECX并跳转到由arg指定的地址,如果他们的条件被满足(即,特定的标记被设置),除非递减ECX引起其值设置为变成零。

  • LOOPE循环,如果等于

  • loopne循环,如果不相等

  • LOOPNZ循环,如果不为零

  • LOOPZ循环,如果零

来源:X86 Assembly, Control Flow

+0

有效,但比'dec/jnz'慢。 – 2016-03-13 11:35:05

0
.model small 
.stack 100h 
.code 
Main proc 
Mov cx , 30 ; //that number control the loop 30 means the loop will 
;excite 30 time 
Ioopfront: 
Mov ah , 1 
Int 21h 
Loop loopfront; 

这个鳕鱼将采取30个字符

+2

这是什么添加,这是多年前发布的其他答案中没有解释的? – Michael 2016-04-06 10:30:49