INT 16h AH=00h takes the oldest key out of the BIOS keyboard buffer and returns it: AL = the ASCII character, AH = the scan code of the key. If the buffer is empty it waits, with interrupts enabled, until the keyboard handler puts something there. There is no timeout and no echo, and Ctrl+C is just character 03h: nothing aborts the program.
Because both bytes come back, one call tells apart keys that DOS would need two calls for. A key with a character has AL ≠ 0; a key without one (function keys, arrows, Alt+letter) has AL = 0 and is identified by AH alone. The example stores "A" and F1 in the buffer, then reads them back:
MOV AH, 05h ; put "A" in the buffer: scan 1Eh, ASCII 41hMOV CX, 1E41hINT 16hMOV AH, 05h ; and F1: scan 3Bh, no ASCIIMOV CX, 3B00hINT 16hMOV AH, 00h ; BIOS: wait for a key (one is there)INT 16h ; AH = scan code, AL = ASCIICALL PHEX4 ; 1E41MOV DL, " "MOV AH, 02h ; DOS: print the character in DLINT 21hMOV AH, 00hINT 16h ; F1: AL = 0 marks a key without a characterCALL PHEX4 ; 3B00PHEX4 PROC NEAR ; print AX as four hex digits; keeps every registerPUSH AXMOV AL, AHCALL PHEX2 ; the high byte firstPOP AXCALL PHEX2 ; then the low byteRETPHEX4 ENDPPHEX2 PROC NEAR ; print AL as two hex digits; keeps every registerPUSH AXPUSH CXPUSH DXMOV DH, AL ; keep the byteMOV CL, 4SHR AL, CL ; the high nibbleCALL HEXN1MOV AL, DHAND AL, 0Fh ; the low nibbleCALL HEXN1POP DXPOP CXPOP AXRETHEXN1:ADD AL, "0"CMP AL, "9"JBE HEXN2ADD AL, 7 ; past "9": "A".."F"HEXN2:MOV DL, ALMOV AH, 02hINT 21hRETPHEX2 ENDP
Telling keys apart
The usual dispatch tests AL first. Comparing AH instead makes the program independent of Shift and Caps Lock, which is what games want for movement keys; comparing AL is what a text editor wants for letters.
MOV AH, 00hINT 16hTEST AL, AL ; a character?JZ SPECIAL1 ; no: look at the scan codeCMP AL, 1Bh ; EscJE QUIT1; ... AL is a character ...SPECIAL1:CMP AH, 48h ; UpJE UP1CMP AH, 3Bh ; F1JE HELP1
Pitfalls
The call blocks. A program that must keep running (a game loop, an animation) checks first with INT 16h AH=01h and only reads when a key is there. On a 101-key keyboard the grey arrows, F11 and F12 are filtered for compatibility: a grey arrow comes back as the keypad one (48E0h becomes 4800h) and F11/F12 are silently dropped while the call keeps waiting. Use INT 16h AH=10h to see them. DOS functions such as INT 21h AH=08h read the same buffer, but return an extended key in two steps: first 0, then the scan code on the next call.
In DOSBox-X the wait does not spin the emulated CPU in a tight loop; the BIOS idles until a key arrives, so a program sitting in AH=00h costs little host CPU.