Bartłomiej Szałach
Bartłomiej Szałach

Reputation: 2453

x86 Assembly set of 'push'es and 'pusha' difference

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 pushes? Thank you in advance.

Upvotes: 10

Views: 13032

Answers (3)

Guffa
Guffa

Reputation: 700222

The pusha operation pushes more than the ax, bx, cx and dx registers:

"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."

You can do basically the same by pushing the registers using eight push instructions, but that will push a different value for sp. You can simply omit the stack pointer as there is actually no need to push it. Pushing and poping the seven registers fills the same purpose as pusha and popa even if it doens't do exactly the same thing:

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

Upvotes: 6

Jongware
Jongware

Reputation: 22457

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
...

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.

Upvotes: 6

Daniel Kamil Kozar
Daniel Kamil Kozar

Reputation: 19266

pusha saves some more registers, notably the value of sp before executing, bp, si, and di. Other than that, the behaviour is pretty much identical.

Upvotes: 1

Related Questions