INT 21h AH=4Ch ends the program. DOS closes the files it opened through handles, frees every memory block it owns, puts back the INT 22h, 23h and 24h vectors saved in its PSP, and continues in the parent with the return code in AL: the shell keeps it as ERRORLEVEL, a program that ran this one with EXEC reads it with INT 21h AH=4Dh.
Other interrupt vectors are not restored. A program that hooked INT 1Ch or INT 9 must put the old vectors back itself before exiting, or the next timer tick or key press jumps into memory that is free and soon overwritten.
Every example in this guide ends this way: the skeleton adds MOV AX, 4C00h / INT 21h, AH = 4Ch and AL = 0 in one instruction. This one exits early, with a code of its own:
MOV DX, OFFSET MSG1MOV AH, 09hINT 21hMOV AX, 4C03h ; DOS: exit, AL = 3INT 21h ; no return: the skeleton's exit is never reachedMSG1 DB "leaving with return code 3", 13, 10, "$"
The other ways out
| Exit | Works when | Return code |
|---|---|---|
| MOV AX, 4C00h / INT 21h | always (DOS 2 and later) | AL |
| INT 20h | CS = the PSP, as in a .COM | 0 |
| RET in a .COM's main code | the stack holds the 0000h DOS pushed: it returns to PSP:0000, where INT 20h is | 0 |
| MOV AH, 00h / INT 21h | CS = the PSP | 0 |
| INT 21h AH=31h | to stay resident | AL |
The older exits come from CP/M and need CS to point at the PSP, which is true in a .COM but not in an .EXE. 4Ch works from any code, and is the only one that passes a return code. By convention 0 means success and small numbers mean failures.