【问题标题】:x86 Assembly set of 'push'es and 'pusha' differencex86 组装集'push'es 和'pusha' 的区别
【发布时间】:2015-06-26 00:21:20
【问题描述】:

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

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 

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

puts: ; display character in ax
        pusha
        mov dx, ax
        mov ah, 9h
        int 21h
        popa
        ret 

pushapushes 的集合之间有任何区别吗? 提前谢谢你。

【问题讨论】:

  • 请注意 pushapopa 是 80286+ 指令​​。如果你的目标是一台普通的 8086 机器,这些都行不通。事实上,一些汇编程序可能需要 .286 指令才能将它们识别为有效指令。
  • 不使用 .286(点指令)被认为是不好的做法吗?
  • 我无法想象为什么
  • @mcleod_ideafix : pushapopa 是在 80186 中引入的,因此您也可以使用 .186
  • 那么这些操作码在旧 CPU 上做了什么?操作码表的这些位置是否还有其他指令?或者处理器只是做了一些随机的事情或者可能被绞死?

标签: assembly x86 stack assemblies


【解决方案1】:

pusha 保存了更多的寄存器,特别是在执行前sp 的值、bpsidi。除此之外,行为几乎相同。

【讨论】:

    【解决方案2】:

    pusha 操作比axbxcxdx 寄存器推送更多:

    "下面将所有通用寄存器压入栈中 顺序:(E)AX、(E)CX、(E)DX、(E)BX、(E)SP、(E)BP、(E)SI、(E)DI。这 SP的值是SP实际推送前的值。"

    您可以通过使用 8 条 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
    

    【讨论】:

    • 我知道它可以节省更多的寄存器,但可以在给定的示例中替换它吗?
    • @onegrx 是的,只要您也将pops 替换为popa。 :)
    • @onegrx:您无法仅使用 pushpop 指令复制 pushapopa 的确切行为,但您可以实现相同的目的。
    • pusha 更短(pusha 为 1 个字节,而一系列单独的pushes 为 8 个字节)。可能与一些严格的代码相关,例如小 ROM 上的引导加载程序或 BIOS 例程。
    【解决方案3】:

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

    转移到中断例程
    ...

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

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

    返回:AL = 24h

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

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

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

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

    【讨论】:

    • 你引用的 Tim McGuire 是谁? 14岁的文章靠谱吗?
    • 对于 30 年历史的计算机架构?是的。
    猜你喜欢
    • 1970-01-01
    • 2015-01-20
    • 2016-08-05
    • 2017-03-11
    • 2015-06-01
    • 2013-08-04
    • 2014-05-07
    • 2020-03-03
    • 1970-01-01
    相关资源
    最近更新 更多