A near CALL pushes IP, which already points to the instruction after the CALL, and jumps to the target: SP goes down by 2 and [SP] holds the return offset. A far CALL pushes CS first and then IP, four bytes in all, and loads both CS and IP. The procedure later executes RET (or RETF), which pops that address and comes back. Flags are not touched.
| Form | Encoding | Pushes |
|---|---|---|
| CALL label | E8h + 16-bit displacement | IP |
| CALL BX, CALL WORD PTR [x] | FFh /2 | IP |
| CALL FAR PTR label | 9Ah + offset + segment | CS, then IP |
| CALL DWORD PTR [x] | FFh /3 | CS, then IP |
There is no short form: a near direct call always reaches the whole segment. The indirect forms take the address from a register or memory, for tables of procedures and callbacks.
MOV DX, OFFSET MSG1CALL PRINT1 ; near CALL: pushes the return IPMOV DX, OFFSET MSG2MOV BX, OFFSET PRINT1CALL BX ; the same procedure, address in a registerPRINT1 PROC NEAR ; print the $-string at DS:DXMOV AH, 09hINT 21hRETPRINT1 ENDPMSG1 DB "First call", 13, 10, "$"MSG2 DB "Second call", 13, 10, "$"
Pitfalls
The return address sits on top of the stack, so the procedure must pop exactly what it pushed before RET; one stray PUSH and RET jumps to whatever value that was. A procedure changes registers unless it saves them, so document what it keeps. Near and far must match: a near RET after a far CALL leaves CS on the stack. A .COM program cannot CALL FAR PTR a label of its own, because a .COM file has no relocations; a CALL followed directly by RET can be replaced with a plain JMP.
Later CPUs
From the 386, 32-bit code pushes 4-byte return offsets (EIP). The 16-bit forms are unchanged.