我被告知要将有效寄存器放入堆栈中,以便稍后在"子程序"中覆盖它们。对我来说很清楚,每个人都知道。但是当我读到我的伙伴的代码时,我发现了以下代码:
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
es之间是否存在任何差异?
提前谢谢。
答案 0 :(得分:3)
pusha
操作推送的内容超过ax
,bx
,cx
和dx
个注册:
“在下面将所有通用寄存器压入堆栈 顺序:(E)AX,(E)CX,(E)DX,(E)BX,(E)SP,(E)BP,(E)SI,(E)DI。该 SP的值是实际推送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
答案 1 :(得分:3)
是的,pusha
和popa
在功能上是等效的,只是因为他们推送/弹出所有寄存器。但是,是否有必要对简单的DOS中断调用执行此操作?
转移到中断例程
...
- 中断例程负责恢复它使用的任何寄存器 (http://www.shsu.edu/csc_tjm/spring2001/cs272/interrupt.html)
与所有操作一样,尽可能多地执行操作,仅此而已。中断调用必须保留寄存器 - 除了记录的更改之外。常规状态调用会在AX
中返回结果,并且可能会更改标志,并且不会更改任何其他内容。如果中断改变了其他任何内容(例如ds:dx
),则应在其文档中说明。
在您的代码中,使用ah=09 / int 21
,唯一的更改是
回程:AL = 24h
因此可以安全地假设所有其他寄存器"存储"。
有一个原因是中断尽可能保留。在全球范围内,当其他代码运行时,中断("真实"那些,而非用户调用的)可能随时发生。
还有一个很好的理由不不加区别地使用pusha/popa
。您的堆栈大小有限 - 当然,它很大,但可以嵌套的例程数量也是如此。在32位和64位代码中,寄存器也更大。
每个例程都应该只保留那些已知要更改的寄存器,并且镜像它们,你应该只在以后中保存那些之前需要的那些寄存器来调用这样的例程。在示例代码中没有,因此您可以安全地删除所有推送和弹出。
答案 2 :(得分:1)
pusha
会在执行sp
,bp
和si
之前保存更多寄存器,尤其是di
的值。除此之外,行为几乎完全相同。