我正在学习x86程序集。我想知道你是如何有条件地执行子程序的。 据我了解,跳转到标签不起作用,因为没有存储返回地址,因此它不知道返回的位置。
cmp bx, 0
jz zero ; how do I do this correctly ?
; do something else and exit
zero:
; do something
ret
答案 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:
执行它应该执行的操作。