XLAT replaces AL with a byte from a table. BX holds the offset of the table and AL is the index, taken as unsigned (0 to 255). The CPU reads the byte at DS:BX+AL and stores it in AL. The whole instruction is one byte long, which made it the compact way to translate codes. No flags change, and BX keeps its value, so a loop can translate byte after byte without reloading it.

Prints C5.
MOV BX, OFFSET HEXD ; the table
MOV AL, 0C5h
MOV CH, AL ; keep a copy
MOV CL, 4
SHR AL, CL ; high nibble: 0Ch
XLATB ; AL = 'C'
MOV DL, AL
MOV AH, 02h
INT 21h
MOV AL, CH
AND AL, 0Fh ; low nibble: 05h
XLATB ; AL = '5'
MOV DL, AL
MOV AH, 02h
INT 21h
HEXD DB '0123456789ABCDEF'

Spellings

XLATB takes no operand. MASM also accepts XLAT table: the operand does not change the address, which still comes from BX, but it documents the table and selects the segment. Written as XLAT ES:table, the read uses ES instead of DS, and MASM emits the override prefix.

Uses

Turning a nibble into a hex digit, a scan code into a character, one character set into another, or a value into a bit pattern for a display. Any mapping from 256 or fewer inputs to bytes fits: build the table once with DB and translate with one instruction.

Pitfalls

BX must hold the start of the table, not the index; forgetting MOV BX, OFFSET table reads from wherever BX happens to point. AH plays no part: only AL is the index, so a table cannot be longer than 256 bytes, and an index above the end of a shorter table reads whatever follows it. There is no [BX+AL] addressing mode for other instructions; XLAT is the only one that adds AL to BX.

Later CPUs

Nothing changed in 16-bit code. With 32-bit addressing on the 80386 the table address comes from EBX.