Nowak Grzegorz
Nowak Grzegorz

Reputation: 207

Push all and pop all in emu8086

I'm looking for an easy way of pusha and popa in my assembly program. I want to use emu8086 to find bugs in my programs so .286 is not allowed.

I tried:

push_a proc
push ax
push cx
push dx
push bx
push sp
push bp
push si
push di
push_a endp

pop_a proc
pop di
pop si
pop bp
pop sp
pop bx
pop dx
pop cx
pop ax
pop_a endp

But it doesn't work, thus when we call push_a we push to the stack the address where we are now.
Is there another easy, simple way? I don't want to write eight push and eight pop every time.

Upvotes: 1

Views: 2327

Answers (3)

Sep Roland
Sep Roland

Reputation: 39166

I'm looking for an easy way of pusha and popa in my assembly program.

and

I don't want to write eight push and eight pop every time.

This is exactly what you will be doing when you use the proposed MACRO solution! Each time you use the name of the macro, the entire block of code that it represents is substituted. This truly will be a waste of space.

Although the answer by @MargaretBloom is excellent in explaining all the problems with push sp and pop sp, it doesn't give you the easiest solution. Moreover in the context of preserving and restoring all the general purpose registers, pushing and popping the stackpointer is a silly operation. Even the manufacturer (Intel) knows this and cleverly bypasses the pushed SP value when performing popa. The SP register is pushed by pusha only because it simplifies the hardware design.

The best solution then that fulfills the requirements temporarily removes the return address from call push_a / call pop_a before pushing / popping the 7 useful GPRs. Directly before executing ret the return address is put back on the stack. Voilà.

push_a proc
    pop word ptr [TEMP]
    push ax
    push cx
    push dx
    push bx
    push bp
    push si
    push di
    push word ptr [TEMP]
    ret
push_a endp

pop_a proc
    pop word ptr [TEMP]
    pop di
    pop si
    pop bp
    pop bx
    pop dx
    pop cx
    pop ax
    push word ptr [TEMP]
    ret
pop_a endp

This solution also does not alter any of the flags for its operation. Was observed by Fifoernik.
Here's an idea: Why not preserve the FLAGS as an eighth register?

One final note. To make this code really robust you could put this TEMP variable in the code segment of your program and address it with an explicit CS: override prefix. Your DS segment register might not always be in the correct place when you need to use your new push_a / pop_a calls. Your CS register inherently will be correct. If it weren't, the call would not even function.

Upvotes: 2

Margaret Bloom
Margaret Bloom

Reputation: 44046

One could implement the procedures as macros

pusha MACRO
   push ax
   push cx
   push dx
   push bx
   push sp
   push bp
   push si
   push di
pusha ENDM

popa MACRO
   pop di
   pop si
   pop bp
   pop sp
   pop bx
   pop dx
   pop cx
   pop ax
popa ENDM

but this is not the architectural behaviour of pusha/popa and it is broken code on a real 8086.

The real semantic of pusha

The push sp in pusha should push the state of the stack pointer at the start of the pusha macro.

Temp ← (SP);
Push(AX);
Push(CX);
Push(DX);
Push(BX);
Push(Temp);
Push(BP);
Push(SI);
Push(DI);

                                                                                             Intel pseudo-code for pusha - Intel Manual 2B

Using a scratch memory location

You can do something similar if you have a scratch memory location:

pusha MACRO
   mov WORD PTR [pusha_scratch], sp
   push ax
   push cx
   push dx
   push bx
   push WORD PTR [pusha_scratch]
   push bp
   push si
   push di 
pusha ENDM

Making sure that the pusha_scratch is accessible through DS under any context can be quite of an endeavour.
Not accounting for a separate definition of pusha_scratch.

Using a position independent, in-place, version

Alternatively one could account for the modified value of sp with something that is position independent and operates in-place.
If I did my math correctly - double check it - this code should do the trick

pusha MACRO
   push ax
   push cx
   push dx
   push bx

   push bp                  ;Top of stack = BP
   mov bp, sp               ;BP points to TOS
   lea bp, [bp+0ah]         ;BP is equal to the starting value of SP
   xchg bp, [bp-0ah]        ;BP = Original BP, [SP] = Starting value of BP

   push bp
   push si
   push di
pusha ENDM

Many thanks to Fifoernik for correcting the broken code by noting the inverted math and preventing the altering of the EFLAGS register.

The nice thing about the snippet above is that it avoids using push sp entirely as it is a bit problematic when it comes to backward compatibility.

The problem of push sp

On a real 8086 the instruction push sp behaves differently:

The P6 family, Pentium, Intel486, Intel386, and Intel 286 processors push a different value on the stack for a PUSH SP instruction than the 8086 processor.
The 32-bit processors push the value of the SP register before it is decremented as part of the push operation;
the 8086 processor pushes the value of the SP register after it is decremented.

                                                                                             Intel compatibility section - Intel Manual 3 - 22.17

I don't know how accurate emu8086 is as an emulator of the 8086 but I would avoid push sp.

The semantic of popa

The peculiar way pusha is implemented calls for a peculiar implementation of popa.
On a real 8086 a naive list of pops breaks as soon as pop sp is executed.

Indeed the CPU exploits the fact that you can't really modify sp even after a pusha - otherwise there would be no way to get those values back - and notices that after all the pops the original value is implicitly restored.

DI ← Pop();
SI ← Pop();
BP ← Pop();
Increment ESP by 2; (* Skip next 2 bytes of stack *)
BX ← Pop();
DX ← Pop();
CX ← Pop();
AX ← Pop();

                                                                                             Intel pseudo-code for popa - Intel Manual 2B

So a more correct implementation of popa is

popa MACRO
   pop di
   pop si
   pop bp
   add sp, 02h         ;Beware, this affects EFLAGS
   pop bx
   pop dx
   pop cx
   pop ax
popa ENDM

Note that you must avoid pop sp in a real 8086 as a push sp followed by a pop sp is not idempotent, for

The POP ESP instruction increments the stack pointer (ESP) before data at the old top of stack is written into the destination.

                                                                                             Intel description for pop - Intel Manual 2B

Finally, one would expect pusha/popa to be atomic with respect to interrupts, so a cli/sti pair is needed around the macro bodies.


If you use pusha/popa just as a shorthand for saving all the registers, then all the hassle can be skipped and a sufficient implementation is

 push_all MACRO
   push ax
   push cx
   push dx
   push bx
   ;NOTE: Missing SP
   push bp
   push si
   push di
push_all ENDM

pop_all MACRO
   pop di
   pop si
   pop bp
   ;NOTE: Missing SP
   pop bx
   pop dx
   pop cx
   pop ax
pop_all ENDM

Upvotes: 7

Nowak Grzegorz
Nowak Grzegorz

Reputation: 207

pushuj    MACRO  
    push ax
    push cx
    push dx
    push bx
    push sp
    push bp
    push si
    push di

ENDM

and when i want to call it i write:

pushuj

I it good?

Upvotes: -1

Related Questions