In assembly language programming, especially in the context of MASM (Microsoft Macro Assembler), the stack is a fundamental part of the memory organization. It is used for managing function calls, storing temporary data, and handling interrupt or exception information.
The stack operates on the Last-In, First-Out (LIFO) principle, meaning that the last value pushed onto the stack is the first one that will be popped off. The stack grows downwards in memory, and it is manipulated using special instructions like PUSH, POP, and others.
The stack is a special region of memory used for storing temporary data such as:
There are two primary operations used to manipulate the stack in MASM:
Additionally, there are other instructions and concepts related to the stack:
SP) is a register that points to the top of the stack. It is automatically adjusted when data is pushed to or popped from the stack.The PUSH instruction adds data to the stack. It decreases the SP (stack pointer) by the size of the data being pushed (typically 2 bytes for a word, 4 bytes for a double word), and then stores the data at the new location pointed to by SP.
mov ax, 1234h ; Load AX with a value
push ax ; Push the value of AX onto the stack
The POP instruction retrieves data from the stack. It first loads the value at the current location pointed to by SP into the specified register or memory location, and then increments the SP to point to the next item on the stack.
pop ax ; Pop the value from the stack into AX
When a procedure (or function) is called, the stack plays an important role in saving the current state, including the return address, so the program can continue executing after the procedure finishes.
CALL instruction pushes the return address (the address of the next instruction) onto the stack and then jumps to the procedure.RET instruction pops the return address from the stack and jumps back to the calling procedure.; Procedure that calls another procedure
main:
; Call the Procedure1
call Procedure1
; Execution returns here after Procedure1 finishes
mov ax, 4Ch
int 21h ; Exit the program
; Procedure1
Procedure1 proc
; Save registers or temporary data if needed
push ax ; Push AX onto the stack (save its value)
; Code for the procedure
mov ax, 1234h ; Do some operations
; Perform tasks here...
pop ax ; Pop the value back into AX (restore it)
ret ; Return to the calling function (main)
Procedure1 endp
call Procedure1: When Procedure1 is called, the return address (the address of the instruction after call Procedure1) is pushed onto the stack.ret: The ret instruction pops the return address from the stack and jumps to that address.In many cases, local variables are stored on the stack. When a procedure is called, a "stack frame" is created, which holds local variables, saved registers, and return addresses. This allows each function call to have its own isolated data, even if multiple functions are called in a nested manner.
.model small
.stack 100h
.data
msg db 'Stack example finished.$', 0
.code
main:
mov ax, @data ; Initialize data segment
mov ds, ax
; Call the procedure
call StackExample
; Exit program
mov ah, 4Ch
int 21h
StackExample proc
; Push registers and create space for local variables
push ax
push bx
; Local variables on the stack
mov ax, 5
mov bx, 10
add ax, bx ; ax = 5 + 10 = 15
; Clean up stack before returning
pop bx
pop ax
ret
StackExample endp
end main
push ax, push bx): Before using registers for calculations, we push their current values onto the stack to preserve them.ax, bx): We can treat registers like local variables by storing their values temporarily on the stack.pop bx, pop ax): After the procedure finishes its execution, we restore the values of registers from the stack, cleaning up the stack.The stack allows procedures to be nested. Each call to a procedure pushes a new return address and local data onto the stack. This makes it possible to have multiple function calls that can return to the correct place in the program.
.model small
.stack 100h
.data
msg db 'Finished calling procedures.$', 0
.code
main:
mov ax, @data ; Initialize data segment
mov ds, ax
; Call the first procedure
call OuterProcedure
; Exit program
mov ah, 4Ch
int 21h
OuterProcedure proc
; Save state of registers
push ax
push bx
; Call the inner procedure
call InnerProcedure
; Restore state of registers
pop bx
pop ax
ret
OuterProcedure endp
InnerProcedure proc
; Example function that just prints something
mov ax, 1234h ; Set some value for AX
; Normally you'd do something with AX here, but just return for this example
ret
InnerProcedure endp
end main
OuterProcedure calls InnerProcedure, the return address of OuterProcedure is pushed onto the stack. The stack pointer is adjusted accordingly.InnerProcedure finishes, it returns to OuterProcedure by popping the return address from the stack.InnerProcedure returns, the execution returns to OuterProcedure, which restores any saved register values before returning to the main program.The stack frame ensures that even with nested function calls, the return address and local data for each procedure are kept separate and can be restored correctly.
A stack overflow occurs when the stack exceeds its limit, often due to too many nested function calls or allocating too much space for local variables. This can cause the program to crash or behave unexpectedly. To avoid this, the size of the stack must be carefully managed, and recursion should be used cautiously.
Open this section to load past papers