Last modified: Tue Jun 30 09:56:03 UTC+0200 2026 © A. Tarpai
CALL and RET
8086 CALL and RET
Before the jump, the current program position is pushed on stack. Either IP (near CALL) or both CS and IP (far CALL). These are different opcodes. A matching near/far RET pops the return address. Both have optional N-bytes "pop".
Far call stack, HW/SW interrupt stack: both CS and IP is pushed onto the stack. SP points to similar structure as memory indirect storage: IP (low) then CS (high).
IRET is far and also pops the flags.
near stack far stack INT stack
15 0 15 0 15 0
| | | | | |
+--------------+ +--------------+ +--------------+
| X X X X | | X X X X | | X X X X |
+--------------+ +--------------+ +--------------+
| X X X X | <-- SP+2 | X X X X | <-- SP+4 | X X X X | <-- SP+6
+--------------+ +--------------+ +--------------+
| IP | <-- SP | CS | | FLAGS |
+--------------+ +--------------+ +--------------+
| | | IP | <-- SP | CS |
+--------------+ +--------------+ +--------------+
| | | | | IP | <-- SP
+--------------+ +--------------+ +--------------+
| | | | | |
+--------------+ +--------------+ +--------------+
| | | | | |
+--------------+ 0 +--------------+ 0 +--------------+ 0
C3 RET CB RETF CF IRET
E8 EC FF call -20 9A 7856 3412 call 1234h:5678h HW/SW INT
FF 50 02 call [bx+si+2] FF 58 02 call far [bx+si+2]
FF D0 call ax
... ... ...
... ... ...
... ... ...
C3 RET CB RETF CF IRET
C2 LO HI RET n CA LO HI RETF n
RET N and RETF N
A 16-bit word operand follows opcode (N):
RET N C2 imm16 RETF N CA imm16
Add N to SP after the return address is popped, i.e. to clean passed parameters (the callee cleans the stack). The addition is unsigned.
Example:
Caller pushed 3 WORD parameters and executed near/far CALL
N is 3 x 2 = 6
near stack far stack
15 0 15 0
| | | |
+--------------+ +--------------+
| X X X X | <-- SP before | X X X X | <-- SP before
+--------------+ +--------------+
| param 3 | | param 3 |
+--------------+ +--------------+
| param 2 | | param 2 |
+--------------+ +--------------+
| param 1 | <-- SP + 2 | param 1 | <-- SP + 4
+--------------+ +--------------+
| IP | <-- SP | CS |
+--------------+ +--------------+
| | | IP | <-- SP
+--------------+
| |
C2 06 00 RET 6 CA 06 00 RETF 6
1. pop word into IP 1. pop word into IP
2. increment SP by two 2. increment SP by two
3. add N to SP 3. pop word into CS
4. stack restored by callee 4. increment SP by two
3. add N to SP
4. stack restored by callee
Note. This is not how the C language compiler works, there the caller cleans up the stack, with eg. ADD SP, 8 – as the caller knows exactly how many parameters were pushed. RET N is what MS calls __stdcall.
386 CALL and RET
Operand-size attribute (either implicit from D-bit or overridden by 66h prefix) determines
- DWORD or WORD push of IP- or CS:IP
- EIP or IP is used for jump
Another issue here is the B-bit: but see push.
When operand-size = 16:
- CALL: uses the same EIP calculation as JMP (upper two bytes of the EIP register are cleared when operand-size = 16).
- RET: POP operates the same way (upper two bytes of the EIP register are cleared).
operand-size = 16 operand-size = 32
D=0 or D=1 and 66h D=1 or D=0 and 66h
push cs word push cs dword
+---------+ +---------+---------+
FAR CALL | CS | | ....... CS |
+---------+ +---------+---------+
push ip word push eip dword
+---------+ +-------------------+
CALL | IP | | EIP |
+---------+ +-------------------+
| |
LOAD IP LOAD EIP
| |
v v
+---------+---------+ +-------------------+
|000000000| | EIP | | EIP
+---------+---------+ +-------------------+
^ ^
| |
LOAD IP LOAD EIP
| |
+---------+ +-------------------+
RET | IP | | EIP |
+---------+ +-------------------+
pop ip word pop eip dword
+---------+ +---------+---------+
RETF | CS | | xxxxxxx CS |
+---------+ +---------+---------+
pop cs word pop cs dword
(.) padded with high-order bits, zeroed or word move and HI on stack unchanged (CPU-variations)
(x) high-order 16-bits discarded
386 RET N
Unchanged opcodes in 32-bit:
- N is still number of bytes
- N is still a 16-bit unsigned value
Eg. to pop 3 DWORD parameters N = 12.
operand-size = 16 operand-size = 32
D=0 or D=1 and 66h D=1 or D=0 and 66h
+---------+ +---------+---------+
| imm16 | | 0 0 0 0 imm16 |
+---------+ +---------+---------+
+---------+---------+ +-------------------+
| . . . . | SP | ESP | | ESP
+---------+---------+ +-------------------+
SP = SP + imm16 ESP = ESP + imm16
(.) ESP HI unchanged imm16 zero-extended
unsigned addition