I was told to put valid registers into stack not to overwrite them later in "subprogram" and it's clear to me, everyone knows it. But when I read the code of my mates I found the following code:
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
Then I saw pusha
and popa
commands. I suppose it could be done this way:
puts: ; display character in ax
pusha
mov dx, ax
mov ah, 9h
int 21h
popa
ret
Is there any difference between the pusha
and set of push
es?
Thank you in advance.
PUSHA/PUSHAD--Push All General-Purpose RegistersPushes the contents of the general-purpose registers onto the stack.
pushing a value (not necessarily stored in a register) means writing it to the stack. popping means restoring whatever is on top of the stack into a register.
That is, DI, SI, BP, SP, BX, DX, CX, AX. Used to restore state after a call to PUSHA. This instruction works similarly to pusha, but pushes the 32-bit general purpose registers onto the stack instead of their 16-bit counterparts.
The push instruction places its operand onto the top of the hardware supported stack in memory. Specifically, push first decrements ESP by 4, then places its operand into the contents of the 32-bit location at address [ESP].
Yes, pusha
and popa
are functionally equivalent, if only because they push/pop all registers. However, is it necessary to do so for a simple DOS Interrupt call?
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)
As with all operations, do as much as is needed and nothing more. An interrupt call must preserve registers - apart from the ones that are documented to change. Regular status calls return their result in AX
and may change the flags, and change nothing else. If an interrupt changes anything else (ds:dx
, for example), it should be stated so in its documentation.
In your code, using ah=09 / int 21
, the only change is
Return: AL = 24h
and so all other registers can safely be assumed "stored".
There is a reason an interrupt preserves as much as possible. Globally, an interrupt (the "real" ones, not the user-invoked) may happen any time while other code is running.
There is also a good reason not to use pusha/popa
indiscriminately. Your stack has a limited size -- sure, it's large, but so is the number of routines that can be nested. And in 32- and 64-bit code the registers are way larger as well.
Every single routine should preserve only those registers that are known to change, and mirroring that, you should only save the ones you will need later before you call such a routine. In the example code there are none, so you can safely remove all pushing and popping.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With