Hardware vs Software Interrupts
Distinguish the two interrupt pathways — software INT n and hardware INTR/NMI — and how the CPU handles each
Open interactive version (quiz + challenge)Real-world analogy
What is it?
Software interrupts (INT n) are deliberate calls from the program, always executed regardless of the IF flag. Hardware interrupts arrive from external devices via the INTR pin (maskable, controlled by IF) or NMI pin (non-maskable, always serviced). Both trigger the same CPU sequence: push FLAGS, clear IF/TF, push CS:IP, and jump through the IVT. The key differences are: who initiates them, when they can occur, and whether they can be disabled.
Real-world relevance
Your keyboard generates a hardware interrupt (via INTR and the 8259 PIC) every time you press a key. The system timer generates 18.2 interrupts per second for timekeeping. A power failure triggers NMI to give the system a last chance to save data. Meanwhile, every DOS function call your program makes is a software interrupt. Modern systems still use this same two-channel model, just with more sophisticated interrupt controllers (APIC instead of 8259).
Key points
- Software Interrupts (INT n) — Software interrupts are triggered explicitly by the INT n instruction in the program. The programmer chooses the type number. They are synchronous — they happen at a predictable point in the code. DOS (INT 21h) and BIOS (INT 10h, 13h, 16h) services are all software interrupts.
- Hardware Interrupts — INTR Pin — INTR (Interrupt Request) is a physical pin on the 8086 chip. External devices (keyboard, timer, disk) assert this pin to request attention. The CPU checks INTR between instruction executions. If the Interrupt Flag (IF) is set, the CPU acknowledges and services the interrupt.
- NMI — Non-Maskable Interrupt — NMI is a hardware interrupt that CANNOT be disabled. It always interrupts the CPU, regardless of the IF flag. NMI uses fixed type 2. It is reserved for critical events: memory parity errors, power failure detection, or watchdog timeouts.
- IF Flag — CLI and STI — The Interrupt Flag (IF) in the FLAGS register controls whether INTR is recognized. STI (Set Interrupt Flag) enables hardware interrupts (IF=1). CLI (Clear Interrupt Flag) disables them (IF=0). Software interrupts (INT n) and NMI are NOT affected by IF.
- The Interrupt Sequence (Detailed) — When any interrupt is accepted: (1) Complete current instruction, (2) Push FLAGS, (3) Clear IF and TF, (4) Push CS, (5) Push IP, (6) Load CS:IP from IVT. Clearing IF prevents nested hardware interrupts by default. The ISR can STI if it wants to allow nesting.
- INTA — Interrupt Acknowledge — When the CPU accepts an INTR request, it sends two INTA (Interrupt Acknowledge) pulses on the INTA pin. The first pulse tells the device to prepare. The second pulse tells it to put the interrupt type number on the data bus so the CPU knows which IVT entry to use.
- Interrupt Priority — When multiple interrupts occur simultaneously, priority determines which is serviced first. The built-in priority order (highest to lowest) is: (1) Internal interrupts (divide error, INT n), (2) NMI, (3) INTR, (4) Single-step (TF). An 8259 PIC chip manages priority among multiple INTR devices.
- Software vs Hardware — Key Differences — Software interrupts are programmer-initiated, synchronous, and always execute (not affected by IF). Hardware interrupts are device-initiated, asynchronous (can occur at any time between instructions), and INTR can be masked by IF=0. NMI is the exception — hardware but non-maskable.
- Nested Interrupts — Since INT automatically clears IF, hardware interrupts are disabled inside an ISR by default. If the ISR issues STI, higher-priority interrupts can nest (interrupt the ISR). NMI can always nest. The stack must be large enough to handle the worst-case nesting depth.
- Interrupt Latency — Interrupt latency is the time between an interrupt request and the first instruction of the ISR. It includes: finishing the current instruction, pushing FLAGS/CS/IP, and the IVT lookup. For the 8086, latency varies by the current instruction length — a long MUL or DIV increases latency.
Code example
; Program: Demonstrate software vs hardware interrupt concepts
; Install a custom handler, show IF flag behavior
.MODEL SMALL
.STACK 200h
.DATA
msg_sw DB 'Software INT 60h worked!', 0Dh, 0Ah, '$'
msg_if1 DB 'IF=1 (interrupts enabled)', 0Dh, 0Ah, '$'
msg_if0 DB 'IF=0 (interrupts disabled)', 0Dh, 0Ah, '$'
msg_done DB 'Demo complete.$'
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
; --- Check and display current IF state ---
PUSHF ; push FLAGS
POP AX ; AX = FLAGS
TEST AX, 0200h ; bit 9 = IF
JZ if_clear_1
MOV AH, 09h
LEA DX, msg_if1 ; IF is set (normal)
INT 21h
JMP install
if_clear_1:
MOV AH, 09h
LEA DX, msg_if0
INT 21h
install:
; --- Install custom INT 60h ---
CLI ; disable HW interrupts
PUSH ES
XOR AX, AX
MOV ES, AX
MOV WORD PTR ES:[180h], OFFSET my_handler
MOV WORD PTR ES:[182h], CS
POP ES
STI ; re-enable HW interrupts
; --- Demonstrate software interrupt ---
; Software INT works even if we CLI
CLI ; disable hardware interrupts
INT 60h ; SOFTWARE int still works!
STI ; re-enable
; --- Show final status ---
MOV AH, 09h
LEA DX, msg_done
INT 21h
MOV AH, 4Ch
INT 21h
MAIN ENDP
; Custom ISR for INT 60h
my_handler PROC FAR
PUSH AX
PUSH DX
PUSH DS
MOV AX, @DATA
MOV DS, AX
; Display that we were called
MOV AH, 09h
LEA DX, msg_sw
INT 21h
POP DS
POP DX
POP AX
IRET
my_handler ENDP
END MAINLine-by-line walkthrough
- 1. PUSHF / POP AX — copies FLAGS into AX so we can examine individual flag bits
- 2. TEST AX, 0200h — bit 9 of FLAGS is the IF (Interrupt Flag). TEST performs AND without storing the result, just setting ZF
- 3. JZ if_clear_1 — if the AND result is zero, IF was 0 (interrupts disabled). Otherwise IF is 1 (normal state)
- 4. CLI before IVT modification — disable hardware interrupts so no device can use a partially written vector
- 5. PUSH ES / XOR AX, AX / MOV ES, AX — save ES then point it to segment 0 for IVT access
- 6. MOV WORD PTR ES:[180h]... — write our handler's address. 60h * 4 = 180h for the offset, 182h for the segment
- 7. POP ES / STI — restore ES and re-enable hardware interrupts. The vector is now safely installed
- 8. CLI / INT 60h / STI — key demonstration: CLI disables hardware interrupts, but INT 60h (software) executes normally
- 9. In my_handler: MOV AX, @DATA / MOV DS, AX — ISR must set up its own DS because it could be called from any context
- 10. IRET — pops IP, CS, and FLAGS. This restores IF to its pre-interrupt state (whether it was 0 or 1)
Spot the bug
my_isr PROC FAR
PUSH AX
MOV AL, [counter]
INC AL
MOV [counter], AL
POP AX
RET
my_isr ENDPNeed a hint?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- Hardware and Software Interrupts (GeeksforGeeks)
- 8086 Interrupt System (Wikipedia)
- NMI vs INTR Explained (YouTube)
- Interrupt Handling in x86 (OSDev Wiki)