x86 汇编集'push'es 和 'pusha' 区别
x86 Assembly set of 'push'es and 'pusha' difference
有人告诉我将有效的寄存器放入堆栈中,以免稍后在 "subprogram" 中覆盖它们,这对我来说很清楚,每个人都知道。但是当我阅读我朋友的代码时,我发现了以下代码:
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
然后我看到了pusha
和popa
命令。我想可以这样做:
puts: ; display character in ax
pusha
mov dx, ax
mov ah, 9h
int 21h
popa
ret
pusha
和 push
组有什么区别吗?
提前谢谢你。
pusha
保存了更多的寄存器,特别是执行前sp
、bp
、si
和di
的值。除此之外,行为几乎相同。
pusha
操作比ax
、bx
、cx
和dx
寄存器压入更多:
"Pushes all general purpose registers onto the stack in the following
order: (E)AX, (E)CX, (E)DX, (E)BX, (E)SP, (E)BP, (E)SI, (E)DI. The
value of SP is the value before the actual push of SP."
您可以通过使用八个 push
指令压入寄存器来完成基本相同的操作,但这将为 sp
压入不同的值。您可以简单地省略堆栈指针,因为实际上不需要压入它。压入和弹出七个寄存器的目的与 pusha
和 popa
相同,即使它不做完全相同的事情:
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
是的,pusha
和 popa
在功能上是等价的,只是因为它们 push/pop all 寄存器。但是,对于一个简单的DOS中断调用,有必要这样做吗?
Transferring to an Interrupt Routine
...
- It is the responsibility of the interrupt routine to restore any registers it uses
(http://www.shsu.edu/csc_tjm/spring2001/cs272/interrupt.html)
与所有操作一样,该做多少就做多少,仅此而已。中断调用 必须 保留寄存器 - 除了那些 记录要更改 的寄存器。常规状态调用 return 它们的结果在 AX
中并且可能会更改标志,并且不会更改任何其他内容。如果中断改变了其他任何东西(例如 ds:dx
),则应在其文档中说明。
在您的代码中,使用 ah=09 / int 21
,唯一的变化是
Return: AL = 24h
因此所有其他寄存器都可以安全地假定为“已存储”。
中断尽可能多地保留是有原因的。在全球范围内,中断(“真正的”中断,而不是用户调用的中断)可能随时发生,而其他代码是 运行.
也有很好的理由不乱用pusha/popa
。您的堆栈大小有限——当然,它很大,但可以嵌套的例程数量也是如此。在 32 位和 64 位代码中,寄存器也更大。
每个例程都应该只保留那些已知会更改的寄存器,并且反映这一点,您应该只保存稍后 在 调用此类例程之前需要的那些。在示例代码中有none,因此您可以安全地删除所有推入和弹出。
有人告诉我将有效的寄存器放入堆栈中,以免稍后在 "subprogram" 中覆盖它们,这对我来说很清楚,每个人都知道。但是当我阅读我朋友的代码时,我发现了以下代码:
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
然后我看到了pusha
和popa
命令。我想可以这样做:
puts: ; display character in ax
pusha
mov dx, ax
mov ah, 9h
int 21h
popa
ret
pusha
和 push
组有什么区别吗?
提前谢谢你。
pusha
保存了更多的寄存器,特别是执行前sp
、bp
、si
和di
的值。除此之外,行为几乎相同。
pusha
操作比ax
、bx
、cx
和dx
寄存器压入更多:
"Pushes all general purpose registers onto the stack in the following order: (E)AX, (E)CX, (E)DX, (E)BX, (E)SP, (E)BP, (E)SI, (E)DI. The value of SP is the value before the actual push of SP."
您可以通过使用八个 push
指令压入寄存器来完成基本相同的操作,但这将为 sp
压入不同的值。您可以简单地省略堆栈指针,因为实际上不需要压入它。压入和弹出七个寄存器的目的与 pusha
和 popa
相同,即使它不做完全相同的事情:
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
是的,pusha
和 popa
在功能上是等价的,只是因为它们 push/pop all 寄存器。但是,对于一个简单的DOS中断调用,有必要这样做吗?
Transferring to an Interrupt Routine
...
- It is the responsibility of the interrupt routine to restore any registers it uses
(http://www.shsu.edu/csc_tjm/spring2001/cs272/interrupt.html)
与所有操作一样,该做多少就做多少,仅此而已。中断调用 必须 保留寄存器 - 除了那些 记录要更改 的寄存器。常规状态调用 return 它们的结果在 AX
中并且可能会更改标志,并且不会更改任何其他内容。如果中断改变了其他任何东西(例如 ds:dx
),则应在其文档中说明。
在您的代码中,使用 ah=09 / int 21
,唯一的变化是
Return: AL = 24h
因此所有其他寄存器都可以安全地假定为“已存储”。
中断尽可能多地保留是有原因的。在全球范围内,中断(“真正的”中断,而不是用户调用的中断)可能随时发生,而其他代码是 运行.
也有很好的理由不乱用pusha/popa
。您的堆栈大小有限——当然,它很大,但可以嵌套的例程数量也是如此。在 32 位和 64 位代码中,寄存器也更大。
每个例程都应该只保留那些已知会更改的寄存器,并且反映这一点,您应该只保存稍后 在 调用此类例程之前需要的那些。在示例代码中有none,因此您可以安全地删除所有推入和弹出。