您好我正在修补汇编级编程。我有以下代码
mov al, 'H'
call my_function
my_function:
mov ah,0x0e ; BIOS print-character
int 0x10
ret
jmp $ ; infinite loop because there's no OS to exit to
times 510-($-$$) db 0
dw 0xaa55 ; boot sector signature
我有一个用于打印al
内容的标签,该标签按预期工作(打印H
)。但是在函数返回之后,再次执行相同的标签并打印额外的H
。这是可以理解的,因为ret
从堆栈中弹出了地址,将地址指向调用者并再次执行标签。
现在我的问题是,如何避免这种情况?我不能只使用像实际功能这样的标签而不打印两次吗?我不想要额外的执行,这可能会改变我的计划。
答案 0 :(得分:7)
正如Jester在评论中所说,所有应该是功能的标签必须低于jmp $
声明,因此它不会执行额外的时间。
附: jmp $
指示系统跳转到当前位置,导致无限循环,不允许在函数存在的地方继续前进。
答案 1 :(得分:6)
CPU看不到你的标签,它从指令到指令。
除非当前指令是某种跳转(grep
和call
也是某种跳转) - 在CPU完成当前指令后,它将转到下一个,它
执行ret
时,它将执行函数内的所有指令,然后在执行call my_function
时,它将返回ret
之后的下一条指令。
下一条指令再次是call
的第一条指令,第二次执行......第二次点击my_function
后,它实际上会丢失谁知道哪里({{ 1}}将在堆栈顶部取值并将其用作下一条指令的地址,因此在第二次ret
发生时堆栈中的任何内容,您的代码现在正在运行... )
汇编源不仅仅是指令组,而是将它们放在内存中,并通过一个接一个地放置一条指令来控制代码流。 CPU将按顺序逐行执行它们,就像你编写它们一样(除非你通过使用某种类型的跳转来改变代码流,否则你可以跳过几行源代码)。
因此,如果您希望CPU在ret
完成"之后停止,并且您正在创建引导加载程序,即无法返回(没有操作系统,或者某些东西)像那样),你将通过无限循环在主结尾处创建死胡同,如:
ret
那"主要结束"就在main
之后。 "创建my_function"本身必须在" main"之外定义,例如在此无限循环停止之后。
也许你错过了dead_end_loop:
pause ; give CPU hint this is idling loop
; so it will save power by switching off some circuitry
jmp dead_end_loop
的内容以及它在源代码中的用途。在这种情况下,call my_function
符号表示汇编程序"当前指令/行的地址",因此jmp $
可以转换为"跳转到同一行",这意味着它是一个无限循环,CPU将永远不会执行除$
指令之外的任何其他操作(除非设置为处理某些中断信号,否则任何此类外部信号都会导致CPU将执行切换到特定的中断处理程序代码,因为程序员/操作系统在进入无限循环之前确实配置了它。)
还有一个想法:您可能需要查看https://schweigi.github.io/assembler-simulator/和"步骤"在几个例子中,看看CPU是如何看到源的,但只看到机器代码字节(右侧可见"内存"),以及它如何从一条指令转到下一条指令, jmp $
如何变化等......