在程序集中有条件地调用子程序

时间:2011-09-04 19:41:36

标签: assembly x86

我正在学习x86程序集。我想知道你是如何有条件地执行子程序的。 据我了解,跳转到标签不起作用,因为没有存储返回地址,因此它不知道返回的位置。

 cmp bx, 0
 jz zero ; how do I do this correctly ?
 ; do something else and exit

 zero:
 ; do something
 ret

3 个答案:

答案 0 :(得分:6)

如果您不需要返回该地址,那么它是有效的。通常情况下,您可以构建代码,以便实现这种情况。

否则,您必须使用跳过调用网站的Jxx指令进行分支,或以其他方式围绕此限制构建代码。在这种情况下,反转测试应该有效:

    cmp bx, 0
    jnz not_zero
    call zero
    ; fall through here, return or do what you want
not_zero:
    ; do something else and exit
    ; ...
    ret 

zero:
    ; do something
    ret

编辑2016-04-25:正如@Peter Cordes在评论中提到的,下面的代码可能会表现得非常糟糕。参见例如this article解释原因。

@Manny Ds在评论中的建议激发了我写下面的内容。它可能不是更清洁或更好,但它是构建它的另一种方式:

    push back_from_zero ; Push where we want to return after possibly branching to 'zero' 
    cmp bx, 0
    jz zero ; branch if bx==0
    add esp, 4 ; adjust stack in case we didn't branch
back_from_zero: ; returning from the zero branch here or continuing from above
    ; do something else and exit

zero:
    ; do something
    ret

它显式地推送堆栈上的返回地址,这样zero函数可以返回或弹出堆栈中的值(add esp, 4),如果我们不调用该函数(重新调整到堆栈) 。请注意,如果您希望在16位或64位模式下工作,则需要进行一些微调。

答案 1 :(得分:4)

干净利落的方法很简单:

    cmp bx,0
    jnz notzero
    ; handle case for zero here
    jmp after_notzero
notzero:
    ; handle case for not zero here
after_notzero:
    ; continue with rest of processing

我知道if-else情况没有更好的方法。好的,如果两个分支必须在之后直接返回,你可以这样做:

    cmp bx,0
    jnz notzero
    ; handle case for zero here
    ret

notzero:
    ; handle case for not zero here
    ret

如果必须在ret之前进行某些处理(例如先前推送的弹出值),则应使用第一种方法。

答案 2 :(得分:2)

我认为正确的方法是使用call指令。这相当于更高编程语言的函数调用。 PC存储在堆栈中,因此ret子例程末尾的zero:执行它应该执行的操作。