您好,我正在修改汇编级编程。我有以下代码
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
从堆栈中弹出地址,将其指向调用者并再次执行标签。
现在我的问题是,如何避免这种情况?我不能像实际函数一样使用标签而不打印两次吗?我不想要可能改变我的程序的额外执行。
最佳答案
CPU 不会看到你的标签,它会从一条指令转到另一条指令。
除非当前指令是某种跳转(call
和 ret
也是某种跳转) - CPU 处理完当前指令后,它将继续执行到下一个,跟随它。
当您call my_function
时,它将执行函数内的所有指令,然后在执行ret
时,它将返回call<之后的下一条指令
.
下一条指令又是my_function
的第一条指令,第二次执行...第二次点击ret
后,谁知道它实际上会丢失-where(ret
将获取堆栈顶部的值并将其用作下一条指令的地址,因此无论发生第二个 ret
时堆栈中的内容,您的代码现在正在疯狂运行...)
汇编源代码不仅仅是一组指令,而且您还可以将它们定位在内存中,并通过将一条指令放置在另一条指令中来控制代码流。 CPU 将按顺序逐行执行它们,就像您编写它们一样(除非您通过使用某种跳转来更改代码流程,然后您可以跳过几行源代码)。
因此,如果您希望 CPU 在您的 main
“完成”后停止,并且您正在创建引导加载程序,即没有任何可返回的内容(没有操作系统或类似的东西),您将通过无限循环在 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
“main 结束”就在调用 my_function
之后。 “my_function”本身必须在“main”之外定义,例如在此无限循环停止程序之后。
也许您错过了jmp $
是什么以及它在源代码中的用途。这种情况下的 $
符号对于汇编器来说意味着“当前指令/行的地址”,因此 jmp $
可以翻译为“跳转到同一行”,这意味着它是一个无限循环,除了这个jmp $
指令之外,CPU永远不会执行任何其他指令(除非它被设置为处理某些中断信号,那么任何此类外部信号都会导致CPU将执行切换到特定的中断处理程序代码,因为程序员/操作系统在进入无限循环之前对其进行了配置)。
还有一个想法:您可能需要检查https://schweigi.github.io/assembler-simulator/并“单步”几次示例,看看 CPU 如何看不到源代码,而只看到机器代码字节(在右侧显示为“内存”),以及它如何从一条指令到下一条指令,如何IP
正在改变,等等...
关于assembly - NASM 汇编器 - 如何确保函数标签不会被额外执行一次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48307937/