我正在学习 x86 汇编。我想知道你如何有条件地调用子例程。 据我了解,跳转到标签不起作用,因为返回地址未存储,因此它不知道返回哪里。
cmp bx, 0
jz zero ; how do I do this correctly ?
; do something else and exit
zero:
; do something
ret
干净的方法很简单:
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 之前进行某些处理(例如弹出之前推送的值),则应使用第一种方法。
如果您不需要返回该地址,它就可以了。很多时候,您可以按照这种情况构建代码。
否则,您将不得不使用
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 在评论中提到的,下面的代码可能会表现得很糟糕。参见例如这篇文章解释原因。
@Manny D 评论中的建议启发我写了以下内容。它可能不会更干净或更好,但这是另一种构建它的方式:
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 位模式下工作,您需要做一些细微的调整。
我相信正确的方法是使用
call
指令。这相当于高级编程语言中的函数调用。 PC 存储在堆栈中,因此 ret
子例程末尾的 zero:
会执行其预期的操作。
代替“ret”标签只需使用“pop”到任何寄存器
示例
` cmp bx, 0 零零 xor ax ;这是为了稍后清理“ax”寄存器给我们 ;做点别的事然后退出
零: ;做一点事 流行斧头`
如果我理解正确,你正在寻找的是例如 如果 CMP 的结果为零,则进行 CALL 而不是 JMP。一种解决方法是使用临时子函数来调用预期函数
示例:在下面的#4 程序中,您执行 CMP,然后 Jz 到 (made_up) 标签,然后使用该 (made_up) 标签来调用您想要的实际函数。
#1 instruction xxx,xxx
#2 instruction xxx,xxx
#3 instruction xxx,xxx
#4 CMP XXX , XXX
#5 JZ Made_up
#6 Made_up:
#7 call your_function
#8 next instruction address will be pushed to stack as your return address.
cmp xxx, xxx
jz Made_up
Made_UP:
call your_Function
;now you will have the return address saved onto the stack to return to.
希望这有帮助...