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.

prints: OK
MOV SI, OFFSET KEYS1
CALL STUFF1 ; "o" and "k" wait in the BIOS buffer
NEXT1:
MOV DL, 0FFh ; DL = FFh: read, if a key is there
MOV AH, 06h ; DOS: direct console I/O
INT 21h
JZ DONE1 ; ZF = 1: nothing waiting, and no waiting for it
AND AL, 0DFh ; lower case to upper case
MOV DL, AL ; DL <> FFh: print it
MOV AH, 06h
INT 21h
JMP NEXT1
DONE1:
KEYS1 DW 186Fh, 256Bh, 0 ; "o", "k": scan code and ASCII
STUFF1 PROC NEAR ; "type" the keys at DS:SI: words, 0 ends the list
CLD
ST1:
LODSW ; AH = scan code, AL = ASCII
TEST AX, AX
JZ ST2
MOV CX, AX ; CH = scan code, CL = ASCII
MOV AH, 05h ; BIOS: store a keystroke in the keyboard buffer
INT 16h
JMP ST1
ST2:
RET
STUFF1 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.

ServiceWaits for a keyEchoCtrl+C
INT 21h AH=01hyesyes^C, INT 23h: normally the end of the program
INT 21h AH=06h (DL = FFh)no: ZF = 1 when none is waitingnoreturned as 03h
INT 21h AH=07hyesnoreturned as 03h
INT 21h AH=08hyesno^C, INT 23h
INT 21h AH=0Ahfor a whole line, up to Enteryes^C, INT 23h