【问题标题】:x86 Assembly , stack push instructionx86汇编,堆栈推送指令
【发布时间】:2024-01-23 19:59:01
【问题描述】:

我刚刚发现 push 指令可以有一个立即数 byte 、 word 、 dword 参数,并且每个参数都有不同的操作码。我正在阅读的书中没有示例,所以我不明白汇编程序如何区分这三种类型。例如,如果我写push 12h,汇编器将如何解释它,堆栈上实际会发生什么?

【问题讨论】:

标签: assembly x86 callstack machine-code opcode


【解决方案1】:

在 DOS 或 Windows Debug.com(不是 Debug.exe,至少在我的版本中不是),你会说 压入字节 12 或压入字 1234。 Push dword 12345678 也可以工作,对于双字(4 字节,32 位),但我现在不在我的电脑上,所以我无法尝试

【讨论】:

    【解决方案2】:

    汇编器为每个变体生成不同的操作码。它将在决定组装到哪个操作码之前检查参数。因为您的示例中的 12h 不是寄存器的名称,而是满足数字的十六进制表示的特性,所以得出需要推入立即值的结论,并生成相应的操作码以及二进制值作为指令。它还将检查参数是否用方括号括起来,用于间接。 对于 CPU,在执行该代码时,这些不同的变体实际上是不同的指令——尽管在执行时有一些共同点。

    检查参数以确定它们的性质,这是汇编器对许多指令所做的事情,除了 push 之外,也是为了相同的目的:决定需要为指令选择哪个操作码。

    【讨论】:

      【解决方案3】:

      这取决于汇编程序。它可能会选择具有足够大的最小操作数字段以保存立即值的操作码。它可能还需要您告诉它您要使用哪个变体。

      例如,NASM 会将push 12h 组装成6A 12 (push byte 12h)。

      如果你想要,例如要获得push imm16 变体,您会说push strict word 12h(如果您不希望NASM 将指令优化为字节推送,则需要strict)。

      请注意,立即字节push 实际上并不会将字节压入堆栈。在推送之前,该值将被符号扩展至至少 16 位(这发生在执行期间,而不是在编译期间)。

      【讨论】:

      • 如果将字节扩展为字或双字,是否有理由使用立即字节指令?
      • 我在这方面有两个 cmet,我撤回并重做以供其他人感兴趣:当我在 Gnu AS 中编译“push 1”时,它变成了 push imm8 指令“6A01”。但这有点误导——我认为它会做一些疯狂的事情,比如推送一个字节或一个单词。但它没有,它推动了 32 位。 “push 1”然后“pop eax”在 eax 中返回 1。所以,迈克尔是正确的:-)
      • @NarekMargaryan:是的,代码大小。 push 1 只是一个 2 字节的指令,如 push imm8,而不是 5 的 push imm32。在原始 8086 上,它只节省了 1 个字节(imm16imm8),但代码大小是 8086 尤其是 8088 的关键因素:性能的主要瓶颈是代码获取。因此,大多数指令都存在符号扩展的 imm8 与完整的 imm16/32 即时版本。请参阅手册:felixcloutier.com/x86/push。另见How many bytes does the push instruction push onto the stack when I don't specify the operand size?
      • @Peter Cordes:您关于代码大小的观点是正确的,但请注意,立即推送指令出现在 186 指令集中,而不是 8086。
      • @ecm:哎呀,对。不过,8086 确实有 add/sub/xor/... r/m16, imm8imm16,因此具有符号扩展 imm8 和完整尺寸的操作码的模式确实来自 8086。