Why does PUSHA also push the stack pointer?

This puzzle is actually a quite easy one – but when I asked it in a university course, it kept some people busy for some time to find out the answer, so I thought it might be a good idea to ask you nevertheless:

The 8086 “PUSHA” instruction saves the following registers on the stack:
AX, CX, DX, BX, SP, BP, SI, DI
In i386+ 32 bit mode, these registers are EAX etc., and in x86_64 64 bit mode, it is RAX etc., but all of them always also save the stack pointer.

Why do x86 CPUs do this? Isn’t it true that it makes no sense to save the stack pointer, as the stack pointer is needed to retrieve these values anyway?

In a few days, I’ll post some trivia on this topic…

11 thoughts on “Why does PUSHA also push the stack pointer?”

  1. It looks more convenient to store 8 instead of 7 registers, but PUSHA/POPA are too old for the CPU to have cache lines, so its rather really due to the fact that the regs are internally numerated 0..7 and stored in exactly that order.

  2. I’m no x86 expert, but here’s a couple of possibilities…

    1) If the SP points at SP *before* the PUSHA, it means you can eaily pop off a frame without knowing how big it is

    2) If the SP points at itself, it’s an easy way to find one of these frames on the stack :)

    3) It makes things easier if you’re accessing the stack via an alias (eg. old-school segments, or the MMU). Dunno what you do about the IP, though…

  3. @balial: Your ideas are very creative. :-)

    1) Right, but then ESP should be the topmost value on the stack, but it’s not.

    2) Correct. Do you have a use case? ;-)

    3) I don’t quite understand this one. If you’re accessing stack which is mapped into your address space at the wrong location (because of the MMU or segmentation), you can find out where the stack should usually reside – if you know that a PUSHA has taken place on this stack. But I think this is more like a hack, and less Intel’s intention. :-)

  4. So here is the solution: Ge0rg was basically right. There is no real *sense* in doing it, and pushing SP as well was just easier.

    That there is no sense in doing it is supported by the fact that for the successors of the 8086, it is undefined when the value of SP is read, i.e. an i386 CPU is free to push the SP for example at the time of the “push SP” (so that it points to itself), or the SP at the time of the push AX (so it points to the value of EAX). Furthermore, most post-8086 CPUs just ignore the SP field on the stack when doing a POPA.

    The reason for this is most probably the internal numbering of the registers: AX, CX, DX, BX, SP, SI, DI. (Intel obviously first designed the CPU and then the assembly language, retrofitting names that are easy to remember to the 8 registers: 0=accumulator, 1=counter, 2=data, 3=data, 4=stack pointer, 5=base pointer, 6=source index, 7=destination index.) The stack pointer is register 4, and PUSHA was microcoded in the 8086 CPU as a “for” loop counting from 0 to 7. Omitting register 4 would have meant two “for” loops, one counting from 0 to 3, and one from 5 to 7, effectively requiring twice as many microcode instructions.

    All successors of the 8086 of course had to be (more or less) comatible with this behaviour, although they implement PUSHA very differently today.

  5. @Michael: Yeah, that’s what i meant ;-)

    Also, you forgot BP in your register enumeration: AX, CX, DX, BX, SP, *BP*, SI, DI.

  6. But there is one flaw in the theory – original 8086/8088 don’t have the PUSHA/POPA instructions. This was added later in 80186/80188 chips.

  7. So, it does’nt matter if i change the value of ESP in the stack? i.e the POPA instruction will do the same thing?

  8. First, remember that (E)SP points to the TOS (top-of-stack). PUSH and POP modify (E)SP. PUSHA(D) and POPA(D) essentially use the PUSH and POP instructions, respectively, internally.

    PUSHA(D) temporarily saves the original value of (E)SP and then PUSHes (E)AX, (E)CX, (E)DX, (E)BX, (E)SP (original value), (E)BP, (E)SI, (E)DI onto the stack. (E)SP will point to the value of (E)DI after this instruction executes.

    POPA(D) doesn’t pop (E)SP but instead merely skips the (E)SP value that’s on the stack. Again, this is because POP modifies (E)SP.

    Check the “Intel 64 and IA-32 Architectures Software Developer’s Manual: Volume 2B” for more information about how the PUSHA(D) and POPA(D) instructions work.

Leave a Reply to Anonymous Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.