x86组合集“推”和“推”的区别

Bar*_*ach 5 x86 assembly stack assemblies

有人告诉我将有效寄存器放入堆栈中,以免稍后在“子程序”中覆盖它们,对我来说很明显,每个人都知道。但是,当我阅读队友的代码时,发现了以下代码:

puts: ; display character in ax
        push ax
        push bx
        push cx
        push dx
        mov dx, ax
        mov ah, 9h
        int 21h
        pop dx
        pop cx
        pop bx
        pop ax 
        ret 
Run Code Online (Sandbox Code Playgroud)

然后我看到pushapopa命令。我想可以这样来做:

puts: ; display character in ax
        pusha
        mov dx, ax
        mov ah, 9h
        int 21h
        popa
        ret 
Run Code Online (Sandbox Code Playgroud)

和的es 之间有什么区别吗?先感谢您。pushapush

Guf*_*ffa 6

该操作推送的pusha不仅仅是axbx和寄存器:cxdx

“按以下顺序将所有通用寄存器压入堆栈:(E)AX、(E)CX、(E)DX、(E)BX、(E)SP、(E)BP、(E)SI、( E)DI.SP的值为实际压入SP前的值。

您可以通过使用八条指令推送寄存器来完成基本相同的操作push,但这将推送不同的值sp。您可以简单地省略堆栈指针,因为实际上不需要推送它。压入和弹出七个寄存器具有相同的目的pushapopa即使它不做完全相同的事情:

push ax
push bx
push cx
push dx
push bp
push si
push di
...
pop di
pop si
pop bp
pop dx
pop cx
pop bx
pop ax
Run Code Online (Sandbox Code Playgroud)

  • @onegrx 是的,只要你也用“popa”替换“pop”。:) (2认同)
  • @onegrx:您无法仅使用“push”和“pop”指令来复制“pusha”和“popa”的确切行为,但您可以实现相同的目的。 (2认同)

usr*_*301 5

是的,pusha并且popa在功能上是等效的,因为它们推送/弹出所有寄存器。但是,对于简单的 DOS 中断调用是否有必要这样做?

转移到中断例程
...

与所有操作一样,只做需要的,仅此而已。中断调用必须保留寄存器——除了记录在案的要更改的寄存器。常规状态调用返回其结果AX并可能更改标志,而不会更改其他任何内容。如果中断更改了其他任何内容(ds:dx例如),则应在其文档中说明。

在您的代码中,使用ah=09 / int 21,唯一的变化是

返回:AL = 24h

因此可以安全地假设所有其他寄存器“已存储”。

中断尽可能多地保留是有原因的。在全球范围内,当其他代码正在运行时,任何时候都可能发生中断(“真正的”中断,而不是用户调用的)。

还有一个很好的理由使用pusha/popa不加区别地。您的堆栈大小有限——当然,它很大,但可以嵌套的例程数量也是如此。在 32 位和 64 位代码中,寄存器也更大。

每个例程都应该只保留那些已知会发生变化的寄存器,并且镜像它,您应该只保存稍后调用此类例程之前需要的寄存器。在示例代码中没有,因此您可以安全地删除所有推送和弹出。

  • 对于 30 岁的计算机架构?是的。 (9认同)