CLI clears the interrupt flag (IF = 0): the CPU stops answering requests on its INTR line, which on a PC is everything that comes through the interrupt controller: the timer, the keyboard, disks, serial ports. STI sets IF = 1 again. Requests that arrive meanwhile wait at the controller; nothing is lost unless you keep interrupts off so long that a second tick or key arrives.
IF does not block everything: the non-maskable interrupt (NMI), software INT n, INTO, and the divide error still happen.
Rules of thumb
Keep the window short: the clock stops counting and keys can be dropped. Instead of a bare STI afterwards, consider PUSHF / CLI ... POPF, which restores whatever IF was before, so the code also works when called with interrupts already off. Entering any interrupt handler clears IF, and IRET restores the old flags, so a handler runs with interrupts off unless it executes STI.
The one-instruction delay
After STI the CPU executes one more instruction before it accepts an interrupt. So STI / HLT cannot lose a wakeup between the two, and STI / RET returns before any handler runs. Loading a segment register with MOV or POP holds off interrupts the same way for one instruction, so MOV SS, AX / MOV SP, BX switches the stack safely. Some very early 8088 chips got this wrong, which is why old code wraps stack switches in CLI / STI anyway.
CLI ; IF = 0PUSHFPOP BX ; the flags, IF offSTI ; IF = 1MOV AX, BXMOV CL, 9SHR AX, CL ; IF is bit 9AND AL, 1 ; 0ADD AL, '0' ; digit -> characterMOV DL, ALMOV AH, 02h ; DOS: print DLINT 21hPUSHFPOP AXMOV CL, 9SHR AX, CLAND AL, 1 ; 1: on againADD AL, '0' ; digit -> characterMOV DL, ALMOV AH, 02h ; DOS: print DLINT 21h
Later CPUs: in protected and virtual-8086 mode CLI / STI are privileged, controlled by the I/O privilege level.