Once INT 21h AH=4Bh has returned, the parent asks with 4Dh how the child went. AL is the code the child passed in AL to INT 21h AH=4Ch (0 if it ended with INT 20h or a RET), and AH says how it ended:
| AH | The child ended |
|---|---|
| 0 | normally: 4Ch, INT 20h, a RET to its PSP |
| 1 | with Ctrl+C or Ctrl+Break (INT 23h) |
| 2 | through a critical error: Abort at the Abort, Retry, Fail prompt (INT 24h) |
| 3 | with INT 21h AH=31h: it stayed resident |
The shell does the same after every program it runs and keeps AL as ERRORLEVEL for batch files. IF ERRORLEVEL 3 is true when the code is 3 or more, so batch files test the highest codes first.
In MS-DOS the code can be read only once: DOS clears it on reading, and a second 4Dh returns 0. DOSBox-X keeps it and returns the same code again. Either way, read it right after the EXEC.
The example writes itself a child: a five-byte TEST.COM, B8 07 4C CD 21, which is MOV AX, 4C07h / INT 21h. It runs it, reads the code, and deletes the file:
MOV AH, 4Ah ; make room for a childMOV BX, 1000hINT 21hMOV AH, 3Ch ; DOS: create TEST.COMXOR CX, CX ; a normal fileMOV DX, OFFSET PROG1INT 21hJC DONE1MOV BX, AX ; the handleMOV AH, 40h ; DOS: write the child, five bytesMOV CX, 5MOV DX, OFFSET CHILD1INT 21hMOV AH, 3Eh ; DOS: close itINT 21hMOV TAILP1+2, CSMOV FCB1P+2, CSMOV FCB2P+2, CSMOV SAVESP1, SPMOV AX, 4B00h ; DOS: run itMOV DX, OFFSET PROG1MOV BX, OFFSET ENV1 ; ES:BX = the parameter blockINT 21hMOV BX, CS ; MOV leaves CF alone: segments back firstMOV SS, BXMOV SP, CS:SAVESP1MOV DS, BXMOV ES, BXJC GONE1 ; it did not runMOV DX, OFFSET CODEM1MOV AH, 09hINT 21hMOV AH, 4Dh ; DOS: how did the child end?INT 21h ; AL = its code, AH = 0: through 4ChCALL PHEXMOV DX, OFFSET CRLFMOV AH, 09hINT 21hGONE1:MOV AH, 41h ; DOS: delete TEST.COMMOV DX, OFFSET PROG1INT 21hDONE1:PROG1 DB "TEST.COM", 0CHILD1 DB 0B8h, 07h, 4Ch, 0CDh, 21h ; MOV AX, 4C07h / INT 21hTAIL1 DB 0, 13 ; an empty command tailENV1 DW 0 ; parameter block: environment, 0 = a copy of oursTAILP1 DW OFFSET TAIL1, 0 ; the command tail, offset and segmentFCB1P DW 5Ch, 0 ; FCB 1: the one in our PSPFCB2P DW 6Ch, 0 ; FCB 2SAVESP1 DW 0CODEM1 DB "the child returned AX = $"PHEX PROC NEAR ; print AX as four hex digits (changes AX BX CX DX)MOV BX, AXMOV CX, 4PHEX1:PUSH CXMOV CL, 4ROL BX, CL ; the top digit to the bottomPOP CXMOV DL, BLAND DL, 0FhADD DL, "0"CMP DL, "9"JBE PHEX2ADD DL, 7 ; "A" to "F"PHEX2:MOV AH, 02hINT 21hLOOP PHEX1RETPHEX ENDPCRLF DB 13, 10, "$"