8086 Stack Operations and Procedures
Master the 8086 stack mechanism, understand procedure calls and returns, learn parameter passing techniques, and practice with real-world programming examples.
Stack Fundamentals in 8086
The stack is a Last In First Out (LIFO) data structure used for temporary storage, procedure calls, and interrupt handling. In 8086, the stack grows downward in memory.
Stack Registers:
- SS (Stack Segment): 16-bit register pointing to stack segment
- SP (Stack Pointer): 16-bit offset within stack segment
- BP (Base Pointer): 16-bit pointer for stack frame access
Stack Physical Address:
Stack Physical Address = (SS × 16) + SP
Example:
If SS = 3000H and SP = 2000H
Stack Address = (3000H × 16) + 2000H = 30000H + 2000H = 32000H
Basic Stack Operations
PUSH Operation
PUSH instruction stores data on stack and decrements SP by 2 (for word data).
PUSH AX ; Push AX register onto stack
PUSH 1234H ; Push immediate value onto stack
PUSH [BX] ; Push memory content onto stack
PUSH CS ; Push segment register onto stack
Operation Steps:
1. SP = SP - 2 (decrement stack pointer)
2. [SS:SP] = Source data (store data at new top)
POP Operation
POP instruction retrieves data from stack and increments SP by 2.
POP AX ; Pop stack top into AX register
POP [BX] ; Pop stack top into memory location
POP DS ; Pop stack top into segment register
Operation Steps:
1. Destination = [SS:SP] (retrieve data from top)
2. SP = SP + 2 (increment stack pointer)
Stack Example:
Initial: SS = 2000H, SP = 1000H, AX = 1234H, BX = 5678H
PUSH AX:
- SP becomes 0FFEH (1000H - 2)
- Memory[2FFEH] = 34H, Memory[2FFFH] = 12H (little endian)
PUSH BX:
- SP becomes 0FFCH (0FFEH - 2)
- Memory[2FFCH] = 78H, Memory[2FFDH] = 56H
POP CX:
- CX = 5678H (BX value retrieved)
- SP becomes 0FFEH (0FFCH + 2)
POP DX:
- DX = 1234H (AX value retrieved)
- SP becomes 1000H (back to original)
Procedure Calls and Returns
CALL Instruction
CALL instruction transfers control to a procedure and saves return address on stack.
Types of CALL:
CALL NEAR PTR procedure_name ; Near call (within same segment)
CALL FAR PTR procedure_name ; Far call (different segment)
CALL BX ; Indirect near call
CALL DWORD PTR [BX] ; Indirect far call
CALL Operation Steps:
Near CALL:
1. SP = SP - 2
2. [SS:SP] = IP (save current IP)
3. IP = procedure address
Far CALL:
1. SP = SP - 2
2. [SS:SP] = CS (save current CS)
3. SP = SP - 2
4. [SS:SP] = IP (save current IP)
5. CS:IP = procedure address
RET Instruction
RET instruction returns control to calling program by restoring address from stack.
RET ; Near return
RET n ; Near return with stack cleanup
RETF ; Far return
RETF n ; Far return with stack cleanup
Near RET:
1. IP = [SS:SP] (restore IP)
2. SP = SP + 2
Far RET:
1. IP = [SS:SP] (restore IP)
2. SP = SP + 2
3. CS = [SS:SP] (restore CS)
4. SP = SP + 2
Parameter Passing Techniques
1. Register Passing
Parameters passed through processor registers (fastest method).
; Main program
MOV AX, 10 ; First parameter
MOV BX, 20 ; Second parameter
CALL ADD_PROC ; Call procedure
; Result returned in AX
ADD_PROC PROC
ADD AX, BX ; Add parameters
RET ; Return result in AX
ADD_PROC ENDP
2. Stack Passing
Parameters passed through stack (allows unlimited parameters).
; Main program
PUSH 20 ; Second parameter (pushed first)
PUSH 10 ; First parameter (pushed second)
CALL ADD_PROC ; Call procedure
ADD SP, 4 ; Clean up stack (2 parameters × 2 bytes)
ADD_PROC PROC
PUSH BP ; Save old BP
MOV BP, SP ; Set up stack frame
MOV AX, [BP+4] ; Get first parameter
ADD AX, [BP+6] ; Add second parameter
POP BP ; Restore BP
RET ; Return result in AX
ADD_PROC ENDP
3. Global Variable Passing
Parameters passed through memory locations.
PARAM1 DW ? ; Global variables
PARAM2 DW ?
RESULT DW ?
; Main program
MOV PARAM1, 10
MOV PARAM2, 20
CALL ADD_PROC
ADD_PROC PROC
MOV AX, PARAM1
ADD AX, PARAM2
MOV RESULT, AX
RET
ADD_PROC ENDP
Stack Frames and Local Variables
Stack Frame Structure
A stack frame is created for each procedure call to manage local variables and parameters.
Stack Frame Example:
CALC_PROC PROC
; Procedure prologue
PUSH BP ; Save caller's BP
MOV BP, SP ; Set up new stack frame
SUB SP, 6 ; Allocate 3 local variables (2 bytes each)
; Local variable access
MOV WORD PTR [BP-2], 100 ; Local var 1
MOV WORD PTR [BP-4], 200 ; Local var 2
MOV WORD PTR [BP-6], 300 ; Local var 3
; Parameter access
MOV AX, [BP+4] ; First parameter
MOV BX, [BP+6] ; Second parameter
; Procedure epilogue
MOV SP, BP ; Deallocate local variables
POP BP ; Restore caller's BP
RET 4 ; Return and clean up 2 parameters
CALC_PROC ENDP
Stack Frame Memory Layout:
Memory Address Content Access Method
[BP+6] Parameter 2 [BP+6]
[BP+4] Parameter 1 [BP+4]
[BP+2] Return Address (saved by CALL)
[BP+0] Saved BP [BP+0]
[BP-2] Local Variable 1 [BP-2]
[BP-4] Local Variable 2 [BP-4]
[BP-6] Local Variable 3 [BP-6]
Recursive Procedures
Recursive procedures call themselves. Each recursive call creates a new stack frame.
Factorial Example:
; Calculate factorial recursively
; Input: AX = number, Output: AX = factorial
FACTORIAL PROC
PUSH BP
MOV BP, SP
PUSH BX ; Save registers
CMP AX, 1 ; Check base case
JLE BASE_CASE ; If n <= 1, return 1
; Recursive case: n * factorial(n-1)
MOV BX, AX ; Save n
DEC AX ; n-1
CALL FACTORIAL ; Recursive call
MUL BX ; n * factorial(n-1)
JMP DONE
BASE_CASE:
MOV AX, 1 ; Return 1 for base case
DONE:
POP BX ; Restore registers
POP BP
RET
FACTORIAL ENDP
; Usage:
MOV AX, 5 ; Calculate 5!
CALL FACTORIAL ; Result in AX = 120
Stack Growth in Recursion:
Call Stack for FACTORIAL(3):
Stack Frame 3: factorial(1) -> returns 1
Stack Frame 2: factorial(2) -> 2 * 1 = 2
Stack Frame 1: factorial(3) -> 3 * 2 = 6
Main Program: receives result 6
Stack-Based Calculations and Examples
Problem 1: Stack Space Calculation
Question: A program has 5 nested procedure calls, each using 4 local variables (2 bytes each). Calculate total stack space needed.
Solution:
Per procedure stack usage:
- Saved BP: 2 bytes
- Return address: 2 bytes (near call)
- Local variables: 4 × 2 = 8 bytes
- Total per call: 2 + 2 + 8 = 12 bytes
For 5 nested calls:
Total stack space = 5 × 12 = 60 bytes
Additional considerations:
- Register saving: varies per procedure
- Parameters: depends on calling convention
- Temporary calculations: varies
Recommended stack size: 60 + 20% overhead = 72 bytes minimum
Problem 2: Parameter Passing Efficiency
Question: Compare clock cycles for register vs stack parameter passing for a procedure with 2 parameters.
Solution:
Register Passing:
MOV AX, param1 ; 4 cycles
MOV BX, param2 ; 4 cycles
CALL proc ; 19 cycles
Total: 27 cycles
Stack Passing:
PUSH param2 ; 15 cycles (memory to stack)
PUSH param1 ; 15 cycles
CALL proc ; 19 cycles
ADD SP, 4 ; 4 cycles (cleanup)
Total: 53 cycles
Efficiency: Register passing is 53/27 = 1.96× faster
Time saved: (53-27)/53 = 49%
Answer: Register passing saves 49% execution time
Problem 3: Stack Overflow Detection
Question: Design a stack overflow check for a system with 1KB stack space.
Solution:
Given: Stack size = 1KB = 1024 bytes = 400H bytes
Stack organization:
Stack Base = SS:0FFFH (top of segment)
Stack Limit = SS:(0FFFH - 400H) = SS:0BFFH
Overflow check before PUSH:
CHECK_STACK PROC
CMP SP, 0C00H ; Check if SP near limit
JA STACK_OK ; Jump if SP > 0C00H (safe)
; Stack overflow error handling
MOV AH, 09H ; Display string function
LEA DX, OVERFLOW_MSG
INT 21H ; DOS interrupt
JMP EXIT_PROGRAM
STACK_OK:
RET
CHECK_STACK ENDP
OVERFLOW_MSG DB 'Stack Overflow!$'
Usage before critical operations:
CALL CHECK_STACK
PUSH AX ; Safe to push
Advanced Stack Techniques
1. Stack-Based Expression Evaluation
Use stack to evaluate complex expressions in postfix notation.
; Evaluate: 5 3 + 2 * (equals (5+3)*2 = 16)
PUSH 5 ; Stack: [5]
PUSH 3 ; Stack: [5, 3]
POP BX ; BX = 3, Stack: [5]
POP AX ; AX = 5, Stack: []
ADD AX, BX ; AX = 8
PUSH AX ; Stack: [8]
PUSH 2 ; Stack: [8, 2]
POP BX ; BX = 2, Stack: [8]
POP AX ; AX = 8, Stack: []
MUL BX ; AX = 16 (result)
2. Stack-Based Sorting Algorithm
Implement quicksort using stack for recursion simulation.
QUICKSORT PROC
; Simulate recursion using explicit stack
PUSH 0 ; Start index
PUSH array_size ; End index
SORT_LOOP:
CMP SP, initial_SP ; Check if stack empty
JE DONE
POP high ; Get range to sort
POP low
; Partition array and get pivot
CALL PARTITION ; Returns pivot in AX
; Push sub-ranges if needed
CMP AX, low
JLE SKIP_LEFT
PUSH low
PUSH AX
DEC WORD PTR [SP] ; AX-1
SKIP_LEFT:
CMP AX, high
JGE SKIP_RIGHT
PUSH AX
INC WORD PTR [SP] ; AX+1
PUSH high
SKIP_RIGHT:
JMP SORT_LOOP
DONE:
RET
QUICKSORT ENDP
3. Exception Handling with Stack
Implement try-catch mechanism using stack frames.
; Exception handling structure
EXCEPTION_FRAME STRUC
prev_frame DW ? ; Previous exception frame
handler_addr DW ? ; Exception handler address
saved_sp DW ? ; Stack pointer at try block
saved_bp DW ? ; Base pointer at try block
EXCEPTION_FRAME ENDS
TRY_BLOCK PROC
; Set up exception frame
SUB SP, SIZE EXCEPTION_FRAME
MOV BP, SP
MOV [BP].prev_frame, current_frame
MOV [BP].handler_addr, OFFSET CATCH_BLOCK
MOV [BP].saved_sp, SP
MOV [BP].saved_bp, BP
MOV current_frame, BP
; Protected code here
CALL RISKY_OPERATION
; Normal cleanup
CALL CLEANUP_EXCEPTION_FRAME
RET
CATCH_BLOCK:
; Exception handler
MOV SP, [BP].saved_sp
MOV BP, [BP].saved_bp
; Handle exception...
RET
TRY_BLOCK ENDP
Stack Performance Optimization
1. Minimize Stack Usage
; Inefficient: Multiple local variables
PROC1:
PUSH BP
MOV BP, SP
SUB SP, 10 ; 5 local variables
; ... code ...
MOV SP, BP
POP BP
RET
; Efficient: Reuse registers
PROC2:
PUSH BP
PUSH AX ; Save only needed registers
PUSH BX
; Use AX, BX as "local variables"
; ... code ...
POP BX
POP AX
POP BP
RET
2. Optimize Parameter Passing
; For frequently called procedures with few parameters:
; Use registers instead of stack
; Inefficient
PUSH param1
PUSH param2
CALL proc
ADD SP, 4
; Efficient
MOV AX, param1
MOV BX, param2
CALL proc
3. Stack Alignment
Align stack data for better performance on modern systems.
; Ensure stack is aligned to word boundaries
; This improves memory access performance
ALIGN_STACK PROC
TEST SP, 1 ; Check if SP is odd
JZ ALIGNED ; Jump if even (aligned)
DEC SP ; Make SP even
ALIGNED:
RET
ALIGN_STACK ENDP
Common Stack Programming Errors
1. Stack Imbalance
; Error: Unmatched PUSH/POP
PUSH AX
PUSH BX
POP AX ; Error: Should POP BX first
; Stack now corrupted
; Correct:
PUSH AX
PUSH BX
POP BX ; Restore in reverse order
POP AX
2. Stack Overflow
; Error: Infinite recursion
ENDLESS_PROC:
CALL ENDLESS_PROC ; No base case!
RET
; Correct: Include base case
FACTORIAL_PROC:
CMP AX, 1
JLE BASE_CASE ; Base case prevents overflow
DEC AX
CALL FACTORIAL_PROC
; ... multiply logic ...
BASE_CASE:
RET
3. Incorrect Stack Cleanup
; Error: Wrong parameter cleanup
PUSH param1
PUSH param2
CALL proc
ADD SP, 2 ; Error: Should be 4 for 2 parameters
; Correct:
PUSH param1
PUSH param2
CALL proc
ADD SP, 4 ; Correct: 2 params × 2 bytes = 4
Summary
Stack operations and procedures are fundamental to 8086 programming. Proper understanding of stack mechanics, parameter passing, and procedure calls is essential for writing efficient and reliable assembly programs. Always maintain stack balance and consider performance implications when choosing parameter passing methods.
Key Points to Remember:
- Stack grows downward (decreasing memory addresses)
- PUSH decrements SP, POP increments SP
- CALL saves return address, RET restores it
- Use BP for stack frame management
- Always balance PUSH/POP operations
- Choose parameter passing method based on performance needs
- Implement stack overflow protection in critical applications
- Recursive procedures require careful stack management