Stack and Subroutines
PUSH, POP, CALL, RET instructions.
As programs grow in complexity, managing temporary data and reusing code segments becomes critical. The 8085 provides two powerful mechanisms for this: the stack and subroutines. The stack is a special region of RAM used to temporarily store register values, while subroutines are reusable code blocks that can be called from multiple locations in the main program. Together, PUSH, POP, CALL, and RET instructions form the backbone of structured 8085 programming.
Core Concept Explanation
The stack in 8085 is a Last-In-First-Out (LIFO) data structure implemented in RAM. The Stack Pointer (SP) is a 16-bit register that always points to the topmost occupied location of the stack. Initially, the programmer sets SP to a high RAM address (for example, using LXI SP, 2100H) so the stack has space to grow downward into lower addresses.
The PUSH rp instruction saves a 16-bit register pair onto the stack. It first decrements SP by one and stores the high byte of the register pair, then decrements SP again and stores the low byte. So after PUSH B, SP has decreased by 2 and both B and C register values are safely stored on the stack. The POP rp instruction reverses this: it reads the byte at the current SP into the low register, increments SP, reads the next byte into the high register, and increments SP again.
A subroutine is a reusable block of instructions identified by a label. The CALL address instruction transfers control to the subroutine. Before jumping, it automatically pushes the address of the next instruction (the return address) onto the stack. The subroutine executes its body and when it encounters RET, it pops the return address from the stack back into the Program Counter, resuming execution in the calling program at the exact point where CALL was issued.
Mathematical Expression
For PUSH rp: SP = SP - 1, Memory[SP] = High byte of rp. Then SP = SP - 1, Memory[SP] = Low byte of rp. Net effect: SP decreases by 2. For POP rp: Low byte of rp = Memory[SP], SP = SP + 1. High byte of rp = Memory[SP], SP = SP + 1. Net effect: SP increases by 2. For CALL addr: SP = SP - 1, Memory[SP] = PCH (high byte of return address). SP = SP - 1, Memory[SP] = PCL (low byte). PC = addr. For RET: PCL = Memory[SP], SP = SP + 1. PCH = Memory[SP], SP = SP + 1. PC now holds return address.
Practical Understanding
The most important practical use of PUSH and POP is preserving register values during subroutine execution. If a subroutine needs to use register B but the calling program also uses B, the subroutine should begin with PUSH B and end with POP B just before RET. This ensures the calling program's register values are intact after the subroutine returns.
The nesting of subroutine calls is possible because each CALL pushes a new return address on the stack and each RET pops the most recent one. The stack naturally unwinds in the correct order. However, if PUSH and POP are not balanced inside a subroutine, the stack pointer will be incorrect when RET executes, causing it to pop the wrong value into PC and resulting in unpredictable program behavior. This is a common programming error.
Given:
Main program calls a subroutine at address 3000H.
SP is initialized to 2100H. CALL is at address 2010H, so return address is 2013H.
Why this formula applies:
CALL pushes return address onto stack. RET pops it back into PC.
Stack operations for CALL 3000H (executed at 2010H):
Return address = 2013H (next instruction after CALL)
Step 1: SP = 2100H - 1 = 20FFH, Memory[20FFH] = 20H (PCH)
Step 2: SP = 20FFH - 1 = 20FEH, Memory[20FEH] = 13H (PCL)
Step 3: PC = 3000H (jump to subroutine)
On RET execution:
Step 1: PCL = Memory[20FEH] = 13H, SP = 20FEH + 1 = 20FFH
Step 2: PCH = Memory[20FFH] = 20H, SP = 20FFH + 1 = 2100H
Step 3: PC = 2013H (return to main program)
Final Answer:
Program resumes at 2013H. SP restored to 2100H.Exam Tip: Every PUSH inside a subroutine must have a matching POP before the RET instruction. An unbalanced stack causes RET to pop the wrong data into the PC, leading to program crashes. GATE questions often test this by asking what happens to SP or PC when PUSH/POP counts are mismatched.
Key Mechanism Points
- PUSH rp: SP decremented by 2. High byte stored at SP+1, low byte at SP. Registers preserved on stack.
- POP rp: Low byte restored from SP into low register, high byte from SP+1. SP incremented by 2.
- CALL addr: Return address (next instruction PC) pushed onto stack. PC loaded with subroutine address.
- RET: Return address popped from stack into PC. Program continues from where CALL was made.
- Stack is LIFO. Nested CALL and PUSH sequences unwind correctly because each RET and POP restores the most recently pushed value.
- SP must be initialized using LXI SP before any PUSH, POP, CALL, or RET is executed. Without initialization, SP holds an undefined value.
- Conditional CALL variants: CC, CNC, CZ, CNZ, CP, CM, CPE, CPO. Conditional RET variants: RC, RNC, RZ, RNZ, RP, RM, RPE, RPO.
Quick Revision
- PUSH rp: SP = SP-2. Stores 16-bit register pair on stack. Valid pairs: BC, DE, HL, PSW.
- POP rp: Restores 16-bit pair from stack. SP = SP+2.
- CALL addr: Pushes PC+3 (return address) onto stack, jumps to addr.
- RET: Pops 2 bytes from stack into PC. Returns to calling program.
- Stack grows downward (toward lower addresses). SP points to topmost occupied byte.
- PUSH PSW saves accumulator (A) and flag register (F) together. POP PSW restores both.
- Exam trap: Unbalanced PUSH and POP inside a subroutine corrupts the stack. RET will pop wrong value into PC.
Stack and Subroutines Quiz
Test your understanding of CALL, RET, and stack behavior during subroutine execution in the 8085.