Besides memory, the 8086 has a separate I/O address space of 65536 byte ports, where devices such as the keyboard controller, the timer and the video card are connected. IN reads one of these ports into the accumulator: a byte into AL or a word into AX. A word read from port p takes the low byte from p and the high byte from p+1. No flags change.

Forms

The port number is either an immediate from 0 to FFh, written in the instruction, or the 16-bit value in DX, which reaches every port from 0 to FFFFh. The destination is always AL or AX: IN BL, 60h does not exist, and neither does IN AL, 3DAh (the number is too large for the immediate form; load it into DX).

Prints the last scan code in hex: a value of 80h or more is a key release, such as 9C for Enter.
IN AL, 60h ; the last byte the keyboard sent
MOV BL, AL
MOV CL, 4
SHR AL, CL ; high nibble
CALL HEX1
MOV AL, BL
AND AL, 0Fh ; low nibble
CALL HEX1
HEX1 PROC NEAR ; print AL (0..15) as a hex digit
ADD AL, '0'
CMP AL, '9'
JBE PUT1
ADD AL, 7 ; 'A'..'F'
PUT1:
MOV DL, AL
MOV AH, 02h
INT 21h
RET
HEX1 ENDP

Waiting on a status bit

A common pattern reads a status port in a loop until a bit changes. On a VGA card, bit 3 of port 3DAh is set during the vertical retrace, the moment to update the palette or the screen without flicker.

MOV DX, 3DAh ; VGA input status register 1
WAITR:
IN AL, DX
TEST AL, 08h ; bit 3: vertical retrace in progress?
JZ WAITR

Pitfalls

Reading a port can have side effects: some devices clear a status or advance an internal pointer when read, so read only what you mean to. A port is not memory; reading it twice can give two different answers. Under DOSBox the ports are emulated, but the common PC ones behave as on real hardware.

Later CPUs

The 80186 adds the string forms INSB and INSW (with REP, a block read into ES:DI). The 80386 reads 32 bits with IN EAX, DX. In protected mode the I/O privilege level, and on the 80386 the I/O permission bitmap, decide whether a program may use a port.