INT 21h AH=06h is DOS's raw console call. With DL = FFh it is a read that does not wait: if a key is ready it comes back in AL with ZF = 0, and if not the call returns at once with ZF = 1 and AL = 0. With any other value in DL it prints that character. Neither direction echoes and neither looks for Ctrl+C: the key 03h is returned like any other.
MOV SI, OFFSET KEYS1CALL STUFF1 ; "o" and "k" wait in the BIOS bufferNEXT1:MOV DL, 0FFh ; DL = FFh: read, if a key is thereMOV AH, 06h ; DOS: direct console I/OINT 21hJZ DONE1 ; ZF = 1: nothing waiting, and no waiting for itAND AL, 0DFh ; lower case to upper caseMOV DL, AL ; DL <> FFh: print itMOV AH, 06hINT 21hJMP NEXT1DONE1:KEYS1 DW 186Fh, 256Bh, 0 ; "o", "k": scan code and ASCIISTUFF1 PROC NEAR ; "type" the keys at DS:SI: words, 0 ends the listCLDST1:LODSW ; AH = scan code, AL = ASCIITEST AX, AXJZ ST2MOV CX, AX ; CH = scan code, CL = ASCIIMOV AH, 05h ; BIOS: store a keystroke in the keyboard bufferINT 16hJMP ST1ST2:RETSTUFF1 ENDP
The loop ends because the third read finds the buffer empty and returns ZF = 1 instead of waiting. That is the shape of a game's input handling: once per frame, take whatever keys have arrived and carry on. STUFF1 types the two keys first, each a word with the scan code in the high byte and the ASCII code in the low byte, through INT 16h AH=05h.
Pitfalls
Test ZF, not AL. AL = 0 is also the first half of a special key: F1 arrives as AL = 0 with ZF = 0, and the next call returns its scan code, 3Bh. The character FFh cannot be printed with this service, since DL = FFh means "read"; print it with INT 21h AH=02h. And since the output side skips the checks of 02h, MS-DOS does not stop a program for Ctrl+C while it prints this way.
Both halves still go through handles 0 and 1, so redirection applies: with input from a file, DL = FFh reads the next byte of the file. For a keyboard poll that ignores redirection, ask the BIOS: INT 16h AH=01h reports a waiting key without removing it. DOSBox-X adds a small delay to each read, imitating the time a real DOS spends in the call, because some games time themselves by polling this service.
| Service | Waits for a key | Echo | Ctrl+C |
|---|---|---|---|
INT 21h AH=01h | yes | yes | ^C, INT 23h: normally the end of the program |
INT 21h AH=06h (DL = FFh) | no: ZF = 1 when none is waiting | no | returned as 03h |
INT 21h AH=07h | yes | no | returned as 03h |
INT 21h AH=08h | yes | no | ^C, INT 23h |
INT 21h AH=0Ah | for a whole line, up to Enter | yes | ^C, INT 23h |