HLT stops the processor. It fetches and executes nothing until one of three things happens: a maskable interrupt while IF = 1, a non-maskable interrupt, or a reset. On an interrupt the CPU runs the handler, and the handler's IRET returns to the instruction after HLT. From the program's point of view HLT is "wait for the next interrupt".

On a PC the timer interrupts about 18.2 times a second and every key press interrupts too, so a HLT with interrupts on lasts at most about 55 ms. Since any interrupt wakes it, put it in a loop that checks for the thing you are waiting for. The example waits about one second by watching the BIOS tick counter at 0040:006Ch.

STI ; HLT needs interrupts on
PUSH DS
MOV AX, 40h
MOV DS, AX ; the BIOS data area
MOV BX, DS:[6Ch] ; timer ticks so far
TICK1:
HLT ; sleep until an interrupt
MOV AX, DS:[6Ch]
SUB AX, BX
CMP AX, 18
JB TICK1 ; about one second
POP DS
MOV DX, OFFSET MSG1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MSG1 DB 'One second later', 13, 10, '$'

Pitfalls

Never HLT after CLI: with IF = 0 only an NMI or a reset can wake the CPU, and the machine looks frozen. That pairing is used deliberately only to stop a system for good. The reverse order, STI then HLT, is safe thanks to the one-instruction delay after STI.

Compared with a busy loop, HLT lets the processor idle, and later CPUs use that time to save power. Later CPUs: in protected mode HLT is privileged (ring 0 only).