A stack is an area of memory for storing data temporarily. Unlike other segments that store data starting from low memory, the stack stores data in reverse order—starting from high memory. Data is always pushed or popped from the top of the stack. The data on the stack can be the calling addresses of procedures or interrupts, procedure arguments, or any operands, flags, or registers your program needs to store temporarily.
At first, the stack is an uninitialized segment of a finite size. As data is added to the stack at run time, the stack grows downward from high memory to low memory. When items are removed from the stack, it shrinks upward from low to high memory.
Summary: PUSH and POP always operate on word-sized data.
The PUSH instruction stores a two-byte operand on the stack. The POP instruction retrieves a previously pushed value. When a value is pushed onto the stack, the assembler decreases the SP (Stack Pointer) register by 2. On 8086-based processors, the SP register always points to the top of the stack. The PUSH and POP instructions use the SP register to keep track of the current position.
When a value is popped off the stack, the assembler increases the SP register by 2. Although the stack always contains word values, the SP register points to byte addresses. Thus, SP changes in multiples of two. When a PUSH or POP instruction executes in a 32-bit code segment (one with USE32 use type), the assembler transfers a four-byte value, and ESP changes in multiples of four.
NOTE:
The 8086 and 8088 processors differ from later Intel processors in how they push and pop the SP register. If you give the statement push sp with the 8086 or 8088, the word pushed is the word in SP after the push operation.
Figure 4.2 illustrates how pushes and pops change the SP register.
On the 8086, PUSH and POP take only registers or memory expressions as their operands. The other processors allow an immediate value to be an operand for PUSH. For example, the following statement is legal on the 80186–80486 processors:
push 7 ; 3 clocks on 80286
That statement is faster than these equivalent statements, which are required on the 8088 or 8086:
mov ax, 7 ; 2 clocks plus
push ax ; 3 clocks on 80286
Summary: There are two ways to clean up the stack.
Words are popped off the stack in reverse order: the last item pushed is the first popped. To return the stack to its original status, you can do the same number of pops as pushes. You can subtract the correct number of words from the SP register if you want to restore the stack without using the values on it.
To reference operands on the stack, keep in mind that the values pointed to by the BP (Base Pointer) and SP registers are relative to the SS (Stack Segment) register. The BP register is often used to point to the base of a frame of reference (a stack frame) within the stack.
This example shows how you can access values on the stack using indirect memory operands with BP as the base register.
push bp ; Save current value of BP
mov bp, sp ; Set stack frame
push ax ; Push first; SP = BP - 2
push bx ; Push second; SP = BP - 4
push cx ; Push third; SP = BP - 6
.
.
.
mov ax, [bp-6] ; Put third in AX
mov bx, [bp-4] ; Put second in BX
mov cx, [bp-2] ; Put first in CX
.
.
.
add sp, 6 ; Restore stack pointer
; two bytes per push
pop bp ; Restore BP
Summary: Creating labels for stack variables makes code easier to read.
If you use these stack values often in your program, you may want to give them labels. For example, you can use TEXTEQU to create a label such as count TEXTEQU <bp-6>. Now you can replace the mov ax, [bp - 6] statement in the example above with mov ax, count. Section 9.1, “Text Macros,” gives more information about the TEXTEQU directive.
Flags can be pushed and popped onto the stack with the PUSHF and POPF instructions. You can use these instructions to save the status of flags before a procedure call and then to restore the original status after the procedure. You can also use them within a procedure to save and restore the flag status of the caller. The 32-bit versions of these instructions are PUSHFD and POPFD.
This example saves the flags register before calling the systask procedure:
pushf
call systask
popf
If you do not need to store the entire flag register, you can use the LAHF instruction to manually load and store the status of the lower byte of the flag register in the AH register. (You need to save AH before making a procedure call.) SAHF restores the value.
Starting with the 80186 processor, the PUSHA and POPA instructions push or pop all the general-purpose registers with only one instruction. These instructions save the status of all registers before a procedure call and then restore them after the return. Using PUSHA and POPA is significantly faster and takes fewer bytes of code than pushing and popping each register individually.
The processor pushes the registers in the following order: AX, CX, DX, BX, SP, BP, SI, and DI. The SP word pushed is the value before the first register is pushed.
The processor pops the registers in the opposite order. The 32-bit versions of these instructions are PUSHAD and POPAD.